pono_thea wrote:
> Tentang simpan ke database lebih dari 2 tabel.

SHORT VERSION

Pasti ada banyak solusi untuk ini, saya hanya akan menunjukkan 1 macam solusi
disini, simpan form model awal di session kemudian lanjut ke form model kedua
(terakhir) ketika save model terakhir save lah juga model yang ada di session.

LONG VERSION

Saya punya kata-kata radikal utk kasus pono_thea ini:
Inilah kelemahan ORM AR di Rails!!! Masak mau apa-apa cuma 1 form model kenal 1
model??? Coba pakai PEAR+Smarty+PHP dulu itu langsung aja bisa :-P

FYI Saya dulu pernah juga bikin Booking Engine untuk Hotel pakai Rails dimana
kasusnya sangat sangat mirip dengan yang Anda hadapi.

Di codes berikut ini saya akan mensimulasikan apa yang pernah saya bikin itu.

id-ruby:~/sukebe/spec arie$ mkdir 2 && cd 2
id-ruby:~/sukebe/spec/2 arie$ rails . --database=sqlite3
       exists
       create  app/controllers
       create  app/helpers
[...]
id-ruby:~/sukebe/spec/2 arie$

[...]

coding saya hentikan saat melihat ada yang tidak sinkron antara pertanyaan Anda 
dengan contoh kasus yang Anda sodorkan.

> 1. Validasi (di setiap models)
> 2. Jika sukses maka simpan ke databse
> saya menggunakan 
> if @user.save and @hobbies.save and @address.save
>   pesan sukses simpan data
> end
> Tetapai tidak sesuai dengan yg saya harapakan, saya juga pernah pake
> OR, hasilnya tetap.
> Masalahnya :
> Jika menggunakan AND, error message akan tampil yang @user.save aja,
> sedangkan yang lain engga, kecuali klo @user.save sukse
> Klo pake OR dia baca dari belakang,
> 
> Jika saya pake 
> if @user.save
>   @hobbies.save
> end

BTW kasus Anda ini dengan contoh diatas itu sebetulnya nggak sinkron! Kalau 
cuma,
hobbies, sih itu tinggal user has_many hobbies through something aja selesai 
perkara, gak perlu
repot2x kita ngomong akali simpan data lebih dari satu tabel. jadi Logic dari 
si 
Felix itu jitu! ;-) mustinya bahkan kalau simple satu aja udah cukup, anyway, 
kalau tetap mau banyak-banyak hobbynya juga bisa. gini caranya.

id-ruby:~/sukebe/spec arie$ rails ponothea --database=mysql && cd ponothea
       create
       create  app/controllers
[...]
id-ruby:~/sukebe/spec/ponothea arie$ vi config/database.yml
id-ruby:~/sukebe/spec/ponothea arie$ cat config/database.yml
config: &config
   adapter: mysql
   username: ponothea
   password: p0N07h34
   socket: /tmp/mysql.sock

development:
   database: ponothea_dev
   <<: *config

test:
   database: ponothea_test
   <<: *config

id-ruby:~/sukebe/spec/ponothea arie$ mysql -uroot -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1 to server version: 5.0.22-standard

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> create database ponothea_dev; create database ponothea_test;
Query OK, 1 row affected (0.16 sec)

Query OK, 1 row affected (0.00 sec)

mysql> grant all on ponothea_dev.* to [EMAIL PROTECTED]; grant all on 
ponothea_test.* to [EMAIL PROTECTED];
Query OK, 0 rows affected (0.23 sec)

Query OK, 0 rows affected (0.00 sec)

mysql> set password for [EMAIL PROTECTED]("p0N07h34");
Query OK, 0 rows affected (0.08 sec)

mysql> flush privileges;
Query OK, 0 rows affected (0.10 sec)

mysql> exit
Bye
id-ruby:~/sukebe/spec/ponothea arie$ script/about
About your application's environment
Ruby version                 1.8.6 (powerpc-darwin8.9.0)
RubyGems version             0.9.4
Rails version                1.2.3
Active Record version        1.15.3
Action Pack version          1.13.3
Action Web Service version   1.2.3
Action Mailer version        1.3.3
Active Support version       1.4.2
Application root 
/Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea
Environment                  development
Database adapter             mysql
id-ruby:~/sukebe/spec/ponothea arie$ rake stats
(in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea)
+----------------------+-------+-------+---------+---------+-----+-------+
| Name                 | Lines |   LOC | Classes | Methods | M/C | LOC/M |
+----------------------+-------+-------+---------+---------+-----+-------+
| Controllers          |     7 |     3 |       1 |       0 |   0 |     0 |
| Helpers              |     3 |     2 |       0 |       0 |   0 |     0 |
| Models               |     0 |     0 |       0 |       0 |   0 |     0 |
| Libraries            |     0 |     0 |       0 |       0 |   0 |     0 |
| Components           |     0 |     0 |       0 |       0 |   0 |     0 |
| Integration tests    |     0 |     0 |       0 |       0 |   0 |     0 |
| Functional tests     |     0 |     0 |       0 |       0 |   0 |     0 |
| Unit tests           |     0 |     0 |       0 |       0 |   0 |     0 |
+----------------------+-------+-------+---------+---------+-----+-------+
| Total                |    10 |     5 |       1 |       0 |   0 |     0 |
+----------------------+-------+-------+---------+---------+-----+-------+
   Code LOC: 5     Test LOC: 0     Code to Test Ratio: 1:0.0

id-ruby:~/sukebe/spec/ponothea arie$ script/plugin install 
svn://rubyforge.org/var/svn/rspec/tags/REL_1_0_8/rspec && script/plugin install 
svn://rubyforge.org/var/svn/rspec/tags/REL_1_0_8/rspec_on_rails
A 
/Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea/vendor/plugins/rspec
A 
/Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea/vendor/plugins/rspec/plugins
A 
/Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea/vendor/plugins/rspec/plugins/mock_frameworks
[...]
Exported revision 2483.
id-ruby:~/sukebe/spec/ponothea arie$ script/generate rspec
       create  spec
       create  spec/spec_helper.rb
       create  spec/spec.opts
       create  previous_failures.txt
       create  script/spec_server
       create  script/spec
id-ruby:~/sukebe/spec/ponothea arie$ script/generate rspec_model User 
name:string 
&& script/generate rspec_model Hobby title:string && script/generate 
rspec_model 
Crazyfication name_id:integer hobby_id:integer since_at:datetime rating:integer
       exists  app/models/
       create  spec/models/
       create  spec/fixtures/
       create  app/models/user.rb
       create  spec/fixtures/users.yml
       create  spec/models/user_spec.rb
       create  db/migrate
       create  db/migrate/001_create_users.rb
       exists  app/models/
       exists  spec/models/
       exists  spec/fixtures/
       create  app/models/hobby.rb
       create  spec/fixtures/hobbies.yml
       create  spec/models/hobby_spec.rb
       exists  db/migrate
       create  db/migrate/002_create_hobbies.rb
       exists  app/models/
       exists  spec/models/
       exists  spec/fixtures/
       create  app/models/crazyfication.rb
       create  spec/fixtures/crazyfications.yml
       create  spec/models/crazyfication_spec.rb
       exists  db/migrate
       create  db/migrate/003_create_crazyfications.rb
id-ruby:~/sukebe/spec/ponothea arie$ awk '/t\.column/' db/migrate/00*
       t.column :name, :string
       t.column :title, :string
       t.column :name_id, :integer
       t.column :hobby_id, :integer
       t.column :since_at, :datetime
       t.column :rating, :integer
id-ruby:~/sukebe/spec/ponothea arie$ rake spec:models
(in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea)
FFF

1)
ActiveRecord::StatementInvalid in 'User should be valid'
Mysql::Error: Table 'ponothea_test.users' doesn't exist: SHOW FIELDS FROM users
./spec/models/user_spec.rb:5:in `new'
./spec/models/user_spec.rb:5:

2)
ActiveRecord::StatementInvalid in 'Hobby should be valid'
Mysql::Error: Table 'ponothea_test.hobbies' doesn't exist: SHOW FIELDS FROM 
hobbies
./spec/models/hobby_spec.rb:5:in `new'
./spec/models/hobby_spec.rb:5:

3)
ActiveRecord::StatementInvalid in 'Crazyfication should be valid'
Mysql::Error: Table 'ponothea_test.crazyfications' doesn't exist: SHOW FIELDS 
FROM 
crazyfications
./spec/models/crazyfication_spec.rb:5:in `new'
./spec/models/crazyfication_spec.rb:5:

Finished in 0.58407 seconds

3 examples, 3 failures
rake aborted!
Command ruby 
-I"/Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea/vendor/plugins/rspec/lib"
 
"/Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea/vendor/plugins/rspec/bin/spec"
 
"spec/models/crazyfication_spec.rb" "spec/models/hobby_spec.rb" 
"spec/models/user_spec.rb" --options 
"/Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea/config/../spec/spec.opts"
 
failed

(See full trace by running task with --trace)

# tentu saja kita dapat error, karena belum migrate, ini cuma menunjukkan 
penekanan praktek XP dengan BDD di Ruby ya.

id-ruby:~/sukebe/spec/ponothea arie$ rake db:migrate
(in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea)
== CreateUsers: migrating =====================================================
-- create_table(:users)
    -> 0.2599s
== CreateUsers: migrated (0.2612s) ============================================

== CreateHobbies: migrating ===================================================
-- create_table(:hobbies)
    -> 0.0490s
== CreateHobbies: migrated (0.0509s) ==========================================

== CreateCrazyfications: migrating ============================================
-- create_table(:crazyfications)
    -> 0.0087s
== CreateCrazyfications: migrated (0.0106s) ===================================

# spec'ing again dan BDD sukses 100%

id-ruby:~/sukebe/spec/ponothea arie$ rake spec:models
(in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea)
...

Finished in 0.399572 seconds

3 examples, 0 failures
id-ruby:~/sukebe/spec/ponothea arie$ cat spec/models/crazyfication_spec.rb
require File.dirname(__FILE__) + '/../spec_helper'

describe Crazyfication do
   before(:each) do
     @crazyfication = Crazyfication.create(:since_at => Time.now, :rating => 5)
     @user1 = User.create(:name => 'dandy')
     @hobby1 = Hobby.create(:title => 'racing')
     @user1.crazyfications << @crazyfication
     @hobby1.crazyfications << @crazyfication
     @crazyfication.save
     @crazyfication2 = Crazyfication.create(:since_at => Time.mktime(2007, 7, 
7, 
7, 7, 7), :rating => 5)
     @user2 = User.create(:name => 'arie')
     @hobby2 = Hobby.create(:title => 'minori aoi')
     @user2.crazyfications << @crazyfication2
     @hobby2.crazyfications << @crazyfication2
     @crazyfication2.save
     @hobby3 = Hobby.create(:title => 'ruby')
     @crazyfication3 = Crazyfication.create(:since_at => Time.mktime(2004, 1, 
1, 
1, 1, 1), :rating => 5)
     @hobby3.crazyfications << @crazyfication3
     @user2.crazyfications << @crazyfication3
     @crazyfication3.save
   end

   it "dandy's hobby should be racing only" do
     @user1.hobbies.size.should == 1
     @user1.hobbies.at(0).title.should == 'racing'
   end

   it "arie's hobbies should be both ruby and minori aoi" do
     @user2.hobbies.size.should == 2
     @user2.hobbies.map { |e| e.title }.should == ['minori aoi', 'ruby']
   end
end

# OK, testing duluan sudah ditulis, sekarang kita tes dah

id-ruby:~/sukebe/spec/ponothea arie$ spec spec/models/crazyfication_spec.rb
FF

1)
NoMethodError in 'Crazyfication dandy's hobby should be racing only'
undefined method `crazyfications' for #<User:0x31e663c>
./spec/models/crazyfication_spec.rb:8:

2)
NoMethodError in 'Crazyfication arie's hobbies should be both ruby and minori 
aoi'
undefined method `crazyfications' for #<User:0x31cbd3c>
./spec/models/crazyfication_spec.rb:8:

Finished in 0.342293 seconds

2 examples, 2 failures
id-ruby:~/sukebe/spec/ponothea arie$

# nah pasti error kan, baru kita coding sesuai spec yg sudah ditentukan 
sebelumnya, inilah dia intinya XP, kalo coding tuh harus jelas requirementnya, 
kalau belum jelas, jangan mau!!!

# ini hasil cat setelah saya update semua model pakai textmate

id-ruby:~/sukebe/spec/ponothea arie$ cat app/models/*
class Crazyfication < ActiveRecord::Base
   belongs_to :hobby
   belongs_to :user
end
class Hobby < ActiveRecord::Base
   has_many :crazyfications
   has_many :users, :through => :crazyfications
end
class User < ActiveRecord::Base
   has_many :crazyfications
   has_many :hobbies, :through => :crazyfications
end

# nah setelah ini kalau kita tes lagi mustinya harus berhasil ;-)

id-ruby:~/sukebe/spec/ponothea arie$ spec spec/models/crazyfication_spec.rb
FF

1)
ActiveRecord::StatementInvalid in 'Crazyfication dandy's hobby should be racing 
only'
Mysql::Error: Unknown column 'crazyfications.user_id' in 'where clause': SELECT 
* 
FROM crazyfications WHERE (crazyfications.user_id = 3)
./spec/models/crazyfication_spec.rb:8:

2)
ActiveRecord::StatementInvalid in 'Crazyfication arie's hobbies should be both 
ruby and minori aoi'
Mysql::Error: Unknown column 'crazyfications.user_id' in 'where clause': SELECT 
* 
FROM crazyfications WHERE (crazyfications.user_id = 4)
./spec/models/crazyfication_spec.rb:8:

Finished in 0.621236 seconds

2 examples, 2 failures

# oops :-D

# coba di cek dan ricek, ini akibat ngantuk atau kurang makan nih...

id-ruby:~/sukebe/spec/ponothea arie$ awk '/pass|user/' config/database.yml
   username: ponothea
   password: p0N07h34
id-ruby:~/sukebe/spec/ponothea arie$ mysql -uponothea -pp0N07h34 ponothea_dev -A
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 15 to server version: 5.0.22-standard

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> show tables;
+------------------------+
| Tables_in_ponothea_dev |
+------------------------+
| crazyfications         |
| hobbies                |
| schema_info            |
| users                  |
+------------------------+
4 rows in set (0.00 sec)

mysql> desc crazyfications;
+----------+----------+------+-----+---------+----------------+
| Field    | Type     | Null | Key | Default | Extra          |
+----------+----------+------+-----+---------+----------------+
| id       | int(11)  | NO   | PRI | NULL    | auto_increment |
| name_id  | int(11)  | YES  |     | NULL    |                |
| hobby_id | int(11)  | YES  |     | NULL    |                |
| since_at | datetime | YES  |     | NULL    |                |
| rating   | int(11)  | YES  |     | NULL    |                |
+----------+----------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

mysql> exit
Bye

# nah sudah jelas, mustinya user_id, bukan name_id, ayo kita betuli dulu

id-ruby:~/sukebe/spec/ponothea arie$ cat db/migrate/003_create_crazyfications.rb
class CreateCrazyfications < ActiveRecord::Migration
   def self.up
     create_table :crazyfications do |t|
       t.column :user_id, :integer
       t.column :hobby_id, :integer
       t.column :since_at, :datetime
       t.column :rating, :integer
     end
   end

   def self.down
     drop_table :crazyfications
   end
end

# OK sudah diubah jadi user_id ya. tinggal di migrate. BTW ini utk kasus ringan 
kita bisa revert, kalau kasus perusahaan besar ya mustinya gak ada salah2x! 
desain 
database jangan sampai ngaco tuh, tbl_ tbl_bullshit I'll kill them...

id-ruby:~/sukebe/spec/ponothea arie$ rake db:migrate VERSION=0 && rake 
db:migrate
(in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea)
== CreateCrazyfications: reverting ============================================
-- drop_table(:crazyfications)
    -> 0.2341s
== CreateCrazyfications: reverted (0.2352s) ===================================

== CreateHobbies: reverting ===================================================
-- drop_table(:hobbies)
    -> 0.0058s
== CreateHobbies: reverted (0.0069s) ==========================================

== CreateUsers: reverting =====================================================
-- drop_table(:users)
    -> 0.0338s
== CreateUsers: reverted (0.0349s) ============================================

(in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea)
== CreateUsers: migrating =====================================================
-- create_table(:users)
    -> 0.0261s
== CreateUsers: migrated (0.0274s) ============================================

== CreateHobbies: migrating ===================================================
-- create_table(:hobbies)
    -> 0.2051s
== CreateHobbies: migrated (0.2065s) ==========================================

== CreateCrazyfications: migrating ============================================
-- create_table(:crazyfications)
    -> 0.0079s
== CreateCrazyfications: migrated (0.0092s) ===================================

id-ruby:~/sukebe/spec/ponothea arie$ spec spec/models/crazyfication_spec.rb
FF

1)
ActiveRecord::StatementInvalid in 'Crazyfication dandy's hobby should be racing 
only'
Mysql::Error: Unknown column 'crazyfications.user_id' in 'where clause': SELECT 
* 
FROM crazyfications WHERE (crazyfications.user_id = 5)
./spec/models/crazyfication_spec.rb:8:

2)
ActiveRecord::StatementInvalid in 'Crazyfication arie's hobbies should be both 
ruby and minori aoi'
Mysql::Error: Unknown column 'crazyfications.user_id' in 'where clause': SELECT 
* 
FROM crazyfications WHERE (crazyfications.user_id = 6)
./spec/models/crazyfication_spec.rb:8:

Finished in 0.555789 seconds

2 examples, 2 failures

# boom, kalo masih error .. tenang ;-) pesawat masih dalam kendali kapten star 
trek mabok

id-ruby:~/sukebe/spec/ponothea arie$ rake db:test:purge
(in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea)
id-ruby:~/sukebe/spec/ponothea arie$ rake db:test:clone
(in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea)

# ya, jadi database tesnya kan masih cloning db dev yang sebelumnya yang salah, 
jadi dihapus dulu baru di create db lagi mengcloning sesuai db dev yg benar 
saat 
ini, lalu setelah ini harus betul, kalo gak awas!

id-ruby:~/sukebe/spec/ponothea arie$ spec spec/models/crazyfication_spec.rb
..

Finished in 1.14454 seconds

2 examples, 0 failures

id-ruby:~/sukebe/spec/ponothea arie$ rake spec:models
(in /Users/arie/Documents/takaitoshokan/python/codes-ariekeren/spec/ponothea)
....

Finished in 0.775302 seconds

4 examples, 0 failures

id-ruby:~/sukebe/spec/ponothea arie$

# nah OK, testing sudah betul 100% :-D

Loh Anda protes, jadi proses yang lewat formnya mana pak? ya... mustinya dari 
gambaran testing yang descriptive tadi itu sudah bisa dikerjakan sendiri PR 
nya. 
Ayo dicoba dulu mana copy paste codingnya biar dipelototin/dikoreksi lagi sama 
teman-teman kita bareng2x yang pada beken-beken di id-ruby ini ;-) supaya 
keliatan 
progress belajarnya...

> Gimana cara rollback nya.
> Help!

Kalau ada di email saya ini yang setelah Anda coba di komputer Anda malah Anda
dapat error, selalu laporkan ke saya beserta keterangan komputer Anda yang 
jelas,
jadi selalu kasih perkembangannya ke kita-kita disini.

-- 
id-ruby:~ arie$ irb-19
irb(main):001:0> {name:'arie',
blog:'http://ariekusumaatmaja.wordpress.com'}.is_a?(Hash)
=> true



Kirim email ke