4

Rails ではActiveRecord::Base.new、データベースにまだ保存されていない新しいレコードをインスタンス化するために使用されます。

new_user = User.new(name: "Bob")
new_user.new_record? # => true

では、Rails はデータベースから取得したレコードをどのようにインスタンス化するのでしょうか? 同じ新しいメソッドを使用し、@new_record事後のように値を変更しますか? それとも、データベースから取得したレコードに対して何らかの特別なインスタンス化方法を使用していますか?

4

2 に答える 2

5

new_record ? メソッドは、ActiveRecord フレームワークのactive_record/persistence.rbにあり、次のようになります。

def new_record?
  @new_record
end

次に、コンストラクターでactive_record/core.rbを見ると、次のように表示されます。

def initialize(attributes = nil, options = {})
  @attributes = self.class.initialize_attributes(self.class.column_defaults.deep_dup)
  @columns_hash = self.class.column_types.dup

  init_internals # here

  ensure_proper_type

  populate_with_current_scope_attributes

  assign_attributes(attributes, options) if attributes

  yield self if block_given?
  run_callbacks :initialize if _initialize_callbacks.any?
end

コードをもう少し深く掘り下げると、次のようになります。

def init_internals
  pk = self.class.primary_key

  @attributes[pk] = nil unless @attributes.key?(pk)

  @aggregation_cache       = {}
  @association_cache       = {}
  @attributes_cache        = {}
  @previously_changed      = {}
  @changed_attributes      = {}
  @readonly                = false
  @destroyed               = false
  @marked_for_destruction  = false
  @new_record              = true # here
  @mass_assignment_options = nil
end

ご覧のとおり、@new_record はデフォルトで true に初期化されています

ただし、レコードを複製するときのように、 @new_record 属性が true に設定される場合があります。

user = User.first
new_user = user.clone

これにより、次のようなinitialize_dupメソッドが呼び出されます。

def initialize_dup(other) # :nodoc:
  # Code removed

  @new_record  = true

  # Code removed
  super
end

もちろん、ActiveRecord がデータベースからレコードを取得するときも同様です。この部分についてはよくわかりませんが、このメソッドが呼び出されていると思います:

def init_with(coder)
  @attributes = self.class.initialize_attributes(coder['attributes'])
  @columns_hash = self.class.column_types.merge(coder['column_types'] || {})

  init_internals

  @new_record = false

  run_callbacks :find
  run_callbacks :initialize

  self
end

これは次のように行うことができます:

post = Post.allocate
post.init_with('attributes' => { 'title' => 'hello world' })

最初のステートメントでは、new のようにコンストラクターを呼び出さずに、ヒープにメモリ空間を割り当てます。次に、特別なコンストラクターinit_withを呼び出します。

于 2013-05-30T21:40:48.830 に答える