Meltemi
Meltemi

Reputation: 38349

Rails: make custom URL helper behave like built in _path & _url helpers

I'm having a hard time figuring out the "Rails Way" to add a email confirmation URL to a mailer.

I'm opting not to do this purely RESTfully because, well, it's difficult with text email because they can't PUT requests.

so here's my routes.rb: get 'confirm/:id' => 'Confirmations#confirm'

and in my mailer I'd like to put email_confirm_url(@user.email_token) where I want the URL to occur.

I created a helper:

#app/helpers/confirmations_helper.rb
module ConfirmationsHelper
  def email_confirm_url(token)
    "/confirm/#{token}"
  end  
end

this all works, sort of, except when I call email_confirm_url(@user.email_token)

I literally get: "/confirm/abcdefg…"

When what I want is: http://myhostname/confirm/abcdefg…

Or in development: http://localhost:3000/confirm/abcdefg…

How can I make my URL helper behave more like the built in <resource>_path and <resource>_url helpers in Rails? though realistically I suppose I really only need _url.

#Edit: I have this in my environment config:

#config/environments/development.rb
...
config.action_mailer.default_url_options = { :host => "localhost:3000" }

Upvotes: 5

Views: 3916

Answers (3)

James EJ
James EJ

Reputation: 2134

I recently wrote a helper to convert my _path method into a _url method.

Rails uses ActionDispatch::Http::URL.full_url_for to produce the _url methods, and passes in Rails.application.routes.default_url_options to set the host, port, and protocol.

This means you can generate a URL from a given path with

ActionDispatch::Http::URL.full_url_for(Rails.application.routes.default_url_options.merge(path: path))

My work in progress helper looks like:

def self.url_helper(route_name)
  define_method("#{route_name}_url") do |*args|
    path = public_send(:"#{route_name}_path", *args)
    options = Rails.application.routes.default_url_options.merge(path: path)
    ActionDispatch::Http::URL.full_url_for(options)
  end
end

This could then be used in combination with your path helper to build an email_confirm_url method:

url_helper :email_confirm
def email_confirm_path(token)
  "/confirm/#{token}"
end 

Upvotes: 1

Francesco Belladonna
Francesco Belladonna

Reputation: 11689

What about using an already existing url plus concatenation? I.e. you can use root_path and root_url, then you concatenate and the behavior is exactly the same as rails!

For example you can do this:

def mystrangemethod_url(option1, option2)
  "#{ root_url }/#{ option1 }/#{ option2 }"
end

And you are done. Easy and the only requirement is set set your root path in routes.rb. Also with the option you have set in development.rb, it will work also in mailers too.

Upvotes: 0

zarazan
zarazan

Reputation: 1174

In order to access the request object you should implement this function in the controller for your mailer and pass it to the template with a variable.

app/mailers/emailer.rb

@tracking_url = "http://#{request.host}:#{request.port}/confirm/#{token}"

app/view/emailer/template_name.html.erb

<%= link_to 'name', @tracking_url %>

Upvotes: 0

Related Questions