【问题标题】:What is the value of self in a Rails model and why aren't obvious instance methods available?在 Rails 模型中 self 的价值是什么?为什么没有明显的实例方法可用?
【发布时间】:2012-07-12 18:14:04
【问题描述】:

我的 rails 3.1.6 应用程序中有一个自定义访问器方法,它为属性分配一个值,即使该值不存在。my_attr 属性是一个序列化的哈希,除非为空白,否则它应该与给定的值合并value 被指定,在这种情况下,它会将当前值设置为空白值。 (添加了检查以确保值是它们应有的值,但为简洁起见将其删除,因为它们不是我的问题的一部分。)我的 setter 定义为:

def my_attr=(new_val)
  cur_val = read_attribute(:my_attr)  #store current value

  #make sure we are working with a hash, and reset value if a blank value is given
  write_attribute(:my_attr, {}) if (new_val.nil? || new_val.blank? || cur_val.blank?)

  #merge value with new 
  if cur_val.blank?
    write_attribute(:my_attr, new_val)
  else
    write_attribute(:my_attr,cur_val.deep_merge(new_val))
  end
  read_attribute(:my_attr)
end

此代码按原样运行良好,但在我使用 self.write_attribute() 时却不行。然后我收到以下错误:

NoMethodError:
       private method `write_attribute' called for #<MyModel:0x00000004f10528>

因此,我的问题是:让 write_attribute 可用于实例似乎更合乎逻辑,那么为什么它仅可用于类而 可用于实例?我对 Ruby 或 Rails(或两者)中 self 的基本知识有什么不足吗?

【问题讨论】:

    标签: ruby-on-rails ruby self


    【解决方案1】:

    实例方法self 的内部就是那个实例。但是,当您使用显式接收器调用方法时,ruby 的可见性控制会启动并禁止调用私有方法。

    class Foo
      def implicit
        self # => #<Foo:0x007fc019091060>
        private_method
      end
    
      def explicit
        self # => #<Foo:0x007fc019091060>
        self.private_method
      end
    
      private
      def private_method
        "bar"
      end
    end
    
    f = Foo.new
    f.implicit # => "bar"
    f.explicit # => 
    # ~> -:9:in `explicit': private method `private_method' called for #<Foo:0x007fc019091060> (NoMethodError)
    # ~>    from -:25:in `<main>'
    

    如果要调用私有方法,请使用隐式接收器或send

    self.send :private_method
    

    更新

    摘自ruby metaprogramming book

    私有的真正含义

    现在您已经了解了 self,您可以对 Ruby 的 private 关键字重新认识。私有方法受一个简单的规则控制:您不能使用显式接收者调用私有方法。换句话说,每次调用私有方法时,它都必须在隐式接收者——self 上。让我们看一个极端案例:

    class C
      def public_method
        self.private_method 
      end
      private
      def private_method; end
    end
    C.new.public_method
    
    ⇒ NoMethodError: private method ‘private_method' called [...]
    

    您可以通过删除 self 关键字使此代码正常工作。

    这个人为的例子表明私有方法来自两个共同作用的规则:首先,您需要一个显式接收器来调用不是您自己的对象上的方法,其次,私有方法只能通过隐式接收器调用。把这两条规则放在一起,你会发现你只能调用自己的私有方法。你可以称之为“私人规则”。

    您会发现 Ruby 的私有方法令人困惑——尤其是如果您来自 Java 或 C#,它们的私有行为非常不同。当您有疑问时,只需回到私人规则,一切都会变得有意义。如果两个对象共享同一个类,对象 x 可以在对象 y 上调用私有方法吗?答案是否定的,因为无论你属于哪个类,你仍然需要一个显式的接收者来调用另一个对象的方法。您可以调用从超类继承的私有方法吗?答案是肯定的,因为你不需要显式的接收者来调用你自己的继承方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-06
      • 1970-01-01
      • 2014-12-22
      • 2015-08-09
      • 2011-12-07
      • 1970-01-01
      • 2015-12-11
      • 1970-01-01
      相关资源
      最近更新 更多