Remove Routes Specified in a Gem? - ruby-on-rails

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)

Related

Change default url from Active Storage

Can we change the default 'permanent' url create from active storage to redirect to S3. Is something like rails/active_storage/representations/. I don't like the framework name in the url.
Thanks
UPDATE:
Recently, there was an addition which makes the route prefix configurable in Rails 6: https://guides.rubyonrails.org/6_0_release_notes.html#active-storage-notable-changes
It's just a matter of configuration:
Rails.application.configure do
config.active_storage.routes_prefix = '/whereever'
end
Unfortunately, the url is defined in ActiveStorage routes.rb without easy means to change:
get "/rails/active_storage/blobs/:signed_id/*filename" =>
"active_storage/blobs#show", as: :rails_service_blob
get "/rails/active_storage/representations/:signed_blob_id/:variation_key/*filename" =>
"active_storage/representations#show", as: :rails_blob_representation
One solution starting point I can think of is defining your own Routes in addition and overriding the "rails_blob_representation_path" or similar
get "/my_uploads/:signed_blob_id/:variation_key/*filename" =>
"active_storage/representations#show", as: :my_upload
and then overriding the path in a helper file and include the helper into the Named Helpers:
How to override routes path helper in rails?
module CustomUrlHelper
def rails_blob_representation(*args)
my_upload(*args)
end
end
# initializer etc.
Rails.application.routes.named_routes.url_helpers_module.send(:include, CustomUrlHelper)
The solution might need some adjustments though, I didn't tested it.

Dynamically add route to beginning of rails routes

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.

Rails routes, constraints and variable scope

I have the following wildcard routes & constraints setup ...
get '*path' => 'profiles#show', constraints: SlugConstraint.new
get '*path' => 'blogs#show', constraints: SlugConstraint.new
and
class SlugConstraint
def initialize
#slugs = Slug.all.map(&:name)
end
def matches?(request)
request.url =~ /\/(.+)/
#slugs.include?($1)
end
end
... a variation based on the issue I described here:
Rails wildcard route with database lookup & multiple controllers
My issue now is that if the first call to SlugConstraint.new returns false (so that the 2nd routes.rb SlugConstraint.new now gets called) I don't want to have to redo the call to:
Slug.all.map(&:name)
How do I properly save (or scope) the #slugs data from the first constraint call that failed, so that I can access it if needed in the next constraint call?
Thanks.
Routing
You're not going to be able to use 2 routing patterns for the same path
When you send a request to Rails (or any other MVC application), Rails will take the path you've sent & consequently try to assign the right route (controller#action) for it.
This happens sequentially - IE Rails will look through your routes from top -> bottom until it finds one which corresponds. As you have two routes to match the same path, you're not going to be able to use the set up you have
--
App-Wide Slugs
What you're looking for is something called app-wide slugs - which essentially means you're able to manage a single slug path, and have a system in the back-end to accommodate it.
You're on the brink of being able to achieve this, and whilst I don't have any code to help, I do have an idea, which I found here:
#config/routes.rb
get '*path' => MyRouter.new, constraints: SlugConstraint.new
#lib/my_router.rb
class MyRouter
def call(env)
# Matched from routes, you can access all matched parameters
view_name= env['action_dispatch.request.path_parameters'][:view_name]
# Compute these the way you like, possibly using view_name
controller= 'post'
my_action= 'show'
controller_class= (controller + '_controller').camelize.constantize
controller_class.action(my_action.to_sym).call(env)
end
end
This will allow you to pick up the slugged paths, whilst routing to the correct controller. This is TOTALLY untested & just a stab in the dark - if you want to go over it with me, comment & we can have a look

change routing name dynamically

I have to modify the routes file in order to have SEO improvement.
This is my context, a rails backend generate a JSON feed with the route's name in, I have to read it and change the default name.
For example, I have this:
get '/people' => 'people#show', as: :people
and I'd like to change /people in some value read from my JSON feed.
I created a class to get the JSON object in my app
class JSONDatabase
def initialize(kind_of_site)
#kind_of_site = kind_of_site
end
def fetch_database_remote(url)
JSON.parse(open(url).read)
end
end
but how can i access it in routes file?
Thank you
You don't necessarily need to modify your application's routes. What you can do is define a wild card route that leads to a unique controller where you read the updated route. This approach is kind of hackish but gives you the unlimited routes you need without modifying the routes.
Your config/routes.rb file would look something like this:
resources :defined_models
root to: 'controller#action'
# At last we define the wildcard route
get '/:route' => 'routing_controller#routing_action'
Then, at this routing action we can do the job of seeing if this route (now defined in the params[:route] variable) corresponds to the modified one. Just remember to redirect to a 404 if the route given is not defined, since with this approach you loose the Rails default way of dealing with undefined routes.

Named routes in mounted rails engine

I'm making a small rails engine which I mount like this:
mount BasicApp::Engine => "/app"
Using this answer I have verified that all the routes in the engine are as the should be:
However - when I (inside the engine) link to a named route (defined inside the engine) I get this error
undefined local variable or method `new_post_path' for #<#<Class:0x000000065e0c08>:0x000000065d71d0>
Running "rake route" clearly verifies that "new_post" should be a named path, so I have no idea why Rails (3.1.0) can't figure it out. Any help is welcome
my config/route.rb (for the engine) look like this
BasicApp::Engine.routes.draw do
resources :posts, :path => '' do
resources :post_comments
resources :post_images
end
end
I should add that it is and isolated engine. However paths like main_app.root_path works fine - while root_path does not
The right way
I believe the best solution is to call new_post_path on the Engine's routes proxy, which is available as a helper method. In your case, the helper method will default to basic_app_engine, so you can call basic_app_engine.new_post_path in your views or helpers.
If you want, you can set the name in one of two ways.
# in engine/lib/basic_app/engine.rb:
module BasicApp
class Engine < ::Rails::Engine
engine_name 'basic'
end
end
or
# in app/config/routes.rb:
mount BasicApp::Engine => '/app', :as => 'basic'
In either case, you could then call basic.new_posts_path in your views or helpers.
Another way
Another option is to not use a mounted engine and instead have the engine add the routes directly to the app. Thoughtbot's HighVoltage does this. I don't love this solution because it is likely to cause namespace conflicts when you add many engines, but it does work.
# in engine/config/routes.rb
Rails.application.routes.draw do
resources :posts, :path => '' do
resources :post_comments
resources :post_images
end
end
# in app/config/routes.rb:
# (no mention of the engine)
On Rails 4 the engine_name directive did not work for me.
To access a named route defined in engine's routes from engine's own view or controller, I ended up using the verbose
BasicApp::Engine.routes.url_helpers.new_post_path
I recommend defining a simple helper method to make this more usable
# in /helpers/basic_app/application_helper.rb
module BasicApp::ApplicationHelper
def basic_app_engine
##basic_app_engine_url_helpers ||= BasicApp::Engine.routes.url_helpers
end
end
With this in place you can now use
basic_app_engine.new_post_path
In case you need to access your main application helper from the engine you can just use main_app:
main_app.root_path
use the below in you app to access the engine routes
MyApp::Engine.routes.url_helpers.new_post_path

Resources