I'm trying to access the url helpers in my engine to set up rack-cors. Right now, I've hard-coded the strings for one of the urls in the rack-cors middleware configuration. I have read the order in which Rails initializers are run, and at this point in the load order I should have the engine routes available to me. I thought I would have them available at the event add_routing_paths, but I couldn't find the routes after digging around using pry. Another statement that leads me to think I'm doing this incorrectly is that the docs say: "Some parts of your application, notably routing, are not yet set up at the point where the after_initialize block is called." According to this list:
require "config/boot.rb" to setup load paths
require railties and engines
Define Rails.application as "class MyApp::Application < Rails::Application"
Run config.before_configuration callbacks
Load config/environments/ENV.rb
Run config.before_initialize callbacks
Run Railtie#initializer defined by railties, engines and application.
One by one, each engine sets up its load paths, routes and runs its config/initializers/* files.
Custom Railtie#initializers added by railties, engines and applications are executed
Build the middleware stack and run to_prepare callbacks
Run config.before_eager_load and eager_load! if eager_load is true
Run config.after_initialize callbacks
I'm trying to hook into (7), but perhaps routes aren't available until (11)?
module Zillow
class Engine < ::Rails::Engine
isolate_namespace Zillow
# Rails.application.routes.url_helpers
initializer "zillow.cors", after: :set_routes_reloader do |app|
require 'pry'; binding.pry
app.config.app_middleware.insert_before 0, Rack::Cors do
allow do
origins 'localhost:3000'
resource '/zillow/search_results', methods: :get
end
end
end
end
end
Here is the output of my routes
zillow /zillow Zillow::Engine
Routes for Zillow::Engine:
api_deep_comps GET /api/deep_comps(.:format) zillow/api#deep_comps
api_zestimate GET /api/zestimate(.:format) zillow/api#zestimate
api_search_results GET /api/search_results(.:format) zillow/api#search_results
api_updated_property_details GET /api/updated_property_details(.:format) zillow/api#updated_property_details
You can fire your own event when routes are loaded and then subscribe to that event during initialization to get routes data. For that:
Add this to the end of config/routes.rb file (outside of routes.draw block)
ActiveSupport::Notifications.instrument 'routes_loaded.application'
Subscribe to this event in initialization code and use URL helpers!
ActiveSupport::Notifications.subscribe 'routes_loaded.application' do
Rails.logger.info Rails.application.routes.url_helpers.home_path
end
For more information see:
Subscribing to an event
Creating custom events
Load it just after initialize.
I needed to use root_url in my initializer but it wasn't properly initialized. I tried using the Pub / Sub method that Anton Styagun suggests but even when that's called the URL Helpers were not properly initialized.
Instead, I opted to load my initializer file manually just after the Rails app itself has initialized. This takes a few steps:
Move your initializer from config/initializers/ to lib/.
Require your file in environment.rb just after your application initializes. Ours looks something like this:
CNTRAL::Application.initialize!
require "my_initializer" # Omit the `lib/` because that's the load path it looks in.
Now, inside of my_initializer.rb I was able to call root_url by doing this:
Rails.application.routes.url_helpers.root_url
NOTE: It's important not to just use include Rails.application.routes.url_helpers. It raised an exception on me.
I also added a comment in both the initializer and the environment.rb of where this was loaded/required so it's more obvious to myself in the future and other engineers.
Alternative Method
You can also use the following method and it works well:
In your application.rb file, add:
config.after_initialize do
Rails.application.reload_routes! # Necessary to load the Routes.
require "my_initializer"
end
After looking into this further and reading the rack-cors example done in Rails3, it may not be possible to retrieve the route helpers inside the initializer at any point in time.
Related
Usually I add the following in config/application.rb to add autload_paths:
config.autoload_paths += Dir[Rails.root.join('app', 'poros', '{**}')]
How can I achieve the same in an engine?
It seems to work when I just use the same code in application.rb in the host app, however I think it's ugly that the code is not in the engine and needs to be added to the host app to make things work.
The only solution I found to add the load path through the engine is by adding this to lib/engine/engine.rb:
config.to_prepare do
Dir.glob(Rails.root + "../../app/poros/**/*.rb").each do |c|
require_dependency(c)
end
end
However there seems to be something fundamentally wrong with this as this leads to problems when I'm doing console reloads (e.g. it tells me that constants are already defined or that concerns can't execute the include block twice)
What is the right way to do this in the engine itself? (can't believe this is so hard/uncommon, I have really googled a lot but I can't find a solution)
According to the Rails::Engine documentation, you can add autoload paths in your Railtie like this:
class MyEngine < Rails::Engine
# Add a load path for this specific Engine
config.autoload_paths << File.expand_path("../lib/some/path", __FILE__)
initializer "my_engine.add_middleware" do |app|
app.middleware.use MyEngine::Middleware
end
end
If poros is a subdirectory of app, you do not need add it again.
All subdirectories of app in the application and engines present at boot time. For example, app/controllers. They do not need to be the default ones, any custom directories like app/workers belong automatically to autoload_paths.
I have a bunch of custom classes in my Rails 3.2 app in lib folder: i.e. extending ActiveRecord, etc. It all works fine.
However I'm trying to add a couple of custom methods to FileUtils, i.e.
module FileUtils
def last_modified_file(path='.')
# blah ...
end
end
I put it in lib/file_utils.rb
In my application.rb I have
config.autoload_paths += %W(#{config.root}/lib)
My other custom classed are loaded but not the module.
I read (Best way to load module/class from lib folder in Rails 3? ) that I'm supposed to define a class inside module in order for Rails to pick it up and according to FileUtils.class - it should be Object < BasicObject.
So I tried
module FileUtils
class Object
def last_modified_file(path='.')
# blah ...
end
end
end
But that doesn't work either.
However when I fire up irb and just paste my code which effectivly puts my new code inside object and reinclude my module - it works fine.
Whaat amd I missing here?
Your patch is never going to be loaded because autoload is only invoked when Rails can't find a constant. Since the FileUtils constant already exists, the autoloader is never called, and your file is never loaded.
Simply require it from an initializer.
require File.join(Rails.root, "lib/file_utils.rb")
I want to set a class attribute when my Rails app starts up. It requires inspecting some routes, so the routes need to be loaded before my custom code runs. I am having trouble finding a reliable place to hook in.
This works PERFECTLY in the "test" environment:
config.after_initialize do
Rails.logger.info "#{Rails.application.routes.routes.map(&:path)}"
end
But it doesn't work in the "development" environment (the routes are empty)
For now I seem to have things working in development mode by running the same code in config.to_prepare which I understand happens before every request. Unfortunately using to_prepare alone doesn't seem to work in test mode, hence the duplication.
I'm curious why the routes are loaded before after_initialize in test mode, but not in development mode. And really, what is the best hook for this? Is there a single hook that will work for all environments?
*EDIT*
mu's suggestion of reloading the routes was great. It gave me consistent access to the routes within after_initialize in all environments. For my use case though, I think I still need to run the code from to_prepare as well, since I'm setting a class attribute on a model and the models are reloaded before each request.
So here's what I ended up doing.
[:after_initialize, :to_prepare].each do |hook|
config.send(hook) do
User.invalid_usernames += Rails.application.routes.routes.map(&:path).join("\n").scan(/\s\/(\w+)/).flatten.compact.uniq
end
end
It seems a bit messy to me. I think I'd rather do something like:
config.after_initialize do
User.exclude_routes_from_usernames!
end
config.to_prepare do
User.exclude_routes_from_usernames!
end
But I'm not sure if User is the right place to be examining Rails.application.routes. I guess I could do the same thing with code in lib/ but I'm not sure if that's right either.
Another option is to just apply mu's suggestion on to_prepare. That works but there seems to be a noticeable delay reloading the routes on every request in my dev environment, so I'm not sure if this is a good call, although it's DRY, at least.
config.to_prepare do
Rails.application.reload_routes!
User.invalid_usernames += Rails.application.routes.routes.map(&:path).join("\n").scan(/\s\/(\w+)/).flatten.compact.uniq
end
You can force the routes to be loaded before looking at Rails.application.routes with this:
Rails.application.reload_routes!
So try this in your config/application.rb:
config.after_initialize do
Rails.application.reload_routes!
Rails.logger.info "#{Rails.application.routes.routes.map(&:path)}"
end
I've done similar things that needed to check the routes (for conflicts with /:slug routes) and I ended up putting the reload_routes! and the checking in a config.after_initialize like you're doing.
If you're trying to run code in an initializer after the routes have loaded, you can try using the after: option:
initializer "name_of_initializer", after: :add_routing_paths do |app|
# do custom logic here
end
You can find initialization events here: http://guides.rubyonrails.org/configuring.html#initialization-events
I have a rails 3 engine. In initializer it requires a bunch of files from some folder.
In this file user of my engine defines code, business logic, configures engine, etc..
All this data is stored statically in my engine main module (in application attribute)
module MyEngine
class << self
def application
#application ||= MyEngine::Application.new
end
end
end
I want this files to be reloaded on each request in development mode.
(So that the user don't have to reload server to see changes he just made)
Of course I can do something like this instead of initializer
config.to_prepare do
MyEngine.application.clear!
load('some/file')
end
But this way i will have issues (because constants defined in this file won't really be reloaded).
The ideal solution would be to make my whole engine reloadable on each request, but a have not found the way to do it.
It's an old question but I think adding ActiveSupport::Dependencies.explicitly_unloadable_constants += %w[ GemName ] to your development.rb should do the trick.
Have you tried turning reload_plugins on?
# environments/development.rb
config.reload_plugins = true
Its a bit of hack but using require_dependency and just reopening the class might work?
# app/models/project.rb
require_dependency File.join(MyEngine::Engine.root, 'app', 'models', 'project')
class Project
end
For those who are working on Engine views or I18n translations only: Those parts are autoreloaded by default, no need to restart the server!
I'm in the process of refactoring some logic built into a Rails application into middleware, and one annoyance I've run into is a seeming lack of convention for where to put them.
Currently I've settled on app/middleware but I could just as easily move it to vendor/middleware or maybe vendor/plugins/middleware...
The biggest problem is having to require the individual files at the top of config/environment.rb
require "app/middleware/system_message"
require "app/middleware/rack_backstage"
or else I get uninitialized constant errors on the config.middleware.use lines. That could get messy very quickly. I'd rather this was tucked away in an initializer somewhere.
Is there a conventional place to put this stuff?
The specific answer I'm looking for with this bounty is: where can I put the require lines so that they are not cluttering the environment.rb file but still get loaded before the config.middleware.use calls? Everything I have tried leads to uninitialized constant errors.
Update: Now that we're using Rails 3.0, I treat a Rails app like any other Rack app; code files for middleware go in lib (or a gem listed in Gemfile) and are required and loaded in config.ru.
As of Rails 3.2, Rack middleware belongs in the app/middleware directory.
It works "out-of-the-box" without any explicit require statements.
Quick example:
I'm using a middleware class called CanonicalHost which is implemented in app/middleware/canonical_host.rb. I've added the following line to production.rb (note that the middleware class is explicitly given, rather than as a quoted string, which works for any environment-specific config files):
config.middleware.use CanonicalHost, "example.com"
If you're adding middleware to application.rb, you'll need to include quotes, as per #mltsy's comment.
config.middleware.use "CanonicalHost", "example.com"
You can put it in lib/tableized/file_name.rb. As long as the class you're trying to load is discoverable by its filename, Rails will automatically load the file necessary. So, for example:
config.middleware.use "MyApp::TotallyAwesomeMiddleware"
You would keep in:
lib/my_app/totally_awesome_middleware.rb
Rails catches const_missing and attemts to load files corresponding to the missing constants automatically. Just make sure your names match and you're gravy. Rails even provides nifty helpers that'll help you identify the path for a file easily:
>> ChrisHeald::StdLib.to_s.tableize.singularize
=> "chris_heald/std_lib"
So my stdlib lives in lib/chris_heald/std_lib.rb, and is autoloaded when I reference it in code.
In my Rails 3.2 app, I was able to get my middleware TrafficCop loading by putting it at app/middleware/traffic_cop.rb, just as #MikeJarema described. I then added this line to my config/application.rb, as instructed:
config.middleware.use TrafficCop
However, upon application start, I kept getting this error:
uninitialized constant MyApp::Application::TrafficCop
Explicitly specifying the root namespace didn't help either:
config.middleware.use ::TrafficCop
# uninitialized constant TrafficCop
For some reason (which I've yet to discover), at this point in the Rails lifecycle, app/middleware wasn't included in the load paths. If I removed the config.middleware.use line, and ran the console, I could access the TrafficCop constant without any issue. But it couldn't find it in app/middleware at config time.
I fixed this by enclosing the middleware class name in quotes, like so:
config.middleware.use "TrafficCop"
This way, I would avoid the uninitialized constant error, since Rails isn't trying to find the TrafficCop class just yet. But, when it starts to build the middleware stack, it will constantize the string. By this time, app/middleware is in the load paths, and so the class will load correctly.
For Rails 3:
#config/application.rb
require 'lib/rack/my_adapter.rb'
module MyApp
class Application < Rails::Application
config.middleware.use Rack::MyAdapter
end
end
I'm not aware of a convention, but why not put it in the /lib directory? Files in there get automatically loaded by Rails.
You could create an initializer which requires the necessary files and then leave the files wherever you want.
According to this the initializers are executed before the rack middleware is loaded.
The working solution I have so far is moving the middleware requires to config/middleware.rb and requiring that file in environment.rb, reducing it to a single require which I can live with.
I'd still like to hear how other people have solved this seemingly basic problem of adding middleware to Rails.