Dynamically add route to beginning of rails routes - ruby-on-rails

For a testing helper I am working on I need to inject a route into the beginning of the routes. To add it at the end is easy:
test_routes = Proc.new do
get "/#{route_root}/:id", to: "#{route_root}#test"
end
Rails.application.routes.eval_block(test_routes)
The problem is that often there are "catch all" routes at the end of the application routes list, so I need to inject this as the FIRST route.
I have been using the technique here: How to dynamically add routes in Rails 3.2
but its hacky, and after a gem update last night it broke (not sure why yet, but its breaking inside of the routes.clear!) so I am looking for a less hacky solution.

Why not just create your own module like this:
module DynamicRouter
def self.routes router
router.get "/#{route_root}/:id", to: "#{route_root}#test"
end
end
and in your routes.rb just call your function:
# in routes.rb
# first line
DynamicRouter::routes(self)
That's how we do with some of our custom gems. You can also monkey patch the router, but it's more hacky.

Related

Auto-generate routes for scaffolded controller in Rails 4?

I'm trying to get a quick-and-dirty Ajax UI going for an app that already has its data model well in hand - it's basically been managed via rails console so far. Anyway, I thought I would start by auto-generating the missing controller logic that you would get from a rails g scaffold, only instead with rails g scaffold_controller for an existing controller.
It created the controller, and the views, and the assets.. but it didn't touch the routes at all! It didn't even try, didn't say "warning: routes.rb has been modified, not changing" or anything like that, and there's no mention of routes at all in the help output of rails g scaffold_controller.
So how do I say "Just give me the normal routes you would have given me if I started from scratch, please!"?
If I understand the question:
Please, open the config/routes.rb file, and inside the block (routes.draw) add the resources method with the table name (plural of model) as param. Like this:
MyApp::Application.routes.draw do
resources :products
... # rest of code
end
That define the routes for RESTful actions over products. You can read more here
At the console you can run: rake routes to see the available routes at your app.
Although this is asking about Rails 4 for long time ago, but with Rails 5, rails g scaffold_controller still won't auto-generate route, I did it with below monkey patch:
require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
patcher = Module.new do
extend ActiveSupport::Concern
included do
hook_for :resource_route, required: true
end
end
Rails::Generators::ScaffoldControllerGenerator.send :include, patcher

Rails 4.1.2 - to_param escapes slashes (and breaks app)

I use in my app to_param to create custom URL (this custom path contains slashes):
class Machine < ActiveRecord::Base
def to_param
MachinePrettyPath.show_path(self, cut_model_text: true)
end
end
The thing is, that since Rails 4.1.2 behaviour changed and Rails doesn't allow to use slashes in the URL (when use custom URL), so it escapes slashes.
I had such routes:
Rails.application.routes.draw do
scope "(:locale)", locale: /#{I18n.available_locales.join("|")}/ do
resources :machines, except: :destroy do
collection do
get :search
get 'search/:ad_type(/:machine_type(/:machine_subtype(/:brand)))', action: 'search', as: :pretty_search
get ':subcategory/:brand(/:model)/:id', action: 'show', as: :pretty
patch ':subcategory/:brand(/:model)/:id', action: 'update' # To be able to update machines with new rich paths.
end
end
end
end
I tried by recommendation in the thread to use glob param just for show method to make sure it works:
resources :machines, except: :destroy do
#...
end
scope format: false do
get '/machines/*id', to: "machines#show"
end
But it absolutely doesn't work. I still have such broken links:
http://localhost:3000/machines/tractor%2Fminitractor%2Fmodel1%2F405
Of course, if I replace escaped slashes on myself:
http://localhost:3000/machines/tractor/minitractor/model1/405
And try to visit path, then page'll be opened.
Any ideas how can I fix that?
I've been having the same problem when using the auto-generated url helpers. I used a debugger to trace the new behavior to its source (somewhere around ActionDispatch::Journey::Visitors::Formatter), but didn't find any promising solutions. It looks like the parameterized model is now strictly treated as a single slash-delimited segment of the path and escaped accordingly, with no options to tell the formatter otherwise.
As far as I can tell, the only way to get the url helper to produce the old result is to use your original routes file and pass each segment separately, something like:
pretty_machine_path(machine.subcategory, machine.brand, machine.model, machine.id)
This is ugly as hell and obviously not something you'll want to do over and over. You could add a method to MachinePrettyPath to generate the segments as an array and explode the result for the helper (say, pretty_machine_path(*MachinePrettyPath.show_path_segments(machine))) but that's still pretty verbose.
Between the above headaches and the "You're Doing it Wrong" attitude from the devs in that Rails ticket you linked to, the simplest option for me was to bite the bullet and write a custom URL helper instead of using to_param. I've yet to find a good example of the "right" way to do that, but something like this bare-bones example should serve the purpose:
#app/helpers/urls_helper.rb
module UrlsHelper
def machine_path(machine, options = {})
pretty_machine_path(*MachinePrettyPath.show_path_segments(machine), options)
end
end
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
helper :urls #for the views
include UrlsHelper #for controllers
#...
end
If you're sure the returned url is safe you should add .html_safe to the returned string:
MachinePrettyPath.show_path(self, cut_model_text: true).html_safe
(didn't see anywhere else where it can be escaped but check all the flow in your app, maybe manually testing method by by method)
How about defining the url yourself?
def to_param
"#{ subcategory.title.parameterize }/#{ brand.name.parameterize }/#{ model.name.parameterize) }/#{ id }"
end
And then in your routes something like this:
get 'machines/*id', to: "machines#show"
You also have to split your params[:id] when you do a find on your model.
Machine.find( params[:id].split("/").last )

What will be my route?

I'm working on Rails 3.
My URL is: http://localhost:3000/terms_and_conditions?val=pp
My method is below:
class GeneralController < ApplicationController
def terms_and_conditions
if !params[:val].nil?
#val=params[:val]
else
#val='tc'
end
end
end
What will be my route? Please help me to create the route.
I suggest you first read the guides titled Rails Routing from the Outside In.
To setup a simple GET accessible route add the following to your routes.rb file
get "/terms_and_conditions" => "general#terms_and_conditions"
If you need more than just GET, you can use match instead. In your app root you can perform rake routes to see all the routes of your app as well. With regards to your choice of exposing /terms_and_conditions — it would be better if you used a shorter path such as /terms and/or consider doing /terms-and-conditions instead.
Try:
[YourAppNameHere]::Application.routes.draw do
match '/terms_and_conditions', to: 'general#terms_and_conditions'
end

Remove Routes Specified in a Gem?

Is there a way to remove routes specified in a gem in Rails 3? The exception logger gem specifies routes which I don't want. I need to specify constraints on the routes like so:
scope :constraints => {:subdomain => 'secure', :protocol => 'https'} do
collection do
post :query
post :destroy_all
get :feed
end
end
Based on the Rails Engine docs, I thought I could create a monkey patch and add a routes file with no routes specified to the paths["config/routes"].paths Array but the file doesn't get added to ExceptionLogger::Engine.paths["config/routes"].paths
File: config/initializers/exception_logger_hacks.rb
ExceptionLogger::Engine.paths["config/routes"].paths.unshift(File.expand_path(File.join(File.dirname(__FILE__), "exception_logger_routes.rb")))
Am I way off base here? Maybe there is a better way of doing this?
It is possible to prevent Rails from loading the routes of a specific gem, this way none of the gem routes are added, so you will have to add the ones you want manually:
Add an initializer in application.rb like this:
class Application < Rails::Application
...
initializer "myinitializer", :after => "add_routing_paths" do |app|
app.routes_reloader.paths.delete_if{ |path| path.include?("NAME_OF_GEM_GOES_HERE") }
end
Here's one way that's worked for me.
It doesn't "remove" routes but lets you take control of where they match. You probably want every route requested to match something, even if it is a catch all 404 at the bottom.
Your application routes (MyApp/config/routes.rb) will be loaded first (unless you've modified the default load process). And routes matched first will take precedence.
So you could redefine the routes you want to block explicitely, or block them with a catch all route at the bottom of YourApp/config/routes.rb file.
Named routes, unfortunately, seem to follow ruby's "last definition wins" rule. So if the routes are named and your app or the engine uses those names, you need to define the routes both first (so yours match first), and last (so named routes point as you intended, not as the engine defines.)
To redefine the engine's routes after the engine adds them, create a file called something like
# config/named_routes_overrides.rb
Rails.application.routes.draw do
# put your named routes here, which you also included in config/routes.rb
end
# config/application.rb
class Application < Rails::Application
# ...
initializer 'add named route overrides' do |app|
app.routes_reloader.paths << File.expand_path('../named_routes_overrides.rb',__FILE__)
# this seems to cause these extra routes to be loaded last, so they will define named routes last.
end
end
You can test this routing sandwich in the console:
> Rails.application.routes.url_helpers.my_named_route_path
=> # before your fix, this will be the engine's named route, since it was defined last.
> Rails.application.routes.recognize_path("/route/you/want/to/stop/gem/from/controlling")
=> # before your fix, this will route to the controller and method you defined, rather than what the engine defined, because your route comes first.
After your fix, these calls should match each other.
(I posted this originally on the refinery gem google group here: https://groups.google.com/forum/?fromgroups#!topic/refinery-cms/N5F-Insm9co)

Generating nested routes in a custom generator

I'm building a generator in rails that generates a frontend and admin controller then adds the routes to the routes file. I can get the frontend working with this:
m.route_resources controller_file_name
but I can't figure out how to do the same for the nested admin route (admin/controller_file_name). Anyone know how to generate these routes?
Looking at the code for route_resources, it doesn't look like it will do anything beyond a bog-standard map.resources :foos.
Instead, let's write our own method to deal with this issue, based on the original
def route_namespaced_resources(namespace, *resources)
resource_list = resources.map { |r| r.to_sym.inspect }.join(', ')
sentinel = 'ActionController::Routing::Routes.draw do |map|'
logger.route "#{namespace}.resources #{resource_list}"
unless options[:pretend]
gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
"#{match}\n map.namespace(:#{namespace}) do |#{namespace}|\n #{namespace}.resources #{resource_list}\n end\n"
end
end
end
We can start this off as a local method in your generator, which you can now call with:
m.route_namespaced_resources :admin, controller_file_name

Resources