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

Rendering scope method lookups #28

Closed
viking opened this issue Aug 11, 2014 · 7 comments
Closed

Rendering scope method lookups #28

viking opened this issue Aug 11, 2014 · 7 comments
Assignees
Labels

Comments

@viking
Copy link

viking commented Aug 11, 2014

If I have a View method called select, if I try to call select in the template, it calls Kernel#select instead of my view's select method.

The output of p method(:select) in the context of the template is:

#<Method: Lotus::View::Rendering::Scope(Kernel)#select>
@viking
Copy link
Author

viking commented Aug 11, 2014

Is this a bug or intended?

@jodosha
Copy link
Member

jodosha commented Aug 12, 2014

It looks like a bug.
Can you please add the code here to replicate the error?

On 11/ago/2014, at 18:23, Jeremy Stephens notifications@github.com wrote:

Is this a bug or intended?


Reply to this email directly or view it on GitHub.

@viking
Copy link
Author

viking commented Aug 12, 2014

@jodosha jodosha self-assigned this Sep 2, 2014
@jodosha
Copy link
Member

jodosha commented Sep 2, 2014

@viking This is an update after an extensive debugging session.

Lotus::View uses Template to wrap a Tilt::Template subclass: an instance of Tilt::ERBTemplate or Tilt::HamlTemplate.

Each subclass can override some methods to customize the behavior: eg #prepare.
For all them who don't override #evaluate(scope, locals, &block), they inherit it from the superclass.

This implementation wants concrete methods from the given scope, because it does some tricks by bounding and then unbounding methods to it. In other words, it doesn't send the #select message which is covered by Lotus::View::Rendering::Scope#method_missing, but it tries to "capture" the method and then invoke #call on it. See Method#call.

As proof of what I'm saying, add your failing test, and modify the source of Scope with:

def select
  'x'
end

It will invoke this method, instead of Kernel#select, because Scope now implements a concrete method.

/cc @judofyr

@jodosha jodosha added the bug label Sep 2, 2014
@judofyr
Copy link

judofyr commented Sep 2, 2014

It appears that Rendering::Scope uses method_missing to delegate method
calls to the view. However, Scope seems to inherit from Object (which
includes Kernel). Thus calling #select will never hit method_missing
because it is indeed defined somewhere in the ancestors of Scope.

There's two solutions: (1) Inherit from BasicObject instead. (2) Explicitly
undef_method which you don't want to have preference over method_missing.

// Magnus Holm

@jodosha
Copy link
Member

jodosha commented Sep 2, 2014

@judofyr It works like a charm ✨ Thank you!

@viking
Copy link
Author

viking commented Sep 2, 2014

Excellent. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants