我正在阅读教程(我必须说这是一个很好的资源),我不太了解以下内容:
在6.3.1节中,我们通过创建和运行迁移脚本在db中创建一个password_digest列:
rails generate migration add_password_digest_to_users password_digest:string
bundle exec rake db:migrate
bundle exec rake db:test:prepare
bundle exec rspec spec/
然后在rails控制台上,我能够实例化一个用户模型对象并在其上设置password_digest:
irb(main):007:0> @user = User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil, password_digest: nil>
irb(main):008:0> @user.password_digest = "zzzz" => "zzzz"
irb(main):009:0> @user.password_digest => "zzzz"
但是我在User模型类定义中看不到password_digest属性:
class User < ActiveRecord::Base
attr_accessible :email, :name
before_save { |user| user.email = email.downcase}
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :name, presence: true, length: {maximum: 50}
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false}
end
我认为Rails在幕后做了一些魔术,有人会介意解释它在做什么吗?
谢谢!
答案 0 :(得分:1)
你是对的 - 这里的实际情况 在幕后魔力
。每当你有ActiveRecord::Base
的后代时,ActiveRecord会查看该类的数据库表并自动为你创建访问器 - 它们不会显示在类定义中。如果您来自C#之类的语言,那么这似乎是疯狂,之后您必须手动执行此类操作。
ActiveRecord正在做什么(这是一个非常淡化的解释,实际上它做得更复杂)是在你的类中坚持以下代码:
class User < ActiveRecord::Base
def password_digest
@password_digest
end
def password_digest=(val)
@password_digest = val
end
end
另外需要注意的是,它不仅仅为您创建属性getter和setter - 它根据列的类型混合了某种类型的转换。查看this question以获取更多信息和一些可能的问题。
这样做的最终结果实际上是一种奖励,也是我喜欢rails的原因之一:你在数据库中定义一次列,然后将它放到模型类中免费。
这种模式对于Rails来说很常见,你会经常看到它。如果您仍在学习Ruby或Rails框架,并且您不能100%确定某些内容来自何处,请不要害怕看得更近 - 所谓的Rails'魔术'经常发生并且需要一些时间才能成为惊讶。当我第一次从其他语言转移到Rails时,我有这种经历。
答案 1 :(得分:0)
以下是一些类可以拥有一个在类定义中看不到的成员变量的方法:
class ActiveRecord
def password_digest=(val)
@x = val
end
def password_digest
@x
end
end
class User < ActiveRecord
end
me = User.new
me.password_digest = "hello"
puts me.password_digest #=> "hello"
在运行时动态创建:
class User
end
User.class_eval do
attr_accessor :password_digest
end
me = User.new
me.password_digest = "hello"
puts me.password_digest #=> "hello"
我在rails教程中发现的问题是:
1) It is extremely boring.
2) Because all you do is copy code.
祝贺你进入第6章!