Red > Green > Refactor > Red

cycle is based on desire

save/save! create/create!

前提情報

create_table "products", force: true do |t| 
  t.string   "title"
  t.text     "description"
  t.string   "image_url"
  t.decimal  "price",       precision: 8, scale: 2
  t.datetime "created_at"
  t.datetime "updated_at"
end 

class Product < ActiveRecord::Base
  has_many :line_items
  before_destroy :ensure_not_referenced_by_any_line_item
  validates :title, :description, :image_url, presence: true
  validates :price, numericality: {greater_than_or_equal_to: 0.01}
  validates :title, uniqueness: true
  validates :image_url, allow_blank: true, format: {
    with:    %r{\.(gif|jpg|png)\Z}i,
    message: 'must be a URL for GIF, JPG or PNG image.'
  }
  validates :title, length: {minimum: 10} 
...

[save/save!]

new して validation が通ったとき、DBへデータを保存 => save/save!

失敗したとき(あくまでvalidationの失敗)

save #=> false [1]

save! #=> ActiveRecord::RecordInvalid [2]

[1]

2.0.0p353 :001 > p = Product.new(title: 'foo')
 => #<Product id: nil, title: "foo", description: nil, image_url: nil, price: nil, created_at: nil, updated_at: nil> 
2.0.0p353 :002 > p.save
   (0.4ms)  begin transaction
  Product Exists (0.2ms)  SELECT 1 AS one FROM "products" WHERE "products"."title" = 'foo' LIMIT 1
   (0.1ms)  rollback transaction
 => false 

[2]

2.0.0p353 :003 > p.save!
   (0.2ms)  begin transaction
  Product Exists (0.2ms)  SELECT 1 AS one FROM "products" WHERE "products"."title" = 'foo' LIMIT 1
   (0.1ms)  rollback transaction

ActiveRecord::RecordInvalid: Validation failed: Description can't be blank, Image url can't be blank, Price is not a number, Title is too short (minimum is 10 characters)
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/validations.rb:57:in `save!'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/attribute_methods/dirty.rb:41:in `save!'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/transactions.rb:275:in `block in save!'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/transactions.rb:326:in `block in with_transaction_returning_status'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/connection_adapters/abstract/database_statements.rb:202:in `block in transaction'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/connection_adapters/abstract/database_statements.rb:210:in `within_new_transaction'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/connection_adapters/abstract/database_statements.rb:202:in `transaction'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/transactions.rb:209:in `transaction'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/transactions.rb:323:in `with_transaction_returning_status'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/transactions.rb:275:in `save!'
    from (irb):5
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/railties-4.0.0/lib/rails/commands/console.rb:90:in `start'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/railties-4.0.0/lib/rails/commands/console.rb:9:in `start'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/railties-4.0.0/lib/rails/commands.rb:64:in `<top (required)>'
    from bin/rails:4:in `require'
    from bin/rails:4:in `<main>'

[create/create!]

モデルのインスタンスを生成してsaveもする => create/create!

create #=> [1] DBへの保存に成功しても失敗しても、結果のオブジェクトは戻ってくる

2.0.0p353 :001 > pr = Product.create(title: 'foo')
   (0.2ms)  begin transaction
  Product Exists (0.2ms)  SELECT 1 AS one FROM "products" WHERE "products"."title" = 'foo' LIMIT 1
   (0.1ms)  rollback transaction
 => #<Product id: nil, title: "foo", description: nil, image_url: nil, price: nil, created_at: nil, updated_at: nil> 
2.0.0p353 :002 > pr
 => #<Product id: nil, title: "foo", description: nil, image_url: nil, price: nil, created_at: nil, updated_at: nil> 

create! #=> [2] DBへの保存に成功しても失敗しても、結果のオブジェクトは戻ってこない

2.0.0p353 :01 > pr = Product.create!(title: 'foo')
   (0.2ms)  begin transaction
  Product Exists (0.2ms)  SELECT 1 AS one FROM "products" WHERE "products"."title" = 'foo' LIMIT 1
   (0.1ms)  rollback transaction
ActiveRecord::RecordInvalid: Validation failed: Description can't be blank, Image url can't be blank, Price is not a number, Title is too short (minimum is 10 characters)
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/validations.rb:57:in `save!'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/attribute_methods/dirty.rb:41:in `save!'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/transactions.rb:275:in `block in save!'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/transactions.rb:326:in `block in with_transaction_returning_status'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/connection_adapters/abstract/database_statements.rb:202:in `block in transaction'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/connection_adapters/abstract/database_statements.rb:210:in `within_new_transaction'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/connection_adapters/abstract/database_statements.rb:202:in `transaction'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/transactions.rb:209:in `transaction'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/transactions.rb:323:in `with_transaction_returning_status'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/transactions.rb:275:in `save!'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/activerecord-4.0.0/lib/active_record/validations.rb:41:in `create!'
    from (irb):10
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/railties-4.0.0/lib/rails/commands/console.rb:90:in `start'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/railties-4.0.0/lib/rails/commands/console.rb:9:in `start'
    from /home/matsu/.rvm/gems/ruby-2.0.0-p353@two/gems/railties-4.0.0/lib/rails/commands.rb:64:in `<top (required)>'
    from bin/rails:4:in `require'
    from bin/rails:4:in `<main>'2.0.0p353 :02 > pr
 => nil