1

I'm trying to do some monkey patching in ActiveShipping UPS class .

I need to add a class level method (starting with .self), so here it's what I'm trying to do:

module ActiveMerchant module Shipping class UPS < Carrier def self.process_request(receiver, sender, packages, options = {}) # some code end def regular_method "foobar" end end end end 

Unfortunately when I'm trying to use it:

 ActiveMerchant::Shipping::UPS.process_request(receiver etc) 

I get an error:

NoMethodError: undefined method `process_request' for ActiveMerchant::Shipping::UPS:Class from (irb):6 from C:/Ruby19/bin/irb.bat:19:in `<main>' 

There is no class method named process_request in original class.

In original UPS class provided in gem there is one static method defined self.retry_safe = true and I can use it without errors.

I can also use regular_method after creating instance of UPS class.

More details provided:
I'm working with Rails 2.3 ( :-( ) and Ruby 1.9.2. I have no influce on environment.

Monkey patched code is under plugins/my_plugin/lib/active_shipping/ext/carriers/ups.rb

In /active_shipping I have file named extensions.rb in which i have:

require 'active_shipping' require_relative 'ext/carriers' require_relative 'ext/carriers/ups' 

It deals with loading everything properly (I suppose basing on regular_method beheaviour from first chunk of code in my question).

I try to invoke process_request in one of my Controllers. This part is little tricky, beacuse i'm using sth like this:

MyModel.courier_service.process_request(parameters)

where courier_service, in this case holds the ActiveMerchant::Shipping::UPS class.

I'm still a newbie in Ruby and don't know what sort of details i should provide.

2 Answers 2

3

Maybe you want to do it in another way

File patch_classes.rb:

 module ActiveMerchantExpand module Shipping module ClassMethods def self.process_request(receiver, sender, packages, options = {}) # some code end end module InstanceMethods def regular_method "foobar" end end def self.included(receiver) receiver.extend ClassMethods receiver.send :include, InstanceMethods end end end 

Then you have to load your class "ActiveMerchant::Shipping::UPS" and after that you can attach your methods to your class via

 Rails.configuration.to_prepare do require_dependency [[file for ActiveMerchant::Shipping::UPS]] require 'patch_classes' ) ActiveMerchant::Shipping::UPS.send(:include, ::ActiveMerchantExpand::Shipping) end 

This is from rails plugin writing, i hope this helps.

regards tingel2k

Sign up to request clarification or add additional context in comments.

1 Comment

I finally achieved my goal using simple monkey patching, but that's probably better way. It'll be on my mind when I find time to refactor that piece of code. Thanks
1

Do you explicitly require file with your monkey patch? If you just put it under your app or lib path without requiring, it wouldn't load because constant ActiveMerchant::Shipping::UPS is defined in gem and it doesn't trigger dependency resolution mechanism.

6 Comments

From my understanding I think blid require the file correctly. Otherwise he would not be able to call the regular_method as he said.
Yes, my file is loaded, just as Jing Li explained.
@blid, could you please provide more details. Coz for me it looks weird, no typo, everything looks good. And as far as I know, there is no way to prevent monkey patching. BTW, you can also try ActiveMerchant::Shipping::UPS.singleton_methods to print out all class methods of UPS, of course in your case it would not contain process_request.
@Jing Li: I provided details as you asked, if I can add something more please let me know.
Thanks for the details! Unfortunately I still can't see what goes wrong. You show NoMethodError exception coming from irb (rails console?), can you add full backtrace that comes from your controller? It should be in log/development.log. Did you test regular_method in both console and controller? Also, can you show how MyModel.courier_service gets set, and the contents of your plugin's init.rb: plugins/my_plugin/init.rb ?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.