reform 1.2.6 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -1
- data/CHANGES.md +14 -0
- data/Gemfile +3 -2
- data/README.md +225 -283
- data/Rakefile +27 -0
- data/TODO.md +12 -0
- data/database.sqlite3 +0 -0
- data/gemfiles/Gemfile.rails-3.0 +1 -0
- data/gemfiles/Gemfile.rails-3.1 +1 -0
- data/gemfiles/Gemfile.rails-3.2 +1 -0
- data/gemfiles/Gemfile.rails-4.0 +1 -0
- data/lib/reform.rb +0 -1
- data/lib/reform/contract.rb +64 -170
- data/lib/reform/contract/validate.rb +10 -13
- data/lib/reform/form.rb +74 -19
- data/lib/reform/form/active_model.rb +19 -14
- data/lib/reform/form/coercion.rb +1 -13
- data/lib/reform/form/composition.rb +2 -24
- data/lib/reform/form/multi_parameter_attributes.rb +43 -62
- data/lib/reform/form/populator.rb +85 -0
- data/lib/reform/form/prepopulate.rb +13 -43
- data/lib/reform/form/validate.rb +29 -90
- data/lib/reform/form/validation/unique_validator.rb +13 -0
- data/lib/reform/version.rb +1 -1
- data/reform.gemspec +7 -7
- data/test/active_model_test.rb +43 -0
- data/test/changed_test.rb +23 -51
- data/test/coercion_test.rb +1 -7
- data/test/composition_test.rb +128 -34
- data/test/contract_test.rb +27 -86
- data/test/feature_test.rb +43 -6
- data/test/fields_test.rb +2 -12
- data/test/form_builder_test.rb +28 -25
- data/test/form_option_test.rb +19 -0
- data/test/from_test.rb +0 -75
- data/test/inherit_test.rb +178 -117
- data/test/model_reflections_test.rb +1 -1
- data/test/populate_test.rb +226 -0
- data/test/prepopulator_test.rb +112 -0
- data/test/readable_test.rb +2 -4
- data/test/save_test.rb +56 -112
- data/test/setup_test.rb +48 -0
- data/test/skip_if_test.rb +5 -2
- data/test/skip_setter_and_getter_test.rb +54 -0
- data/test/test_helper.rb +3 -1
- data/test/uniqueness_test.rb +41 -0
- data/test/validate_test.rb +325 -289
- data/test/virtual_test.rb +1 -3
- data/test/writeable_test.rb +3 -4
- metadata +35 -39
- data/lib/reform/composition.rb +0 -63
- data/lib/reform/contract/setup.rb +0 -50
- data/lib/reform/form/changed.rb +0 -9
- data/lib/reform/form/sync.rb +0 -116
- data/lib/reform/representer.rb +0 -84
- data/test/empty_test.rb +0 -58
- data/test/form_composition_test.rb +0 -145
- data/test/nested_form_test.rb +0 -197
- data/test/prepopulate_test.rb +0 -85
- data/test/sync_option_test.rb +0 -83
- data/test/sync_test.rb +0 -56
@@ -0,0 +1,13 @@
|
|
1
|
+
class Reform::Form::UniqueValidator < ActiveModel::EachValidator
|
2
|
+
def validate_each(form, attribute, value)
|
3
|
+
# search for models with attribute equals to form field value
|
4
|
+
query = form.model.class.where(attribute => value)
|
5
|
+
|
6
|
+
# if model persisted, excluded own model from query
|
7
|
+
query = query.merge(form.model.class.where("id <> ?", form.model.id)) if form.model.persisted?
|
8
|
+
|
9
|
+
# if any models found, add error on attribute
|
10
|
+
form.errors.add(attribute, "#{attribute} must be unique.") if query.any?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
data/lib/reform/version.rb
CHANGED
data/reform.gemspec
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# coding: utf-8
|
2
1
|
lib = File.expand_path('../lib', __FILE__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require 'reform/version'
|
@@ -8,8 +7,8 @@ Gem::Specification.new do |spec|
|
|
8
7
|
spec.version = Reform::VERSION
|
9
8
|
spec.authors = ["Nick Sutterer", "Garrett Heinlen"]
|
10
9
|
spec.email = ["apotonick@gmail.com", "heinleng@gmail.com"]
|
11
|
-
spec.description = %q{
|
12
|
-
spec.summary = %q{
|
10
|
+
spec.description = %q{Form object decoupled from models.}
|
11
|
+
spec.summary = %q{Form object decoupled from models with validation, population and presentation.}
|
13
12
|
spec.homepage = "https://github.com/apotonick/reform"
|
14
13
|
spec.license = "MIT"
|
15
14
|
|
@@ -18,13 +17,14 @@ Gem::Specification.new do |spec|
|
|
18
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
18
|
spec.require_paths = ["lib"]
|
20
19
|
|
21
|
-
spec.add_dependency "representable", "
|
22
|
-
spec.add_dependency "disposable", "~> 0.
|
20
|
+
spec.add_dependency "representable", ">= 2.2.2"
|
21
|
+
spec.add_dependency "disposable", "~> 0.1.2"
|
23
22
|
spec.add_dependency "uber", "~> 0.0.11"
|
24
23
|
spec.add_dependency "activemodel"
|
25
|
-
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler"
|
26
26
|
spec.add_development_dependency "rake"
|
27
|
-
spec.add_development_dependency "minitest"
|
27
|
+
spec.add_development_dependency "minitest"
|
28
28
|
spec.add_development_dependency "activerecord"
|
29
29
|
spec.add_development_dependency "sqlite3"
|
30
30
|
spec.add_development_dependency "virtus"
|
data/test/active_model_test.rb
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
+
module IsolatedRailsEngine
|
4
|
+
def self.use_relative_model_naming?
|
5
|
+
true
|
6
|
+
end
|
7
|
+
|
8
|
+
class Lyric < ActiveRecord::Base
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module NormalRailsEngine
|
13
|
+
class Lyric < ActiveRecord::Base
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
3
18
|
class NewActiveModelTest < MiniTest::Spec # TODO: move to test/rails/
|
4
19
|
class SongForm < Reform::Form
|
5
20
|
include Reform::Form::ActiveModel
|
@@ -31,6 +46,27 @@ class NewActiveModelTest < MiniTest::Spec # TODO: move to test/rails/
|
|
31
46
|
it { class_with_model.model_name.must_be_kind_of ActiveModel::Name }
|
32
47
|
it { class_with_model.model_name.to_s.must_equal "Album" }
|
33
48
|
|
49
|
+
let (:class_with_isolated_model) {
|
50
|
+
Class.new(Reform::Form) do
|
51
|
+
include Reform::Form::ActiveModel
|
52
|
+
|
53
|
+
model "isolated_rails_engine/lyric", namespace: "isolated_rails_engine"
|
54
|
+
end
|
55
|
+
}
|
56
|
+
|
57
|
+
it { class_with_isolated_model.model_name.must_be_kind_of ActiveModel::Name }
|
58
|
+
it { class_with_isolated_model.model_name.to_s.must_equal "IsolatedRailsEngine::Lyric" }
|
59
|
+
|
60
|
+
let (:class_with_namespace_model) {
|
61
|
+
Class.new(Reform::Form) do
|
62
|
+
include Reform::Form::ActiveModel
|
63
|
+
|
64
|
+
model "normal_rails_engine/lyric"
|
65
|
+
end
|
66
|
+
}
|
67
|
+
|
68
|
+
it { class_with_namespace_model.model_name.must_be_kind_of ActiveModel::Name }
|
69
|
+
it { class_with_namespace_model.model_name.to_s.must_equal "NormalRailsEngine::Lyric" }
|
34
70
|
|
35
71
|
let (:subclass_of_class_with_model) {
|
36
72
|
Class.new(class_with_model)
|
@@ -39,6 +75,13 @@ class NewActiveModelTest < MiniTest::Spec # TODO: move to test/rails/
|
|
39
75
|
it { subclass_of_class_with_model.model_name.must_be_kind_of ActiveModel::Name }
|
40
76
|
it { subclass_of_class_with_model.model_name.to_s.must_equal 'Album' }
|
41
77
|
|
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
|
42
85
|
|
43
86
|
describe "class named Song::Form" do
|
44
87
|
it do
|
data/test/changed_test.rb
CHANGED
@@ -1,69 +1,41 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
require 'reform/form/coercion'
|
3
3
|
|
4
|
-
class ChangedTest <
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
property :title
|
4
|
+
class ChangedTest < MiniTest::Spec
|
5
|
+
Song = Struct.new(:title, :album, :composer)
|
6
|
+
Album = Struct.new(:name, :songs, :artist)
|
7
|
+
Artist = Struct.new(:name)
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
property :length, type: Integer
|
13
|
-
validates :title, :presence => true
|
14
|
-
end
|
9
|
+
class AlbumForm < Reform::Form
|
10
|
+
property :name
|
15
11
|
|
16
12
|
collection :songs do
|
17
13
|
property :title
|
18
|
-
validates :title, :presence => true
|
19
|
-
end
|
20
14
|
|
21
|
-
|
22
|
-
property :label do
|
15
|
+
property :composer do
|
23
16
|
property :name
|
24
|
-
property :location
|
25
|
-
validates :name, :presence => true
|
26
17
|
end
|
27
|
-
# TODO: make band a required object.
|
28
18
|
end
|
29
|
-
|
30
|
-
validates :title, :presence => true
|
31
19
|
end
|
32
20
|
|
33
|
-
|
21
|
+
let (:song_with_composer) { Song.new("Resist Stance", nil, composer) }
|
22
|
+
let (:composer) { Artist.new("Greg Graffin") }
|
23
|
+
let (:album) { Album.new("The Dissent Of Man", [song_with_composer]) }
|
34
24
|
|
35
|
-
|
36
|
-
let (:form) { AlbumForm.new(Album.new("Drawn Down The Moon", Song.new("The Ripper", 9), [Song.new("Black Candles"), Song.new("The Ripper")], Band.new(Label.new("Cleopatra Records")))) }
|
25
|
+
let (:form) { AlbumForm.new(album) }
|
37
26
|
|
38
|
-
|
39
|
-
it
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
describe "#validate" do
|
45
|
-
before { form.validate(
|
46
|
-
"title" => "Five", # changed.
|
47
|
-
"hit" => {"title" => "The Ripper", # same, but overridden.
|
48
|
-
"length" => "9"}, # gets coerced, then compared, so not changed.
|
49
|
-
"band" => {"label" => {"name" => "Shrapnel Records"}} # only label.name changes.
|
50
|
-
) }
|
51
|
-
|
52
|
-
it { form.changed?(:title).must_equal true }
|
53
|
-
|
54
|
-
# it { form.changed?(:hit).must_equal false }
|
55
|
-
|
56
|
-
# overridden with same value is no change.
|
57
|
-
it { form.hit.changed?(:title).must_equal false }
|
58
|
-
# coerced value is identical to form's => not changed.
|
59
|
-
it { form.hit.changed?(:length).must_equal false }
|
60
|
-
|
61
|
-
# it { form.changed?(:band).must_equal true }
|
62
|
-
# it { form.band.changed?(:label).must_equal true }
|
63
|
-
it { form.band.label.changed?(:name).must_equal true }
|
27
|
+
# nothing changed after setup.
|
28
|
+
it do
|
29
|
+
form.changed?(:name).must_equal false
|
30
|
+
form.songs[0].changed?(:title).must_equal false
|
31
|
+
form.songs[0].composer.changed?(:name).must_equal false
|
32
|
+
end
|
64
33
|
|
65
|
-
|
66
|
-
|
67
|
-
|
34
|
+
# after validate, things might have changed.
|
35
|
+
it do
|
36
|
+
form.validate("name" => "Out Of Bounds", "songs" => [{"composer" => {"name" => "Ingemar Jansson & Mikael Danielsson"}}])
|
37
|
+
form.changed?(:name).must_equal true
|
38
|
+
form.songs[0].changed?(:title).must_equal false
|
39
|
+
form.songs[0].composer.changed?(:name).must_equal true
|
68
40
|
end
|
69
41
|
end
|
data/test/coercion_test.rb
CHANGED
@@ -9,20 +9,17 @@ class CoercionTest < BaseTest
|
|
9
9
|
end
|
10
10
|
|
11
11
|
class Form < Reform::Form
|
12
|
-
include Coercion
|
12
|
+
# include Coercion
|
13
13
|
|
14
14
|
property :released_at, :type => DateTime
|
15
15
|
|
16
16
|
property :hit do
|
17
|
-
# include Coercion
|
18
17
|
property :length, :type => Integer
|
19
18
|
property :good, :type => Virtus::Attribute::Boolean
|
20
19
|
end
|
21
20
|
|
22
21
|
property :band do
|
23
|
-
include Coercion
|
24
22
|
property :label do
|
25
|
-
include Coercion
|
26
23
|
property :value, :type => Irreversible
|
27
24
|
end
|
28
25
|
end
|
@@ -66,7 +63,4 @@ class CoercionTest < BaseTest
|
|
66
63
|
end
|
67
64
|
|
68
65
|
# save
|
69
|
-
|
70
|
-
|
71
|
-
|
72
66
|
end
|
data/test/composition_test.rb
CHANGED
@@ -1,51 +1,145 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FormCompositionTest < MiniTest::Spec
|
4
|
+
Song = Struct.new(:id, :title, :band)
|
5
|
+
Requester = Struct.new(:id, :name, :requester)
|
6
|
+
Band = Struct.new(:title)
|
7
|
+
|
8
|
+
class RequestForm < Reform::Form
|
9
|
+
include Composition
|
10
|
+
|
11
|
+
property :name, :on => :requester
|
12
|
+
property :requester_id, :on => :requester, :from => :id
|
13
|
+
properties :title, :id, :on => :song
|
14
|
+
# property :channel # FIXME: what about the "main model"?
|
15
|
+
property :channel, :virtual => true, :on => :song
|
16
|
+
property :requester, :on => :requester
|
17
|
+
property :captcha, :on => :song, :virtual => true
|
18
|
+
|
19
|
+
validates :name, :title, :channel, :presence => true
|
20
|
+
|
21
|
+
property :band, :on => :song do
|
22
|
+
property :title
|
23
|
+
end
|
4
24
|
end
|
5
25
|
|
6
|
-
let (:
|
26
|
+
let (:form) { RequestForm.new(:song => song, :requester => requester) }
|
27
|
+
let (:song) { Song.new(1, "Rio", band) }
|
28
|
+
let (:requester) { Requester.new(2, "Duran Duran", "MCP") }
|
29
|
+
let (:band) { Band.new("Duran^2") }
|
30
|
+
|
31
|
+
# delegation form -> composition works
|
32
|
+
it { form.id.must_equal 1 }
|
33
|
+
it { form.title.must_equal "Rio" }
|
34
|
+
it { form.name.must_equal "Duran Duran" }
|
35
|
+
it { form.requester_id.must_equal 2 }
|
36
|
+
it { form.channel.must_equal nil }
|
37
|
+
it { form.requester.must_equal "MCP" } # same name as composed model.
|
38
|
+
it { form.captcha.must_equal nil }
|
39
|
+
|
40
|
+
# #model just returns <Composition>.
|
41
|
+
it { form.mapper.must_be_kind_of Disposable::Composition }
|
7
42
|
|
8
|
-
|
9
|
-
|
10
|
-
|
43
|
+
# #model[] -> composed models
|
44
|
+
it { form.model[:requester].must_equal requester }
|
45
|
+
it { form.model[:song].must_equal song }
|
46
|
+
|
47
|
+
|
48
|
+
it "creates Composition for you" do
|
49
|
+
form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb").must_equal false
|
11
50
|
end
|
12
51
|
|
13
|
-
|
14
|
-
|
15
|
-
|
52
|
+
describe "#save" do
|
53
|
+
# #save with {}
|
54
|
+
it do
|
55
|
+
hash = {}
|
56
|
+
|
57
|
+
form.save do |map|
|
58
|
+
hash[:name] = form.name
|
59
|
+
hash[:title] = form.title
|
60
|
+
end
|
61
|
+
|
62
|
+
hash.must_equal({:name=>"Duran Duran", :title=>"Rio"})
|
16
63
|
end
|
17
|
-
end
|
18
64
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
Class.new(Reform::Representer) do
|
24
|
-
property :name, :on => :artist
|
25
|
-
property :title, :on => :song
|
26
|
-
end
|
27
|
-
).
|
28
|
-
new(:artist => duran, :song => rio)
|
65
|
+
it "provides nested symbolized hash as second block argument" do
|
66
|
+
form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb", "channel" => "JJJ", "captcha" => "wonderful")
|
67
|
+
|
68
|
+
hash = nil
|
29
69
|
|
30
|
-
|
31
|
-
|
70
|
+
form.save do |map|
|
71
|
+
hash = map
|
72
|
+
end
|
73
|
+
|
74
|
+
hash.must_equal({
|
75
|
+
:song=>{"title"=>"Greyhound", "id"=>1, "channel" => "JJJ", "captcha"=>"wonderful", "band"=>{"title"=>"Duran^2"}},
|
76
|
+
:requester=>{"name"=>"Frenzal Rhomb", "id"=>2, "requester" => "MCP"}
|
77
|
+
}
|
78
|
+
)
|
32
79
|
end
|
33
|
-
end
|
34
80
|
|
81
|
+
it "xxx pushes data to models and calls #save when no block passed" do
|
82
|
+
song.extend(Saveable)
|
83
|
+
requester.extend(Saveable)
|
84
|
+
band.extend(Saveable)
|
35
85
|
|
36
|
-
|
37
|
-
|
38
|
-
|
86
|
+
form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb", "captcha" => "1337")
|
87
|
+
form.captcha.must_equal "1337" # TODO: move to separate test.
|
88
|
+
|
89
|
+
form.save
|
90
|
+
|
91
|
+
requester.name.must_equal "Frenzal Rhomb"
|
92
|
+
requester.saved?.must_equal true
|
93
|
+
song.title.must_equal "Greyhound"
|
94
|
+
song.saved?.must_equal true
|
95
|
+
song.band.title.must_equal "Duran^2"
|
96
|
+
song.band.saved?.must_equal true
|
39
97
|
end
|
40
98
|
|
41
|
-
it "
|
42
|
-
|
99
|
+
it "returns true when models all save successfully" do
|
100
|
+
song.extend(Saveable)
|
101
|
+
requester.extend(Saveable)
|
102
|
+
band.extend(Saveable)
|
103
|
+
|
104
|
+
form.save.must_equal true
|
43
105
|
end
|
44
106
|
|
45
|
-
it "
|
46
|
-
|
47
|
-
|
48
|
-
|
107
|
+
it "returns false when one or more models don't save successfully" do
|
108
|
+
module Unsaveable
|
109
|
+
def save
|
110
|
+
false
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
song.extend(Unsaveable)
|
115
|
+
requester.extend(Saveable)
|
116
|
+
band.extend(Saveable)
|
117
|
+
|
118
|
+
form.save.must_equal false
|
49
119
|
end
|
50
120
|
end
|
51
|
-
end
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
class FormCompositionCollectionTest < MiniTest::Spec
|
125
|
+
Book = Struct.new(:id, :name)
|
126
|
+
Library = Struct.new(:id) do
|
127
|
+
def books
|
128
|
+
[Book.new(1,"My book")]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class LibraryForm < Reform::Form
|
133
|
+
include Reform::Form::Composition
|
134
|
+
|
135
|
+
collection :books, on: :library do
|
136
|
+
property :id
|
137
|
+
property :name
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
let (:form) { LibraryForm.new(library: library) }
|
142
|
+
let (:library) { Library.new(2) }
|
143
|
+
|
144
|
+
it { form.save do |hash| hash.must_equal({:library=>{"books"=>[{"id"=>1, "name"=>"My book"}]}}) end }
|
145
|
+
end
|
data/test/contract_test.rb
CHANGED
@@ -1,105 +1,46 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class ContractTest <
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
class ContractTest < MiniTest::Spec
|
4
|
+
Song = Struct.new(:title, :album, :composer)
|
5
|
+
Album = Struct.new(:name, :songs, :artist)
|
6
|
+
Artist = Struct.new(:name)
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
class ArtistForm < Reform::Form
|
9
|
+
property :name
|
10
|
+
end
|
11
|
+
|
12
|
+
class AlbumForm < Reform::Contract
|
13
|
+
property :name
|
14
|
+
validates :name, presence: true
|
12
15
|
|
13
16
|
collection :songs do
|
14
17
|
property :title
|
15
|
-
validates :title, :
|
16
|
-
end
|
17
|
-
|
18
|
-
validates :songs, :length => {:minimum => 4}
|
19
|
-
|
20
|
-
property :band do # yepp, people do crazy stuff like that.
|
21
|
-
validates :label, :presence => true
|
18
|
+
validates :title, presence: true
|
22
19
|
|
23
|
-
property :
|
20
|
+
property :composer do
|
21
|
+
validates :name, presence: true
|
24
22
|
property :name
|
25
|
-
validates :name, :presence => true
|
26
23
|
end
|
27
|
-
# TODO: make band a required object.
|
28
24
|
end
|
29
|
-
end
|
30
|
-
|
31
|
-
let (:album) { Album.new(nil, Song.new, [Song.new, Song.new], Band.new() ) }
|
32
|
-
subject { AlbumContract.new(album) }
|
33
|
-
|
34
|
-
|
35
|
-
describe "invalid" do
|
36
|
-
before {
|
37
|
-
res = subject.validate
|
38
|
-
res.must_equal false
|
39
|
-
}
|
40
|
-
|
41
|
-
it { subject.errors.messages.must_equal({:"hit.title"=>["can't be blank"], :"songs.title"=>["can't be blank"], :"band.label"=>["can't be blank"], :songs=>["is too short (minimum is 4 characters)"], :title=>["can't be blank", "is too short (minimum is 3 characters)"]}) }
|
42
|
-
end
|
43
25
|
|
44
|
-
|
45
|
-
describe "valid" do
|
46
|
-
let (:album) { Album.new(
|
47
|
-
"Keeper Of The Seven Keys",
|
48
|
-
nil,
|
49
|
-
[Song.new("Initiation"), Song.new("I'm Alive"), Song.new("A Little Time"), Song.new("Future World"),],
|
50
|
-
Band.new(Label.new("Noise"))
|
51
|
-
) }
|
52
|
-
|
53
|
-
before { subject.validate.must_equal true }
|
54
|
-
|
55
|
-
it { subject.errors.messages.must_equal({}) }
|
26
|
+
property :artist, form: ArtistForm
|
56
27
|
end
|
57
28
|
|
29
|
+
let (:song) { Song.new("Broken") }
|
30
|
+
let (:song_with_composer) { Song.new("Resist Stance", nil, composer) }
|
31
|
+
let (:composer) { Artist.new("Greg Graffin") }
|
32
|
+
let (:artist) { Artist.new("Bad Religion") }
|
33
|
+
let (:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
|
58
34
|
|
59
|
-
|
60
|
-
# without name will always iterate.
|
61
|
-
it do
|
62
|
-
names = []
|
63
|
-
AlbumContract.representer { |dfn| names << dfn.name }
|
64
|
-
names.must_equal ["hit", "songs", "band"]
|
65
|
-
|
66
|
-
# this doesn't cache.
|
67
|
-
names = []
|
68
|
-
AlbumContract.representer { |dfn| names << dfn.name }
|
69
|
-
names.must_equal ["hit", "songs", "band"]
|
70
|
-
end
|
71
|
-
|
72
|
-
# with name caches representer per class and runs once.
|
73
|
-
it do
|
74
|
-
names = []
|
75
|
-
AlbumContract.representer(:sync) { |dfn| names << dfn.name }
|
76
|
-
names.must_equal ["hit", "songs", "band"]
|
77
|
-
|
78
|
-
# this does cache.
|
79
|
-
names = []
|
80
|
-
AlbumContract.representer(:sync) { |dfn| names << dfn.name }
|
81
|
-
names.must_equal []
|
82
|
-
end
|
83
|
-
|
84
|
-
# it allows iterating all properties, not only nested.
|
85
|
-
it do
|
86
|
-
names = []
|
87
|
-
AlbumContract.representer(:save, all: true) { |dfn| names << dfn.name }
|
88
|
-
names.must_equal ["title", "hit", "songs", "band"]
|
89
|
-
|
90
|
-
names = []
|
91
|
-
AlbumContract.representer(:save, all: true) { |dfn| names << dfn.name }
|
92
|
-
names.must_equal []
|
93
|
-
end
|
94
|
-
|
95
|
-
# test :superclass?
|
96
|
-
end
|
35
|
+
let (:form) { AlbumForm.new(album) }
|
97
36
|
|
98
|
-
|
99
|
-
|
37
|
+
# accept `property form: SongForm`.
|
38
|
+
it do
|
39
|
+
form.artist.must_be_instance_of ArtistForm
|
100
40
|
end
|
101
41
|
|
102
42
|
describe "#options_for" do
|
103
|
-
it {
|
43
|
+
it { AlbumForm.options_for(:name).inspect.must_match "#<Representable::Definition ==>name @options" }
|
44
|
+
it { AlbumForm.new(album).options_for(:name).inspect.must_match "#<Representable::Definition ==>name @options" }
|
104
45
|
end
|
105
|
-
end
|
46
|
+
end
|