A Life on Rails

Ruby/Ruby on Rails, WEB programing...

Railsのhas_oneのsaveに注意!

has_one(1:1)のアソシエーションの場合

autosave: trueを付けないと、アソシエーション先authorのnameを変更してpostをsaveしても、authorはsaveされない

class Post
  has_one :author, autosave: true
end
post = Post.find(1)
post.title       # => "The current global position of migrating ducks"
post.author.name # => "alloy"

post.title = "On the migration of ducks"
post.author.name = "Eloy Duran"

post.save
post.reload
post.title       # => "On the migration of ducks"
post.author.name # => "Eloy Duran”

has_many(1:N)のリレーションの場合

  • autosaveオプションは、不要
  • postをsaveすると、commentsもsaveされる
class Post
  has_many :comments # :autosave option is not declared
end

post = Post.new(title: 'ruby rocks')
post.comments.build(body: 'hello world')
post.save # => saves both post and comment

post = Post.create(title: 'ruby rocks')
post.comments.build(body: 'hello world')
post.save # => saves both post and comment

post = Post.create(title: 'ruby rocks')
post.comments.create(body: 'hello world')
post.save # => saves both post and comment

ActiveRecord::AutosaveAssociation

rubyのrequire/include/extend/継承/クラス拡張ミックスインまとめ

require/include/extend/継承の違い

require

  • 外部ライブラリファイルの読み込み

include

  • クラスでモジュールをインクルードすると、モジュールのインスタンスメソッドが手に入る obj.my_method
  • クラスメソッドを手に入れるには、クラスの特異クラスでモジュールをインクルードする:MyClass.my_method
    • それをショートカットするのがextends。:MyClass.my_method
module A
 def hello
  "hello"
 end
end

class B
 include A
end

test = B.new
test.hello => #"hello"
B.hello => #"NoMethodError"

extend

  • includeはインスタンスメソッドを差し込むのに対して、extendは特異メソッドをインクルードするかたちになる
  • だからクラス定義の中でextendすると、クラスの特異メソッド=クラスメソッドになる
  • クラスの特異メソッドにインクルード = extend
class C
 extend A
end

C.hello => #"hello"
test = C.new
test.hello => #"NoMethodError"
test.extend(A)
test.hello => #"hello"
module M1
  module ClassMethods
    def print01
      puts "#print01"
    end
  end
end

class C1
  extend M1::ClassMethods
end

C1.print01

クラスの特異メソッドにインクルード

class T01
  class << self
    include Post3
  end
end

T01.print_post3

継承

  • rubyでは単一継承のみ
  • 多重継承は、mix-in(後述)という概念で対応している
class Ex01
  def print01
    puts "#print01"
  end
 
  class << self
    def print02
      puts "#print02"
    end
  end
end

class T04 < Ex01
 
end

t04 = T04.new
t04.print01
T04.print02

クラス拡張ミックスイン

module Foo
  def self.included base
    base.send :include, InstanceMethods
    base.extend ClassMethods
  end

  module InstanceMethods
    def bar1
      puts 'bar1'
    end
  end

  module ClassMethods
    def bar2
      puts 'bar2'
    end
  end
end

class Test
  include Foo

  bar2
end

t1 = Test.new
t1.bar1 

実行結果

bar1
bar2

注意点

base.include InstanceMethods

これだと

mixin.rb:3:in `included': private method `include' called for Test:Class (NoMethodError)

といって、怒られる includeは、プライベートメソッドなので、base.sendが必要

ruby - Inheriting class methods from mixins - Stack Overflow

includeしているmoduleをextendする

module Mod1
  def module_method1
    puts "#module_method1"
  end
end

module Core
  include Mod1
end

class T01
  extend Core
end

# Core.module_method1 => error
T01.module_method1
t01 = T01.new
# t01.module_method1 => error

module Mod1
  def module_method1
    puts "#module_method1"
  end
end

module Core
  include Mod1
end

class T01
  extend Core
 
  module_method1
end

実行結果

#module_method1
#module_method1

メソッド内でのinclude/extend

  • メソッドをコールした時にinclude/extendすることができる
module Post
  def print_post
    puts "#print_post"
  end
end

module Post2
  def print_post2
    puts "#print_post2"
  end
end

class T03
  class << self
    def register
      # includeでは、エラー
      extend Post
    end
   
    def register2
      class << self
        include Post2
      end
    end
  end 
end

# registerを実行してはじめて、extendされる
# その後、print_post1が実行できる
T03.register
T03.print_post

# register2を実行してはじめて、includeされる
# その後、print_post2が実行できる
T03.register2
T03.print_post2