I'm trying to write a generic route that will allow me to refer to it by the controller's action.
I'm using the following line:
match ':action' => 'pages#:action', :as => 'action'
let's say an action named `foobar' in the pages controller. I'd like to be able to write
link_to 'Click Me', pages_foobar_path
in a view. The problem is that I get the error Invalid route name: ':action' when I try to write that route.
Mind you, the line
match ':action' => 'pages#:action'
without the :as parameter works perfectly, but then I have to manually write the path, as such:
link_to 'Click Me', '/pages/foobar'
any way around that?
If dynamic means "recognize my actions when Rails starts up and generate routes dynamically":
It's not something that I would do, but it does what you want it to do without any redirection nor method_missing runtime overhead.
In config/routes.rb
controller_filenames = Dir.new("#{Rails.root}/app/controllers").entries
controller_filenames.each do |filename|
# you could restrict to only the pages_controller.rb on the next line,
# and in that case, you could simplify even more (see next example)...
if filename =~ /_controller\.rb$/
controller_name = filename.sub(/.rb$/, "")
controller_route_name = controller_name.sub(/_controller$/, "")
controller = controller_name.camelize.constantize.new
controller.action_methods.each do |action|
# if you don't want the controller name in your path match, just omit it...
match "#{controller_route_name}/#{action}" => "#{controller_route_name}##{action}", :as => "#{controller_route_name}_#{action}"
end
end
end
If you only want to do this for your pages_controller.rb file, then:
controller_name = "pages_controller"
controller_route_name = "pages"
controller = controller_name.camelize.constantize.new
controller.action_methods.each do |action|
# I've removed the controller_route_name from the match here...
match "#{action}" => "#{controller_route_name}##{action}", :as => "#{controller_route_name}_#{action}"
end
Now, if dynamic means "generate a route whenever I dynamically generate a new action":
You could really play with fire. Any of your existing actions can define new actions and routes. For example, I could define a route in config/routes.rb (but this could be any existing route):
match '/dynamic_define' => 'application#dynamic_define'
Couple that with a method in ApplicationController (again, this could be any existing action):
def dynamic_define
method_name = params[:mname]
self.class.send(:define_method, method_name) {
render :text => "output from #{method_name}"
}
Rails.application.routes.disable_clear_and_finalize = true
Rails.application.routes.draw do
match "/#{method_name}" => "application##{method_name}", :as => "application_#{method_name}"
end
render :text => "dynamic_define just created a new action named #{method_name}"
end
In your browser, you can visit:
/dynamic_define?mname=my_new_dynamic_action
# browser renders "dynamic_define just created a new action named my_new_dynamic_action"
And then visit:
/my_new_dynamic_action
# browser renders "output from my_new_dynamic_action"
I think you can get as far as:
link_to 'Click me', pages_path(:action)
by redirecting
match ':action' => 'pages#:action'
match '/pages/:action' => redirect(":action") # pages_path(:action) will match
This is less typing than the approach suggested in the first answer, but seems less expressive if anything.
I suppose you could override method_missing in your view class to catch pages_[stuff]_path and generate the proper string, e.g.
def method_missing(name, *args, &block)
if name =~ /^pages_[a-z]*_path$/
"/#{name.to_s.gsub!(/^pages_/,'').gsub!(/_path$/,'')}"
else
super
end
end
Forgive me if my method_missing knowledge or regex capabilities are lacking - hopefully this is helpful directionally at least.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With