The Depot Application

The Depot Application

21 Action Controller: Routing and URLs 19 ActiveRecord: Relationships Between Tables

20 ActiveRecord: Object Life Cycle

irb e1/ar/encrypt.rb
>> $: << File.dirname(__FILE__)
=> ["/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/sqlite3-ruby-1.3.1/lib", "/home/rubys/git/rails/lib", "/home/rubys/git/rails/railties/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/thor-0.14.0/lib", "/home/rubys/git/rails/activeresource/lib", "/home/rubys/git/rails/activerecord/lib", "/home/rubys/git/rails/actionmailer/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mail-2.2.5/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/treetop-1.4.8/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/polyglot-0.3.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mime-types-1.16/lib", "/home/rubys/git/rails/actionpack/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/tzinfo-0.3.23/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-test-0.5.4/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-mount-0.6.13/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-1.2.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/erubis-2.6.6/lib", "/home/rubys/git/rails/activemodel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/i18n-0.4.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/builder-2.1.2/lib", "/home/rubys/git/rails/activesupport/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/abstract-1.0.0/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rake-0.8.7/lib", "/home/rubys/git/gorp/lib", "/home/rubys/git/arel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/bundler-1.0.0/lib", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8/i686-linux", ".", "/home/rubys/git/awdwr/data/code/e1/ar"]
>> require "connect"
=> true
 
>> require "rubygems"
=> nil
>> require "active_record"
=> nil
 
>> ActiveRecord::Schema.define do
 
?>   create_table :orders, :force => true do |t|
?>     t.integer :user_id
>>     t.string  :name
>>     t.string  :address
>>     t.string  :email
>>   end
 
>>   create_table :users, :force => :true do |t|
?>     t.string :name
>>   end
>> end
-- create_table(:orders, {:force=>true})
   -> 0.0221s
-- create_table(:users, {:force=>:true})
   -> 0.0074s
=> nil
 
 
?> class ActiveRecord::Base
>>   def self.encrypt(*attr_names)
>>     encrypter = Encrypter.new(attr_names)
 
>>     before_save encrypter
>>     after_save  encrypter
>>     after_find  encrypter
 
>>     define_method(:after_find) { }
>>   end
>> end
=> nil
 
?> class Encrypter
 
>>   # We're passed a list of attributes that should
?>   # be stored encrypted in the database
?>   def initialize(attrs_to_manage)
>>     @attrs_to_manage = attrs_to_manage
>>   end
 
>>   # Before saving or updating, encrypt the fields using the NSA and
?>   # DHS approved Shift Cipher
?>   def before_save(model)
>>     @attrs_to_manage.each do |field|
?>       model[field].tr!("a-z", "b-za")
>>     end
>>   end
 
>>   # After saving, decrypt them back
?>   def after_save(model)
>>     @attrs_to_manage.each do |field|
?>       model[field].tr!("b-za", "a-z")
>>     end
>>   end
 
>>   # Do the same after finding an existing record
?>   alias_method :after_find, :after_save
>> end
=> Encrypter
 
?> class Order < ActiveRecord::Base
>>   encrypt(:name, :email)
>> end
=> #<Proc:0x00000000@/home/rubys/git/awdwr/data/code/e1/ar/encrypt.rb:33>
 
?> o = Order.new
=> #<Order id: nil, user_id: nil, name: nil, address: nil, email: nil>
>> o.name = "Dave Thomas"
=> "Dave Thomas"
>> o.address = "123 The Street"
=> "123 The Street"
>> o.email   = "dave@example.com"
=> "dave@example.com"
>> o.save
=> true
>> puts o.name
Dave Thomas
=> nil
 
>> o = Order.find(o.id)
=> #<Order id: 1, user_id: nil, name: "Dave Thomas", address: "123 The Street", email: "dave@example.com">
>> puts o.name
Dave Thomas
=> nil
>> >> => nil
sqlite3> select * from orders
     id = 1
user_id = 
   name = Dbwf Tipnbt
address = 123 The Street
  email = ebwf@fybnqmf.dpn
irb e1/ar/observer.rb
>> $: << File.dirname(__FILE__)
=> ["/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/sqlite3-ruby-1.3.1/lib", "/home/rubys/git/rails/lib", "/home/rubys/git/rails/railties/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/thor-0.14.0/lib", "/home/rubys/git/rails/activeresource/lib", "/home/rubys/git/rails/activerecord/lib", "/home/rubys/git/rails/actionmailer/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mail-2.2.5/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/treetop-1.4.8/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/polyglot-0.3.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mime-types-1.16/lib", "/home/rubys/git/rails/actionpack/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/tzinfo-0.3.23/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-test-0.5.4/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-mount-0.6.13/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-1.2.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/erubis-2.6.6/lib", "/home/rubys/git/rails/activemodel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/i18n-0.4.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/builder-2.1.2/lib", "/home/rubys/git/rails/activesupport/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/abstract-1.0.0/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rake-0.8.7/lib", "/home/rubys/git/gorp/lib", "/home/rubys/git/arel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/bundler-1.0.0/lib", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8/i686-linux", ".", "/home/rubys/git/awdwr/data/code/e1/ar"]
 
>> require "rubygems"
=> false
>> require "active_record"
=> true
 
>> require 'connect'
=> true
 
>> ActiveRecord::Base.logger = Logger.new(STDERR)
=> #<Logger:0xb6a7ef48 @default_formatter=#<Logger::Formatter:0xb6a7ef20 @datetime_format=nil>, @logdev=#<Logger::LogDevice:0xb6a7eef8 @shift_age=nil, @filename=nil, @dev=#<IO:0xb749255c>, @mutex=#<Logger::LogDevice::LogDeviceMutex:0xb6a7eed0 @mon_entering_queue=[], @mon_count=0, @mon_owner=nil, @mon_waiting_queue=[]>, @shift_size=nil>, @formatter=nil, @level=0, @progname=nil>
 
>> ActiveRecord::Schema.define do 
?>   create_table :payments, :force => true do |t|
?>   end
>> end
-- create_table(:payments, {:force=>true})
  *[1m*[36mSQL (0.3ms)  *[1mselect sqlite_version(*)
  *[1m*[35mSQL (1.3ms)   SELECT name
 FROM sqlite_master
 WHERE type = 'table' AND NOT name = 'sqlite_sequence'

    
  *[1m*[36mSQL (4.6ms)  *[1mCREATE TABLE "payments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL) 
   -> 0.0199s
=> nil
 
>> class Order < ActiveRecord::Base
>> end
=> nil
 
>> class Payment < ActiveRecord::Base
>> end
=> nil
 
>> class Refund < ActiveRecord::Base
>> end
=> nil
 
?> class OrderObserver < ActiveRecord::Observer
>>   def after_save(an_order)
>>     an_order.logger.info("Order #{an_order.id} created")
>>   end
>> end
=> nil
 
>> OrderObserver.instance
=> #<OrderObserver:0xb69f0db0>
 
?> class AuditObserver < ActiveRecord::Observer
 
>>   observe Order, Payment, Refund
 
>>   def after_save(model)
>>     model.logger.info("[Audit] #{model.class.name} #{model.id} created")
>>   end
>> end
=> nil
 
>> AuditObserver.instance
=> #<AuditObserver:0xb69db21c>
 
 
>> o = Order.create
  *[1m*[35mSQL (0.6ms)   SELECT name
 FROM sqlite_master
 WHERE type = 'table' AND NOT name = 'sqlite_sequence'

    
  *[1m*[36mSQL (0.4ms)  *[1mINSERT INTO "orders" ("address", "email", "name", "user_id") VALUES (NULL, NULL, NULL, NULL)
Order 2 created
[Audit] Order 2 created
=> #<Order id: 2, user_id: nil, name: nil, address: nil, email: nil>
>> p = Payment.create
  *[1m*[35mSQL (0.3ms)  INSERT INTO "payments" VALUES(NULL)
[Audit] Payment 1 created
=> #<Payment id: 1>
 
?> >> 
irb e1/ar/attributes.rb
>> $: << File.dirname(__FILE__)
=> ["/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/sqlite3-ruby-1.3.1/lib", "/home/rubys/git/rails/lib", "/home/rubys/git/rails/railties/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/thor-0.14.0/lib", "/home/rubys/git/rails/activeresource/lib", "/home/rubys/git/rails/activerecord/lib", "/home/rubys/git/rails/actionmailer/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mail-2.2.5/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/treetop-1.4.8/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/polyglot-0.3.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mime-types-1.16/lib", "/home/rubys/git/rails/actionpack/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/tzinfo-0.3.23/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-test-0.5.4/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-mount-0.6.13/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-1.2.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/erubis-2.6.6/lib", "/home/rubys/git/rails/activemodel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/i18n-0.4.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/builder-2.1.2/lib", "/home/rubys/git/rails/activesupport/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/abstract-1.0.0/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rake-0.8.7/lib", "/home/rubys/git/gorp/lib", "/home/rubys/git/arel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/bundler-1.0.0/lib", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8/i686-linux", ".", "/home/rubys/git/awdwr/data/code/e1/ar"]
>> require "connect"
=> true
>> require "logger"
=> nil
>> require "pp"
=> ["PP"]
 
>> #ActiveRecord::Base.logger = Logger.new(STDERR)
 
?> require "rubygems"
=> nil
>> require "active_record"
=> nil
 
>> class LineItem < ActiveRecord::Base
>> end
=> nil
 
>> LineItem.delete_all
=> 1
 
>> LineItem.create(:quantity => 1, :product_id => 27, :order_id => 13, :unit_price => 29.95)
=> #<LineItem id: 3, product_id: 27, order_id: 13, quantity: 1, unit_price: #<BigDecimal:b740060c,'0.2995E2',8(8)>>
>> LineItem.create(:quantity => 2, :unit_price => 29.95)
=> #<LineItem id: 4, product_id: nil, order_id: nil, quantity: 2, unit_price: #<BigDecimal:b6cd2ba0,'0.2995E2',8(8)>>
>> LineItem.create(:quantity => 1, :unit_price => 44.95)
=> #<LineItem id: 5, product_id: nil, order_id: nil, quantity: 1, unit_price: #<BigDecimal:b6cb8ae8,'0.4495E2',8(8)>>
 
>> result = LineItem.find(:first)
=> #<LineItem id: 3, product_id: 27, order_id: 13, quantity: 1, unit_price: #<BigDecimal:b6cb0064,'0.2995E2',8(8)>>
>> p result.quantity
1
=> nil
>> p result.unit_price
#<BigDecimal:b6ca2004,'0.2995E2',8(8)>
=> nil
 
>> result = LineItem.find_by_sql("select quantity, quantity*unit_price " +
?>                               "from line_items")
=> [#<LineItem quantity: 1>, #<LineItem quantity: 2>, #<LineItem quantity: 1>]
>> pp result[0].attributes
{"quantity*unit_price"=>29.95, "quantity"=>1}
=> nil
 
>> result = LineItem.find_by_sql("select quantity, 
                                      quantity*unit_price as total_price " +
?>                               "from line_items")
=> [#<LineItem quantity: 1>, #<LineItem quantity: 2>, #<LineItem quantity: 1>]
>> pp result[0].attributes
{"quantity"=>1, "total_price"=>29.95}
=> nil
 
>> p result[0].total_price
29.95
=> nil
>> sales_tax = 0.07
=> 0.07
>> p result[0].total_price * sales_tax
2.0965
=> nil
 
>> class LineItem < ActiveRecord::Base
>>   def total_price
>>     Float(read_attribute("total_price"))
>>   end
 
>>   CUBITS_TO_INCHES = 2.54
 
>>   def quantity
>>     read_attribute("quantity") * CUBITS_TO_INCHES
>>   end
 
>>   def quantity=(inches)
>>     write_attribute("quantity", Float(inches) / CUBITS_TO_INCHES)
>>   end
>> end
=> nil
 
>> p result[0].quantity
2.54
=> nil
 
>> result[0].quantity = 500
=> 500
>> p result[0].save
true
=> nil
 
?> >> 
sqlite3> select id, quantity*unit_price from line_items
                 id = 3
quantity*unit_price = 29.95
 
                 id = 4
quantity*unit_price = 59.9
 
                 id = 5
quantity*unit_price = 44.95
irb e1/ar/transactions.rb 1
>> $: << File.dirname(__FILE__)
=> ["/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/sqlite3-ruby-1.3.1/lib", "/home/rubys/git/rails/lib", "/home/rubys/git/rails/railties/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/thor-0.14.0/lib", "/home/rubys/git/rails/activeresource/lib", "/home/rubys/git/rails/activerecord/lib", "/home/rubys/git/rails/actionmailer/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mail-2.2.5/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/treetop-1.4.8/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/polyglot-0.3.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mime-types-1.16/lib", "/home/rubys/git/rails/actionpack/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/tzinfo-0.3.23/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-test-0.5.4/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-mount-0.6.13/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-1.2.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/erubis-2.6.6/lib", "/home/rubys/git/rails/activemodel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/i18n-0.4.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/builder-2.1.2/lib", "/home/rubys/git/rails/activesupport/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/abstract-1.0.0/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rake-0.8.7/lib", "/home/rubys/git/gorp/lib", "/home/rubys/git/arel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/bundler-1.0.0/lib", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8/i686-linux", ".", "/home/rubys/git/awdwr/data/code/e1/ar"]
>> require "connect"
=> true
>> require "logger"
=> nil
 
>> #ActiveRecord::Base.logger = Logger.new(STDERR)
 
?> require "rubygems"
=> nil
>> require "active_record"
=> nil
 
 
>> ActiveRecord::Schema.define do 
?>   create_table :accounts, :force => true do |t|
?>     t.string  :number
>>     t.decimal :balance, :precision => 10, :scale => 2, :default => 0
>>   end
>> end
-- create_table(:accounts, {:force=>true})
   -> 0.0258s
=> nil
 
 
?> class Account < ActiveRecord::Base
>>   validate :price_must_be_at_least_a_cent
 
>>   def self.transfer(from, to, amount)
>>     transaction(from, to) do
?>       from.withdraw(amount)
>>       to.deposit(amount)
>>     end
>>   end
 
?>   def withdraw(amount)
>>     adjust_balance_and_save(-amount)
>>   end
 
>>   def deposit(amount)
>>     adjust_balance_and_save(amount)
>>   end
 
>>   private
 
>>   def adjust_balance_and_save(amount)
>>     self.balance += amount
>>     save!
>>   end
 
>>   def price_must_be_at_least_a_cent
>>     errors.add(:balance, "is negative") if balance < 0
>>   end
>> end
=> nil
 
?>   def adjust_balance_and_save(amount)
>>     self.balance += amount
>>   end
=> nil
 
?> peter = Account.create(:balance => 100, :number => "12345")
=> #<Account id: 1, number: "12345", balance: #<BigDecimal:b6cbc198,'0.1E3',4(8)>>
>> paul  = Account.create(:balance => 200, :number => "54321")
=> #<Account id: 2, number: "54321", balance: #<BigDecimal:b6cac8ec,'0.2E3',4(8)>>
 
>> case ARGV[0] || "1"
 
>> when "1"
>>   Account.transaction do
?>     paul.deposit(10)
>>     peter.withdraw(10)
>>   end
 
>> when "2"
>>   Account.transaction do
?>     paul.deposit(350)
>>     peter.withdraw(350)
>>   end
 
>> when "3"
>>   begin
?>     Account.transaction do
?>       paul.deposit(350)
>>       peter.withdraw(350)
>>     end
>>   rescue
>>     puts "Transfer aborted"
>>   end
 
>>   puts "Paul has #{paul.balance}"
>>   puts "Peter has #{peter.balance}"
 
>> when "4"
>>   begin
?>     Account.transaction(peter, paul) do
?>       paul.deposit(350)
>>       peter.withdraw(350)
>>     end
>>   rescue
>>     puts "Transfer aborted"
>>   end
 
>>   puts "Paul has #{paul.balance}"
>>   puts "Peter has #{peter.balance}"
 
>> when "5"
>>   Account.transfer(peter, paul, 350) rescue  puts "Transfer aborted"
 
>>   puts "Paul has #{paul.balance}"
>>   puts "Peter has #{peter.balance}"
 
>> end
=> true
 
 
 
>> >> => nil
sqlite3> select * from accounts
     id = 1
 number = 12345
balance = 90
 
     id = 2
 number = 54321
balance = 210
irb e1/ar/transactions.rb 2
>> $: << File.dirname(__FILE__)
=> ["/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/sqlite3-ruby-1.3.1/lib", "/home/rubys/git/rails/lib", "/home/rubys/git/rails/railties/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/thor-0.14.0/lib", "/home/rubys/git/rails/activeresource/lib", "/home/rubys/git/rails/activerecord/lib", "/home/rubys/git/rails/actionmailer/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mail-2.2.5/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/treetop-1.4.8/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/polyglot-0.3.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mime-types-1.16/lib", "/home/rubys/git/rails/actionpack/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/tzinfo-0.3.23/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-test-0.5.4/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-mount-0.6.13/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-1.2.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/erubis-2.6.6/lib", "/home/rubys/git/rails/activemodel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/i18n-0.4.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/builder-2.1.2/lib", "/home/rubys/git/rails/activesupport/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/abstract-1.0.0/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rake-0.8.7/lib", "/home/rubys/git/gorp/lib", "/home/rubys/git/arel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/bundler-1.0.0/lib", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8/i686-linux", ".", "/home/rubys/git/awdwr/data/code/e1/ar"]
>> require "connect"
=> true
>> require "logger"
=> nil
 
>> #ActiveRecord::Base.logger = Logger.new(STDERR)
 
?> require "rubygems"
=> nil
>> require "active_record"
=> nil
 
 
>> ActiveRecord::Schema.define do 
?>   create_table :accounts, :force => true do |t|
?>     t.string  :number
>>     t.decimal :balance, :precision => 10, :scale => 2, :default => 0
>>   end
>> end
-- create_table(:accounts, {:force=>true})
   -> 0.0268s
=> nil
 
 
?> class Account < ActiveRecord::Base
>>   validate :price_must_be_at_least_a_cent
 
>>   def self.transfer(from, to, amount)
>>     transaction(from, to) do
?>       from.withdraw(amount)
>>       to.deposit(amount)
>>     end
>>   end
 
?>   def withdraw(amount)
>>     adjust_balance_and_save(-amount)
>>   end
 
>>   def deposit(amount)
>>     adjust_balance_and_save(amount)
>>   end
 
>>   private
 
>>   def adjust_balance_and_save(amount)
>>     self.balance += amount
>>     save!
>>   end
 
>>   def price_must_be_at_least_a_cent
>>     errors.add(:balance, "is negative") if balance < 0
>>   end
>> end
=> nil
 
?>   def adjust_balance_and_save(amount)
>>     self.balance += amount
>>   end
=> nil
 
?> peter = Account.create(:balance => 100, :number => "12345")
=> #<Account id: 1, number: "12345", balance: #<BigDecimal:b6cebcf4,'0.1E3',4(8)>>
>> paul  = Account.create(:balance => 200, :number => "54321")
=> #<Account id: 2, number: "54321", balance: #<BigDecimal:b6cdb020,'0.2E3',4(8)>>
 
>> case ARGV[0] || "1"
 
>> when "1"
>>   Account.transaction do
?>     paul.deposit(10)
>>     peter.withdraw(10)
>>   end
 
>> when "2"
>>   Account.transaction do
?>     paul.deposit(350)
>>     peter.withdraw(350)
>>   end
 
>> when "3"
>>   begin
?>     Account.transaction do
?>       paul.deposit(350)
>>       peter.withdraw(350)
>>     end
>>   rescue
>>     puts "Transfer aborted"
>>   end
 
>>   puts "Paul has #{paul.balance}"
>>   puts "Peter has #{peter.balance}"
 
>> when "4"
>>   begin
?>     Account.transaction(peter, paul) do
?>       paul.deposit(350)
>>       peter.withdraw(350)
>>     end
>>   rescue
>>     puts "Transfer aborted"
>>   end
 
>>   puts "Paul has #{paul.balance}"
>>   puts "Peter has #{peter.balance}"
 
>> when "5"
>>   Account.transfer(peter, paul, 350) rescue  puts "Transfer aborted"
 
>>   puts "Paul has #{paul.balance}"
>>   puts "Peter has #{peter.balance}"
 
>> end
ActiveRecord::RecordInvalid: Validation failed: Balance is negative
	from /home/rubys/git/rails/activerecord/lib/active_record/validations.rb:49:in `save!'
	from /home/rubys/git/rails/activerecord/lib/active_record/attribute_methods/dirty.rb:30:in `save!'
	from /home/rubys/git/rails/activerecord/lib/active_record/transactions.rb:242:in `save!'
	from /home/rubys/git/rails/activerecord/lib/active_record/transactions.rb:289:in `with_transaction_returning_status'
	from /home/rubys/git/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:139:in `transaction'
	from /home/rubys/git/rails/activerecord/lib/active_record/transactions.rb:204:in `transaction'
	from /home/rubys/git/rails/activerecord/lib/active_record/transactions.rb:287:in `with_transaction_returning_status'
	from /home/rubys/git/rails/activerecord/lib/active_record/transactions.rb:242:in `save!'
	from /home/rubys/git/awdwr/data/code/e1/ar/transactions.rb:48:in `adjust_balance_and_save'
	from /home/rubys/git/awdwr/data/code/e1/ar/transactions.rb:37:in `withdraw'
	from /home/rubys/git/awdwr/data/code/e1/ar/transactions.rb:84
	from /home/rubys/git/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:139:in `transaction'
	from /home/rubys/git/rails/activerecord/lib/active_record/transactions.rb:204:in `transaction'
	from /home/rubys/git/awdwr/data/code/e1/ar/transactions.rb:82
 
 
 
>> >> => nil
sqlite3> select * from accounts
     id = 1
 number = 12345
balance = 100
 
     id = 2
 number = 54321
balance = 200
irb e1/ar/transactions.rb 3
>> $: << File.dirname(__FILE__)
=> ["/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/sqlite3-ruby-1.3.1/lib", "/home/rubys/git/rails/lib", "/home/rubys/git/rails/railties/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/thor-0.14.0/lib", "/home/rubys/git/rails/activeresource/lib", "/home/rubys/git/rails/activerecord/lib", "/home/rubys/git/rails/actionmailer/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mail-2.2.5/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/treetop-1.4.8/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/polyglot-0.3.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mime-types-1.16/lib", "/home/rubys/git/rails/actionpack/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/tzinfo-0.3.23/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-test-0.5.4/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-mount-0.6.13/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-1.2.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/erubis-2.6.6/lib", "/home/rubys/git/rails/activemodel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/i18n-0.4.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/builder-2.1.2/lib", "/home/rubys/git/rails/activesupport/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/abstract-1.0.0/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rake-0.8.7/lib", "/home/rubys/git/gorp/lib", "/home/rubys/git/arel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/bundler-1.0.0/lib", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8/i686-linux", ".", "/home/rubys/git/awdwr/data/code/e1/ar"]
>> require "connect"
=> true
>> require "logger"
=> nil
 
>> #ActiveRecord::Base.logger = Logger.new(STDERR)
 
?> require "rubygems"
=> nil
>> require "active_record"
=> nil
 
 
>> ActiveRecord::Schema.define do 
?>   create_table :accounts, :force => true do |t|
?>     t.string  :number
>>     t.decimal :balance, :precision => 10, :scale => 2, :default => 0
>>   end
>> end
-- create_table(:accounts, {:force=>true})
   -> 0.0268s
=> nil
 
 
?> class Account < ActiveRecord::Base
>>   validate :price_must_be_at_least_a_cent
 
>>   def self.transfer(from, to, amount)
>>     transaction(from, to) do
?>       from.withdraw(amount)
>>       to.deposit(amount)
>>     end
>>   end
 
?>   def withdraw(amount)
>>     adjust_balance_and_save(-amount)
>>   end
 
>>   def deposit(amount)
>>     adjust_balance_and_save(amount)
>>   end
 
>>   private
 
>>   def adjust_balance_and_save(amount)
>>     self.balance += amount
>>     save!
>>   end
 
>>   def price_must_be_at_least_a_cent
>>     errors.add(:balance, "is negative") if balance < 0
>>   end
>> end
=> nil
 
?>   def adjust_balance_and_save(amount)
>>     self.balance += amount
>>   end
=> nil
 
?> peter = Account.create(:balance => 100, :number => "12345")
=> #<Account id: 1, number: "12345", balance: #<BigDecimal:b6d4ddb4,'0.1E3',4(8)>>
>> paul  = Account.create(:balance => 200, :number => "54321")
=> #<Account id: 2, number: "54321", balance: #<BigDecimal:b6d3d20c,'0.2E3',4(8)>>
 
>> case ARGV[0] || "1"
 
>> when "1"
>>   Account.transaction do
?>     paul.deposit(10)
>>     peter.withdraw(10)
>>   end
 
>> when "2"
>>   Account.transaction do
?>     paul.deposit(350)
>>     peter.withdraw(350)
>>   end
 
>> when "3"
>>   begin
?>     Account.transaction do
?>       paul.deposit(350)
>>       peter.withdraw(350)
>>     end
>>   rescue
>>     puts "Transfer aborted"
>>   end
 
>>   puts "Paul has #{paul.balance}"
>>   puts "Peter has #{peter.balance}"
 
>> when "4"
>>   begin
?>     Account.transaction(peter, paul) do
?>       paul.deposit(350)
>>       peter.withdraw(350)
>>     end
>>   rescue
>>     puts "Transfer aborted"
>>   end
 
>>   puts "Paul has #{paul.balance}"
>>   puts "Peter has #{peter.balance}"
 
>> when "5"
>>   Account.transfer(peter, paul, 350) rescue  puts "Transfer aborted"
 
>>   puts "Paul has #{paul.balance}"
>>   puts "Peter has #{peter.balance}"
 
>> end
Transfer aborted
Paul has 550.0
Peter has -250.0
=> nil
 
 
 
>> >> => nil
irb e1/ar/transactions.rb 4
>> $: << File.dirname(__FILE__)
=> ["/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/sqlite3-ruby-1.3.1/lib", "/home/rubys/git/rails/lib", "/home/rubys/git/rails/railties/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/thor-0.14.0/lib", "/home/rubys/git/rails/activeresource/lib", "/home/rubys/git/rails/activerecord/lib", "/home/rubys/git/rails/actionmailer/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mail-2.2.5/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/treetop-1.4.8/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/polyglot-0.3.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mime-types-1.16/lib", "/home/rubys/git/rails/actionpack/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/tzinfo-0.3.23/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-test-0.5.4/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-mount-0.6.13/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-1.2.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/erubis-2.6.6/lib", "/home/rubys/git/rails/activemodel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/i18n-0.4.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/builder-2.1.2/lib", "/home/rubys/git/rails/activesupport/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/abstract-1.0.0/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rake-0.8.7/lib", "/home/rubys/git/gorp/lib", "/home/rubys/git/arel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/bundler-1.0.0/lib", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8/i686-linux", ".", "/home/rubys/git/awdwr/data/code/e1/ar"]
>> require "connect"
=> true
>> require "logger"
=> nil
 
>> #ActiveRecord::Base.logger = Logger.new(STDERR)
 
?> require "rubygems"
=> nil
>> require "active_record"
=> nil
 
 
>> ActiveRecord::Schema.define do 
?>   create_table :accounts, :force => true do |t|
?>     t.string  :number
>>     t.decimal :balance, :precision => 10, :scale => 2, :default => 0
>>   end
>> end
-- create_table(:accounts, {:force=>true})
   -> 0.0284s
=> nil
 
 
?> class Account < ActiveRecord::Base
>>   validate :price_must_be_at_least_a_cent
 
>>   def self.transfer(from, to, amount)
>>     transaction(from, to) do
?>       from.withdraw(amount)
>>       to.deposit(amount)
>>     end
>>   end
 
?>   def withdraw(amount)
>>     adjust_balance_and_save(-amount)
>>   end
 
>>   def deposit(amount)
>>     adjust_balance_and_save(amount)
>>   end
 
>>   private
 
>>   def adjust_balance_and_save(amount)
>>     self.balance += amount
>>     save!
>>   end
 
>>   def price_must_be_at_least_a_cent
>>     errors.add(:balance, "is negative") if balance < 0
>>   end
>> end
=> nil
 
?>   def adjust_balance_and_save(amount)
>>     self.balance += amount
>>   end
=> nil
 
?> peter = Account.create(:balance => 100, :number => "12345")
=> #<Account id: 1, number: "12345", balance: #<BigDecimal:b6cd2cf4,'0.1E3',4(8)>>
>> paul  = Account.create(:balance => 200, :number => "54321")
=> #<Account id: 2, number: "54321", balance: #<BigDecimal:b6cc2020,'0.2E3',4(8)>>
 
>> case ARGV[0] || "1"
 
>> when "1"
>>   Account.transaction do
?>     paul.deposit(10)
>>     peter.withdraw(10)
>>   end
 
>> when "2"
>>   Account.transaction do
?>     paul.deposit(350)
>>     peter.withdraw(350)
>>   end
 
>> when "3"
>>   begin
?>     Account.transaction do
?>       paul.deposit(350)
>>       peter.withdraw(350)
>>     end
>>   rescue
>>     puts "Transfer aborted"
>>   end
 
>>   puts "Paul has #{paul.balance}"
>>   puts "Peter has #{peter.balance}"
 
>> when "4"
>>   begin
?>     Account.transaction(peter, paul) do
?>       paul.deposit(350)
>>       peter.withdraw(350)
>>     end
>>   rescue
>>     puts "Transfer aborted"
>>   end
 
>>   puts "Paul has #{paul.balance}"
>>   puts "Peter has #{peter.balance}"
 
>> when "5"
>>   Account.transfer(peter, paul, 350) rescue  puts "Transfer aborted"
 
>>   puts "Paul has #{paul.balance}"
>>   puts "Peter has #{peter.balance}"
 
>> end
Transfer aborted
Paul has 200.0
Peter has 100.0
=> nil
 
 
 
>> >> => nil
irb e1/ar/transactions.rb 5
>> $: << File.dirname(__FILE__)
=> ["/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/sqlite3-ruby-1.3.1/lib", "/home/rubys/git/rails/lib", "/home/rubys/git/rails/railties/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/thor-0.14.0/lib", "/home/rubys/git/rails/activeresource/lib", "/home/rubys/git/rails/activerecord/lib", "/home/rubys/git/rails/actionmailer/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mail-2.2.5/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/treetop-1.4.8/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/polyglot-0.3.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mime-types-1.16/lib", "/home/rubys/git/rails/actionpack/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/tzinfo-0.3.23/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-test-0.5.4/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-mount-0.6.13/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-1.2.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/erubis-2.6.6/lib", "/home/rubys/git/rails/activemodel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/i18n-0.4.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/builder-2.1.2/lib", "/home/rubys/git/rails/activesupport/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/abstract-1.0.0/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rake-0.8.7/lib", "/home/rubys/git/gorp/lib", "/home/rubys/git/arel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/bundler-1.0.0/lib", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8/i686-linux", ".", "/home/rubys/git/awdwr/data/code/e1/ar"]
>> require "connect"
=> true
>> require "logger"
=> nil
 
>> #ActiveRecord::Base.logger = Logger.new(STDERR)
 
?> require "rubygems"
=> nil
>> require "active_record"
=> nil
 
 
>> ActiveRecord::Schema.define do 
?>   create_table :accounts, :force => true do |t|
?>     t.string  :number
>>     t.decimal :balance, :precision => 10, :scale => 2, :default => 0
>>   end
>> end
-- create_table(:accounts, {:force=>true})
   -> 0.0277s
=> nil
 
 
?> class Account < ActiveRecord::Base
>>   validate :price_must_be_at_least_a_cent
 
>>   def self.transfer(from, to, amount)
>>     transaction(from, to) do
?>       from.withdraw(amount)
>>       to.deposit(amount)
>>     end
>>   end
 
?>   def withdraw(amount)
>>     adjust_balance_and_save(-amount)
>>   end
 
>>   def deposit(amount)
>>     adjust_balance_and_save(amount)
>>   end
 
>>   private
 
>>   def adjust_balance_and_save(amount)
>>     self.balance += amount
>>     save!
>>   end
 
>>   def price_must_be_at_least_a_cent
>>     errors.add(:balance, "is negative") if balance < 0
>>   end
>> end
=> nil
 
?>   def adjust_balance_and_save(amount)
>>     self.balance += amount
>>   end
=> nil
 
?> peter = Account.create(:balance => 100, :number => "12345")
=> #<Account id: 1, number: "12345", balance: #<BigDecimal:b6d65cfc,'0.1E3',4(8)>>
>> paul  = Account.create(:balance => 200, :number => "54321")
=> #<Account id: 2, number: "54321", balance: #<BigDecimal:b6d54e84,'0.2E3',4(8)>>
 
>> case ARGV[0] || "1"
 
>> when "1"
>>   Account.transaction do
?>     paul.deposit(10)
>>     peter.withdraw(10)
>>   end
 
>> when "2"
>>   Account.transaction do
?>     paul.deposit(350)
>>     peter.withdraw(350)
>>   end
 
>> when "3"
>>   begin
?>     Account.transaction do
?>       paul.deposit(350)
>>       peter.withdraw(350)
>>     end
>>   rescue
>>     puts "Transfer aborted"
>>   end
 
>>   puts "Paul has #{paul.balance}"
>>   puts "Peter has #{peter.balance}"
 
>> when "4"
>>   begin
?>     Account.transaction(peter, paul) do
?>       paul.deposit(350)
>>       peter.withdraw(350)
>>     end
>>   rescue
>>     puts "Transfer aborted"
>>   end
 
>>   puts "Paul has #{paul.balance}"
>>   puts "Peter has #{peter.balance}"
 
>> when "5"
>>   Account.transfer(peter, paul, 350) rescue  puts "Transfer aborted"
 
>>   puts "Paul has #{paul.balance}"
>>   puts "Peter has #{peter.balance}"
 
>> end
Transfer aborted
Paul has 200.0
Peter has 100.0
=> nil
 
 
 
>> >> => nil
irb e1/ar/optimistic.rb
>> $: << File.dirname(__FILE__)
=> ["/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/sqlite3-ruby-1.3.1/lib", "/home/rubys/git/rails/lib", "/home/rubys/git/rails/railties/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/thor-0.14.0/lib", "/home/rubys/git/rails/activeresource/lib", "/home/rubys/git/rails/activerecord/lib", "/home/rubys/git/rails/actionmailer/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mail-2.2.5/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/treetop-1.4.8/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/polyglot-0.3.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/mime-types-1.16/lib", "/home/rubys/git/rails/actionpack/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/tzinfo-0.3.23/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-test-0.5.4/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-mount-0.6.13/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rack-1.2.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/erubis-2.6.6/lib", "/home/rubys/git/rails/activemodel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/i18n-0.4.1/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/builder-2.1.2/lib", "/home/rubys/git/rails/activesupport/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/abstract-1.0.0/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/rake-0.8.7/lib", "/home/rubys/git/gorp/lib", "/home/rubys/git/arel/lib", "/home/rubys/.rvm/gems/ruby-1.8.7-p302/gems/bundler-1.0.0/lib", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/site_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby/1.8/i686-linux", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/vendor_ruby", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8", "/home/rubys/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8/i686-linux", ".", "/home/rubys/git/awdwr/data/code/e1/ar"]
>> require "connect"
=> true
 
>> require "rubygems"
=> nil
>> require "active_record"
=> nil
 
>> ActiveRecord::Schema.define do
?>   create_table :counters, :force => true do |t|
?>     t.integer :count
>>     t.integer :lock_version, :default => 0
>>   end
>> end
-- create_table(:counters, {:force=>true})
   -> 0.0258s
=> nil
 
 
?> class Counter < ActiveRecord::Base
>> end
=> nil
 
>> Counter.delete_all
=> 0
>> Counter.create(:count => 0)
=> #<Counter id: 1, count: 0, lock_version: 0>
 
>> count1 = Counter.find(:first)
=> #<Counter id: 1, count: 0, lock_version: 0>
>> count2 = Counter.find(:first)
=> #<Counter id: 1, count: 0, lock_version: 0>
 
>> count1.count += 3 
=> 3
>> count1.save
=> true
 
>> count2.count += 4 
=> 4
>> count2.save
ActiveRecord::StaleObjectError: Attempted to update a stale object: Counter
	from /home/rubys/git/rails/activerecord/lib/active_record/locking/optimistic.rb:97:in `update'
	from /home/rubys/git/rails/activerecord/lib/active_record/attribute_methods/dirty.rb:68:in `update'
	from /home/rubys/git/rails/activerecord/lib/active_record/timestamp.rb:60:in `update'
	from /home/rubys/git/rails/activerecord/lib/active_record/callbacks.rb:285:in `update'
	from /home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:413:in `_run_update_callbacks'
	from /home/rubys/git/rails/activerecord/lib/active_record/callbacks.rb:285:in `update'
	from /home/rubys/git/rails/activerecord/lib/active_record/persistence.rb:247:in `create_or_update'
	from /home/rubys/git/rails/activerecord/lib/active_record/callbacks.rb:277:in `create_or_update'
	from /home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:413:in `_run_save_callbacks'
	from /home/rubys/git/rails/activerecord/lib/active_record/callbacks.rb:277:in `create_or_update'
	from /home/rubys/git/rails/activerecord/lib/active_record/persistence.rb:39:in `save'
	from /home/rubys/git/rails/activerecord/lib/active_record/validations.rb:43:in `save'
	from /home/rubys/git/rails/activerecord/lib/active_record/attribute_methods/dirty.rb:21:in `save'
	from /home/rubys/git/rails/activerecord/lib/active_record/transactions.rb:237:in `save'
	from /home/rubys/git/rails/activerecord/lib/active_record/transactions.rb:289:in `with_transaction_returning_status'
	from /home/rubys/git/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:139:in `transaction'
	from /home/rubys/git/rails/activerecord/lib/active_record/transactions.rb:204:in `transaction'
	from /home/rubys/git/rails/activerecord/lib/active_record/transactions.rb:287:in `with_transaction_returning_status'
	from /home/rubys/git/rails/activerecord/lib/active_record/transactions.rb:237:in `save'
	from /home/rubys/git/rails/activerecord/lib/active_record/transactions.rb:248:in `rollback_active_record_state!'
	from /home/rubys/git/rails/activerecord/lib/active_record/transactions.rb:236:in `save'
	from /home/rubys/git/awdwr/data/code/e1/ar/optimistic.rb:31
	from :0>> #END:optimistic
?> >> => nil

21 Action Controller: Routing and URLs 19 ActiveRecord: Relationships Between Tables