reform 2.0.0.rc1 → 2.0.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,37 @@
1
+ module Reform::Form::Mongoid
2
+ def self.included(base)
3
+ base.class_eval do
4
+ register_feature Reform::Form::Mongoid
5
+ include Reform::Form::ActiveModel
6
+ include Reform::Form::ORM
7
+ extend ClassMethods
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ def validates_uniqueness_of(attribute, options={})
13
+ options = options.merge(:attributes => [attribute])
14
+ validates_with(UniquenessValidator, options)
15
+ end
16
+ def i18n_scope
17
+ :mongoid
18
+ end
19
+ end
20
+
21
+
22
+ def self.mongoid_namespace
23
+ if mongoid_is_4_or_more?
24
+ 'Validatable'
25
+ else
26
+ 'Validations'
27
+ end
28
+ end
29
+
30
+ def self.mongoid_is_4_or_more?
31
+ Mongoid::VERSION.split('.').first.to_i >= 4
32
+ end
33
+
34
+ UniquenessValidator = Class.new("::Mongoid::#{mongoid_namespace}::UniquenessValidator".constantize) do
35
+ include Reform::Form::ORM::UniquenessValidator
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ module Reform::Form::ORM
2
+ def model_for_property(name)
3
+ return model unless is_a?(Reform::Form::Composition) # i am too lazy for proper inheritance. there should be a ActiveRecord::Composition that handles this.
4
+
5
+ model_name = schema.representable_attrs.get(name)[:on]
6
+ model[model_name]
7
+ end
8
+
9
+ module UniquenessValidator
10
+ # when calling validates it should create the Vali instance already and set @klass there! # TODO: fix this in AM.
11
+ def validate(form)
12
+ property = attributes.first
13
+
14
+ # here is the thing: why does AM::UniquenessValidator require a filled-out record to work properly? also, why do we need to set
15
+ # the class? it would be way easier to pass #validate a hash of attributes and get back an errors hash.
16
+ # the class for the finder could either be infered from the record or set in the validator instance itself in the call to ::validates.
17
+ record = form.model_for_property(property)
18
+ record.send("#{property}=", form.send(property))
19
+
20
+ @klass = record.class # this is usually done in the super-sucky #setup method.
21
+ super(record).tap do |res|
22
+ form.errors.add(property, record.errors.first.last) if record.errors.present?
23
+ end
24
+ end
25
+ end
26
+ end
@@ -11,3 +11,8 @@ class Reform::Form::UniqueValidator < ActiveModel::EachValidator
11
11
  end
12
12
  end
13
13
 
14
+ # FIXME: ActiveModel loads validators via const_get(#{name}Validator). this magic forces us to
15
+ # make the new :unique validator available here.
16
+ Reform::Form::ActiveModel::Validations::Validator.class_eval do
17
+ UniqueValidator = Reform::Form::UniqueValidator
18
+ end
@@ -0,0 +1,4 @@
1
+ require 'reform/form/active_model'
2
+ require 'reform/form/orm'
3
+ require 'reform/form/mongoid'
4
+ require 'reform/form/model_reflections' # only load this in AR context as simple_form currently is bound to AR.
data/lib/reform/rails.rb CHANGED
@@ -1,9 +1,13 @@
1
- require 'reform/form/active_model'
1
+ require "reform/form/active_model"
2
+ require "reform/form/active_model/validations"
2
3
 
3
- require 'reform/active_record' if defined?(ActiveRecord)
4
+ require "reform/active_record" if defined?(ActiveRecord)
5
+ require "reform/mongoid" if defined?(Mongoid)
4
6
 
5
7
  Reform::Form.class_eval do # DISCUSS: i'd prefer having a separate Rails module to be mixed into the Form but this is way more convenient for 99% users.
6
8
  include Reform::Form::ActiveModel
7
9
  include Reform::Form::ActiveModel::FormBuilderMethods
8
10
  include Reform::Form::ActiveRecord if defined?(ActiveRecord)
9
- end
11
+ include Reform::Form::Mongoid if defined?(Mongoid)
12
+ include Reform::Form::ActiveModel::Validations
13
+ end
@@ -1,3 +1,3 @@
1
1
  module Reform
2
- VERSION = "2.0.0.rc1"
2
+ VERSION = "2.0.0.rc2"
3
3
  end
data/reform.gemspec CHANGED
@@ -17,10 +17,8 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
19
 
20
- spec.add_dependency "representable", ">= 2.2.2"
21
20
  spec.add_dependency "disposable", "~> 0.1.5"
22
21
  spec.add_dependency "uber", "~> 0.0.11"
23
- spec.add_dependency "activemodel"
24
22
 
25
23
  spec.add_development_dependency "bundler"
26
24
  spec.add_development_dependency "rake"
@@ -29,6 +27,8 @@ Gem::Specification.new do |spec|
29
27
  spec.add_development_dependency "sqlite3"
30
28
  spec.add_development_dependency "virtus"
31
29
  spec.add_development_dependency "rails"
30
+ spec.add_development_dependency "mongoid"
32
31
 
32
+ spec.add_development_dependency "lotus-validations"
33
33
  spec.add_development_dependency "actionpack"
34
34
  end
@@ -25,11 +25,14 @@ class NewActiveModelTest < MiniTest::Spec # TODO: move to test/rails/
25
25
  let (:artist) { Artist.create(:name => "Frank Zappa") }
26
26
  let (:form) { SongForm.new(artist) }
27
27
 
28
- it { form.persisted?.must_equal true }
29
- it { form.to_key.must_equal [artist.id] }
30
- it { form.to_param.must_equal "#{artist.id}" }
31
- it { form.to_model.must_equal form }
32
- it { form.id.must_equal artist.id }
28
+ it do
29
+ form.persisted?.must_equal true
30
+ form.to_key.must_equal [artist.id]
31
+ form.to_param.must_equal "#{artist.id}"
32
+ form.to_model.must_equal form
33
+ form.id.must_equal artist.id
34
+ form.model_name.must_equal form.class.model_name
35
+ end
33
36
 
34
37
  describe "::model_name" do
35
38
  it { form.class.model_name.must_be_kind_of ActiveModel::Name }
@@ -75,13 +78,11 @@ class NewActiveModelTest < MiniTest::Spec # TODO: move to test/rails/
75
78
  it { subclass_of_class_with_model.model_name.must_be_kind_of ActiveModel::Name }
76
79
  it { subclass_of_class_with_model.model_name.to_s.must_equal 'Album' }
77
80
 
78
- unless Reform.rails3_0?
79
- it { form.class.model_name.route_key.must_equal "new_active_model_test_songs" }
80
- it { class_with_model.model_name.route_key.must_equal "albums" }
81
- it { class_with_isolated_model.model_name.route_key.must_equal "lyrics" }
82
- it { class_with_namespace_model.model_name.route_key.must_equal "normal_rails_engine_lyrics" }
83
- it { subclass_of_class_with_model.model_name.route_key.must_equal 'albums' }
84
- end
81
+ it { form.class.model_name.route_key.must_equal "new_active_model_test_songs" }
82
+ it { class_with_model.model_name.route_key.must_equal "albums" }
83
+ it { class_with_isolated_model.model_name.route_key.must_equal "lyrics" }
84
+ it { class_with_namespace_model.model_name.route_key.must_equal "normal_rails_engine_lyrics" }
85
+ it { subclass_of_class_with_model.model_name.route_key.must_equal 'albums' }
85
86
 
86
87
  describe "class named Song::Form" do
87
88
  it do
@@ -42,9 +42,6 @@ class ActiveRecordTest < MiniTest::Spec
42
42
 
43
43
  it { form.class.i18n_scope.must_equal :activerecord }
44
44
 
45
- it "allows accessing the database" do
46
- end
47
-
48
45
  # uniqueness
49
46
  it "has no errors on title when title is unique for the same artist and album" do
50
47
  form.validate("title" => "The Gargoyle", "artist_id" => artist.id, "album" => album.id, "created_at" => "November 6, 1966")
data/test/errors_test.rb CHANGED
@@ -22,9 +22,10 @@ class ErrorsTest < MiniTest::Spec
22
22
  end
23
23
  # TODO: make band a required object.
24
24
 
25
- validate :validate_musical_taste
25
+ validate :music_taste_ok?
26
26
 
27
- def validate_musical_taste
27
+ private
28
+ def music_taste_ok?
28
29
  errors.add(:base, "You are a bad person") if name == 'Nickelback'
29
30
  end
30
31
  end
@@ -101,7 +102,7 @@ class ErrorsTest < MiniTest::Spec
101
102
  before { @result = form.validate("songs"=>[{"title" => ""}], "band"=>{"label"=>{:name => "Fat Wreck"}}) }
102
103
 
103
104
  it { @result.must_equal false }
104
- it( "xxxx") { form.errors.messages.must_equal({:"songs.title"=>["can't be blank"]}) }
105
+ it { form.errors.messages.must_equal({:"songs.title"=>["can't be blank"]}) }
105
106
  end
106
107
 
107
108
 
@@ -113,10 +114,11 @@ class ErrorsTest < MiniTest::Spec
113
114
  end
114
115
 
115
116
  describe "#validate with nested form using :base invalid" do
116
- before { @result = form.validate("songs"=>[{"title" => "Someday"}], "band" => {"name" => "Nickelback", "label" => {"name" => "Roadrunner Records"}}) }
117
-
118
- it { @result.must_equal false }
119
- it { form.errors.messages.must_equal({:base=>["You are a bad person"]}) }
117
+ it "xxx" do
118
+ result = form.validate("songs"=>[{"title" => "Someday"}], "band" => {"name" => "Nickelback", "label" => {"name" => "Roadrunner Records"}})
119
+ result.must_equal false
120
+ form.errors.messages.must_equal({:base=>["You are a bad person"]})
121
+ end
120
122
  end
121
123
 
122
124
  describe "correct #validate" do
@@ -6,7 +6,7 @@ class FormOptionTest < MiniTest::Spec
6
6
 
7
7
  class SongForm < Reform::Form
8
8
  property :title
9
- validates_presence_of :title
9
+ validates :title, presence: true
10
10
  end
11
11
 
12
12
  class AlbumForm < Reform::Form
@@ -0,0 +1,150 @@
1
+ require "test_helper"
2
+
3
+ require "reform/form/lotus"
4
+
5
+ class LotusValidationsTest < MiniTest::Spec
6
+ class AlbumForm < Reform::Form
7
+ feature Lotus
8
+
9
+ property :title
10
+
11
+ property :hit do
12
+ property :title
13
+ validates :title, :presence => true
14
+ end
15
+
16
+ collection :songs do
17
+ property :title
18
+ validates :title, :presence => true
19
+ end
20
+
21
+ property :band do # yepp, people do crazy stuff like that.
22
+ property :name
23
+ property :label do
24
+ property :name
25
+ validates :name, :presence => true
26
+ end
27
+ # TODO: make band a required object.
28
+
29
+ validate :validate_musical_taste
30
+
31
+ def validate_musical_taste
32
+ errors.add(:base, "You are a bad person") if name == 'Nickelback'
33
+ end
34
+ end
35
+
36
+ validates :title, :presence => true
37
+ end
38
+
39
+ let (:album) do
40
+ OpenStruct.new(
41
+ :title => "Blackhawks Over Los Angeles",
42
+ :hit => song,
43
+ :songs => songs, # TODO: document this requirement,
44
+
45
+ :band => Struct.new(:name, :label).new("Epitaph", OpenStruct.new),
46
+ )
47
+ end
48
+ let (:song) { OpenStruct.new(:title => "Downtown") }
49
+ let (:songs) { [song=OpenStruct.new(:title => "Calling"), song] }
50
+ let (:form) { AlbumForm.new(album) }
51
+
52
+
53
+ # correct #validate.
54
+ it do
55
+ result = form.validate(
56
+ "name" => "Best Of",
57
+ "songs" => [{"title" => "Fallout"}, {"title" => "Roxanne", "composer" => {"name" => "Sting"}}],
58
+ "artist" => {"name" => "The Police"},
59
+ "band" => {"label" => {"name" => "Epitaph"}},
60
+ )
61
+
62
+ result.must_equal true
63
+ form.errors.inspect.must_equal "{}"
64
+ end
65
+
66
+
67
+ describe "incorrect #validate" do
68
+ it("xxx") do
69
+ result = form.validate(
70
+ "hit" =>{"title" => ""},
71
+ "title" => "",
72
+ "songs" => [{"title" => ""}, {"title" => ""}])
73
+
74
+ result.must_equal false
75
+
76
+ form.errors.messages.inspect.must_match "title"
77
+ form.errors.messages.inspect.must_match "hit.title"
78
+ form.errors.messages.inspect.must_match "songs.title"
79
+ form.errors.messages.inspect.must_match "band.label.name"
80
+
81
+
82
+ form.hit.errors.messages.inspect.must_match "title"
83
+ form.songs[0].errors.messages.inspect.must_match "title"
84
+ # FIXME
85
+
86
+ # form.errors.messages.must_equal({
87
+ # :title => ["can't be blank"],
88
+ # :"hit.title"=>["can't be blank"],
89
+ # :"songs.title"=>["can't be blank"],
90
+ # :"band.label.name"=>["can't be blank"]
91
+ # })
92
+
93
+ # # nested forms keep their own Errors:
94
+ # form.hit.errors.messages.must_equal({:title=>["can't be blank"]})
95
+ # form.songs[0].errors.messages.must_equal({:title=>["can't be blank"]})
96
+
97
+ # form.errors.messages.must_equal({
98
+ # :title => ["can't be blank"],
99
+ # :"hit.title" => ["can't be blank"],
100
+ # :"songs.title"=> ["can't be blank"],
101
+ # :"band.label.name"=>["can't be blank"]
102
+ # })
103
+ end
104
+ end
105
+
106
+
107
+ describe "#validate with collection form invalid" do
108
+ it do
109
+ result = form.validate("songs"=>[{"title" => ""}], "band"=>{"label"=>{:name => "Fat Wreck"}})
110
+ result.must_equal false
111
+ # FIXME
112
+ # form.errors.messages.must_equal({:"songs.title"=>["can't be blank"]})
113
+ form.errors.messages.inspect.must_match "songs.title"
114
+ end
115
+ end
116
+
117
+
118
+ describe "#validate with collection and 2-level-nested invalid" do
119
+ it do
120
+ result = form.validate("songs"=>[{"title" => ""}], "band" => {"label" => {}})
121
+ result.must_equal false
122
+ # FIXME
123
+ # form.errors.messages.must_equal({:"songs.title"=>["can't be blank"], :"band.label.name"=>["can't be blank"]})
124
+ form.errors.messages.inspect.must_match "songs.title"
125
+ form.errors.messages.inspect.must_match "band.label.name"
126
+ end
127
+ end
128
+
129
+ # TODO: implement.
130
+ # describe "#validate with nested form using :base invalid" do
131
+ # before { @result = form.validate("songs"=>[{"title" => "Someday"}], "band" => {"name" => "Nickelback", "label" => {"name" => "Roadrunner Records"}}) }
132
+
133
+ # it { @result.must_equal false }
134
+ # it { form.errors.messages.must_equal({:base=>["You are a bad person"]}) }
135
+ # end
136
+
137
+ describe "correct #validate" do
138
+ before { @result = form.validate(
139
+ "hit" => {"title" => "Sacrifice"},
140
+ "title" => "Second Heat",
141
+ "songs" => [{"title"=>"Heart Of A Lion"}],
142
+ "band" => {"label"=>{:name => "Fat Wreck"}}
143
+ ) }
144
+
145
+ it { @result.must_equal true }
146
+ it { form.hit.title.must_equal "Sacrifice" }
147
+ it { form.title.must_equal "Second Heat" }
148
+ it { form.songs.first.title.must_equal "Heart Of A Lion" }
149
+ end
150
+ end
@@ -0,0 +1,311 @@
1
+ # require 'test_helper'
2
+ # def mongoid_present?
3
+ # require 'mongoid'
4
+ # Mongoid.configure do |config|
5
+ # config.connect_to("reform-mongoid-test")
6
+ # end
7
+ # true
8
+ # rescue
9
+ # false
10
+ # end
11
+
12
+ # if mongoid_present?
13
+ # require 'reform/mongoid'
14
+
15
+ # class Disc
16
+ # include Mongoid::Document
17
+ # field :title, type: String
18
+ # has_many :tunes
19
+ # has_and_belongs_to_many :musicians
20
+ # end
21
+
22
+ # class Musician
23
+ # include Mongoid::Document
24
+ # field :name, type: String
25
+ # end
26
+
27
+ # class Tune
28
+ # include Mongoid::Document
29
+ # include Mongoid::Timestamps
30
+ # field :title, type: String
31
+ # belongs_to :disc
32
+ # belongs_to :musician
33
+ # end
34
+
35
+ # class MongoidTest < MiniTest::Spec
36
+ # class TuneForm < Reform::Form
37
+ # include Reform::Form::Mongoid
38
+ # model :tune
39
+
40
+ # property :title
41
+ # property :created_at
42
+
43
+ # validates_uniqueness_of :title, scope: [:disc_id, :musician_id]
44
+ # validates :created_at, :presence => true # have another property to test if we mix up.
45
+
46
+ # property :musician do
47
+ # property :name
48
+ # validates_uniqueness_of :name # this currently also tests if Form::AR is included as a feature.
49
+ # end
50
+ # end
51
+
52
+ # let(:disc) { Disc.create(:title => "Damnation") }
53
+ # let(:musician) { Musician.create(:name => "Opeth") }
54
+ # let(:form) { TuneForm.new(Tune.new(:musician => Musician.new)) }
55
+
56
+ # it { form.class.i18n_scope.must_equal :mongoid }
57
+
58
+ # it "allows accessing the database" do
59
+ # end
60
+
61
+ # # uniqueness
62
+ # it "has no errors on title when title is unique for the same musician and disc" do
63
+ # form.validate("title" => "The Gargoyle", "musician_id" => musician.id, "disc" => disc.id, "created_at" => "November 6, 1966")
64
+ # assert_empty form.errors[:title]
65
+ # end
66
+
67
+ # it "has errors on title when title is taken for the same musician and disc" do
68
+ # skip "replace ActiveModel::Validations with our own, working and reusable gem."
69
+ # Tune.create(title: "Windowpane", musician_id: musician.id, disc_id: disc.id)
70
+ # form.validate("title" => "Windowpane", "musician_id" => musician.id, "disc" => disc)
71
+ # refute_empty form.errors[:title]
72
+ # end
73
+
74
+ # # nested object taken.
75
+ # it "is valid when musician name is unique" do
76
+ # form.validate("musician" => {"name" => "Paul Gilbert"}, "title" => "The Gargoyle", "created_at" => "November 6, 1966").must_equal true
77
+ # end
78
+
79
+ # it "is invalid and shows error when taken" do
80
+ # Tune.delete_all
81
+ # Musician.create(:name => "Racer X")
82
+
83
+ # form.validate("musician" => {"name" => "Racer X"}, "title" => "Ghost Inside My Skin").must_equal false
84
+ # form.errors.messages.must_equal({:"musician.name"=>["is already taken"], :created_at => ["can't be blank"]})
85
+ # end
86
+
87
+ # it "works with Composition" do
88
+ # form = Class.new(Reform::Form) do
89
+ # include Reform::Form::Mongoid
90
+ # include Reform::Form::Composition
91
+
92
+ # property :name, :on => :musician
93
+ # validates_uniqueness_of :name
94
+ # end.new(:musician => Musician.new)
95
+
96
+ # Musician.create(:name => "Bad Religion")
97
+ # form.validate("name" => "Bad Religion").must_equal false
98
+ # end
99
+
100
+ # describe "#save" do
101
+ # # TODO: test 1-n?
102
+ # it "calls model.save" do
103
+ # Musician.delete_all
104
+ # form.validate("musician" => {"name" => "Bad Religion"}, "title" => "Ghost Inside My Skin")
105
+ # form.save
106
+ # Musician.where(:name => "Bad Religion").size.must_equal 1
107
+ # end
108
+
109
+ # it "doesn't call model.save when block is given" do
110
+ # Musician.delete_all
111
+ # form.validate("name" => "Bad Religion")
112
+ # form.save {}
113
+ # Musician.where(:name => "Bad Religion").size.must_equal 0
114
+ # end
115
+ # end
116
+ # end
117
+
118
+
119
+ # class PopulateWithActiveRecordTest < MiniTest::Spec
120
+ # class DiscForm < Reform::Form
121
+
122
+ # property :title
123
+
124
+ # collection :tunes, :populate_if_empty => Tune do
125
+ # property :title
126
+ # end
127
+ # end
128
+
129
+ # let (:disc) { Disc.new(:tunes => []) }
130
+ # it do
131
+ # form = DiscForm.new(disc)
132
+
133
+ # form.validate("tunes" => [{"title" => "Straight From The Jacket"}])
134
+
135
+ # # form populated.
136
+ # form.tunes.size.must_equal 1
137
+ # form.tunes[0].model.must_be_kind_of Tune
138
+
139
+ # # model NOT populated.
140
+ # disc.tunes.must_equal []
141
+
142
+
143
+ # form.sync
144
+
145
+ # # form populated.
146
+ # form.tunes.size.must_equal 1
147
+ # form.tunes[0].model.must_be_kind_of Tune
148
+
149
+ # # model also populated.
150
+ # tune = disc.tunes[0]
151
+ # disc.tunes.must_equal [tune]
152
+ # tune.title.must_equal "Straight From The Jacket"
153
+
154
+
155
+ # # if ActiveRecord::VERSION::STRING !~ /^3.0/
156
+ # # # saving saves association.
157
+ # # form.save
158
+ # #
159
+ # # disc.reload
160
+ # # tune = disc.tunes[0]
161
+ # # disc.tunes.must_equal [tune]
162
+ # # tune.title.must_equal "Straight From The Jacket"
163
+ # # end
164
+ # end
165
+
166
+
167
+ # describe "modifying 1., adding 2." do
168
+ # let (:tune) { Tune.new(:title => "Part 2") }
169
+ # let (:disc) { Disc.create.tap { |a| a.tunes << tune } }
170
+
171
+ # it do
172
+ # form = DiscForm.new(disc)
173
+
174
+ # id = disc.tunes[0].id
175
+ # assert id > 0
176
+
177
+ # form.validate("tunes" => [{"title" => "Part Two"}, {"title" => "Check For A Pulse"}])
178
+
179
+ # # form populated.
180
+ # form.tunes.size.must_equal 2
181
+ # form.tunes[0].model.must_be_kind_of Tune
182
+ # form.tunes[1].model.must_be_kind_of Tune
183
+
184
+ # # model NOT populated.
185
+ # disc.tunes.must_equal [tune]
186
+
187
+
188
+ # form.sync
189
+
190
+ # # form populated.
191
+ # form.tunes.size.must_equal 2
192
+
193
+ # # model also populated.
194
+ # disc.tunes.size.must_equal 2
195
+
196
+ # # corrected title
197
+ # disc.tunes[0].title.must_equal "Part Two"
198
+ # # ..but same tune.
199
+ # disc.tunes[0].id.must_equal id
200
+
201
+ # # and a new tune.
202
+ # disc.tunes[1].title.must_equal "Check For A Pulse"
203
+ # disc.tunes[1].persisted?.must_equal true # TODO: with << strategy, this shouldn't be saved.
204
+ # end
205
+
206
+ # describe 'using nested_models_attributes to modify nested collection' do
207
+ # class ActiveModelDiscForm < Reform::Form
208
+ # include Reform::Form::ActiveModel
209
+ # include Reform::Form::ActiveModel::FormBuilderMethods
210
+
211
+ # property :title
212
+
213
+ # collection :tunes, :populate_if_empty => Tune do
214
+ # property :title
215
+ # end
216
+ # end
217
+
218
+ # let (:disc) { Disc.create(:title => 'Greatest Hits') }
219
+ # let (:form) { ActiveModelDiscForm.new(disc) }
220
+
221
+ # it do
222
+ # form.validate('tunes_attributes' => {'0' => {'title' => 'Tango'}})
223
+
224
+ # # form populated.
225
+ # form.tunes.size.must_equal 1
226
+ # form.tunes[0].model.must_be_kind_of Tune
227
+ # form.tunes[0].title.must_equal 'Tango'
228
+
229
+ # # model NOT populated.
230
+ # disc.tunes.must_equal []
231
+
232
+ # form.save
233
+
234
+ # # nested model persisted.
235
+ # first_tune = disc.tunes[0]
236
+ # assert first_tune.id > 0
237
+
238
+ # # form populated.
239
+ # form.tunes.size.must_equal 1
240
+
241
+ # # model also populated.
242
+ # disc.tunes.size.must_equal 1
243
+ # disc.tunes[0].title.must_equal 'Tango'
244
+
245
+ # form = ActiveModelDiscForm.new(disc)
246
+ # form.validate('tunes_attributes' => {'0' => {'id' => first_tune.id, 'title' => 'Tango nuevo'}, '1' => {'title' => 'Waltz'}})
247
+
248
+ # # form populated.
249
+ # form.tunes.size.must_equal 2
250
+ # form.tunes[0].model.must_be_kind_of Tune
251
+ # form.tunes[1].model.must_be_kind_of Tune
252
+ # form.tunes[0].title.must_equal 'Tango nuevo'
253
+ # form.tunes[1].title.must_equal 'Waltz'
254
+
255
+ # # model NOT populated.
256
+ # disc.tunes.size.must_equal 1
257
+ # disc.tunes[0].title.must_equal 'Tango'
258
+
259
+ # form.save
260
+
261
+ # # form populated.
262
+ # form.tunes.size.must_equal 2
263
+
264
+ # # model also populated.
265
+ # disc.tunes.size.must_equal 2
266
+ # disc.tunes[0].id.must_equal first_tune.id
267
+ # disc.tunes[0].persisted?.must_equal true
268
+ # disc.tunes[1].persisted?.must_equal true
269
+ # disc.tunes[0].title.must_equal 'Tango nuevo'
270
+ # disc.tunes[1].title.must_equal 'Waltz'
271
+ # end
272
+ # end
273
+ # end
274
+
275
+ # # it do
276
+ # # a=Disc.new
277
+ # # a.tunes << Tune.new(title: "Old What's His Name") # Tune does not get persisted.
278
+
279
+ # # a.tunes[1] = Tune.new(title: "Permanent Rust")
280
+
281
+ # # puts "@@@"
282
+ # # puts a.tunes.inspect
283
+
284
+ # # puts "---"
285
+ # # a.save
286
+ # # puts a.tunes.inspect
287
+
288
+ # # b = a.tunes.first
289
+
290
+ # # a.tunes = [Tune.new(title:"Biomag")]
291
+ # # puts "\\\\"
292
+ # # a.save
293
+ # # a.reload
294
+ # # puts a.tunes.inspect
295
+
296
+ # # b.reload
297
+ # # puts "#{b.inspect}, #{b.persisted?}"
298
+
299
+
300
+ # # a.tunes = [a.tunes.first, Tune.new(title: "Count Down")]
301
+ # # b = a.tunes.first
302
+ # # puts ":::::"
303
+ # # a.save
304
+ # # a.reload
305
+ # # puts a.tunes.inspect
306
+
307
+ # # b.reload
308
+ # # puts "#{b.inspect}, #{b.persisted?}"
309
+ # # end
310
+ # end
311
+ # end