I am adding a method to Mailboxer::Conversation to retrieve a link using mailboxer's emails to reply (i.e. reply_link).
I've decided to monkey patch mailboxer within my application. What I've done exactly is the following:
Created the folder structure lib/mailboxer/extensions.
Added files lib/mailboxer/extensions/conversation.rb, lib/mailboxer/extensions.rb, lib/mailboxer.rb.
The following is the content of the files:
# lib/mailboxer/extensions/conversation.rb
module Mailboxer
module Extensions
module Conversation
def reply_link
"/mail?notif_id=#{id}"
end
end
end
end
# lib/mailboxer/extensions.rb
require 'mailboxer/extensions/conversation'
# lib/mailboxer.rb
require 'mailboxer/extensions'
My config/application.rb has the following:
config.autoload_paths += %W(#{config.root}/lib)
Which gives me access to my lib folder.
Then what I do is include Mailboxer::Extensions::Conversation to Mailboxer::Conversation within the mailboxer initializer file initalizers/mailboxer.rb:
Mailboxer.setup do |config|
# ...
end
Mailboxer::Conversation.include Mailboxer::Extensions::Conversation
In my rails console, the code always works. However in the website, the reply_link method works at first, then becomes undefined randomly.
Couple of attempts later....
and it stops working until I restart the server...
Whenever I get an unrelated exception (i.e. typo, refactoring, etc.) the reply_link method becomes undefined. Could this be a development thing?
I could fork mailboxer, make my changes then go on. But the method is so custom to my application that I'd rather just patch.
If anyone has any suggestions, advice or questions I truly appreciate the advice.
First, I am still convinced that this is a development issue only. Whenever I have time to spare, I will test this out and post here.
Second, to ensure this never happened again I copied the source for the Mailboxer's Conversation and added an inclusion include MailboxerExt::Conversation.
I also structured my extension to not collide, reload Mailboxer's namespace.
The final result has the folders app/models/mailboxer, lib/mailboxer_ext.
The files are app/models/mailboxer/conversation.rb, lib/mailboxer_ext.rb and lib/mailboxer_ext/conversation.rb.
Related
I know, this question was asked at least milion times, but I still can't find the right solution. I mean clean and easy, like it is supposed to be in Rails applications.
Let's imagine I am developer of an application that needs some code in the lib folder. My app's name is Xy. I will create a file xy.rb in the lib folder with this content:
require 'xy/version'
module Xy
end
After that I will create next file in lib/xy/ named version.rb:
module Xy
module VERSION
def self.to_s
"1.0.0"
end
end
end
So far, so good. Then I will create some controller and I will edit routes:
class IndexController < ApplicationController
def index
render text: Xy::VERSION
end
end
root 'index#index'
I also need require my code, in application.rb I will add require 'xy'
Then I will run my server rails s and I will wisit the app in the browser http://localhost:3000 - seems good, I see 1.0.0.
Then I will update the version string in Xy::VERSION to 1.0.1 and I'll refresh the browser - here it comes, I will no see the change. To see it I will have to restart the server. And this is quite anoying.
So the question is: What is the right way to force RoR 4+ to reload changes in the lib folder in development environment?
Thank you in advance.
Add this to application.rb:
config.autoload_paths += %W(#{config.root}/lib)
I'm using this in my app now, you never need to even require' the lib files anywhere and don't have to restart the server. Just make sure you name your files correctly. E.g
/lib/foo/rb
class Foo
/lib/foo/bar.rb
class Foo::Bar
I use the following line in an initializer to autoload code in my /lib directory during development:
config/initializers/custom.rb:
RELOAD_LIBS = Dir[Rails.root + 'lib/**/*.rb'] if Rails.env.development?
(from Rails 3 Quicktip: Auto reload lib folders in development mode)
It works great, but it's too inefficient to use in production- Instead of loading libs on each request, I just want to load them on start up. The same blog has another article describing how to do this:
config/application.rb:
# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
However, when I switch to that, even in development, I get NoMethodErrors when trying to use the lib functions.
Example of one of my lib files:
lib/extensions.rb:
Time.class_eval do
def self.milli_stamp
Time.now.strftime('%Y%m%d%H%M%S%L').to_i
end
end
Calling Time.milli_stamp will throw NoMethodError
I realize others have answered similar questions on SO but they all seem to deal with naming conventions and other issues that I didn't to have to worry about before- My lib classes already worked for per-request loading, I just want to change it to per-startup loading. What's the right way to do this?
I think this may solve your problem:
in config/application.rb:
config.autoload_paths << Rails.root.join('lib')
and keep the right naming convention in lib.
in lib/foo.rb:
class Foo
end
in lib/foo/bar.rb:
class Foo::Bar
end
if you really wanna do some monkey patches in file like lib/extensions.rb, you may manually require it:
in config/initializers/require.rb:
require "#{Rails.root}/lib/extensions"
P.S.
Rails 3 Autoload Modules/Classes by Bill Harding.
And to understand what does Rails exactly do about auto-loading?
read Rails autoloading — how it works, and when it doesn't by Simon Coffey.
Though this does not directly answer the question, but I think it is a good alternative to avoid the question altogether.
To avoid all the autoload_paths or eager_load_paths hassle, create a "lib" or a "misc" directory under "app" directory. Place codes as you would normally do in there, and Rails will load files just like how it will load (and reload) model files.
This might help someone like me that finds this answer when searching for solutions to how Rails handles the class loading ... I found that I had to define a module whose name matched my filename appropriately, rather than just defining a class:
In file lib/development_mail_interceptor.rb (Yes, I'm using code from a Railscast :))
module DevelopmentMailInterceptor
class DevelopmentMailInterceptor
def self.delivering_email(message)
message.subject = "intercepted for: #{message.to} #{message.subject}"
message.to = "myemail#mydomain.org"
end
end
end
works, but it doesn't load if I hadn't put the class inside a module.
Use config.to_prepare to load you monkey patches/extensions for every request in development mode.
config.to_prepare do |action_dispatcher|
# More importantly, will run upon every request in development, but only once (during boot-up) in production and test.
Rails.logger.info "\n--- Loading extensions for #{self.class} "
Dir.glob("#{Rails.root}/lib/extensions/**/*.rb").sort.each do |entry|
Rails.logger.info "Loading extension(s): #{entry}"
require_dependency "#{entry}"
end
Rails.logger.info "--- Loaded extensions for #{self.class}\n"
end
When you create a namespaced model with rails scaffolding, you get two files. For example, this scaffold:
rails generate model Staff::Location name:string address:string
Generates these files:
/app/models/staff.rb
module Staff
def self.table_name_prefix
"staff_"
end
...
/app/models/staff/location.rb
class Staff::Location < ActiveRecord::Base
...
I am running into problems when in development mode where rails unloads the Staff module and never reloads it. This causes several annoying bugs such as Location not able to access it's table due to the missing table_name_prefix. The problem seems to crop up when I don't access the models directly, such as through a polymorphic relationship.
I can't seem to get the module loaded on a consistent basis. Is this the best practice way to do namespaced models? If it is, what am I missing?
Although I wasn't able to reproduce the problem in Rails 3.2.2, I've run into something like this before. The generic way to hack around this problem in development mode is through an ActionDispatch callback. Add this to config/environments/development.rb:
MyApp::Application.configure do
ActionDispatch::Callbacks.before do
load Rails.root.join('app', 'models', 'staff.rb')
end
end
Anything you do in that block will be executed before each request, so make sure you're only doing it in development mode.† Otherwise, you're going to suffer a performance hit in production.
I logged a message inside the staff.rb file and within the Staff module itself, and both messages appeared in the log for each request.
† I tried using the to_prepare callback, since that seems to be the documented way to execute code before each request only when cache_classes is false. But that only seemed to execute after restarting the application. There's at least one other open Stack Overflow question regarding this, although he's using a slightly different syntax than I used. If you can get to_prepare to work, I'd suggest that instead of before.
About a year later, I have finally found the answer to this question. This answer is specifically for rails 3.1. I am not sure if it is a problem in rails 3.2.
The problem occurs when setting up a model. If scaffolding is used, no helper file is generated. This would normally be in /app/helpers/staff/location_helper.rb. There are two ways to setup this file:
module Staff::LocationHelper
...
end
module Staff
module LocationHelper
...
end
end
In rails 3.1, specifically for helpers, you must use the first solution. You do not have to use it for other modules that use a namespace in other parts of the rails project. In fact, some structures in ruby require the second solution.
If you use the second solution when declaring a helper, in certain cases the Staff module in the helper file will override the module in /app/models/staff.rb. It will silently replace it with the empty Staff module in the file. This does not happen 100% of the time because helpers are not always loaded.
I'm working on a Rails app that uses an engine. I'm using an initializer to configure one of my engine's controllers so that it will trigger an action in the host app. The code looks something like this:
# config/initializers/my_engine.rb
MyEngine::SomeController.after_filter proc {
# Do something in the host app
}, :only => :update
This works fine in production, but in development mode, the proc is only called on the first request. This is because the classes are getting reloaded and this configuration is lost, because it was stored in a class variable. (For example, MyEngine::SomeController is reloaded from the file it's in, and since the after_filter isn't declared there, it isn't added back on.)
Some Rails background
In development mode, Rails uses the following load strategy:
Code in the app directory is reloaded on each request, on the assumption that you're actively changing it.
Code in the lib directory, along with config/initializer files, are loaded once, when the application boots.
Initializer files are generally used for configuring gems. In the past, gems have mostly had code in the lib directory, so running their configuration once was sufficient.
How engines change things
However, Rails engines have code in the app directory: controllers, models, etc. These files are reloaded in development mode on each request. Therefore, configuration like my example above is lost.
Enter to_prepare
Rails provides config.to_prepare specifically to solve this problem: it run once in production, and on every request in development.
For example, we have this in application.rb, which works fine:
config.to_prepare do
# set up class variables (after_filters, etc)
end
However, if I have to put all my engines' configuration in application.rb, this defeats the point of config/initializers in keeping things organized.
So, for any configuration of classes in my engines' app directories, I want to put that code in files under config/initializers.
Here are my questions.
I'm unclear how to get config into scope in an initializer file. I'm thinking it would be Rails.application.config. Is that right?
Can I add add multiple to_prepare blocks? I'm afraid that calling it multiple times will overwrite previous blocks.
Update
As #Frederick Cheung mentioned, Rails.application.config.to_prepare does work in config/initializer files, and one can use as many of these as needed in the various files; each call appends its block to an array, so nothing is overwritten.
So the solution to this problem is:
# config/initializers/my_engine.rb
Rails.application.config.to_prepare do
MyEngine::SomeController.after_filter proc {
# Do something in the host app
}, :only => :update
end
One thing that still seems odd: I expected the to_prepare block to be called on every request in development mode, but instead it seems to be called randomly every 3rd request or so. I added block:
Rails.application.config.to_prepare do
Rails.logger.info "Running the prepare block!"
end
... restarted my app, and refreshed the page nine times. I only saw the message on the 1st, 5th, 7th and 9th requests. I'm not sure what explains this behavior, but it does explain why my code without the to_prepare worked intermittently in development.
You can add as many to_prepare blocks as you want - when you do config.to_prepare, Rails is doing (in configuration.rb in railties)
def to_prepare(&blk)
to_prepare_blocks << blk if blk
end
and then iterates over those blocks handing them over to ActionDispatch::Reloader, where to_prepare is implemented using ActiveSupport::Callbacks (i.e. the same thing that is used for before_save and so on). Multiple to_prepare blocks are fine.
Currently it looks like Rails iterates over to_prepare_blocks after reading application initialisers so adding to Rails.application.configuration.to_prepare should work. You may prefer to use ActionDispatch::Reloader.to_prepare.
There's nothing to stop you from doing initializer code in a file that lives in app/models.
for example
class MyClass
def self.run_me_when_the_class_is_loaded
end
end
MyClass.run_me_when_the_class_is_loaded
MyClass.run_me... will run when the class is loaded .... which is what we want, right?
Not sure if its the Rails way.... but its extremely straightforward, and does not depend on the shifting winds of Rails.
I'm trying to write a plugin that will extend InheritedResources.
Specifically I want to rewrite some default helpers.
And I'd like it to "just work" once installed, w/o any changes to application code.
The functionality is provided in a module which needs to be included in a right place. The question is where? :)
The first attempt was to do it in my plugin's init.rb:
InheritedResources::Base.send :include, MyModule
It works in production, but fails miserably in development since InheritedResource::Base declared as unloadable and so its code is reloaded on each request. So my module is there for the first request,
and then its gone.
InheritedResource::Base is 'pulled' in again by any controller that uses it:
Class SomeController < InheritedResource::Base
But no code is 'pulling in' my extension module since it is not referenced anywhere except init.rb which is not re-loaded on each request
So right now I'm just including the module manually in every controller that needs it which sucks.
I can't even include it once in ApplicationController because InheritedResources inherites from it and so it will override any changes back.
update
I'm not looking for advice on how to 'monkey patch'. The extension is working in production just great. my problem is how to catch moment exactly after InheritedResources loaded to stick my extension into it :)
update2
another attempt at clarification:
the sequence of events is
a) rails loads plugins. my plugin loads after inherited_resources and patches it.
b) a development mode request is served and works
c) rails unloads all the 'unloadable' code which includes all application code and also
inherited_resources
d) another request comes in
e) rails loads controller, which inherites from inherited resources
f) rails loads inherited resources which inherit from application_controller
g) rails loads application_contrller (or may be its already loaded at this stage, not sure)
g) request fails as no-one loaded my plugin to patch inherited_resources. plugin init.rb files are not reloaded
I need to catch the point in time between g and h
The Rails::Configuration, config in the environment files, allows registering a callback on the dispatcher that runs before each request in development mode, or once when in production mode.
config.to_prepare do
# do something here
end
The problem is, I don't think your plugin has access to config when the init.rb file is run. Here is a way to register your callback directly in the dispatcher. Just put this in the init.rb file.
require 'dispatcher'
::Dispatcher.to_prepare do
puts "hi there from a plugin"
end
Warning: I don't know what side effects this may have. If possible, try to get access to config and register the callback tha right way.
What you are attempting to do is usually called "MonkeyPatch" - changing the way one module or class is working by "overriding" methods.
It is a common practice in Rails, but it doesn't mean it is the best way to do things - when possible, it is better to use common inheritance (it is more explicit about the changes you make).
Regarding your questions about "where to put the files": it is usually the lib/ directory. This can mean the lib of the rails app, or a lib directory inside a gem or plugin, if you are into that sort of thing.
For example, if the file you want to change is lib/generators/rails/templates/controller.rb of inherited resources, the first thing you have to do is replicate that directory structure inside your lib/ folder ('lib/generators/rails/templates/controller.rb')
Inside that new file of yours, (empty at the beginning) you can override methods. However, you must also the modules/classes hierarchy. So if the original gem had this:
module foo
module bar
def f1
...
end
def f2
...
end
end
def f3
...
end
end
And you wanted to modify f1, you would have to respect the foo-bar modules.
module foo
module bar
def f1
... # your code here
end
end
end
Now the last thing you need is to make sure this code is executed at the right time. If you are using the application's lib/ folder, you will need to create an entry on the initializers/ folder and require your new file. If you are developing a gem/plugin, you will have a init.rb file on the "root" folder of that plugin. Put the 'require' there.
I'm not very familiar with this unloadable stuff; maybe I'm asking something obvious but- have you tried making your extension module unloadable, too? (You shouldn't need this if you monkeypatched the module instead of creating a new one)