1

I've got a library module I'd like to override based on the rails environment I'm running in

Module is located in lib/package/my_module.rb:

module Package
  module MyModule
    puts "Defining original module"
    def foo
      puts "This is the original foo"
    end
  end
end

I have been able to partially solve with the info at Overriding a module method from a gem in Rails - specifically, in my environments/dev_stub.rb:

Package::MyModule.module_eval do
 puts "Defining override"
 def foo
   puts "This is foo override"
 end
end

(The other solution at that link seems to cause errors when rails tries to lookup other classes related to package)

Now, this seems to get me most of the way there, and works if I set

config.cache_classes = true

...but I want to use this as a stub development environment, and the comment recommendation on this value for a dev environment is to use false... in which case the override only works the first time the module is included, and any subsequent times, it uses the original.

My question: Am I going about this the right way? I could hack up the lib module itself to conditionally override based on RAILS_ENV, but I'd like to keep it cleaner than that...

Edit

My use case for this is to reference it from a controller function. If I have

class SomethingController < ApplicationController
  def show
    Package::MyModule.foo
  end
end

and config.cache_classes=false (which I ideally want since it is a development environment), and access the action through my web browser (http://localhost/something/show) then the first time I hit it, my override is loaded and it works, but the second and any subsequent times, the original library class is reloaded (outputs "Defining original module" on my console without "Defining override"), and the override is lost.

Another alternative I tried was add something like config.load_paths += %W( #{RAILS_ROOT}/lib_patch/#{RAILS_ENV}) to environment.rb - but defining the same module/class didn't quite work without putting in an explicit hook in the original library to basically load the patch if it existed

Edit 2 (in response to @apneadiving answer)

I've tried doing this without module_eval, and just using the following in development_stub.rb:

require 'package/my_module'
module Package
  module MyModule
    puts "Defining override"
    def foo
      puts "This is foo override"
    end
  end
end

The problem I initially had with doing this is that Rails no longer automatically finds all content in my lib directory, and I need to sprinkle 'require' statements throughout all other lib files (and my controllers that reference the libs) to cover all of their dependencies. Although this is all done, it does work, but it also has a similar effect as config.cache_classes=true does, in that all the lib classes are not reloaded on change, even in my regular development environment that does not have a monkey-patch (since all the 'require' statements are added).

Community
  • 1
  • 1
Krease
  • 15,805
  • 8
  • 54
  • 86

2 Answers2

1

Setting config.cache_classes=true in dev_stub.rb and using module_eval to define the patch as described in the question seems the way to go for what the goal is here - to create an environment specific patch for a module that doesn't impact the other environments in both code path and Rails class loading behavior.

Krease
  • 15,805
  • 8
  • 54
  • 86
0

you could simply override the module and it's instance without module_eval.

I guess your module is included as a Mixin and it's methods aren't impacted by your monkey patch.

That's where alias_method_chain comes in action.

Look at this great article to get how you should use it to fit your needs.

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
apneadiving
  • 114,565
  • 26
  • 219
  • 213
  • I've actually tried it without using module_eval, but something in Rails doesn't like that and it causes the definitions not to be found when I try to use my other lib code. I am using my module as a Mixin, though the method I'm overriding is also in the same module, so I'm not sure alias_method_chain is useful here. – Krease Jul 29 '11 at 20:53
  • Just retried overriding module method with simple redefinition, it works fine. I can't help you more there. – apneadiving Jul 29 '11 at 21:25
  • I am running an older version of Rails (2.3.8), so maybe that has something to do with it? – Krease Jul 29 '11 at 22:51
  • I don't think so. Whatever Rails version, this is sheer ruby. – apneadiving Jul 30 '11 at 11:35
  • I've edited my question to provide a bit more details on the Rails context for this, and why the patch only works the first time. – Krease Aug 04 '11 at 23:19