Why can't I directly access instance variables in Active Record callback?

落爺英雄遲暮 提交于 2019-12-08 04:26:11

问题


In Active Record callbacks, I always see examples using self.variable. Sometimes, the self. prefix isn't needed, so they use just variable (see this related question).

From what I've read, these two methods of referencing the class instance variable are using the accessor function, whereas @variable would directly access the variable. In my code, I tried to use @variable and it didn't work - it was as if @variable was not defined yet... can you not access instance variables directly in an Active Record callback?

Also, I haven't defined an accessor function for this particular variable, so what actually happens when I say self.variable or variable (I've tried both, and both work aside from the LHS of an assignment)?

This question is essentially what I am asking, but I don't understand the answer and want more clarification (I can't comment on the answer because I'm new to Stack Overflow). His answer is this:

It [password] is the same as self.password. ActiveRecord defines methods, not instance variables to access stuff related to the database. That's why you cannot access it with @password.

But the callback methods I've seen are always defined in the model's class, which should give them access to instance variables, right? He also says this:

PS: Callbacks are only method calls on your object. The only difference is that Rails calls them for you, not yourself.

So does he maybe mean that the callback methods aren't call from a reference to the object? E.g. rather than calling as model_instance.callback_method, it is just called as callback_method? How would callback_method be found without a reference to an instance of the class if it is a class instance method? And even if it did work this way, how would it know what self.variable was?


回答1:


can you not access instance variables directly in an Active Record callback?

You can access instance variables from any instance method, including those used as ActiveRecord callbacks.

Also, I haven't defined an accessor function for this particular variable, so what actually happens when I say self.variable or variable (I've tried both, and both work aside from the LHS of an assignment)?

First, it is important to know what the difference between the two syntaxes. variable = 123 will assign the value 123 to a local variable. self.variable = 123 will call a method named variable= on self and pass 123 as an argument.

> class Foo
>   def x=(value)
>     puts "Foo#x called with #{ value }"
>   end
>
>   def bar
>     x = 123       # local variable assignment, `x` only exists inside the `bar` method.
>     self.x = 456  # calls `x=` 
>   end
> end

> Foo.new.bar
Foo#x called with 456

Something that can be a bit confusing is that methods invoked with an implicit receiver shares the same syntax with referencing a local variable.

> class Bar
>   def x
>     "In method x"
>   end
>
>   def foo
>     # No local variable `x` has been initialized yet, so references to `x` will call the method with that name.
>     puts x           # calls method `x` and prints "In method x".
>                      # This example is equivalent to `puts self.x`.
>
>     x = "Hello"      # Now that a local variable is defined it will hide the method `x`.
>     puts x           # references the local variable and prints "Hello".
>     puts self.x      # calls the method `x` and prints "In method x".
>   end
> end

> Bar.new.foo
In method x
Hello
In method x

Second, you need to know that ActiveRecord creates accessor methods for each column of your model's table. Let's say that I have a model User(id: integer, name: string, age: integer). ActiveRecord will define instance methods id, name and age to read the attributes and id=, name= and age= to set the attributes.

Instance variables are something else entirely. You cannot access the database attributes with instance variables like @id, @name, @age.

So does he maybe mean that the callback methods aren't call from a reference to the object?

No, ActiveRecord callbacks do indeed invoke instance methods on the object.




回答2:


I believe you don't quite understand that Rails attributes are not stored within instance variables. All your attributes (defined by the database table schema) are stored in one instance variable @attributes, which is intended to be never accessed directly, as it is a part of the Rails implementation details.

Example:

class Thing < ActiveRecord::Base
  # assume that we have a 'name' column in the database

  def foo
    # correct ways to access the name
    name
    self.name
    self[:name]

    # this contains name value and other name metadata
    # but you shouldn't use it
    @attributes[:name]

    # this holds nil as that variable has no relation to the "name" attribute
    @name
  end
end

In callbacks it works the same way as within all the other methods — you can access the instance variables defined by you, but you can't access database attributes as instance variables.

 class Thing < ActiveRecord::Base
  attr_accessor :secret

  after_initialize do
    @secret = '123'
  end

  before_create do
    p @secret
  end

  after_validation :on_validation
  def on_validation
    p @secret
  end
end

If you try to create a Thing object both the block-based before_create callback and the method-based after_validation callbacks can access the instance variable @secret defined in the after_initialize callback.



来源:https://stackoverflow.com/questions/33681188/why-cant-i-directly-access-instance-variables-in-active-record-callback

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!