reform 2.2.4 → 2.3.3
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 +5 -5
- data/.gitignore +5 -1
- data/.travis.yml +11 -6
- data/Appraisals +8 -0
- data/CHANGES.md +57 -4
- data/CONTRIBUTING.md +31 -0
- data/Gemfile +2 -16
- data/ISSUE_TEMPLATE.md +25 -0
- data/LICENSE.txt +1 -1
- data/README.md +5 -7
- data/Rakefile +16 -9
- data/gemfiles/0.13.0.gemfile +8 -0
- data/gemfiles/1.5.0.gemfile +9 -0
- data/lib/reform.rb +1 -0
- data/lib/reform/contract.rb +7 -17
- data/lib/reform/contract/custom_error.rb +41 -0
- data/lib/reform/contract/validate.rb +53 -23
- data/lib/reform/errors.rb +61 -0
- data/lib/reform/form.rb +36 -10
- data/lib/reform/form/call.rb +1 -1
- data/lib/reform/form/composition.rb +2 -2
- data/lib/reform/form/dry.rb +10 -58
- data/lib/reform/form/dry/input_hash.rb +37 -0
- data/lib/reform/form/dry/new_api.rb +45 -0
- data/lib/reform/form/dry/old_api.rb +61 -0
- data/lib/reform/form/populator.rb +11 -27
- data/lib/reform/form/prepopulate.rb +4 -3
- data/lib/reform/form/validate.rb +28 -13
- data/lib/reform/result.rb +90 -0
- data/lib/reform/validation.rb +19 -11
- data/lib/reform/validation/groups.rb +12 -27
- data/lib/reform/version.rb +1 -1
- data/reform.gemspec +14 -13
- data/test/benchmarking.rb +39 -6
- data/test/call_new_api.rb +23 -0
- data/test/call_old_api.rb +23 -0
- data/test/changed_test.rb +14 -14
- data/test/coercion_test.rb +57 -25
- data/test/composition_new_api.rb +186 -0
- data/test/composition_old_api.rb +184 -0
- data/test/contract/custom_error_test.rb +55 -0
- data/test/contract_new_api.rb +77 -0
- data/test/contract_old_api.rb +77 -0
- data/test/default_test.rb +4 -4
- data/test/deserialize_test.rb +17 -20
- data/test/errors_new_api.rb +225 -0
- data/test/errors_old_api.rb +230 -0
- data/test/feature_test.rb +10 -12
- data/test/fixtures/dry_error_messages.yml +73 -23
- data/test/fixtures/dry_new_api_error_messages.yml +104 -0
- data/test/form_new_api.rb +57 -0
- data/test/{form_test.rb → form_old_api.rb} +8 -8
- data/test/form_option_new_api.rb +24 -0
- data/test/{form_option_test.rb → form_option_old_api.rb} +5 -5
- data/test/from_test.rb +18 -22
- data/test/inherit_new_api.rb +105 -0
- data/test/inherit_old_api.rb +105 -0
- data/test/{module_test.rb → module_new_api.rb} +26 -31
- data/test/module_old_api.rb +146 -0
- data/test/parse_option_test.rb +40 -0
- data/test/parse_pipeline_test.rb +4 -4
- data/test/populate_new_api.rb +304 -0
- data/test/populate_old_api.rb +304 -0
- data/test/populator_skip_test.rb +11 -11
- data/test/prepopulator_test.rb +23 -24
- data/test/read_only_test.rb +12 -1
- data/test/readable_test.rb +9 -9
- data/test/reform_new_api.rb +204 -0
- data/test/{reform_test.rb → reform_old_api.rb} +44 -65
- data/test/save_new_api.rb +101 -0
- data/test/save_old_api.rb +101 -0
- data/test/setup_test.rb +17 -17
- data/test/skip_if_new_api.rb +85 -0
- data/test/skip_if_old_api.rb +92 -0
- data/test/skip_setter_and_getter_test.rb +9 -10
- data/test/test_helper.rb +25 -14
- data/test/validate_new_api.rb +453 -0
- data/test/{validate_test.rb → validate_old_api.rb} +121 -131
- data/test/validation/dry_validation_new_api.rb +835 -0
- data/test/validation/dry_validation_old_api.rb +772 -0
- data/test/validation/result_test.rb +77 -0
- data/test/validation_library_provided_test.rb +16 -0
- data/test/virtual_test.rb +47 -7
- data/test/writeable_test.rb +38 -9
- metadata +111 -56
- data/gemfiles/Gemfile.disposable-0.3 +0 -6
- data/lib/reform/contract/errors.rb +0 -43
- data/lib/reform/form/mongoid.rb +0 -37
- data/lib/reform/form/orm.rb +0 -26
- data/lib/reform/mongoid.rb +0 -4
- data/test/call_test.rb +0 -23
- data/test/composition_test.rb +0 -149
- data/test/contract_test.rb +0 -77
- data/test/deprecation_test.rb +0 -27
- data/test/errors_test.rb +0 -165
- data/test/inherit_test.rb +0 -119
- data/test/populate_test.rb +0 -270
- data/test/readonly_test.rb +0 -14
- data/test/save_test.rb +0 -89
- data/test/skip_if_test.rb +0 -74
- data/test/validation/dry_test.rb +0 -60
- data/test/validation/dry_validation_test.rb +0 -352
- data/test/validation/errors.yml +0 -4
data/test/readable_test.rb
CHANGED
@@ -1,30 +1,30 @@
|
|
1
|
-
require
|
1
|
+
require "test_helper"
|
2
2
|
|
3
3
|
class ReadableTest < MiniTest::Spec
|
4
4
|
Credentials = Struct.new(:password)
|
5
5
|
|
6
|
-
class PasswordForm <
|
6
|
+
class PasswordForm < TestForm
|
7
7
|
property :password, readable: false
|
8
8
|
end
|
9
9
|
|
10
|
-
let
|
11
|
-
let
|
10
|
+
let(:cred) { Credentials.new }
|
11
|
+
let(:form) { PasswordForm.new(cred) }
|
12
12
|
|
13
13
|
it {
|
14
|
-
form.password
|
14
|
+
assert_nil form.password # password not read.
|
15
15
|
|
16
16
|
form.validate("password" => "123")
|
17
17
|
|
18
|
-
form.password.must_equal "123"
|
18
|
+
_(form.password).must_equal "123"
|
19
19
|
|
20
20
|
form.sync
|
21
|
-
cred.password.must_equal "123" # password written.
|
21
|
+
_(cred.password).must_equal "123" # password written.
|
22
22
|
|
23
23
|
hash = {}
|
24
24
|
form.save do |nested|
|
25
25
|
hash = nested
|
26
26
|
end
|
27
27
|
|
28
|
-
hash.must_equal("password"=> "123")
|
28
|
+
_(hash).must_equal("password" => "123")
|
29
29
|
}
|
30
|
-
end
|
30
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ReformTest < Minitest::Spec
|
4
|
+
let(:comp) { OpenStruct.new(name: "Duran Duran", title: "Rio") }
|
5
|
+
|
6
|
+
let(:form) { SongForm.new(comp) }
|
7
|
+
|
8
|
+
class SongForm < TestForm
|
9
|
+
property :name
|
10
|
+
property :title
|
11
|
+
|
12
|
+
validation do
|
13
|
+
params { required(:name).filled }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "(new) form with empty models" do
|
18
|
+
let(:comp) { OpenStruct.new }
|
19
|
+
|
20
|
+
it "returns empty fields" do
|
21
|
+
assert_nil form.title
|
22
|
+
_(form.name).must_be_nil
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "and submitted values" do
|
26
|
+
it "returns filled-out fields" do
|
27
|
+
form.validate("name" => "Duran Duran")
|
28
|
+
|
29
|
+
assert_nil form.title
|
30
|
+
_(form.name).must_equal "Duran Duran"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "(edit) form with existing models" do
|
36
|
+
it "returns filled-out fields" do
|
37
|
+
_(form.name).must_equal "Duran Duran"
|
38
|
+
_(form.title).must_equal "Rio"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#validate" do
|
43
|
+
let(:comp) { OpenStruct.new }
|
44
|
+
|
45
|
+
it "ignores unmapped fields in input" do
|
46
|
+
form.validate("name" => "Duran Duran", :genre => "80s")
|
47
|
+
assert_raises NoMethodError do
|
48
|
+
form.genre
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it "returns true when valid" do
|
53
|
+
_(form.validate("name" => "Duran Duran")).must_equal true
|
54
|
+
end
|
55
|
+
|
56
|
+
it "exposes input via property accessors" do
|
57
|
+
form.validate("name" => "Duran Duran")
|
58
|
+
|
59
|
+
_(form.name).must_equal "Duran Duran"
|
60
|
+
end
|
61
|
+
|
62
|
+
it "doesn't change model properties" do
|
63
|
+
form.validate("name" => "Duran Duran")
|
64
|
+
|
65
|
+
assert_nil comp.name # don't touch model, yet.
|
66
|
+
end
|
67
|
+
|
68
|
+
# TODO: test errors. test valid.
|
69
|
+
describe "invalid input" do
|
70
|
+
class ValidatingForm < TestForm
|
71
|
+
property :name
|
72
|
+
property :title
|
73
|
+
|
74
|
+
validation do
|
75
|
+
params do
|
76
|
+
required(:name).filled
|
77
|
+
required(:title).filled
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
let(:form) { ValidatingForm.new(comp) }
|
82
|
+
|
83
|
+
it "returns false when invalid" do
|
84
|
+
_(form.validate({})).must_equal false
|
85
|
+
end
|
86
|
+
|
87
|
+
it "populates errors" do
|
88
|
+
form.validate({})
|
89
|
+
_(form.errors.messages).must_equal(name: ["must be filled"], title: ["must be filled"])
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "#save" do
|
95
|
+
let(:comp) { OpenStruct.new }
|
96
|
+
let(:form) { SongForm.new(comp) }
|
97
|
+
|
98
|
+
before { form.validate("name" => "Diesel Boy") }
|
99
|
+
|
100
|
+
it "xxpushes data to models" do
|
101
|
+
form.save
|
102
|
+
|
103
|
+
_(comp.name).must_equal "Diesel Boy"
|
104
|
+
assert_nil comp.title
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "#save with block" do
|
108
|
+
it do
|
109
|
+
hash = {}
|
110
|
+
|
111
|
+
form.save do |map|
|
112
|
+
hash = map
|
113
|
+
end
|
114
|
+
|
115
|
+
_(hash).must_equal("name" => "Diesel Boy", "title" => nil)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "#model" do
|
121
|
+
it { _(form.model).must_equal comp }
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "inheritance" do
|
125
|
+
class HitForm < SongForm
|
126
|
+
property :position
|
127
|
+
validation do
|
128
|
+
params { required(:position).filled }
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
let(:form) { HitForm.new(OpenStruct.new()) }
|
133
|
+
it do
|
134
|
+
form.validate("title" => "The Body")
|
135
|
+
_(form.title).must_equal "The Body"
|
136
|
+
assert_nil form.position
|
137
|
+
_(form.errors.messages).must_equal(name: ["must be filled"], position: ["must be filled"])
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class OverridingAccessorsTest < BaseTest
|
143
|
+
class SongForm < TestForm
|
144
|
+
property :title
|
145
|
+
|
146
|
+
def title=(v) # used in #validate.
|
147
|
+
super v * 2
|
148
|
+
end
|
149
|
+
|
150
|
+
def title # used in #sync.
|
151
|
+
super.downcase
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
let(:song) { Song.new("Pray") }
|
156
|
+
subject { SongForm.new(song) }
|
157
|
+
|
158
|
+
# override reader for presentation.
|
159
|
+
it { _(subject.title).must_equal "pray" }
|
160
|
+
|
161
|
+
describe "#save" do
|
162
|
+
before { subject.validate("title" => "Hey Little World") }
|
163
|
+
|
164
|
+
# reader always used
|
165
|
+
it { _(subject.title).must_equal "hey little worldhey little world" }
|
166
|
+
|
167
|
+
# the reader is not used when saving/syncing.
|
168
|
+
it do
|
169
|
+
subject.save do |hash|
|
170
|
+
_(hash["title"]).must_equal "Hey Little WorldHey Little World"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# no reader or writer used when saving/syncing.
|
175
|
+
it do
|
176
|
+
song.extend(Saveable)
|
177
|
+
subject.save
|
178
|
+
_(song.title).must_equal "Hey Little WorldHey Little World"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
class MethodInFormTest < MiniTest::Spec
|
184
|
+
class AlbumForm < TestForm
|
185
|
+
property :title
|
186
|
+
|
187
|
+
def title
|
188
|
+
"The Suffer And The Witness"
|
189
|
+
end
|
190
|
+
|
191
|
+
property :hit do
|
192
|
+
property :title
|
193
|
+
|
194
|
+
def title
|
195
|
+
"Drones"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# methods can be used instead of created accessors.
|
201
|
+
subject { AlbumForm.new(OpenStruct.new(hit: OpenStruct.new)) }
|
202
|
+
it { _(subject.title).must_equal "The Suffer And The Witness" }
|
203
|
+
it { _(subject.hit.title).must_equal "Drones" }
|
204
|
+
end
|
@@ -1,47 +1,46 @@
|
|
1
|
-
require
|
1
|
+
require "test_helper"
|
2
2
|
|
3
|
-
# TODO: this test should be removed.
|
4
3
|
class ReformTest < Minitest::Spec
|
5
|
-
let
|
4
|
+
let(:comp) { OpenStruct.new(name: "Duran Duran", title: "Rio") }
|
6
5
|
|
7
|
-
let
|
6
|
+
let(:form) { SongForm.new(comp) }
|
8
7
|
|
9
|
-
class SongForm <
|
8
|
+
class SongForm < TestForm
|
10
9
|
property :name
|
11
10
|
property :title
|
12
11
|
|
13
12
|
validation do
|
14
|
-
|
13
|
+
required(:name).filled
|
15
14
|
end
|
16
15
|
end
|
17
16
|
|
18
17
|
describe "(new) form with empty models" do
|
19
|
-
let
|
18
|
+
let(:comp) { OpenStruct.new }
|
20
19
|
|
21
20
|
it "returns empty fields" do
|
22
|
-
form.title
|
23
|
-
form.name.
|
21
|
+
assert_nil form.title
|
22
|
+
_(form.name).must_be_nil
|
24
23
|
end
|
25
24
|
|
26
25
|
describe "and submitted values" do
|
27
26
|
it "returns filled-out fields" do
|
28
27
|
form.validate("name" => "Duran Duran")
|
29
28
|
|
30
|
-
form.title
|
31
|
-
form.name.must_equal
|
29
|
+
assert_nil form.title
|
30
|
+
_(form.name).must_equal "Duran Duran"
|
32
31
|
end
|
33
32
|
end
|
34
33
|
end
|
35
34
|
|
36
35
|
describe "(edit) form with existing models" do
|
37
36
|
it "returns filled-out fields" do
|
38
|
-
form.name.must_equal "Duran Duran"
|
39
|
-
form.title.must_equal "Rio"
|
37
|
+
_(form.name).must_equal "Duran Duran"
|
38
|
+
_(form.title).must_equal "Rio"
|
40
39
|
end
|
41
40
|
end
|
42
41
|
|
43
42
|
describe "#validate" do
|
44
|
-
let
|
43
|
+
let(:comp) { OpenStruct.new }
|
45
44
|
|
46
45
|
it "ignores unmapped fields in input" do
|
47
46
|
form.validate("name" => "Duran Duran", :genre => "80s")
|
@@ -51,71 +50,56 @@ class ReformTest < Minitest::Spec
|
|
51
50
|
end
|
52
51
|
|
53
52
|
it "returns true when valid" do
|
54
|
-
form.validate("name" => "Duran Duran").must_equal true
|
53
|
+
_(form.validate("name" => "Duran Duran")).must_equal true
|
55
54
|
end
|
56
55
|
|
57
56
|
it "exposes input via property accessors" do
|
58
57
|
form.validate("name" => "Duran Duran")
|
59
58
|
|
60
|
-
form.name.must_equal "Duran Duran"
|
59
|
+
_(form.name).must_equal "Duran Duran"
|
61
60
|
end
|
62
61
|
|
63
62
|
it "doesn't change model properties" do
|
64
63
|
form.validate("name" => "Duran Duran")
|
65
64
|
|
66
|
-
comp.name
|
65
|
+
assert_nil comp.name # don't touch model, yet.
|
67
66
|
end
|
68
67
|
|
69
68
|
# TODO: test errors. test valid.
|
70
69
|
describe "invalid input" do
|
71
|
-
class ValidatingForm <
|
70
|
+
class ValidatingForm < TestForm
|
72
71
|
property :name
|
73
72
|
property :title
|
74
73
|
|
75
74
|
validation do
|
76
|
-
|
77
|
-
|
75
|
+
required(:name).filled
|
76
|
+
required(:title).filled
|
78
77
|
end
|
79
78
|
end
|
80
|
-
let
|
79
|
+
let(:form) { ValidatingForm.new(comp) }
|
81
80
|
|
82
81
|
it "returns false when invalid" do
|
83
|
-
form.validate({}).must_equal false
|
82
|
+
_(form.validate({})).must_equal false
|
84
83
|
end
|
85
84
|
|
86
85
|
it "populates errors" do
|
87
86
|
form.validate({})
|
88
|
-
form.errors.messages.must_equal({:
|
87
|
+
_(form.errors.messages).must_equal({name: ["must be filled"], title: ["must be filled"]})
|
89
88
|
end
|
90
89
|
end
|
91
90
|
end
|
92
91
|
|
93
|
-
# FIXME: add this test to reform-rails.
|
94
|
-
describe "#errors" do
|
95
|
-
before { form.validate({})}
|
96
|
-
|
97
|
-
it { form.errors.must_be_kind_of Reform::Contract::Errors }
|
98
|
-
|
99
|
-
it { form.errors.messages.must_equal({}) }
|
100
|
-
|
101
|
-
it do
|
102
|
-
form.validate({"name"=>""})
|
103
|
-
form.errors.messages.must_equal({:name=>["must be filled"]})
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
|
108
92
|
describe "#save" do
|
109
|
-
let
|
110
|
-
let
|
93
|
+
let(:comp) { OpenStruct.new }
|
94
|
+
let(:form) { SongForm.new(comp) }
|
111
95
|
|
112
96
|
before { form.validate("name" => "Diesel Boy") }
|
113
97
|
|
114
98
|
it "xxpushes data to models" do
|
115
99
|
form.save
|
116
100
|
|
117
|
-
comp.name.must_equal "Diesel Boy"
|
118
|
-
comp.title
|
101
|
+
_(comp.name).must_equal "Diesel Boy"
|
102
|
+
assert_nil comp.title
|
119
103
|
end
|
120
104
|
|
121
105
|
describe "#save with block" do
|
@@ -126,42 +110,39 @@ class ReformTest < Minitest::Spec
|
|
126
110
|
hash = map
|
127
111
|
end
|
128
112
|
|
129
|
-
hash.must_equal({"name"=>"Diesel Boy"})
|
113
|
+
_(hash).must_equal({"name" => "Diesel Boy", "title" => nil})
|
130
114
|
end
|
131
115
|
end
|
132
116
|
end
|
133
117
|
|
134
|
-
|
135
118
|
describe "#model" do
|
136
|
-
it { form.model.must_equal comp }
|
119
|
+
it { _(form.model).must_equal comp }
|
137
120
|
end
|
138
121
|
|
139
|
-
|
140
122
|
describe "inheritance" do
|
141
123
|
class HitForm < SongForm
|
142
124
|
property :position
|
143
125
|
validation do
|
144
|
-
|
126
|
+
required(:position).filled
|
145
127
|
end
|
146
128
|
end
|
147
129
|
|
148
|
-
let
|
130
|
+
let(:form) { HitForm.new(OpenStruct.new()) }
|
149
131
|
it do
|
150
132
|
form.validate({"title" => "The Body"})
|
151
|
-
form.title.must_equal "The Body"
|
152
|
-
form.position
|
153
|
-
form.errors.messages.must_equal({:
|
133
|
+
_(form.title).must_equal "The Body"
|
134
|
+
assert_nil form.position
|
135
|
+
_(form.errors.messages).must_equal({name: ["must be filled"], position: ["must be filled"]})
|
154
136
|
end
|
155
137
|
end
|
156
138
|
end
|
157
139
|
|
158
|
-
|
159
140
|
class OverridingAccessorsTest < BaseTest
|
160
|
-
class SongForm <
|
141
|
+
class SongForm < TestForm
|
161
142
|
property :title
|
162
143
|
|
163
144
|
def title=(v) # used in #validate.
|
164
|
-
super v*2
|
145
|
+
super v * 2
|
165
146
|
end
|
166
147
|
|
167
148
|
def title # used in #sync.
|
@@ -169,23 +150,22 @@ class OverridingAccessorsTest < BaseTest
|
|
169
150
|
end
|
170
151
|
end
|
171
152
|
|
172
|
-
let
|
153
|
+
let(:song) { Song.new("Pray") }
|
173
154
|
subject { SongForm.new(song) }
|
174
155
|
|
175
156
|
# override reader for presentation.
|
176
|
-
it { subject.title.must_equal "pray" }
|
177
|
-
|
157
|
+
it { _(subject.title).must_equal "pray" }
|
178
158
|
|
179
159
|
describe "#save" do
|
180
160
|
before { subject.validate("title" => "Hey Little World") }
|
181
161
|
|
182
162
|
# reader always used
|
183
|
-
it { subject.title.must_equal "hey little worldhey little world" }
|
163
|
+
it { _(subject.title).must_equal "hey little worldhey little world" }
|
184
164
|
|
185
165
|
# the reader is not used when saving/syncing.
|
186
166
|
it do
|
187
167
|
subject.save do |hash|
|
188
|
-
hash["title"].must_equal "Hey Little WorldHey Little World"
|
168
|
+
_(hash["title"]).must_equal "Hey Little WorldHey Little World"
|
189
169
|
end
|
190
170
|
end
|
191
171
|
|
@@ -193,14 +173,13 @@ class OverridingAccessorsTest < BaseTest
|
|
193
173
|
it do
|
194
174
|
song.extend(Saveable)
|
195
175
|
subject.save
|
196
|
-
song.title.must_equal "Hey Little WorldHey Little World"
|
176
|
+
_(song.title).must_equal "Hey Little WorldHey Little World"
|
197
177
|
end
|
198
178
|
end
|
199
179
|
end
|
200
180
|
|
201
|
-
|
202
181
|
class MethodInFormTest < MiniTest::Spec
|
203
|
-
class AlbumForm <
|
182
|
+
class AlbumForm < TestForm
|
204
183
|
property :title
|
205
184
|
|
206
185
|
def title
|
@@ -217,7 +196,7 @@ class MethodInFormTest < MiniTest::Spec
|
|
217
196
|
end
|
218
197
|
|
219
198
|
# methods can be used instead of created accessors.
|
220
|
-
subject { AlbumForm.new(OpenStruct.new(:
|
221
|
-
it { subject.title.must_equal "The Suffer And The Witness" }
|
222
|
-
it { subject.hit.title.must_equal "Drones" }
|
199
|
+
subject { AlbumForm.new(OpenStruct.new(hit: OpenStruct.new)) }
|
200
|
+
it { _(subject.title).must_equal "The Suffer And The Witness" }
|
201
|
+
it { _(subject.hit.title).must_equal "Drones" }
|
223
202
|
end
|