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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +13 -7
  3. data/CHANGES.md +26 -4
  4. data/CONTRIBUTING.md +31 -0
  5. data/Gemfile +1 -12
  6. data/ISSUE_TEMPLATE.md +25 -0
  7. data/LICENSE.txt +1 -1
  8. data/README.md +3 -3
  9. data/lib/reform.rb +1 -0
  10. data/lib/reform/contract.rb +1 -11
  11. data/lib/reform/contract/validate.rb +49 -23
  12. data/lib/reform/errors.rb +49 -0
  13. data/lib/reform/form.rb +20 -5
  14. data/lib/reform/form/dry.rb +57 -29
  15. data/lib/reform/form/populator.rb +2 -16
  16. data/lib/reform/form/prepopulate.rb +1 -1
  17. data/lib/reform/form/validate.rb +10 -2
  18. data/lib/reform/result.rb +63 -0
  19. data/lib/reform/validation.rb +19 -13
  20. data/lib/reform/validation/groups.rb +11 -25
  21. data/lib/reform/version.rb +1 -1
  22. data/reform.gemspec +7 -6
  23. data/test/benchmarking.rb +39 -5
  24. data/test/call_test.rb +1 -1
  25. data/test/changed_test.rb +1 -1
  26. data/test/coercion_test.rb +2 -2
  27. data/test/composition_test.rb +47 -9
  28. data/test/contract_test.rb +5 -5
  29. data/test/default_test.rb +1 -1
  30. data/test/deserialize_test.rb +3 -3
  31. data/test/errors_test.rb +36 -21
  32. data/test/feature_test.rb +1 -1
  33. data/test/fixtures/dry_error_messages.yml +70 -23
  34. data/test/form_option_test.rb +3 -3
  35. data/test/form_test.rb +3 -3
  36. data/test/from_test.rb +2 -2
  37. data/test/inherit_test.rb +44 -51
  38. data/test/module_test.rb +12 -12
  39. data/test/parse_option_test.rb +40 -0
  40. data/test/parse_pipeline_test.rb +2 -2
  41. data/test/populate_test.rb +59 -19
  42. data/test/populator_skip_test.rb +9 -8
  43. data/test/prepopulator_test.rb +3 -3
  44. data/test/readable_test.rb +2 -2
  45. data/test/readonly_test.rb +1 -1
  46. data/test/reform_test.rb +16 -31
  47. data/test/save_test.rb +23 -8
  48. data/test/setup_test.rb +2 -2
  49. data/test/skip_if_test.rb +4 -4
  50. data/test/skip_setter_and_getter_test.rb +1 -1
  51. data/test/test_helper.rb +13 -10
  52. data/test/validate_test.rb +18 -18
  53. data/test/validation/dry_validation_test.rb +430 -117
  54. data/test/validation/result_test.rb +79 -0
  55. data/test/validation_library_provided_test.rb +16 -0
  56. data/test/virtual_test.rb +1 -1
  57. data/test/writeable_test.rb +31 -2
  58. metadata +42 -23
  59. data/gemfiles/Gemfile.disposable-0.3 +0 -6
  60. data/lib/reform/contract/errors.rb +0 -43
  61. data/lib/reform/form/mongoid.rb +0 -37
  62. data/lib/reform/form/orm.rb +0 -26
  63. data/lib/reform/mongoid.rb +0 -4
  64. data/test/deprecation_test.rb +0 -27
  65. data/test/validation/dry_test.rb +0 -60
  66. data/test/validation/errors.yml +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 851795d3f1959935496c4a582f24dffc6020b848
4
- data.tar.gz: f62755eb2d9e6302a00790b703698149838474af
3
+ metadata.gz: 833e85f6efec80dfacc227371dfb5ec4fe5d1fdd
4
+ data.tar.gz: e084df9fc8f080155d3af4398a80c78347db5b78
5
5
  SHA512:
6
- metadata.gz: 865c755aea555fedc28f61ae04a7903544ad637c621e656c68a64fc9f952b0a105906f4abc77571dce490099227224cd966c12ccc802b8ae7e830c0b27d108f3
7
- data.tar.gz: a90c092125bd9f2a7db2a6092267ba3ac439c6d92bd3b2e657a50156128c4de5b8f28e3f62cc2eefc47b6a8f36b1e8d5f94e393f31ab115cf297a60210fc58c4
6
+ metadata.gz: dba35a83313b4f5a2cecde6d85800b19c83f5b55553d2600e448ca042f1a8429a85b26ff3d522aa95b308bc1fadba731bb7a550cbe81e3ebab02d4c2052a4de8
7
+ data.tar.gz: cac8a4bddbbbef87379fd823bcfccce351fb9a0af36448d36d092393abaebcabc5343cc1665a68652a570bc7e765593ae0c873912033166d870e729962bddf70
@@ -1,11 +1,17 @@
1
1
  language: ruby
2
+ cache: bundler
3
+ bundler_args: --without benchmarks tools
2
4
  rvm:
3
- - 2.2.3
4
- - 2.0.0
5
- gemfile:
6
- - gemfiles/Gemfile.disposable-0.3
7
-
5
+ - 2.1.10
6
+ - 2.2.5
7
+ - 2.3.1
8
+ - 2.4.0
8
9
  matrix:
9
10
  fast_finish: true
10
- before_install:
11
- - gem install bundler
11
+ notifications:
12
+ webhooks:
13
+ urls:
14
+ - https://webhooks.gitter.im/e/680e86d98056f2ae2fd7
15
+ on_success: change # options: [always|never|change] default: always
16
+ on_failure: always # options: [always|never|change] default: always
17
+ on_start: never # options: [always|never|change] default: always
data/CHANGES.md CHANGED
@@ -1,9 +1,31 @@
1
+ ## 3.0.0
2
+
3
+ [* Removed `Reform::Contract` ?]
4
+ [* Move Form#deserializer to Form::deserializer]
5
+
6
+ ## 2.3.0
7
+
8
+ You can upgrade from 2.2.0 without worries.
9
+
10
+ * Require Representable 3.0.0 and **removed Representable 2.4 deprecation code**.
11
+ * Require Disposable 0.4.0 which fixes issues with `nil` field values, `sync {}` and dry-validation.
12
+ * Fix boolean coercion.
13
+ * Allow using `:populator` classes marked with `Uber::Callable`.
14
+ * Introduce `parse: false` as a shortcut for `deserialzer: { writeable: false}`. Thanks to @pabloh for insisting on this handy change.
15
+ * Memoize the deserializer instance on the class level via `::deserializer`. This saves the inferal of a deserializing representer and speeds up following calls by 130%.
16
+ * Deprecated positional arguments for `validation :default, options: {}`. New API: `validation name: :default, **`.
17
+ * Reform now maintains a generic `Dry::Schema` class for global schema configuration. Can be overridden via `::validation`.
18
+ * When validating with dry-validation, we now pass a symbolized hash. We also replaced `Dry::Validation::Form` with `Schema` which won't coerce values where it shouldn't.
19
+ * [private] `Group#call` API now is: `call(form, errors)`.
20
+ * Removed `Form#valid?`.
21
+
22
+ * In `:if` for validation groups, you now get a hash of result objects, not just true/false.
23
+
24
+
1
25
  ## 2.2.4
2
26
 
3
- * Always require `disposable` >= 0.4.1.
4
-
5
- The only difference here is that `Form#sync`/`#save` with a block will include `nil` properties into the nested hash.
6
- * Remove `uber` dependency.
27
+ * You can now use any object with `call` as a populator, no need to `include Uber::Callable` anymore. This is because we have only three types and don't need a `is_a?` or `respond_to?` check.
28
+ * Use `declarative-option` and loosen `uber` dependency.
7
29
 
8
30
  ## 2.2.3
9
31
 
@@ -0,0 +1,31 @@
1
+ ## How to contribute to Reform
2
+
3
+ #### **Did you find a bug?**
4
+
5
+ * **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/trailblazer/reform/issues).
6
+
7
+ * If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/trailblazer/reform/issues/new). Be sure to follow the issue template.
8
+
9
+ #### **Did you write a patch that fixes a bug?**
10
+
11
+ * Open a new GitHub pull request with the patch.
12
+
13
+ * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
14
+
15
+ * All code in pull requests is assumed to be MIT licensed. Do not submit a pull request if that isn't the case.
16
+
17
+ #### **Do you intend to add a new feature or change an existing one?**
18
+
19
+ * Suggest your change in the [Trailblazer Gitter Room](https://gitter.im/trailblazer/chat) and start writing code.
20
+
21
+ * Do not open an issue on GitHub until you have collected positive feedback about the change. GitHub issues are primarily intended for bug reports and fixes.
22
+
23
+ #### **Do you have questions using Reform?**
24
+
25
+ * Ask any questions about how to use Reform in the [Trailblazer Gitter Room](https://gitter.im/trailblazer/chat). Github issues are restricted to bug reports and fixes.
26
+
27
+ * GitHub Issues should not be used as a help forum and any such issues will be closed.
28
+
29
+ #### **Do you want to contribute to the Reform documentation?**
30
+
31
+ * Reform documentation is provided via the [Trailblazer site](http://trailblazer.to/gems/reform/) and not the repository readme. Please add your contributions to the [Trailblazer site repository](https://github.com/trailblazer/trailblazer.github.io)
data/Gemfile CHANGED
@@ -2,18 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
-
6
- # gem "representable", "2.4.0.rc5"
7
- # gem 'representable', path: "../representable"
8
- # # gem 'representable', github: "apotonick/representable"
9
- # gem "disposable", path: "../disposable"
10
- # gem "disposable", github: "apotonick/disposable"
11
-
12
-
13
- # gem "declarative", path: "../declarative"
14
-
15
5
  gem "minitest-line"
16
6
  gem 'byebug'
7
+ # gem "disposable", path: "../disposable"
17
8
 
18
- # gem "uber", path: "../uber"
19
- gem "representable", ">= 3.0.1"
@@ -0,0 +1,25 @@
1
+ Note: If you have a question about Reform, would like help using
2
+ Reform, want to request a feature, or do anything else other than
3
+ submit a bug report, please use the Trailblazer gitter channel.
4
+
5
+ Note: Rails/ ActiveRecord/ ActiveModel support.
6
+ As of Reform 2.2.0 all Rails/ Active-* code was moved to the [reform-rails](https://github.com/trailblazer/reform-rails) gem.
7
+ Make sure you are contributing to the correct gem!
8
+
9
+ ### Complete Description of Issue
10
+
11
+
12
+ ### Steps to reproduce
13
+
14
+
15
+ ### Expected behavior
16
+ Tell us what should happen
17
+
18
+ ### Actual behavior
19
+ Tell us what happens instead
20
+
21
+ ### System configuration
22
+ **Reform version**:
23
+
24
+ ### Full Backtrace of Exception (if any)
25
+
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 - 2014 Nick Sutterer
1
+ Copyright (c) 2013 - 2016 Nick Sutterer
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Gitter Chat](https://badges.gitter.im/trailblazer/chat.svg)](https://gitter.im/trailblazer/chat)
4
4
  [![TRB Newsletter](https://img.shields.io/badge/TRB-newsletter-lightgrey.svg)](http://trailblazer.to/newsletter/)
5
5
  [![Build
6
- Status](https://travis-ci.org/apotonick/reform.svg)](https://travis-ci.org/apotonick/reform)
6
+ Status](https://travis-ci.org/trailblazer/reform.svg)](https://travis-ci.org/trailblazer/reform)
7
7
  [![Gem Version](https://badge.fury.io/rb/reform.svg)](http://badge.fury.io/rb/reform)
8
8
 
9
9
  _Form objects decoupled from your models._
@@ -296,7 +296,7 @@ Put this in an initializer or on top of your script.
296
296
  Reform allows to map multiple models to one form. The [complete documentation](https://github.com/apotonick/disposable#composition) is here, however, this is how it works.
297
297
 
298
298
  ```ruby
299
- class AlbumTwin < Reform::Form
299
+ class AlbumForm < Reform::Form
300
300
  include Composition
301
301
 
302
302
  property :id, on: :album
@@ -328,7 +328,7 @@ By explicitely defining the form layout using `::property` there is no more need
328
328
 
329
329
  ## This is not Reform 1.x!
330
330
 
331
- Temporary note: This is the README and API for Reform 2. On the public API, only a few tiny things have changed. Here are the [Reform 1.2 docs](https://github.com/apotonick/reform/blob/v1.2.6/README.md).
331
+ Temporary note: This is the README and API for Reform 2. On the public API, only a few tiny things have changed. Here are the [Reform 1.2 docs](https://github.com/trailblazer/reform/blob/v1.2.6/README.md).
332
332
 
333
333
  Anyway, please upgrade and _report problems_ and do not simply assume that we will magically find out what needs to get fixed. When in trouble, join us on [Gitter](https://gitter.im/trailblazer/chat).
334
334
 
@@ -6,3 +6,4 @@ require "reform/contract"
6
6
  require "reform/form"
7
7
  require "reform/form/composition"
8
8
  require "reform/form/module"
9
+ require "reform/errors" # TODO: remove in Reform 3.
@@ -27,13 +27,12 @@ module Reform
27
27
  super
28
28
  end
29
29
 
30
- # FIXME: test me.
31
30
  def self.properties(*args)
32
31
  options = args.last.is_a?(Hash) ? args.pop : {}
33
32
  args.each { |name| property(name, options.dup) }
34
33
  end
35
34
 
36
- require "reform/contract/errors"
35
+ require "reform/result"
37
36
  require 'reform/contract/validate'
38
37
  include Reform::Contract::Validate
39
38
 
@@ -44,15 +43,6 @@ module Reform
44
43
  require "disposable/twin/sync"
45
44
  include Disposable::Twin::Sync
46
45
 
47
-
48
-
49
- # module ValidatesWarning
50
- # def validates(*)
51
- # raise "[Reform] Please include either Reform::Form::ActiveModel::Validations or Reform::Form::Lotus in your form class."
52
- # end
53
- # end
54
- # extend ValidatesWarning
55
-
56
46
  private
57
47
  # DISCUSS: separate file?
58
48
  module Readonly
@@ -1,33 +1,59 @@
1
- module Reform::Contract::Validate
2
- def initialize(*)
3
- super
4
- @errors = build_errors
5
- end
1
+ class Reform::Contract < Disposable::Twin
2
+ module Validate
3
+ def initialize(*)
4
+ # this will be removed in Reform 3.0. we need this for the presenting form, form builders
5
+ # call the Form#errors method before validation.
6
+ super
7
+ @result = Result.new([])
8
+ end
6
9
 
7
- attr_reader :errors
10
+ def validate
11
+ validate!(nil).success?
12
+ end
8
13
 
9
- def validate
10
- validate!(errors, [])
14
+ # The #errors method will be removed in Reform 2.4/3.0 core.
15
+ def errors(*args)
16
+ Result::Errors.new(@result, self)
17
+ end
11
18
 
12
- errors.empty?
13
- end
19
+ #:private:
20
+ # only used in tests so far. this will be the new API in #call, where you will get @result.
21
+ def to_result
22
+ @result
23
+ end
14
24
 
15
- def validate!(errors, prefix)
16
- validate_nested!(nested_errors = build_errors, prefix) # call valid? recursively and collect nested errors.
25
+ def validate!(name, pointers=[])
26
+ # run local validations. this could be nested schemas, too.
27
+ local_errors_by_group = Reform::Validation::Groups::Validate.(self.class.validation_groups, self).compact # TODO: discss compact
17
28
 
18
- valid? # calls AM/Lotus validators and invokes self.errors=.
29
+ # blindly add injected pointers. will be readable via #errors.
30
+ # also, add pointers from local errors here.
31
+ pointers_for_nested = pointers + local_errors_by_group.collect { |errs| Result::Pointer.new(errs, []) }.compact
19
32
 
20
- errors.merge!(self.errors, prefix) # local errors.
21
- errors.merge!(nested_errors, [])
22
- end
33
+ nested_errors = validate_nested!(pointers_for_nested)
34
+
35
+ # Result: unified interface #success?, #messages, etc.
36
+ @result = Result.new(local_errors_by_group + pointers, nested_errors)
37
+ end
38
+
39
+ private
40
+
41
+ # Recursively call validate! on nested forms.
42
+ # A pointer keeps an entire result object (e.g. Dry result) and
43
+ # the relevant path to its fragment, e.g. <Dry::result{.....} path=songs,0>
44
+ def validate_nested!(pointers)
45
+ arr = []
46
+
47
+ schema.each(twin: true) do |dfn|
48
+ # on collections, this calls validate! on each item form.
49
+ Disposable::Twin::PropertyProcessor.new(dfn, self).() { |form, i|
50
+ nested_pointers = pointers.collect { |pointer| pointer.advance(dfn[:name].to_sym, i) }.compact # pointer contains fragment for us, so go deeper
23
51
 
24
- private
52
+ arr << form.validate!(dfn[:name], nested_pointers)
53
+ }
54
+ end
25
55
 
26
- # runs form.validate! on all nested forms
27
- def validate_nested!(errors, prefixes)
28
- schema.each(twin: true) do |dfn|
29
- # recursively call valid? on nested form.
30
- Disposable::Twin::PropertyProcessor.new(dfn, self).() { |form| form.validate!(errors, prefixes+[dfn[:name]]) }
56
+ arr
31
57
  end
32
58
  end
33
- end
59
+ end
@@ -0,0 +1,49 @@
1
+ # Provides the old API for Rails and friends.
2
+ # Note that this might become an optional "deprecation" gem in Reform 3.
3
+ class Reform::Contract::Result::Errors
4
+ def initialize(result, form)
5
+ @result = result # DISCUSS: we don't use this ATM?
6
+ @form = form
7
+ @dotted_errors = {} # Reform does not endorse this style of error msgs.
8
+
9
+ DottedErrors.(@form, [], @dotted_errors)
10
+ end
11
+
12
+ # PROTOTYPING. THIS WILL GO TO A SEPARATE GEM IN REFORM 2.4/3.0.
13
+ DottedErrors = ->(form, prefix, hash) do
14
+ result = form.to_result
15
+ result.messages.collect { |k,v| hash[ [*prefix, k].join(".").to_sym] = v }
16
+
17
+ form.schema.each(twin: true) { |dfn|
18
+ Disposable::Twin::PropertyProcessor.new(dfn, form).() do |frm, i|
19
+ # DottedErrors.(form.send(dfn[:name])[i], [*prefix, dfn[:name], i], hash) and next if i
20
+ DottedErrors.(form.send(dfn[:name])[i], [*prefix, dfn[:name]], hash) and next if i
21
+ DottedErrors.(form.send(dfn[:name]), [*prefix, dfn[:name]], hash)
22
+ end
23
+ }
24
+ end
25
+
26
+ def messages(*args)
27
+ @dotted_errors
28
+ end
29
+
30
+ def full_messages
31
+ @dotted_errors.collect do |path, errors|
32
+ human_field = path.to_s.gsub(/([\.\_])+/, " ").gsub(/(\b\w)+/) { |s| s.capitalize }
33
+ errors.collect { |message| "#{human_field} #{message}" }
34
+ end.flatten
35
+ end
36
+
37
+ def [](name)
38
+ @dotted_errors[name]
39
+ end
40
+
41
+ def size
42
+ messages.size
43
+ end
44
+ end
45
+
46
+ # Ensure that we can return Active Record compliant full messages when using dry
47
+ # we only want unique messages in our array
48
+ #
49
+ # @full_errors.add()
@@ -16,7 +16,22 @@ module Reform
16
16
  module Property
17
17
  # Add macro logic, e.g. for :populator.
18
18
  def property(name, options={}, &block)
19
- definition = super # let representable sort out inheriting of properties, and so on.
19
+ # if composition and inherited we also need this setting
20
+ # to correctly inherit modules
21
+ if options.key?(:on) && options.key?(:inherit)
22
+ options[:_inherited] = options[:inherit]
23
+ end
24
+
25
+ if options.key?(:parse)
26
+ options[:deserializer] ||= {}
27
+ options[:deserializer][:writeable] = options.delete(:parse)
28
+ end
29
+
30
+ if options.key?(:writable)
31
+ options[:writeable] ||= options.delete(:writable)
32
+ end
33
+
34
+ definition = super # let disposable and declarative gems sort out inheriting of properties, and so on.
20
35
  definition.merge!(deserializer: {}) unless definition[:deserializer] # always keep :deserializer per property.
21
36
 
22
37
  deserializer_options = definition[:deserializer]
@@ -35,8 +50,8 @@ module Reform
35
50
  # always compute a parse_pipeline for each property of the deserializer and inject it via :parse_pipeline.
36
51
  # first, let representable compute the pipeline functions by invoking #parse_functions.
37
52
  if definition[:nested]
38
- parse_pipeline = ->(input, options) do
39
- functions = options[:binding].send(:parse_functions)
53
+ parse_pipeline = ->(input, opts) do
54
+ functions = opts[:binding].send(:parse_functions)
40
55
  pipeline = Representable::Pipeline[*functions] # Pipeline[StopOnExcluded, AssignName, ReadFragment, StopOnNotFound, OverwriteOnNil, Collect[#<Representable::Function::CreateObject:0xa6148ec>, #<Representable::Function::Decorate:0xa6148b0>, Deserialize], Set]
41
56
 
42
57
  pipeline = Representable::Pipeline::Insert.(pipeline, external_populator, replace: Representable::CreateObject::Instance)
@@ -45,8 +60,8 @@ module Reform
45
60
  pipeline = Representable::Pipeline::Insert.(pipeline, Representable::SetValue, delete: true) # FIXME: only diff to options without :populator
46
61
  end
47
62
  else
48
- parse_pipeline = ->(input, options) do
49
- functions = options[:binding].send(:parse_functions)
63
+ parse_pipeline = ->(input, opts) do
64
+ functions = opts[:binding].send(:parse_functions)
50
65
  pipeline = Representable::Pipeline[*functions] # Pipeline[StopOnExcluded, AssignName, ReadFragment, StopOnNotFound, OverwriteOnNil, Collect[#<Representable::Function::CreateObject:0xa6148ec>, #<Representable::Function::Decorate:0xa6148b0>, Deserialize], Set]
51
66
 
52
67
  # FIXME: this won't work with property :name, inherit: true (where there is a populator set already).
@@ -9,10 +9,6 @@ module Reform::Form::Dry
9
9
  end
10
10
 
11
11
  module Validations
12
- def build_errors
13
- Reform::Contract::Errors.new(self)
14
- end
15
-
16
12
  module ClassMethods
17
13
  def validation_group_class
18
14
  Group
@@ -23,44 +19,76 @@ module Reform::Form::Dry
23
19
  includer.extend(ClassMethods)
24
20
  end
25
21
 
22
+
26
23
  class Group
27
- def initialize
28
- @schemas = []
24
+ def initialize(options = {})
25
+ options ||= {}
26
+ schema_class = options[:schema] || Dry::Validation::Schema
27
+ @validator = Dry::Validation.Schema(schema_class, build: false)
28
+
29
+ @schema_inject_params = options[:with] || {}
29
30
  end
30
31
 
31
32
  def instance_exec(&block)
32
- @schemas << block
33
- @validator = Builder.new(@schemas.dup).validation_graph
34
- end
33
+ @validator = Dry::Validation.Schema(@validator, build: false, &block)
35
34
 
36
- def call(fields, reform_errors, form)
37
- # a message item looks like: {:confirm_password=>["confirm_password size cannot be less than 2"]}
38
- @validator.with(form: form).call(fields).messages.each do |field, dry_error|
39
- dry_error.each do |attr_error|
40
- reform_errors.add(field, attr_error)
35
+ # inject the keys into the configure block automatically
36
+ keys = @schema_inject_params.keys
37
+ @validator.class_eval do
38
+ configure do
39
+ keys.each { |k| option k }
41
40
  end
42
41
  end
43
42
  end
44
43
 
45
- class Builder < Array
46
- def initialize(array)
47
- super(array)
48
- @validator = Dry::Validation.Form({}, &shift)
49
- end
44
+ def call(form)
45
+ dynamic_options = {}
46
+ dynamic_options[:form] = form if @schema_inject_params[:form]
47
+ inject_options = @schema_inject_params.merge(dynamic_options)
50
48
 
51
- def validation_graph
52
- build_graph(@validator)
53
- end
49
+ # TODO: only pass submitted values to Schema#call?
50
+ dry_result = call_schema(inject_options, input_hash(form))
51
+ # dry_messages = dry_result.messages
52
+
53
+ return dry_result
54
+ reform_errors = Reform::Contract::Errors.new(dry_result) # TODO: dry should be merged here.
55
+ end
54
56
 
57
+ private
58
+ def call_schema(inject_options, input)
59
+ @validator.new(@validator.rules, inject_options).(input)
60
+ end
55
61
 
56
- private
62
+ # if dry_error is a hash rather than an array then it contains
63
+ # the messages for a nested property
64
+ # these messages need to be added to the correct collection
65
+ # objects.
57
66
 
58
- def build_graph(validator)
59
- if empty?
60
- return validator
61
- end
62
- build_graph(Dry::Validation.Schema(validator, {}, &shift))
63
- end
67
+ # collections:
68
+ # {0=>{:name=>["must be filled"]}, 1=>{:name=>["must be filled"]}}
69
+
70
+ # Objects:
71
+ # {:name=>["must be filled"]}
72
+ # simply load up the object and attach the message to it
73
+
74
+ # we can't use to_nested_hash as it get's messed up by composition.
75
+ def input_hash(form)
76
+ hash = form.class.nested_hash_representer.new(form).to_hash
77
+ symbolize_hash(hash)
78
+ end
79
+
80
+ # dry-v needs symbolized keys
81
+ # TODO: Don't do this here... Representers??
82
+ def symbolize_hash(old_hash)
83
+ old_hash.each_with_object({}) { |(k, v), new_hash|
84
+ new_hash[k.to_sym] = if v.is_a?(Hash)
85
+ symbolize_hash(v)
86
+ elsif v.is_a?(Array)
87
+ v.map{ |h| h.is_a?(Hash) ? symbolize_hash(h) : h }
88
+ else
89
+ v
90
+ end
91
+ }
64
92
  end
65
93
  end
66
94
  end