reform 2.2.4 → 2.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +5 -1
  3. data/.travis.yml +11 -6
  4. data/Appraisals +8 -0
  5. data/CHANGES.md +57 -4
  6. data/CONTRIBUTING.md +31 -0
  7. data/Gemfile +2 -16
  8. data/ISSUE_TEMPLATE.md +25 -0
  9. data/LICENSE.txt +1 -1
  10. data/README.md +5 -7
  11. data/Rakefile +16 -9
  12. data/gemfiles/0.13.0.gemfile +8 -0
  13. data/gemfiles/1.5.0.gemfile +9 -0
  14. data/lib/reform.rb +1 -0
  15. data/lib/reform/contract.rb +7 -17
  16. data/lib/reform/contract/custom_error.rb +41 -0
  17. data/lib/reform/contract/validate.rb +53 -23
  18. data/lib/reform/errors.rb +61 -0
  19. data/lib/reform/form.rb +36 -10
  20. data/lib/reform/form/call.rb +1 -1
  21. data/lib/reform/form/composition.rb +2 -2
  22. data/lib/reform/form/dry.rb +10 -58
  23. data/lib/reform/form/dry/input_hash.rb +37 -0
  24. data/lib/reform/form/dry/new_api.rb +45 -0
  25. data/lib/reform/form/dry/old_api.rb +61 -0
  26. data/lib/reform/form/populator.rb +11 -27
  27. data/lib/reform/form/prepopulate.rb +4 -3
  28. data/lib/reform/form/validate.rb +28 -13
  29. data/lib/reform/result.rb +90 -0
  30. data/lib/reform/validation.rb +19 -11
  31. data/lib/reform/validation/groups.rb +12 -27
  32. data/lib/reform/version.rb +1 -1
  33. data/reform.gemspec +14 -13
  34. data/test/benchmarking.rb +39 -6
  35. data/test/call_new_api.rb +23 -0
  36. data/test/call_old_api.rb +23 -0
  37. data/test/changed_test.rb +14 -14
  38. data/test/coercion_test.rb +57 -25
  39. data/test/composition_new_api.rb +186 -0
  40. data/test/composition_old_api.rb +184 -0
  41. data/test/contract/custom_error_test.rb +55 -0
  42. data/test/contract_new_api.rb +77 -0
  43. data/test/contract_old_api.rb +77 -0
  44. data/test/default_test.rb +4 -4
  45. data/test/deserialize_test.rb +17 -20
  46. data/test/errors_new_api.rb +225 -0
  47. data/test/errors_old_api.rb +230 -0
  48. data/test/feature_test.rb +10 -12
  49. data/test/fixtures/dry_error_messages.yml +73 -23
  50. data/test/fixtures/dry_new_api_error_messages.yml +104 -0
  51. data/test/form_new_api.rb +57 -0
  52. data/test/{form_test.rb → form_old_api.rb} +8 -8
  53. data/test/form_option_new_api.rb +24 -0
  54. data/test/{form_option_test.rb → form_option_old_api.rb} +5 -5
  55. data/test/from_test.rb +18 -22
  56. data/test/inherit_new_api.rb +105 -0
  57. data/test/inherit_old_api.rb +105 -0
  58. data/test/{module_test.rb → module_new_api.rb} +26 -31
  59. data/test/module_old_api.rb +146 -0
  60. data/test/parse_option_test.rb +40 -0
  61. data/test/parse_pipeline_test.rb +4 -4
  62. data/test/populate_new_api.rb +304 -0
  63. data/test/populate_old_api.rb +304 -0
  64. data/test/populator_skip_test.rb +11 -11
  65. data/test/prepopulator_test.rb +23 -24
  66. data/test/read_only_test.rb +12 -1
  67. data/test/readable_test.rb +9 -9
  68. data/test/reform_new_api.rb +204 -0
  69. data/test/{reform_test.rb → reform_old_api.rb} +44 -65
  70. data/test/save_new_api.rb +101 -0
  71. data/test/save_old_api.rb +101 -0
  72. data/test/setup_test.rb +17 -17
  73. data/test/skip_if_new_api.rb +85 -0
  74. data/test/skip_if_old_api.rb +92 -0
  75. data/test/skip_setter_and_getter_test.rb +9 -10
  76. data/test/test_helper.rb +25 -14
  77. data/test/validate_new_api.rb +453 -0
  78. data/test/{validate_test.rb → validate_old_api.rb} +121 -131
  79. data/test/validation/dry_validation_new_api.rb +835 -0
  80. data/test/validation/dry_validation_old_api.rb +772 -0
  81. data/test/validation/result_test.rb +77 -0
  82. data/test/validation_library_provided_test.rb +16 -0
  83. data/test/virtual_test.rb +47 -7
  84. data/test/writeable_test.rb +38 -9
  85. metadata +111 -56
  86. data/gemfiles/Gemfile.disposable-0.3 +0 -6
  87. data/lib/reform/contract/errors.rb +0 -43
  88. data/lib/reform/form/mongoid.rb +0 -37
  89. data/lib/reform/form/orm.rb +0 -26
  90. data/lib/reform/mongoid.rb +0 -4
  91. data/test/call_test.rb +0 -23
  92. data/test/composition_test.rb +0 -149
  93. data/test/contract_test.rb +0 -77
  94. data/test/deprecation_test.rb +0 -27
  95. data/test/errors_test.rb +0 -165
  96. data/test/inherit_test.rb +0 -119
  97. data/test/populate_test.rb +0 -270
  98. data/test/readonly_test.rb +0 -14
  99. data/test/save_test.rb +0 -89
  100. data/test/skip_if_test.rb +0 -74
  101. data/test/validation/dry_test.rb +0 -60
  102. data/test/validation/dry_validation_test.rb +0 -352
  103. data/test/validation/errors.yml +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 851795d3f1959935496c4a582f24dffc6020b848
4
- data.tar.gz: f62755eb2d9e6302a00790b703698149838474af
2
+ SHA256:
3
+ metadata.gz: 0f5d5dea8c04f5d993a91776afffea2a0b48603d850c11204d04e79248be6cf8
4
+ data.tar.gz: e4132e9db35384198bf31797b8620219c6418df4781a8f5300bbcf38c1a1c630
5
5
  SHA512:
6
- metadata.gz: 865c755aea555fedc28f61ae04a7903544ad637c621e656c68a64fc9f952b0a105906f4abc77571dce490099227224cd966c12ccc802b8ae7e830c0b27d108f3
7
- data.tar.gz: a90c092125bd9f2a7db2a6092267ba3ac439c6d92bd3b2e657a50156128c4de5b8f28e3f62cc2eefc47b6a8f36b1e8d5f94e393f31ab115cf297a60210fc58c4
6
+ metadata.gz: '0845ada0bbafc7c3a45485d51f93c66c8616bee7b23c851712d297be20c73e82f23a8e575f803adb2972c382c4c9003951ae8d788fe12df87195950490505028'
7
+ data.tar.gz: bd463ddb326f4db34ef9ad6948cdb9466a56c87514f2a18fcafba6dff29b54baf5640b97b490657b92e438c336869f35adc3e407b91be80351f427fd84f28c35
data/.gitignore CHANGED
@@ -4,7 +4,6 @@
4
4
  .config
5
5
  .yardoc
6
6
  Gemfile.lock
7
- gemfiles/*.lock
8
7
  InstalledFiles
9
8
  _yardoc
10
9
  coverage
@@ -16,3 +15,8 @@ spec/reports
16
15
  test/tmp
17
16
  test/version_tmp
18
17
  tmp
18
+ .rubocop-https*
19
+ .byebug_history
20
+ .idea
21
+ *.iml
22
+ gemfiles/*.gemfile.lock
@@ -1,11 +1,16 @@
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
+ - ruby-head
6
+ - 2.7
7
+ - 2.6
8
+ - 2.5
9
+ - 2.4
5
10
  gemfile:
6
- - gemfiles/Gemfile.disposable-0.3
7
-
11
+ - gemfiles/1.5.0.gemfile
12
+ - gemfiles/0.13.0.gemfile
8
13
  matrix:
9
14
  fast_finish: true
10
- before_install:
11
- - gem install bundler
15
+ allow_failures:
16
+ - rvm: ruby-head
@@ -0,0 +1,8 @@
1
+ appraise "1.5.0" do
2
+ gem 'dry-monads', "1.3.5"
3
+ gem 'dry-validation', '~> 1.5.0'
4
+ end
5
+
6
+ appraise "0.13.0" do
7
+ gem 'dry-validation', '~> 0.13.0'
8
+ end
data/CHANGES.md CHANGED
@@ -1,9 +1,62 @@
1
+ ## 2.3.3
2
+
3
+ * Rename validation option for dry-v 1+ to `contract` instead of `schema`
4
+
5
+ ## 2.3.2
6
+
7
+ * Fix Validation block option :form incorrectly memoized between tests
8
+
9
+ ## 2.3.1
10
+ * With dry-validation 1.5 the form is always injected. Just add option :form to access it in the schema.
11
+ * Removed global monkey patching of Dry::Schema::DSL
12
+ * Tests in ruby 2.7
13
+
14
+ ## 2.3.0
15
+
16
+ You can upgrade from 2.2.0 without worries.
17
+
18
+ * Require Representable 3.0.0 and **removed Representable 2.4 deprecation code**.
19
+ * Require Disposable 0.4.0 which fixes issues with `nil` field values, `sync {}` and dry-validation.
20
+ * Fix boolean coercion.
21
+ * Allow using `:populator` classes marked with `Uber::Callable`.
22
+ * Introduce `parse: false` as a shortcut for `deserialzer: { writeable: false}`. Thanks to @pabloh for insisting on this handy change.
23
+ * 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%.
24
+ * Deprecated positional arguments for `validation :default, options: {}`. New API: `validation name: :default, **`.
25
+ * Reform now maintains a generic `Dry::Schema` class for global schema configuration. Can be overridden via `::validation`.
26
+ * 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.
27
+ * [private] `Group#call` API now is: `call(form, errors)`.
28
+ * Modify `Form#valid?` - simply calls `validate({})`.
29
+ * In `:if` for validation groups, you now get a hash of result objects, not just true/false.
30
+ * Allow adding a custom error AFTER validate has been already called
31
+
32
+ Compatibility with `dry-validation` with 1.x:
33
+ * [CHANGE] seems like "custom" predicate are not supported by `dry-schema` anymore or better the same result is reached using the `rule` method:
34
+ Something like this:
35
+ ```ruby
36
+ validation do
37
+ def a_song?(value)
38
+ value == :really_cool_song
39
+ end
40
+
41
+ required(:songs).filled(:a_song?)
42
+ end
43
+ ```
44
+ will be something like:
45
+ ```ruby
46
+ validation do
47
+ required(:songs).filled
48
+
49
+ rule(:songs) do
50
+ key.failure(:a_song?) unless value == :really_cool_song
51
+ end
52
+ end
53
+ ```
54
+ * [BREAKING] inheriting/merging/overriding schema/rules is not supported by `dry-v` so the `inherit:` option is **NOT SUPPORTED** for now. Also extend a `schema:` option using a block is **NOT SUPPORTED** for now. Possible workaround is to use reform module to compose different validations but this won't override existing validations but just merge them
55
+
1
56
  ## 2.2.4
2
57
 
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.
58
+ * 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.
59
+ * Use `declarative-option` and loosen `uber` dependency.
7
60
 
8
61
  ## 2.2.3
9
62
 
@@ -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
@@ -1,19 +1,5 @@
1
- source 'https://rubygems.org'
1
+ 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
- gem "minitest-line"
16
- gem 'byebug'
17
-
18
- # gem "uber", path: "../uber"
19
- gem "representable", ">= 3.0.1"
5
+ gem "appraisal", "~> 2.2"
@@ -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](https://gitter.im/trailblazer/chat).
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 - 2020 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
@@ -315,20 +315,18 @@ AlbumForm.new(album: album, cd: CD.find(1))
315
315
 
316
316
  Reform comes many more optional features, like hash fields, coercion, virtual fields, and so on. Check the [full documentation here](http://trailblazer.to/gems/reform).
317
317
 
318
- <a href="https://leanpub.com/trailblazer">
319
- ![](http://trailblazer.to/images/3dbuch-freigestellt.png)
320
- </a>
318
+ [![](http://trailblazer.to/images/3dbuch-freigestellt.png)](https://leanpub.com/trailblazer)
321
319
 
322
320
  Reform is part of the [Trailblazer project](http://trailblazer.to). Please [buy my book](https://leanpub.com/trailblazer) to support the development and learn everything about Reform - there's two chapters dedicated to Reform!
323
321
 
324
322
 
325
323
  ## Security And Strong_parameters
326
324
 
327
- By explicitely defining the form layout using `::property` there is no more need for protecting from unwanted input. `strong_parameter` or `attr_accessible` become obsolete. Reform will simply ignore undefined incoming parameters.
325
+ By explicitly defining the form layout using `::property` there is no more need for protecting from unwanted input. `strong_parameter` or `attr_accessible` become obsolete. Reform will simply ignore undefined incoming parameters.
328
326
 
329
327
  ## This is not Reform 1.x!
330
328
 
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).
329
+ 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
330
 
333
331
  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
332
 
data/Rakefile CHANGED
@@ -1,15 +1,22 @@
1
1
  require "bundler/gem_tasks"
2
- require 'rake/testtask'
2
+ require "rake/testtask"
3
+ require "dry/types/version"
3
4
 
4
- task :default => [:test]
5
- Rake::TestTask.new(:test) do |test|
6
- test.libs << 'test'
7
- test.test_files = FileList['test/*_test.rb'] + FileList["test/validation/*_test.rb"]
8
- test.verbose = true
5
+ task default: %i[test]
6
+
7
+ TEST_WITH_OLD_AND_NEW_API = %w[
8
+ validation/dry_validation call composition contract errors inherit module reform
9
+ save skip_if populate validate form
10
+ ].freeze
11
+
12
+ def dry_v_test_files
13
+ api = Gem::Version.new(Dry::Types::VERSION).to_s.split('.').first.to_i >= 1 ? "new" : "old"
14
+ TEST_WITH_OLD_AND_NEW_API.map { |file| "test/#{file}_#{api}_api.rb" }
9
15
  end
10
16
 
11
- Rake::TestTask.new(:test_rails) do |test|
12
- test.libs << 'test'
13
- test.test_files = FileList['test/rails/*_test.rb']
17
+ Rake::TestTask.new(:test) do |test|
18
+ test.libs << "test"
19
+ test.test_files = FileList["test/*_test.rb"] + FileList["test/validation/*_test.rb"] + dry_v_test_files
14
20
  test.verbose = true
15
21
  end
22
+
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal", "~> 2.2"
6
+ gem "dry-validation", "~> 0.13.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal", "~> 2.2"
6
+ gem "dry-monads", "1.3.5"
7
+ gem "dry-validation", "~> 1.5.0"
8
+
9
+ gemspec path: "../"
@@ -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.
@@ -1,9 +1,8 @@
1
- require "uber/inheritable_attr"
2
-
3
1
  module Reform
4
2
  # Define your form structure and its validations. Instantiate it with a model,
5
3
  # and then +validate+ this object graph.
6
4
  class Contract < Disposable::Twin
5
+ require "reform/contract/custom_error"
7
6
  require "disposable/twin/composition" # Expose.
8
7
  include Expose
9
8
 
@@ -15,7 +14,7 @@ module Reform
15
14
  Contract
16
15
  end
17
16
 
18
- def self.property(name, options={}, &block)
17
+ def self.property(name, options = {}, &block)
19
18
  if twin = options.delete(:form)
20
19
  options[:twin] = twin
21
20
  end
@@ -27,14 +26,13 @@ module Reform
27
26
  super
28
27
  end
29
28
 
30
- # FIXME: test me.
31
29
  def self.properties(*args)
32
30
  options = args.last.is_a?(Hash) ? args.pop : {}
33
31
  args.each { |name| property(name, options.dup) }
34
32
  end
35
33
 
36
- require "reform/contract/errors"
37
- require 'reform/contract/validate'
34
+ require "reform/result"
35
+ require "reform/contract/validate"
38
36
  include Reform::Contract::Validate
39
37
 
40
38
  require "reform/validation"
@@ -44,23 +42,16 @@ module Reform
44
42
  require "disposable/twin/sync"
45
43
  include Disposable::Twin::Sync
46
44
 
45
+ private
47
46
 
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
- private
57
47
  # DISCUSS: separate file?
58
48
  module Readonly
59
49
  def readonly?(name)
60
50
  options_for(name)[:writeable] == false
61
51
  end
52
+
62
53
  def options_for(name)
63
- self.class.options_for(name)
54
+ self.class.options_for(name)
64
55
  end
65
56
  end
66
57
 
@@ -69,7 +60,6 @@ module Reform
69
60
  end
70
61
  include Readonly
71
62
 
72
-
73
63
  def self.clone # TODO: test. THIS IS ONLY FOR Trailblazer when contract gets cloned in suboperation.
74
64
  Class.new(self)
75
65
  end
@@ -0,0 +1,41 @@
1
+ module Reform
2
+ class Contract < Disposable::Twin
3
+ # a "fake" Dry schema object to add into the @results array
4
+ # super ugly hack required for 2.3.x version since we are creating
5
+ # a new Reform::Errors instance every time we call form.errors
6
+ class CustomError
7
+ def initialize(key, error_text, results)
8
+ @key = key
9
+ @error_text = error_text
10
+ @errors = {key => Array(error_text)}
11
+ @messages = @errors
12
+ @hint = {}
13
+ @results = results
14
+
15
+ merge!
16
+ end
17
+
18
+ attr_reader :messages, :hint
19
+
20
+ def success?
21
+ false
22
+ end
23
+
24
+ def failure?
25
+ true
26
+ end
27
+
28
+ # dry 1.x errors method has 1 kwargs argument
29
+ def errors(**_args)
30
+ @errors
31
+ end
32
+
33
+ def merge!
34
+ # to_h required for dry_v 1.x since the errors are Dry object instead of an hash
35
+ @results.map(&:errors)
36
+ .detect { |hash| hash.to_h.key?(@key) }
37
+ .tap { |hash| hash.nil? ? @results << self : hash.to_h[@key] |= Array(@error_text) }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,33 +1,63 @@
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
24
+
25
+ def custom_errors
26
+ @result.to_results.select { |result| result.is_a? Reform::Contract::CustomError }
27
+ end
14
28
 
15
- def validate!(errors, prefix)
16
- validate_nested!(nested_errors = build_errors, prefix) # call valid? recursively and collect nested errors.
29
+ def validate!(name, pointers = [])
30
+ # run local validations. this could be nested schemas, too.
31
+ local_errors_by_group = Reform::Validation::Groups::Validate.(self.class.validation_groups, self).compact # TODO: discss compact
17
32
 
18
- valid? # calls AM/Lotus validators and invokes self.errors=.
33
+ # blindly add injected pointers. will be readable via #errors.
34
+ # also, add pointers from local errors here.
35
+ pointers_for_nested = pointers + local_errors_by_group.collect { |errs| Result::Pointer.new(errs, []) }.compact
19
36
 
20
- errors.merge!(self.errors, prefix) # local errors.
21
- errors.merge!(nested_errors, [])
22
- end
37
+ nested_errors = validate_nested!(pointers_for_nested)
38
+
39
+ # Result: unified interface #success?, #messages, etc.
40
+ @result = Result.new(custom_errors + local_errors_by_group + pointers, nested_errors)
41
+ end
42
+
43
+ private
44
+
45
+ # Recursively call validate! on nested forms.
46
+ # A pointer keeps an entire result object (e.g. Dry result) and
47
+ # the relevant path to its fragment, e.g. <Dry::result{.....} path=songs,0>
48
+ def validate_nested!(pointers)
49
+ arr = []
50
+
51
+ schema.each(twin: true) do |dfn|
52
+ # on collections, this calls validate! on each item form.
53
+ Disposable::Twin::PropertyProcessor.new(dfn, self).() do |form, i|
54
+ nested_pointers = pointers.collect { |pointer| pointer.advance(dfn[:name].to_sym, i) }.compact # pointer contains fragment for us, so go deeper
23
55
 
24
- private
56
+ arr << form.validate!(dfn[:name], nested_pointers)
57
+ end
58
+ end
25
59
 
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]]) }
60
+ arr
31
61
  end
32
62
  end
33
- end
63
+ end