Trying to send an email in yesod using hamlet - yesod

I am building a survey site in Yesod (0.10) and am getting lost in the types.
Here is a simplified version on what I am trying to do.
invitation url = do
render <- getUrlRender
return $ renderHtml [hamlet|
<p>Dear foo, please take our
<a href=#{ShowSurveyR url}>survey.
|] render
Another function is going to call this in hopes of getting something that can be passed to simpleMail from Network.Mail.Mime. The above function gives a type error:
Handler/Root.hs:404:13:
The function `renderHtml' is applied to two arguments,
but its type `Html -> LT.Text' has only one
This is confusing, because the templates tutorial seems to do things this way.
But if I modify the code, like this...
invitation url = do
return $ renderHtml [hamlet|
<p>Dear foo, please take our
<a href=#{ShowSurveyR url}>survey.
|]
I get this type error.
Handler/Root.hs:403:24:
The lambda expression `\ _render[a4TO] -> ...' has one argument,
but its type `Html' has none
In the first argument of `renderHtml', namely
I think that renderHtml is the wrong function to use, but I can't find what the right function is. Does anyone know what I am missing? How am I supposed to pass the routing function into my hamlet code?

The quasiquote ([hamlet|...|]) returns a function whose argument is also a function. You must first apply that quasiquote value, and pass the results to renderHtml:
[Edit: as #MattoxBeckman discovered, another issue is the need to use getUrlRenderParams instead of gutUrlRender.]
invitation url = do
render <- getUrlRenderParams
return $ renderHtml $ [hamlet|
<p>Dear foo, please take our
<a href=#{ShowSurveyR url}>survey.
|] render
(Note the additional $).
P.S. renderHtml is of type Html -> Text, while the result of the quasiquote, [hamlet|..|], is of type Render url -> Html. The first error message you saw notes that you tried to pass two arguments to renderHtml, while the second error message notes that you didn't pass any arguments to the quasiquote value.

To make this easier for the next person who goes searching for it...
There were two problems. One was pointed out by the first answer; the hamlet quasiquoter itself takes a function. The other problem was that I needed to use the function getUrlRenderParams, not getUrlRender.
The final code is
invitation :: Text -> Handler LT.Text
invitation url = do
render <- getUrlRenderParams
return $ renderHtml $ [hamlet|
<p>Dear foo, please take our
<a href=#{ShowSurveyR url}>survey.
|] render

Just replace shamlet instead of hamlet; it doesn't need a render argument at all.
(As was pointed to me by joelteon at #yesod.)

Related

What is best practice for passing variables via GET?

I am passing a variable in my URL:
mydomain.com/app/?next_page=my_page
I can get this variable in a view with:
def mypage(request):
var = request.GET['next_page']
Is it best practice to also change the URL to require next_page? Something along the lines of:
path('app/?nextpage=<str>', mypage, name='my_page')
What's best practice? If so, what's the correct syntax for this (I know the example is incorrect)?
It depends on your needs.
Do not define a fixed url route; if you use the query parameters for filtering and there is more than one possible parameter
Example: "app/photos?size=100x100" and "app/photos/?color=blue"
Define a fixed url route; if it will be the same for each and every page, like details of a particular page:
Example: "app/orders/123123123" and "app/orders/123123123"
Btw, the correct syntax is:
path(app/<str:next_page>/, mypage, name="my_page")
You should take a look at path patterns. Enforcing a GET parameter in a path is not really a good practice. So if you want to require a username for example you can use:
path('bio/<username>/', views.bio, name='bio'),
You can find more patterns in Django documentation to catch strings, slugs, integers etc.
And in views you should define your function as such:
def mypage(request, username):
...code...
About GET:
Keep in mind that request.GET["value"] will raise a ValueError if that parameter does not exist. So you can catch that error to inform user that they are missing a parameter. (This will make this parameter obligatory.)
You can also use request.GET.get("value") which will return None if the key does not exist. If you want to use a default parameter you can use of course, request.GET.get("value", "default")
You can use as many parameters as you want in your link with or without path patterns. Their values will be stored in request.GET

Ruby convert Url parameters to array

I have this url encoded:
Started PUT "/path/thing/9812/close?status=close&shutdown_on=2018-12-05%2010%3A08%3A06&affected_external_id=15027&fqdns%5B0%5D=150.212.3.249"
which decoded is this:
"/path/thing/9812/close?status=close&shutdown_on=2018-12-05 10:08:06&affected_external_id=15027&fqdns[0]=150.212.3.249"
I get this parameters:
Parameters: {"status"=>"close", "shutdown_on"=>"2018-12-05 10:08:06", "affected_external_id"=>"15027", "fqdns"=>{"0"=>"150.212.3.249"}, "id"=>"9812"}
How can get fqdn as a array? on Rails 4
You should do the following:
params[:fqdns].to_a
Doing this, will produce the following:
{['0', '150.212.3.249', ...]}
If you wnat only the values, may you can try:
params[:fqdns].values
Doing this, will give you the following:
['150.212.3.249', ...]
But for this, you have to do it inside a ruby class, i strongly recommends you to do it inside your controller. Hope i can help.
UPDATE
After a recommends, you can do it with strong parameters, permiting the param fqdns as a hash (because you route receiving a hash):
def resource_params
params.permit(....., fqdns: {})
end
After this, you already have to execute the solutions above to get fqsnd as a array

Is there a way to check if part of the URL contains a certain string

Is there a way to check if part of the URL contains a certain string:
Eg. <% if current_spree_page?("/products/*") %>, where * could be anything?
I tested, and gmacdougall's answer works, I had already found a solution though.
This is what I used to render different layouts depending on what the url is:
url = request.path_info
if url.include?('products')
render :layout => 'product_layout'
else
render :layout => 'layout'
end
The important thing to note is that different pages will call different methods within the controller (eg. show, index). What I did was put this code in its own method and then I am calling that method where needed.
If you are at a place where you have access to the ActionDispatch::Request you can do the following:
request.path.start_with?('/products')
You can use include? method
my_string = "abcdefg"
if my_string.include? "cde"
puts "String includes 'cde'"
end`
Remember that include? is case sensetive. So if my_string in the example above would be something like "abcDefg" (with an uppercase D), include?("cde") would return false. You may want to do a downcase() before calling include?()
The other answers give absolutely the cleanest ways of checking your URL. I want to share a way of doing this using a regular expression so you can check your URL for a string at a particular location in the URL.
This method is useful when you have locales as first part of your URL like /en/users.
module MenuHelper
def is_in_begin_path(*url_parts)
url_parts.each do |url_part|
return true if request.path.match(/^\/\w{2}\/#{url_part}/).present?
end
false
end
end
This helper method picks out the part after the second slash if the first part contains 2 word characters as is the case if you use locales. Drop this in your ApplicationController to have it available anywhere.
Example:
is_in_begin_path('users', 'profile')
That matches /en/users/4, /en/profile, /nl/users/9/statistics, /nl/profile etc.

django template throws NoReverseMatch error

I had two methods create and update in the views, in which update takes one argument whereas create does not take any. I have decided to turn them into only one function update_create because they were not that different.
This is how the new method in views looks:
def update_create(request, id=None):
This is my urls.py:
url(r'^(\d+)/update/$|create/$', update_create, name='update_create'),
This is how my template looks in templates/list.html
Create a new event
I got this error when using the above code:
NoReverseMatch at /agenda/list/
Reverse for 'update_create' with arguments '()' and keyword arguments '{}' not found.
But, if I use this in my templates instead (I have added an argument), it works without any errors:
Create a new event
Can someone explain what's happening? Why previous code didn't work, and Why the new code is working?
URL pattern (\d+) expects number to be provided as argument. To resolve the issue simply provide urls like this:
url(r'^(\d+)/update/$', update_create, name='update_create'),
url(r'^update/$', update_create, name='update_create'),
As mariodev pointed out, your url pattern was expecting a digit in front of the url. As such, your first url:
Create a new event
would generate a url like /update, which wasn't a valid url. However, the latter url:
Create a new event
would generate a url like /1/update, which was a valid url.
From the django docs: https://docs.djangoproject.com/en/dev/topics/http/urls/
Basically subsequent arguments get parsed on first come first serve, and passed to your view. Another thing to consider when developing is using explicitly named parameters, as the django docs elaborate on.

Django URL match in HttpResponse object?

In django, when a URL is matched, the match group is passed as a second parameter to the view function, the first being a HttpRequest object. For example, with a URL patter like this
'/foo/(\d{2})/', 'app.views.handler'
the handler routine will have
def handler(request, value):
where value will contain a two digit number (as a string).
My question is: is value also contained in the request object, and if yes, how can I get it (of course, parsing the URL from the request object is not an option, too impractical).
Thanks
I'm not going to debate the merit of your idea. Just try to answer your question:
No there is no way, other than applying the regex to the URL again, to get at the url parameter.
Your view will be the first point where the parameter list will be available. Why don't you just create a wrapper object to encapsulate your request and your parameter list at that point?
Just pass that around...
Can you give any reason why you would need this?
I don't see why parsing the url path is 'impractical', given that you've already got a regexp that works, in your urlconf.