reform 2.2.4 → 2.3.0.rc1
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 +13 -7
- data/CHANGES.md +26 -4
- data/CONTRIBUTING.md +31 -0
- data/Gemfile +1 -12
- data/ISSUE_TEMPLATE.md +25 -0
- data/LICENSE.txt +1 -1
- data/README.md +3 -3
- data/lib/reform.rb +1 -0
- data/lib/reform/contract.rb +1 -11
- data/lib/reform/contract/validate.rb +49 -23
- data/lib/reform/errors.rb +49 -0
- data/lib/reform/form.rb +20 -5
- data/lib/reform/form/dry.rb +57 -29
- data/lib/reform/form/populator.rb +2 -16
- data/lib/reform/form/prepopulate.rb +1 -1
- data/lib/reform/form/validate.rb +10 -2
- data/lib/reform/result.rb +63 -0
- data/lib/reform/validation.rb +19 -13
- data/lib/reform/validation/groups.rb +11 -25
- data/lib/reform/version.rb +1 -1
- data/reform.gemspec +7 -6
- data/test/benchmarking.rb +39 -5
- data/test/call_test.rb +1 -1
- data/test/changed_test.rb +1 -1
- data/test/coercion_test.rb +2 -2
- data/test/composition_test.rb +47 -9
- data/test/contract_test.rb +5 -5
- data/test/default_test.rb +1 -1
- data/test/deserialize_test.rb +3 -3
- data/test/errors_test.rb +36 -21
- data/test/feature_test.rb +1 -1
- data/test/fixtures/dry_error_messages.yml +70 -23
- data/test/form_option_test.rb +3 -3
- data/test/form_test.rb +3 -3
- data/test/from_test.rb +2 -2
- data/test/inherit_test.rb +44 -51
- data/test/module_test.rb +12 -12
- data/test/parse_option_test.rb +40 -0
- data/test/parse_pipeline_test.rb +2 -2
- data/test/populate_test.rb +59 -19
- data/test/populator_skip_test.rb +9 -8
- data/test/prepopulator_test.rb +3 -3
- data/test/readable_test.rb +2 -2
- data/test/readonly_test.rb +1 -1
- data/test/reform_test.rb +16 -31
- data/test/save_test.rb +23 -8
- data/test/setup_test.rb +2 -2
- data/test/skip_if_test.rb +4 -4
- data/test/skip_setter_and_getter_test.rb +1 -1
- data/test/test_helper.rb +13 -10
- data/test/validate_test.rb +18 -18
- data/test/validation/dry_validation_test.rb +430 -117
- data/test/validation/result_test.rb +79 -0
- data/test/validation_library_provided_test.rb +16 -0
- data/test/virtual_test.rb +1 -1
- data/test/writeable_test.rb +31 -2
- metadata +42 -23
- 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/deprecation_test.rb +0 -27
- data/test/validation/dry_test.rb +0 -60
- data/test/validation/errors.yml +0 -4
@@ -7,7 +7,7 @@
|
|
7
7
|
class Reform::Form::Populator
|
8
8
|
def initialize(user_proc)
|
9
9
|
@user_proc = user_proc # the actual `populator: ->{}` block from the user, via ::property.
|
10
|
-
@value = Declarative::Option(user_proc, instance_exec: true) # we can now process Callable, procs, :symbol.
|
10
|
+
@value = Declarative::Option(user_proc, instance_exec: true, callable: Object) # we can now process Callable, procs, :symbol.
|
11
11
|
end
|
12
12
|
|
13
13
|
def call(input, options)
|
@@ -29,10 +29,7 @@ class Reform::Form::Populator
|
|
29
29
|
private
|
30
30
|
def call!(options)
|
31
31
|
form = options[:represented]
|
32
|
-
|
33
|
-
deprecate_positional_args(form, @user_proc, options) do
|
34
|
-
@value.(form, options)
|
35
|
-
end
|
32
|
+
@value.(form, options) # Declarative::Option call.
|
36
33
|
end
|
37
34
|
|
38
35
|
def handle_fail(twin, options)
|
@@ -43,17 +40,6 @@ private
|
|
43
40
|
Representable::GetValue.(nil, options)
|
44
41
|
end
|
45
42
|
|
46
|
-
def deprecate_positional_args(form, proc, options) # TODO: remove in 2.2.
|
47
|
-
arity = proc.is_a?(Symbol) ? form.method(proc).arity : proc.arity
|
48
|
-
return yield if arity == 1
|
49
|
-
warn "[Reform] Positional arguments for :populator and friends are deprecated. Please use ->(options) and enjoy the rest of your day. Learn more at http://trailblazerb.org/gems/reform/upgrading-guide.html#to-21"
|
50
|
-
args = []
|
51
|
-
args << options[:index] if options[:index]
|
52
|
-
args << options[:representable_options]
|
53
|
-
form.instance_exec(options[:fragment], options[:model], *args, &proc)
|
54
|
-
end
|
55
|
-
|
56
|
-
|
57
43
|
class IfEmpty < self # Populator
|
58
44
|
def call!(options)
|
59
45
|
binding, twin, index, fragment = options[:binding], options[:model], options[:index], options[:fragment] # TODO: remove once we drop 2.0.
|
data/lib/reform/form/validate.rb
CHANGED
@@ -18,16 +18,19 @@ module Reform::Form::Validate
|
|
18
18
|
|
19
19
|
def validate(params)
|
20
20
|
# allow an external deserializer.
|
21
|
+
@input_params = params # we want to store these for access via dry later
|
21
22
|
block_given? ? yield(params) : deserialize(params)
|
22
23
|
|
23
24
|
super() # run the actual validation on self.
|
24
25
|
end
|
26
|
+
attr_reader :input_params # make the raw input params public
|
25
27
|
|
26
28
|
def deserialize(params)
|
27
29
|
params = deserialize!(params)
|
28
30
|
deserializer.new(self).from_hash(params)
|
29
31
|
end
|
30
32
|
|
33
|
+
|
31
34
|
private
|
32
35
|
# Meant to return params processable by the representer. This is the hook for munching date fields, etc.
|
33
36
|
def deserialize!(params)
|
@@ -40,7 +43,7 @@ private
|
|
40
43
|
|
41
44
|
# Default deserializer for hash.
|
42
45
|
# This is input-specific, e.g. Hash, JSON, or XML.
|
43
|
-
def deserializer(source=self.class, options={}) # called on top-level, only, for now.
|
46
|
+
def deserializer!(source=self.class, options={}) # called on top-level, only, for now.
|
44
47
|
deserializer = Disposable::Rescheme.from(source,
|
45
48
|
{
|
46
49
|
include: [Representable::Hash::AllowSymbols, Representable::Hash],
|
@@ -54,7 +57,12 @@ private
|
|
54
57
|
deserializer
|
55
58
|
end
|
56
59
|
|
60
|
+
def deserializer(*args)
|
61
|
+
# DISCUSS: should we simply delegate to class and sort out memoizing there?
|
62
|
+
self.class.deserializer_class || self.class.deserializer_class = deserializer!(*args)
|
63
|
+
end
|
57
64
|
|
58
|
-
|
65
|
+
def self.included(includer)
|
66
|
+
includer.singleton_class.send :attr_accessor, :deserializer_class
|
59
67
|
end
|
60
68
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Reform
|
2
|
+
class Contract < Disposable::Twin
|
3
|
+
|
4
|
+
# Collects all native results of a form of all groups and provides
|
5
|
+
# a unified API: #success?, #errors, #messages, #hints.
|
6
|
+
# #success? returns validity of the branch.
|
7
|
+
class Result
|
8
|
+
def initialize(results, nested_results=[]) # DISCUSS: do we like this?
|
9
|
+
@results = results # native Result objects, e.g. `#<Dry::Validation::Result output={:title=>"Fallout", :composer=>nil} errors={}>`
|
10
|
+
@failure = (results + nested_results).find(&:failure?) # TODO: test nested.
|
11
|
+
end
|
12
|
+
|
13
|
+
def failure?; @failure end
|
14
|
+
def success?; !failure? end
|
15
|
+
|
16
|
+
def errors(*args); filter_for(:errors, *args) end
|
17
|
+
def messages(*args); filter_for(:messages, *args) end
|
18
|
+
def hints(*args); filter_for(:hints, *args) end
|
19
|
+
|
20
|
+
private
|
21
|
+
def filter_for(method, *args)
|
22
|
+
@results.collect { |r| r.public_send(method, *args) }
|
23
|
+
.inject({}) { |hsh, errs| hsh.merge(errs) }
|
24
|
+
.find_all { |k, v| v.is_a?(Array) } # filter :nested=>{:something=>["too nested!"]} #DISCUSS: do we want that here?
|
25
|
+
.to_h
|
26
|
+
end
|
27
|
+
|
28
|
+
# Note: this class will be redundant in Reform 3, where the public API
|
29
|
+
# allows/enforces to pass options to #errors (e.g. errors(locale: "br"))
|
30
|
+
# which means we don't have to "lazy-handle" that with "pointers".
|
31
|
+
# :private:
|
32
|
+
class Pointer
|
33
|
+
extend Forwardable
|
34
|
+
|
35
|
+
def initialize(result, path)
|
36
|
+
@result, @path = result, path
|
37
|
+
end
|
38
|
+
|
39
|
+
def_delegators :@result, :success?, :failure?
|
40
|
+
|
41
|
+
def errors(*args); traverse_for(:errors, *args) end
|
42
|
+
def messages(*args); traverse_for(:messages, *args) end
|
43
|
+
def hints(*args); traverse_for(:hints, *args) end
|
44
|
+
|
45
|
+
def advance(*path)
|
46
|
+
path = @path + path.compact # remove index if nil.
|
47
|
+
return if traverse(@result.errors, path) == {}
|
48
|
+
|
49
|
+
Pointer.new(@result, path)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def traverse(hash, path)
|
54
|
+
path.inject(hash) { |errs, segment| errs[segment] || {} } # FIXME. test if all segments present.
|
55
|
+
end
|
56
|
+
|
57
|
+
def traverse_for(method, *args)
|
58
|
+
traverse(@result.public_send(method, *args), @path) # TODO: return [] if nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/reform/validation.rb
CHANGED
@@ -3,28 +3,36 @@
|
|
3
3
|
module Reform::Validation
|
4
4
|
module ClassMethods
|
5
5
|
def validation_groups
|
6
|
-
@groups ||= Groups.new(validation_group_class)
|
6
|
+
@groups ||= Groups.new(validation_group_class)
|
7
7
|
end
|
8
8
|
|
9
9
|
# DSL.
|
10
|
-
def validation(name
|
11
|
-
|
10
|
+
def validation(name=nil, options={}, &block)
|
11
|
+
options = deprecate_validation_positional_args(name, options)
|
12
|
+
name = options[:name] # TODO: remove in favor of kw args in 3.0.
|
12
13
|
|
14
|
+
heritage.record(:validation, options, &block)
|
13
15
|
group = validation_groups.add(name, options)
|
14
16
|
|
15
17
|
group.instance_exec(&block)
|
16
18
|
end
|
17
19
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
20
|
+
def deprecate_validation_positional_args(name, options)
|
21
|
+
if name.is_a?(Symbol)
|
22
|
+
warn "[Reform] Form::validation API is now: validation(name: :default, if:nil, schema:Schema). Please use keyword arguments instead of positional arguments."
|
23
|
+
return { name: name }.merge(options)
|
24
|
+
end
|
25
|
+
|
26
|
+
if name.nil?
|
27
|
+
return { name: :default }.merge(options)
|
28
|
+
end
|
21
29
|
|
22
|
-
|
23
|
-
validation(:default, inherit: true) { validate *args, &block }
|
30
|
+
{ name: :default }.merge(name)
|
24
31
|
end
|
25
32
|
|
26
|
-
def
|
27
|
-
|
33
|
+
def validation_group_class
|
34
|
+
raise NoValidationLibraryError, 'no validation library loaded. Please include a ' +
|
35
|
+
'validation library such as Reform::Form::Dry'
|
28
36
|
end
|
29
37
|
end
|
30
38
|
|
@@ -32,9 +40,7 @@ module Reform::Validation
|
|
32
40
|
includer.extend(ClassMethods)
|
33
41
|
end
|
34
42
|
|
35
|
-
|
36
|
-
Groups::Result.new(self.class.validation_groups).(to_nested_hash, errors, self)
|
37
|
-
end
|
43
|
+
NoValidationLibraryError = Class.new(RuntimeError)
|
38
44
|
end
|
39
45
|
|
40
46
|
require "reform/validation/groups"
|
@@ -19,7 +19,7 @@ module Reform::Validation
|
|
19
19
|
|
20
20
|
i = index_for(options)
|
21
21
|
|
22
|
-
self.insert(i, [name, group = @group_class.new, options]) # Group.new
|
22
|
+
self.insert(i, [name, group = @group_class.new(options), options]) # Group.new
|
23
23
|
group
|
24
24
|
end
|
25
25
|
|
@@ -31,43 +31,29 @@ module Reform::Validation
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def [](name)
|
34
|
-
cfg = find { |
|
34
|
+
cfg = find { |c| c.first == name }
|
35
35
|
return unless cfg
|
36
36
|
cfg[1]
|
37
37
|
end
|
38
38
|
|
39
39
|
|
40
|
-
# Runs all validations groups according to their rules and returns
|
41
|
-
|
42
|
-
|
43
|
-
def initialize(groups)
|
44
|
-
@groups = groups
|
45
|
-
end
|
46
|
-
|
47
|
-
def call(fields, errors, form)
|
48
|
-
result = true
|
40
|
+
# Runs all validations groups according to their rules and returns all Result objects.
|
41
|
+
class Validate
|
42
|
+
def self.call(groups, form)
|
49
43
|
results = {}
|
50
44
|
|
51
|
-
|
52
|
-
|
53
|
-
depends_on = options[:if]
|
45
|
+
groups.collect do |(name, group, options)|
|
46
|
+
next unless evaluate?(options[:if], results, form)
|
54
47
|
|
55
|
-
|
56
|
-
# puts "evaluating #{group.instance_variable_get(:@validator).instance_variable_get(:@checker).inspect}"
|
57
|
-
results[name] = group.(fields, errors, form).empty? # validate.
|
58
|
-
end
|
59
|
-
|
60
|
-
result &= errors.empty?
|
48
|
+
results[name] = group.(form) # run validation for group. store and collect <Result>.
|
61
49
|
end
|
62
|
-
|
63
|
-
result
|
64
50
|
end
|
65
51
|
|
66
|
-
def
|
52
|
+
def self.evaluate?(depends_on, results, form)
|
67
53
|
return true if depends_on.nil?
|
68
|
-
return results[depends_on] if depends_on.is_a?(Symbol)
|
54
|
+
return results[depends_on].success? if depends_on.is_a?(Symbol)
|
69
55
|
form.instance_exec(results, &depends_on)
|
70
56
|
end
|
71
57
|
end
|
72
58
|
end
|
73
|
-
end
|
59
|
+
end
|
data/lib/reform/version.rb
CHANGED
data/reform.gemspec
CHANGED
@@ -5,11 +5,11 @@ require 'reform/version'
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "reform"
|
7
7
|
spec.version = Reform::VERSION
|
8
|
-
spec.authors = ["Nick Sutterer", "
|
9
|
-
spec.email = ["apotonick@gmail.com", "
|
8
|
+
spec.authors = ["Nick Sutterer", "Fran Worley"]
|
9
|
+
spec.email = ["apotonick@gmail.com", "frances@safetytoolbox.co.uk"]
|
10
10
|
spec.description = %q{Form object decoupled from models.}
|
11
11
|
spec.summary = %q{Form object decoupled from models with validation, population and presentation.}
|
12
|
-
spec.homepage = "https://github.com/
|
12
|
+
spec.homepage = "https://github.com/trailblazer/reform"
|
13
13
|
spec.license = "MIT"
|
14
14
|
|
15
15
|
spec.files = `git ls-files`.split($/)
|
@@ -17,13 +17,14 @@ 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 "disposable",
|
21
|
-
spec.add_dependency "
|
20
|
+
spec.add_dependency "disposable", ">= 0.4.2", "< 0.5.0"
|
21
|
+
spec.add_dependency "uber", "< 0.2.0"
|
22
|
+
spec.add_dependency "representable", ">= 2.4.0", "< 3.1.0"
|
22
23
|
|
23
24
|
spec.add_development_dependency "bundler"
|
24
25
|
spec.add_development_dependency "rake"
|
25
26
|
spec.add_development_dependency "minitest"
|
26
27
|
spec.add_development_dependency "dry-types"
|
27
28
|
spec.add_development_dependency "multi_json"
|
28
|
-
spec.add_development_dependency "dry-validation", ">= 0.10.
|
29
|
+
spec.add_development_dependency "dry-validation", ">= 0.10.1"
|
29
30
|
end
|
data/test/benchmarking.rb
CHANGED
@@ -1,15 +1,49 @@
|
|
1
1
|
require 'reform'
|
2
|
-
require '
|
3
|
-
require
|
2
|
+
require 'benchmark/ips'
|
3
|
+
require "reform/form/dry"
|
4
4
|
|
5
5
|
class BandForm < Reform::Form
|
6
|
-
|
6
|
+
feature Reform::Form::Dry
|
7
|
+
property :name#, validates: {presence: true}
|
8
|
+
collection :songs do
|
9
|
+
property :title#, validates: {presence: true}
|
10
|
+
end
|
11
|
+
end
|
7
12
|
|
13
|
+
class OptimizedBandForm < Reform::Form
|
14
|
+
feature Reform::Form::Dry
|
15
|
+
property :name#, validates: {presence: true}
|
8
16
|
collection :songs do
|
9
|
-
property :title
|
17
|
+
property :title#, validates: {presence: true}
|
18
|
+
|
19
|
+
def deserializer(*args)
|
20
|
+
# DISCUSS: should we simply delegate to class and sort out memoizing there?
|
21
|
+
self.class.deserializer_class || self.class.deserializer_class = deserializer!(*args)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def deserializer(*args)
|
26
|
+
# DISCUSS: should we simply delegate to class and sort out memoizing there?
|
27
|
+
self.class.deserializer_class || self.class.deserializer_class = deserializer!(*args)
|
10
28
|
end
|
11
29
|
end
|
12
30
|
|
31
|
+
songs = 10.times.collect { OpenStruct.new(title: "Be Stag") }
|
32
|
+
band = OpenStruct.new(name: "Teenage Bottlerock", songs: songs)
|
33
|
+
|
34
|
+
unoptimized_form = BandForm.new(band)
|
35
|
+
optimized_form = OptimizedBandForm.new(band)
|
36
|
+
|
37
|
+
songs_params = songs_params = 10.times.collect { {title: "Commando"} }
|
38
|
+
|
39
|
+
Benchmark.ips do |x|
|
40
|
+
x.report("2.2") { BandForm.new(band).validate("name" => "Ramones", "songs" => songs_params) }
|
41
|
+
x.report("2.3") { OptimizedBandForm.new(band).validate("name" => "Ramones", "songs" => songs_params) }
|
42
|
+
end
|
43
|
+
|
44
|
+
exit
|
45
|
+
|
46
|
+
|
13
47
|
songs = 50.times.collect { OpenStruct.new(title: "Be Stag") }
|
14
48
|
band = OpenStruct.new(name: "Teenage Bottlerock", songs: songs)
|
15
49
|
|
@@ -23,4 +57,4 @@ time = Benchmark.measure do
|
|
23
57
|
end
|
24
58
|
end
|
25
59
|
|
26
|
-
puts time
|
60
|
+
puts time
|
data/test/call_test.rb
CHANGED
data/test/changed_test.rb
CHANGED
data/test/coercion_test.rb
CHANGED
@@ -8,7 +8,7 @@ class CoercionTest < BaseTest
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
class Form <
|
11
|
+
class Form < TestForm
|
12
12
|
feature Coercion
|
13
13
|
|
14
14
|
property :released_at, type: Types::Form::DateTime
|
@@ -58,7 +58,7 @@ class CoercionTest < BaseTest
|
|
58
58
|
|
59
59
|
it { subject.released_at.must_equal DateTime.parse("30/03/1981") }
|
60
60
|
it { subject.hit.length.must_equal 312 }
|
61
|
-
it { subject.hit.good
|
61
|
+
it { assert_nil subject.hit.good }
|
62
62
|
it { subject.band.label.value.must_equal "9999.999999.99" } # coercion happened once.
|
63
63
|
end
|
64
64
|
|
data/test/composition_test.rb
CHANGED
@@ -1,11 +1,49 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
+
class FormCompositionInheritanceTest < MiniTest::Spec
|
4
|
+
module SizePrice
|
5
|
+
include Reform::Form::Module
|
6
|
+
|
7
|
+
property :price
|
8
|
+
property :size
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
def price(for_size: size)
|
12
|
+
case for_size.to_sym
|
13
|
+
when :s then super() * 1
|
14
|
+
when :m then super() * 2
|
15
|
+
when :l then super() * 3
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class OutfitForm < TestForm
|
22
|
+
include Reform::Form::Composition
|
23
|
+
include SizePrice
|
24
|
+
|
25
|
+
property :price, inherit: true, on: :tshirt
|
26
|
+
property :size, inherit: true, on: :measurement
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
let (:measurement) { Measurement.new(:l) }
|
31
|
+
let (:tshirt) { Tshirt.new(2, :m) }
|
32
|
+
let (:form) { OutfitForm.new(tshirt: tshirt, measurement: measurement) }
|
33
|
+
|
34
|
+
Tshirt = Struct.new(:price, :size)
|
35
|
+
Measurement = Struct.new(:size)
|
36
|
+
|
37
|
+
it { form.price.must_equal 6 }
|
38
|
+
it { form.price(for_size: :s).must_equal 2 }
|
39
|
+
end
|
40
|
+
|
3
41
|
class FormCompositionTest < MiniTest::Spec
|
4
42
|
Song = Struct.new(:id, :title, :band)
|
5
43
|
Requester = Struct.new(:id, :name, :requester)
|
6
44
|
Band = Struct.new(:title)
|
7
45
|
|
8
|
-
class RequestForm <
|
46
|
+
class RequestForm < TestForm
|
9
47
|
include Composition
|
10
48
|
|
11
49
|
property :name, :on => :requester
|
@@ -17,9 +55,8 @@ class FormCompositionTest < MiniTest::Spec
|
|
17
55
|
property :captcha, :on => :song, :virtual => true
|
18
56
|
|
19
57
|
validation do
|
20
|
-
|
21
|
-
|
22
|
-
key(:title).required
|
58
|
+
required(:name).filled
|
59
|
+
required(:title).filled
|
23
60
|
end
|
24
61
|
|
25
62
|
property :band, :on => :song do
|
@@ -37,20 +74,21 @@ class FormCompositionTest < MiniTest::Spec
|
|
37
74
|
it { form.title.must_equal "Rio" }
|
38
75
|
it { form.name.must_equal "Duran Duran" }
|
39
76
|
it { form.requester_id.must_equal 2 }
|
40
|
-
it { form.channel
|
77
|
+
it { assert_nil form.channel }
|
41
78
|
it { form.requester.must_equal "MCP" } # same name as composed model.
|
42
|
-
it { form.captcha
|
79
|
+
it { assert_nil form.captcha }
|
43
80
|
|
44
81
|
# #model just returns <Composition>.
|
45
82
|
it { form.mapper.must_be_kind_of Disposable::Composition }
|
46
83
|
|
47
84
|
# #model[] -> composed models
|
48
85
|
it { form.model[:requester].must_equal requester }
|
49
|
-
it { form.model[:song].must_equal
|
86
|
+
it { form.model[:song].must_equal song }
|
50
87
|
|
51
88
|
|
52
89
|
it "creates Composition for you" do
|
53
|
-
form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb").must_equal
|
90
|
+
form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb").must_equal true
|
91
|
+
form.validate("title" => "", "name" => "Frenzal Rhomb").must_equal false
|
54
92
|
end
|
55
93
|
|
56
94
|
describe "#save" do
|
@@ -133,7 +171,7 @@ class FormCompositionCollectionTest < MiniTest::Spec
|
|
133
171
|
end
|
134
172
|
end
|
135
173
|
|
136
|
-
class LibraryForm <
|
174
|
+
class LibraryForm < TestForm
|
137
175
|
include Reform::Form::Composition
|
138
176
|
|
139
177
|
collection :books, on: :library do
|