Override method (monkey patch?) on rails core - ruby-on-rails

On this file:
...gems/actionpack-2.3.18/lib/action_controller/rescue.rb
Exists a method called local_request? inside module Rescue, that is contained in module ActionController, so its like this:
module ActionController
...
module Rescue
...
protected
...
# True if the request came from localhost, 127.0.0.1. Override this
# method if you wish to redefine the meaning of a local request to
# include remote IP addresses or other criteria.
def local_request?
....
I want to override that method in order to handle all requests as non-local, I tried with this (what I think is a monkey patch, not sure if the term is wrong)
module ActionController
module Rescue
protected
def local_request? #:doc:
false
end
end
end
It seems to work (that method return false), but when making a request I receive an error:
undefined method `call_with_exception' for ApplicationController:Class
That method exists in
module ActionController > module Rescue > module ClassMethods
1) If i'm overriding only one method why that got undefined? im deleting the other methods/modules inside the one i'm modifying?
2) How to and whats the right way to do this?

In rails 2.3.x Rails uses ruby's autoload method to load the modules that make up ActionController (see here: the file defining each module is only loaded when the constant is first accessed.
Because you are defining a constant of the same name, autoload will never get triggered (since as far as ruby is concerned it doesn't need to), so the rails provided code never gets loaded. You could instead do
ActionController::Rescue.module_eval do
def local_request?
false
end
end
Although as pointed out in the comments you can just define this on your controller - no need to do it this way.

Related

Calling a module's methods on a Gem's object?

I've generated an object via a ruby gem (Koala) and I've written a few modules with helper methods. What do I need to do in order to be able to use the methods within the modules on the object?
If I, model_object = Model.new, model_object will have access to all the instance variables but object does not (see below).
Ruby 2.1, Rails 4.1
config/application.rb - Autoloading modules in folder
config.autoload_paths << Rails.root.join('lib/module_folder')
Model
class Model < ActiveRecord::Base
include Module
include Module::Module2
include Module::Module3
def self.create_account(token)
object = Module.module_class_method(token) #this works and generates the Koala object
ERROR: object.module2_instance_method # Error: NoMethodError Exception: undefined method
end
end
Module
module Module
extend ActiveSupport::Concern
end
Module2
module Module
module Module2
def module2_instance_method
end
end
end
SOLVED MYSELF
- the issue was the include statements being within the class, if I moved them outside it worked.
I believe if you include your modules somewhere under the app/ directory - they will be included automatically. Otherwise, you actually have to require them in your rails code explicitly with a require statement
Without seeing the actual code, I think the problem with Module2 in your code snippet is the self. method.
Because you are calling module2_instance_method on an instance of your object, the method in the module cannot have the self. because that designates a class method and, as such, would have to be called as Module::Module2.module2_instance_but_not_really_because_I_am_a_class_method.
I believe if you change def self.module2_instance_method ... end to def module2_instance_method ... end, you should no longer receive the NoMethodError exception.
Apologies if I've misread or misunderstand the OP.
Moved the include statements from inside to above the class declaration and all methods began to work. My assumption is that when they are within the statement they are only available to objects of that class.

Rails module autoloading preserve state

I have set up a couple of modules that provide similar functionality, and I have another module that collects all of these, so that at runtime it is possible to determine what functionality is available.
This works fine, but whenever the code gets reloaded, Rails' autoloading functionality clobbers the instance variables on the collection module.
The code looks something like this:
module ServiceCollection
def self.available_services
#available_services ||= []
end
end
module ServiceProvider
extend ActiveSupport::Concern
included do |includer|
ServiceCollection.available_services.push(includer)
end
end
module MyService
include ServiceProvider
#some functionality here
end
Calling ServiceCollection.available_services will return the list of modules that include ServiceProvider, however on reload, the instance variable #available_services will be reset, and subsequent calls return an empty array.
Is there an easy way to get around this?
You can force the load of the modules putting a require call in your application.rb file.
Something like:
Dir["#{File.expand_path('../..', __FILE__)}/extras/*.rb"].each { |rb| require rb }
Thanks to #thaleshcv for the answer, but I decided to go a different route, partially inspired by his answer.
I changed the implementation of ServiceCollection to something like this:
module ServiceCollection
def self.available_services
Dir[Rails.root.join("app", "services", "*.rb")].each do |filename|
model_name = Pathname.new(filename.to_s).basename.to_s.chomp('.rb').camelcase
begin
Module.const_get(model_name)
rescue Exception => e
#Log something or whatever
end
end
#available_services ||= []
end
end
Calling Module.const_get triggers Rails' autoloading functionality. Assuming that I follow the module/class/filename naming convention, then this should work fine. Here's a gist that extracts this functionality into a module

Rails undefined method for Module

In Rails, how do you use a specific method from a module. For eg,
# ./app/controllers/my_controller.rb
class MyController < ApplicationController
include MyModule
def action
MyModule.a_method
end
private
def a_method
...
end
end
# ------------------------------------------------ #
# ./app/helpers/my_module.rb
module MyModule
def a_method
...
end
end
MyController includes MyModule. And in action ,I want to use MyModule.a_method (Please note I also have a private a_method in MyController and I don't want to use this.)
Things I've tried :
1) Defining the method in the module as self.
def self.a_method
end
2) Using the :: notation in controller (MyModule::a_method)
The error that I keep getting is
Undefined method:a_method for MyModule:module
For now, I've resorted to using a different name for the modules method. But I'd like to know how to namespace the function with either the Module:: or Module. notation
[UPDATE - 11/24/2014]
adding file structure in code, since Rails heavily relies on convention.
So I am not really sure what you are trying to accomplish with your module but a quick solution to get it working is below.
Move my_module.rb out of helpers and into lib/my_module.rb. The helpers directory is for methods that you use in your views. The convention is to utilize helpers that are namespaced after their respective controller or the application_helper.rb for global methods for your views. Not sure if that's what you are trying to accomplish with your module but wanted to throw that out there.
Create an initializer (you can all it whatever) in config/initializers/custom_modules.rb and add require 'my_module'
Update the a_method back to be self.a_method
You can now call MyModule.a_method in your app
Don't forget to restart your server for changes to lib/my_module.rb to take effect.
Also, a lot of people reference this post by Yehuda Katz as guidance on where to store code for your app. Thought it might be a helpful reference.
if you include MyModule into MyController, all the "instance methods" of the first will be mixed-in into the 2nd.
So if you only want to call MyModule.a_method, no need to include your module.
Then you'd want to require (or better autoload) your module before using it. To do so place it in controllers/concerns/my_module.rb, rails (4 at least) should autoload it, otherwise require its file in an intializer
# my_module.rb
module MyModule
def self.a_method
...
end
end
should work, but doing
# my_module.rb
module MyModule
extend self
def a_method
...
end
end
is more clean to me. You'd like to have a look to rails active support concern to understand the "rails way" on this topic.

(ruby on rails) how to override module method placed in lib/

I'm developing a web application in Rails 2.3.5.
I defined the module in lib/ folder as follows.
module TestModule
class Basic < ApplicationController
def show
p "module method"
end
end
end
and the load_paths are described in the environment.rb for this library as follows
Rails::Initializer.run do |config|
config.load_paths += %W[ #{RAILS_ROOT}/lib/test_module ]
end
but temporally, I want to override the above method without editing the lib method.
So, I put the override method in the "config/initializers/override_show.rb" as follows.
require_dependency "lib/test_module/basic.rb"
module TestModule
class Basic
def show
p "new method"
end
end
end
without "require_dependency", I get an error, because the original method located in lib/ folder wasn't loaded, so I put the "require_dependency" before overriding the TestModule.
In the above code, the new method works fine only once just after activating the server.
However, the new method is never called again, and the old method is called.
When I restart the rails server, the new method will be called just once.
Please give me some advice on how to override the method in the lib folder.
Thank you very much in advance.
Is it possible that you simply append the code in config/initializers/override_show.rb to lib/test_module/basic.rb ?

Can't access model from inside ActionController method added by a Rails engine

I am developing a Rails engine to be packaged as a gem. In my engine's main module file, I have:
module Auditor
require 'engine' if defined?(Rails) && Rails::VERSION::MAJOR == 3
require 'application_controller'
end
module ActionController
module Auditor
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def is_audited
include ActionController::Auditor::InstanceMethods
before_filter :audit_request
end
end
module InstanceMethods
def audit_request
a = AuditorLog.new
a.save!
end
end
end
end
ActionController::Base.send(:include, ActionController::Auditor)
where AuditorLog is a model also provided by the engine. (My intent is to have "is_audited" added to the controllers in an application using this engine which will cause audit logging of the details of the request.)
The problem I have is that when this code gets called from an application where the engine is being used, the AuditorLog model isn't accessible. It looks like Ruby thinks it should be a class in ActionController:
NameError (uninitialized constant
ActionController::Auditor::InstanceMethods::AuditorLog)
rather than a model from my engine.
Can anyone point me in the right direction? This is my first time creating an engine and attempting to package it as a gem; I've searched for examples of this and haven't had much luck. My approach to adding this capability to the ActionController class was based on what mobile_fu does, so please let me know if I'm going about this all wrong.
Use ::AuditorLog to access the ActiveRecord class (unless you have it in a module or namespace, in which case you'll need to include the module name).

Resources