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.
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