#371 ✓resolved
Carl Lerche

A lot of funkiness going on with Hooks and dm-more

Reported by Carl Lerche | June 11th, 2008 @ 05:55 PM

Alright, I will try to explain this as best as I can. First, let me start by explaining how I came across this bug.

I created a simple model in Merb and I required dm-validations. All is good. I can save my model and specify the context ( User.new.save(:default) ).

Ok, now I decide that I would like to have the dm-timestamps extension. I require dm-timestamps BEFORE dm-validations.

All of a sudden, I have the OLD #save method (without validations). However, all the other methods from the Validation module are present. I can still do User.new.valid?

Funkiness!!!!

So, I decided to do some digging. I isolated the problem to line #33 in dm-timestamps.rb (model.before :save, :set_timestamp_properties).

It turns out that there is a bug with declaring instance hooks inside a def self.included(base) method. I believe that this method is run BEFORE the module is actually included (at least, before the class that is including the module has the methods defined in it). Now, I checked out the Hook code and hooks work by redefining the method that is being hooked (seems a bit fishy to me). In other words, the method that is being hooked is being redefined before it is actually defined by a module that is being included... or something.

I'm not exactly sure what it is, but it smells fishy.

Personally, I don't get why Hook is coded as is instead of something simpler where callbacks are explicitly invoked. It seems pretty complicated as is.

Comments and changes to this ticket

  • Carl Lerche

    Carl Lerche June 11th, 2008 @ 05:59 PM

    The other tickets I found relating to Hooks are #343 and #322. I'm not sure if they are related.

  • Carl Lerche

    Carl Lerche June 11th, 2008 @ 09:51 PM

    Check out this little bit of code. It demos why the bug happens.

    
    #!/usr/bin/env ruby
    
    module FirstModule
      
      def self.included(base)
        base.class_eval %{
          def test
            "world"
          end
        }
        base.class_eval { include SecondModule }
      end
      
    end
    
    module SecondModule
      
      def test
        "hello"
      end
      
    end
    
    class MyClass
      include FirstModule
    end
    
    # Will put "world". Hook assumes it will put "hello"
    puts MyClass.new.test
    
    

    Basically, dm-timestamp adds a before save Hook when included (using class_eval and rewriting the method being hooked) and dm-validations tries to include a new version of save so the dm-validations save method is completely lost.

  • Carl Lerche

    Carl Lerche June 11th, 2008 @ 11:18 PM

    Welp, I think I figured the problem out and I created a patch and spec.

  • Adam French

    Adam French June 12th, 2008 @ 10:48 AM

    • State changed from “new” to “resolved”

    I just applied the patch and it works like a charm. Thanks a bunch carl!

Please Sign in or create a free account to add a new ticket.

With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

People watching this ticket

Attachments

Referenced by

Pages