reform 2.2.4 → 2.3.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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