Hello all. This is a rails 3 oriented question.
I’m creating a simple, application-wide view-helper method in my
app/helpers/application_helper.rb that will be called in the context of
several different controller/action views.
module ApplicationHelper
def my_custom_link_to
link_to “Here Silly”
end
end
As you can see, by NOT providing any link-location (url_for) options, my
little helper will default to simply creating links to the current page
(uses the current controller and action to generate the link). This is
exactly what I want…
Except (I’m sure you saw this coming): I want the generated link to
always
be identical (except for query string parameters) to the current URL as
shown in the browser’s window. Normally, this is already the case for
default resource-routed links. However, I’ve got some resources
available
through several (aliased?) routes. Example (config/routes.rb):
…
resources :entities
resources :rolodex, :controller => :entities
…
When I’m using my helper in my app/views/entities/index.html.erb, the
path
in the generated link is always /entities, regardless of whether the
page
was accessed through the /rolodex or /entities route. I’d like the path
to
be /rolodex when the page is requested using this path and /entities
when
/entities is requested. And just FYI, changing the fact that my “entity”
resource is available via these multiple routes isn’t an option (and
this
isn’t the only resource we have with such aliasing).
The technique that (seems) to work the way I want it, it to change my
method
above to use request.fullpath:
def my_custom_link_to
link_to “Here Silly”, request.fullpath
end
Though this works, I’m wondering if there is a more idiomatic way to
accomplish this. So, is there?
Thanks!
Just a follow-up to my own post, another reason I’m looking for an
alternative method here (besides being more idiomatic) is to support a
version that can add query-string parameters (and a fragment identifier)
to
the generated url:
Example Version 2:
module ApplicationHelper
def my_custom_link_to(my_extra_params = {})
# messy having to do this by hand…
fragment = my_extra_params.delete(:fragment)
utils = Rack::Utils # shortcut variable
query = my_extra_params.map { |k, v|
“#{utils.escape(k.to_s)}=#{utils.escape(v.to_s)}” }.join("&")
path = request.fullpath
path += “?#{query}” unless query.blank?
path += “##{fragment}” unless fragment.blank?
link_to “Here Silly”, path
end
end
Again, this works but look how friggin messy it is. Is there some kind
of
cool variation of #link_to I don’t know about that accepts a symbol
:self
instead of an AR model, hash, or string? Just curious.
Okay, since I’ve got no response and since the traffic here is high
enough
that this topic will be effectively “buried” soon, I’ll follow up with
my
final solution.
I decided what would be nice is a universal #link_to_self helper method.
The
idea is that it otherwise works just like the existing #link_to family
of
helpers (and in fact is coded based on their implementations). The
difference is that you simply don’t provided the “where” aspect, since
you’re linking to the same page. The improvement over simply using
#link_tois, as I described above, that the existing path will be re-used
instead of
the default route in cases where multiple routes alias the same
controller/action pair.
My final implementation in my ApplicationHelper class (
app/helpers/application_helper.rb):
This will create a link to the current document with the provided
options
being added to the URL as query string parameters. You may either
provide
the link text (the body) as the first parameter, or instead provide
a
block
that returns the content of the link. You may also provide a
standard
html_options hash.
In addition to adding any provided options as parameters to the
query
string
this will also add all query string parameters from the current
request
back
into the query string as well. To override an existing query string
parameter, simply provide an option of the same name (string or
symbol
form)
with the new value. If the new value is nil, the existing parameter
will
be
omitted. All non-string values will be converted using #to_s.
Finally, you may provide an option with the key :fragment to specify
a
fragment identifier you’d like to be appended onto the end of the
URL.
You
must use the symbol :fragment, not the string “fragment” for the
key,
otherwise the value will be added to the query string:
“?fragment=value”
== Signatures
link_to_self(body, options = {}, html_options = {})
# body is the “name” (contents) of the link
link_to_self(options = {}, html_options = {}) do
# link contents defined here
end
def link_to_self(*args, &block)
if block_given?
options = args.first || {}
html_options = args.second
link_to_self(capture(&block), options, html_options)
else
name = args[0]
options = args[1] ? args[1].dup : {}
html_options = args[2]
fragment = options.delete(:fragment)
query_string =
options.with_indifferent_access.reverse_merge(request.query_parameters).map
{ |k, v|
v.nil? ? nil :
“#{Rack::Utils.escape(k.to_s)}=#{Rack::Utils.escape(v.to_s)}”
}.compact.join("&")
path = request.path
path += “?#{query_string}” unless query_string.blank?
path += “##{fragment}” unless fragment.blank?
result = link_to(name, path, html_options)
result
end
end
This is just for posterity and in case anyone else ever has a similar
need.
I personally would love it if this functionality was in rails itself
(modification of #link_to, taking the :self symbol or simply acting this
way
in the absence of a url string, a resource record, or a #url_for options
hash).
Seems to work great for what I need. Let me know anyone if rails already
does this somewhere (I doubt it now that I’ve done significant digging).
I’d
also like recommendations on improvements that can be made within my
definition above for it to be more idiomatic (not rely on request.path,
request.query_parameters or use Rack::Utils.escape for example).