Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot call Contract in AS#included block #176

Closed
PikachuEXE opened this issue Jul 8, 2015 · 19 comments
Closed

Cannot call Contract in AS#included block #176

PikachuEXE opened this issue Jul 8, 2015 · 19 comments

Comments

@PikachuEXE
Copy link
Collaborator

Env

MRI 2.2.2 or 2.2.3

What happened

  • I extended a class (e.g. Post) with a module (e.g. ClassMethods) where ClassMethods has included Contracts
  • When I call Contract method inside Post after including ClassMethods:
    • It works fine in 0.9
    • It raises an error in 0.10.1 or 0.11.0 (see the Error section below)

To reproduce the error please look at the test script gist
Too long to read? Shorter version: https://gist.github.com/PikachuEXE/1672d5c0434863bee6ae

Error

Message from application: undefined method `Contract' for Class:Class (NoMethodError)

Updates

Update 1: Even after I have removed ActiveSupport::Concern usage (and switch to Module#included, it's still failing. So I updated the code to ensure it's not AS related.
Update 2: Included Ruby versions
Update 3: Added test script gist
Update 4: Update the test script again (removed AS dependency)

@PikachuEXE
Copy link
Collaborator Author

Still no idea what causes this yet, too many commits to read :S

@PikachuEXE
Copy link
Collaborator Author

I have the following AR model:

require "contracts"
require "contracts/version"

class Agency < ActiveRecord::Base
  p Contracts::VERSION
  include Contracts
  p respond_to?(:Contract)
  p method(:Contract).source_location

  p Contract None => Any
  def whatever_1
  end

  include ConcernModule

  module ConcernModule
    extend ActiveSupport::Concern

    included do
      include Contracts
      p respond_to?(:Contract)
      p method(:Contract).source_location

      p None => Any
      def whatever_2
      end
    end
  end
  # rest
end

For 0.9:

"0.9"
true
["/Users/PikachuEXE/.rvm/gems/ruby-2.2.2/gems/contracts-0.9/lib/contracts/decorators.rb", 222]
[[Contract, [{Contracts::None=>Contracts::Any}]]]
true
["/Users/PikachuEXE/.rvm/gems/ruby-2.2.2/gems/contracts-0.9/lib/contracts/decorators.rb", 222]
[[Contract, [{Contracts::None=>Contracts::Any}]]]

For 0.10:

"0.10"
true
["/Users/PikachuEXE/.rvm/gems/ruby-2.2.2/gems/contracts-0.10/lib/contracts/decorators.rb", 32]
[[Contract, [{Contracts::None=>Contracts::Any}]]]
true
["/Users/PikachuEXE/.rvm/gems/ruby-2.2.2/gems/contracts-0.10/lib/contracts.rb", 41]
/Users/PikachuEXE/.rvm/gems/ruby-2.2.2/gems/contracts-0.10/lib/contracts.rb:42:in `Contract': undefined method `Contract' for Class:Class (NoMethodError)

@egonSchiele
Copy link
Owner

Ugh, wonder how this happened. Thanks for reporting.

@waterlink
Copy link
Collaborator

Might be related to engine refactoring. If you look at the output of #source_location you will see, that in second case it is different. And in this place it tries to do self.class.Contract(*args). And class of current object doesn't have Contract method.

And Class:Class there scares me. That means that it is already being called on class of the class, and on top it tries to go even further and call it on Class of Class of YourClass. ie:

YourClass     # => YourClass
_.class       # => Class
_.class       # => Class:Class     <------ this is exactly what `Contract` try to being called on..

@PikachuEXE
Copy link
Collaborator Author

0.11.0 has not fixed this issue

@PikachuEXE
Copy link
Collaborator Author

Any update on this yet?

@egonSchiele
Copy link
Owner

Nope...I need to do a git bisect when I have some time...

@waterlink
Copy link
Collaborator

I will look into it today (2-2.5 hours) later.

@waterlink
Copy link
Collaborator

@PikachuEXE I don't have this problem on active* = 4.2.4 and with contracts = 0.11.0. Though I had to use Contract Contracts::None => Contracts::Any because include in block does not change syntactical scope.

Which version of active* do you have?

@waterlink
Copy link
Collaborator

Just tried following versions too, and all of them worked:

  • = 4.2.4
  • = 4.1.13
  • = 4.0.13
  • = 3.2.22

@waterlink
Copy link
Collaborator

And what about ruby version, BTW ?

@PikachuEXE
Copy link
Collaborator Author

Sorry for not providing those ;P
I am switching away from AS Concern
And this problem can be reproduced with the replacement #class_eval & #included hook
Reproduced on MRI 2.2.2 & 2.2.3

@waterlink
Copy link
Collaborator

So if you say, that code is AS/AR independent, can it be rewritten like this?:

class Something
  module WithSettings
    def self.included(base)
      base.class_eval do
        include Contracts

        Contract None => ArrayOf[String]
        def emails
          #...
        end
      end
    end
  end
end

?

@waterlink
Copy link
Collaborator

So, I'm trying to run this guy: https://gist.github.com/waterlink/ca3080bfee096fa7d08a
with:

contracts: 0.11.0
ruby: 2.2.3

And I don't get any strange error. Everything works just fine..

@PikachuEXE do you see anything I am not doing correctly to reproduce the bug?

@PikachuEXE
Copy link
Collaborator Author

Let me create a test script that can reproduce the bug at work...
I am not at work now :P

@PikachuEXE
Copy link
Collaborator Author

OK test script gist created: https://gist.github.com/PikachuEXE/e7efb3a58ef3c9764722

@alex-fedorov
Copy link
Collaborator

Well, code definitely is AS dependent. Removing extend ActiveSupport::Concern makes it work.

@waterlink
Copy link
Collaborator

@PikachuEXE
Copy link
Collaborator Author

Got a more simple version than my previous gist: https://gist.github.com/PikachuEXE/1672d5c0434863bee6ae

Not much different from https://gist.github.com/waterlink/0018249f6c3b86403d87
But added 2 examples:

  • extending Contracts in class
  • including Contracts in singleton class
    They both pass

waterlink added a commit to waterlink/contracts.ruby that referenced this issue Sep 9, 2015
waterlink added a commit to waterlink/contracts.ruby that referenced this issue Sep 9, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants