Monkey patching Devise (or any Rails gem)
If you try to reopen a class, it's the same syntax as declaring a new class:
class DeviseController
end
If this code is executed before the real class declaration, it inherits from Object instead of extending the class declared by Devise. Instead I try to use the following
DeviseController.class_eval do
# Your new methods here
end
This way, you'll get an error if DeviseController
has not been declared. As a result, you'll probably end up with
require 'devise/app/controllers/devise_controller'
DeviseController.class_eval do
# Your new methods here
end
Where to put code when monkey patching
There is no set rule on this. Technically you can open it (the class; and add your method) anywhere. I usually make a special file called monkey_patches.rb
and put it in config/initializers
or in a misc
folder in my Rails app so if theres ever a conflict I know where to look.
Also I'd advise to use a Module
to wrap the monkey patch. Check out 3 ways to monkey patch without making a mess for more info.
His example:
module CoreExtensions
module DateTime
module BusinessDays
def weekday?
!sunday? && !saturday?
end
end
end
end
DateTime.include CoreExtensions::DateTime::BusinessDays
how to use monkey patching helper in devise in password controller to change after_sending_reset_password_instructions_path_for
There are several how-to articles on the Devise Wiki that may provide straightforward solutions to the problem you're trying to solve. One might be here: https://github.com/plataformatec/devise/wiki/How-To:-Change-the-default-sign_in-and-sign_out-routes, and another here: https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-on-successful-sign-in-out
Note that Devise has gone through a lot of changes recently (new version 2.0 is out) that addresses many of the challenges earlier adopters had through better documentation and by exposing hooks that weren't readily available before. I urge you to consider using the new version. My team dealt with many frustrations and difficulties with the earlier version, and we quickly learned that messing with the internals of Devise frequently had unintended consequences. It's a really great gem, especially its OAuth integration, but it does a lot, and has very clear opinions on how to do things.
When monkey-patching a single method in a gem can I make it aware of an active record in my app?
I'd store it in a plain class
class StoreRecord
def self.activeRecord
@active_record
end
def self.activeRecord=(record)
@active_record = record
end
end
so your code becomes
StoreRecord.activeRecord = ... # gets an ActiveRecord object that I want to update
options = {..}
@gem_object = SomeLibrary::Class.new(options)
@gem_object.someMethod()
and you can access StoreRecord.activeRecord
in your patched method.
Rails bootstrap gem monkeypatching method not working
The problem is that you patch the method on this module but the module already got included at this point. Try to define this in your application_helper.rb
def name_and_caret(name)
super("blub #{name}")
end
Monkey Patching Mongoid models contained in a Ruby gem
I think I've nailed this now. Rather than putting the monkey patch inside lib/models
, I moved them to lib/sorting_office/models
and required them like so:
require 'sorting_office/models/street'
require 'sorting_office/models/locality'
require 'sorting_office/models/town'
require 'sorting_office/models/postcode'
I'd still be interested to know WHY this worked though
Ruby on Rails Monkey Patching a Gem's Model
First of all instead of reopening the class I would create a Module and include it into the Person. So it would look like that
module CustomString
def to_custom_string
address.street.to_s
end
end
Person.send(:include, CustomString)
Also it seems like the Person model is not yet available at the point of running the initializer. You may want to put this in your application.rb if still doesn't work.
config.railties_order = [ModelEngine::Engine, :main_app, :all]
I guess the reason why it works in irb and not in rake is because they look up classes differently. Irb (which I believe you run by running rails console) loads all the classes at once therefore it loads the classes from engine, then it runs the initializer where you have the classes from engine already defined. I guess (though I'm not sure) Rake in development mode uses lazy loading of constants. So it doesn't load all the classes at the very beginning and only when it finds a constants that is undefined. Then it starts looking for a file that may define that constant. Since you put some Person in initializer it doesn't look up the engine's model at all cause at the point it sees Person it has the Person definition already. That's why the inclusion of module instead of reopening the class may help -> it enforces that it will lookup the Person constant from engine.
Monkey Patching in Rails 3
The initializer directory is a good place to collect all those little scraps. Since I tend to go a bit overboard with core extensions, I like to make a folder there called "extensions" and toss them all in there.
So, try /config/initializers/string_extension.rb
, or /config/initializers/extensions/string.rb
, or something similar. Either way, you can just forget about them afterward - Rails will require them for you, so you don't need to do it yourself.
Monkey patching a core class with business logic with Rails
Where should finder.rb be?
Ultimately, it doesn't matter. It only matters that this code gets loaded. This mix of patching base libraries and adding business logic there looks like something that MUST be documented thoroughly (in the project's wiki or something like that). And if it is documented, then it doesn't matter. The code is where the documentation says it is.
That being out of the way, here's a design suggestion:
when user seeks a Family
Family.find(params[family_id],session[:company_id])
, this find will compare the company of the family result family.company witht the parameter
Why not do something like this:
family = current_company.families.find(params[:family_id])
where current_company can be defined as @current_company ||= Company.find(session[:company_id])
Here, if this company doesn't have this family, you'll get an exception.
Same effect*, only without any patching. Much more futureproof. You can even add a couple of rubocop rules to ensure that you never write a naked Family.find
.
* it's not like you add that patch and rest of your code magically acquires super-powers. No. You still have to change all the finders, to pass that company id.
Related Topics
Unexpected Output in Ruby on Rails
Why Do People Say That Ruby Is Slow
How to Map/Collect with Index in Ruby
Tell Ruby Program to Wait Some Amount of Time
How to Invoke an Instance Method on a Ruby Module Without Including It
Heroku and Rails: Gem Load Error with Postgres, However It Is Specified in Gemfile
Get Index of String Scan Results in Ruby
How to Run a Single Test in Minitest
Welcome/Home Page in Ruby on Rails - Best Practice
Select Arrays Between Date Ranges with Ruby
Why Do I Get a Bcrypt-Ruby Gem Install Error
How to Test a File Upload in Rails
File Upload with Activeadmin Rails Using Paperclip
Why Isn't 'Method=' Treated the Same as Any Other Method