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.
- checksums.yaml +4 -4
- data/.travis.yml +6 -4
- data/CHANGES.md +3 -0
- data/Gemfile +1 -1
- data/README.md +47 -3
- data/database.sqlite3 +0 -0
- data/gemfiles/Gemfile.rails-4.1 +8 -0
- data/gemfiles/Gemfile.rails-4.2 +8 -0
- data/lib/reform/active_record.rb +1 -0
- data/lib/reform/contract.rb +7 -11
- data/lib/reform/contract/errors.rb +1 -15
- data/lib/reform/contract/validate.rb +13 -18
- data/lib/reform/form/active_model.rb +5 -0
- data/lib/reform/form/active_model/validations.rb +66 -0
- data/lib/reform/form/active_record.rb +2 -23
- data/lib/reform/form/lotus.rb +55 -0
- data/lib/reform/form/mongoid.rb +37 -0
- data/lib/reform/form/orm.rb +26 -0
- data/lib/reform/form/validation/unique_validator.rb +5 -0
- data/lib/reform/mongoid.rb +4 -0
- data/lib/reform/rails.rb +7 -3
- data/lib/reform/version.rb +1 -1
- data/reform.gemspec +2 -2
- data/test/active_model_test.rb +13 -12
- data/test/active_record_test.rb +0 -3
- data/test/errors_test.rb +9 -7
- data/test/form_option_test.rb +1 -1
- data/test/lotus_test.rb +150 -0
- data/test/mongoid_test.rb +311 -0
- data/test/reform_test.rb +14 -11
- data/test/test_helper.rb +13 -0
- data/test/{uniqueness_test.rb → unique_test.rb} +0 -0
- data/test/validate_test.rb +2 -11
- metadata +38 -27
@@ -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
|
data/lib/reform/rails.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
|
-
require
|
1
|
+
require "reform/form/active_model"
|
2
|
+
require "reform/form/active_model/validations"
|
2
3
|
|
3
|
-
require
|
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
|
-
|
11
|
+
include Reform::Form::Mongoid if defined?(Mongoid)
|
12
|
+
include Reform::Form::ActiveModel::Validations
|
13
|
+
end
|
data/lib/reform/version.rb
CHANGED
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
|
data/test/active_model_test.rb
CHANGED
@@ -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
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
data/test/active_record_test.rb
CHANGED
@@ -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 :
|
25
|
+
validate :music_taste_ok?
|
26
26
|
|
27
|
-
|
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
|
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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
data/test/form_option_test.rb
CHANGED
data/test/lotus_test.rb
ADDED
@@ -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
|