Frage

module Foo
  attr_accessor :val
end

module Bar
  def bar
    val = 50
  end
end

class FooBar
  include Foo
  include Bar
end

fb = FooBar.new
puts fb.bar # 50
puts fb.val # nil

I would like to, from within Bar, change Foo's "val" value.

Just so you know, I'm trying to do this because I want to add user authentication through a Facebook Canvas app in Clearance (0.8.2 (old, I know)). The variable that I want to change is https://github.com/thoughtbot/clearance/blob/b8ccca00d91cb336e044b76b5c6ae1d8de9d2d8d/lib/clearance/authentication.rb#L25.

This module gets included into ApplicationController, and I'm including another module afterwards (FacebookAuthenticationHelper) that does something like

def authenticate_facebook
  current_user ||= facebook_user if coming_from_facebook?
end

I would love to know if there's a better way to do this as well. I'm not using OAuth, I'm just saving Facebook's user_id sends to my app from signed_request into my DB.

Thanks!

War es hilfreich?

Lösung

In ruby, any statement of the form varname = value will create a local variable named varname if it doesn't already exist. This is even true within class methods where the class has a setter method of the same name. Furthermore, if a local variable exists, it takes precedence over getters and setters. For example:

class Demo
    attr_accessor :foo

    def demonstrate!
        @foo = 1      #1: Set member variable foo, so we get meaningful output
        puts foo      #2: Prints 1 (this calls the getter)
        puts self.foo #3: Prints 1 (also calls the getter)

        foo = 2       #4: Creates a LOCAL variable named foo with the value 2
        puts foo      #5: Prints 2 (locals take precedence over getters)
        puts self.foo #6: Prints 1 (calls the getter - the member variable hasn't changed)

        self.foo = 3  #7: Use SELF to ensure you call a getter or setter
        puts foo      #8: Prints 2 (the local again)
        puts self.foo #9: Prints 3 (the member, which is now 3)
    end
end

The bad thing about this system is: Look at lines 2 and 5. The same exact code does different things! On line 2, the local variable foo didn't exist yet, so ruby did the "next best" thing and called the getter. But on line 5, the local foo exists, so it takes precedence.

I'd say that this is bad language design: If setters can't be called without self, why should it be okay for getters - especially when it can lead to rickety, context-sensitive code like the above? But it's what we have to work with, and it leads to some general guidelines:

  1. If you can, use @foo. It's obvious what that does.
  2. If you're using a getter or setter, always use self, even when it's not strictly necessary. This makes it obvious what you're calling. It also guarantees your code won't behave differently if you add a local with the same name later.
  3. If you do 1 and 2, you can assume that anything without a @ or a self is a local variable (or a function call, but you can probably tell those apart by name).

And that ended up being a bit long-winded, but I was unable to find a good demonstration of the getter/setter/local problem to link to. So maybe this is it - hope it's helpful!

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top