Search code examples
rubymonkeypatchingprepend

Ruby using prepend to patch a class doesn't work


I'm trying to patch the verb methods in sinatra to add an extra function call before it. I was having problems in using an alias chain to keep the original methods, but I discovered that prepend would allow me to do what I want without using such a hacky method.

However my prepended functions are not being called and are simply being ignored. what's going on?

Here's my patch:

if defined? Sinatra::Base   
    module Restman
        module Patches
            module Sinatra_Base_Patch           
                [:get, :post, :put, :delete, :head, :options, :patch, :link, :unlink].each do |func_name|
                    define_method func_name do |*args,&block|
                        if args.first.class == Symbol
                            super(Restman::Routes.get(args.first)[:uri],*block)
                        else
                            super(*args,*block)
                        end

                    end
                end
            end
        end
    end
    ::Sinatra::Base.prepend Restman::Patches::Sinatra_Base_Patch
end

EDIT:(explanation)

The patch is pretty simple, it overrides sinatra's normal HTTP verb methods and checks if a symbol was passed to it or not, if one was then it passes the symbol to a method that returns a mapping and takes the url out of the mapping and then passes that up to sinatra's normal HTTP verb methods.

This is so I can have:

Restman::Routes.define do
  map :root, to: '/'
end

and then do

get :root do
 'hello world!'
end

I thinking of trying refinements, that might work better.. maybe?


Solution

  • The get, post, put etc. methods in Sinatra are class methods, but you are creating instance methods with those names. In order to intercept the methods you need to prepend the Sinatra::Base singleton class.

    Try this:

    ::Sinatra::Base.singleton_class.prepend Restman::Patches::Sinatra_Base_Patch