reform 2.3.0.rc1 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +5 -1
  3. data/.travis.yml +7 -11
  4. data/CHANGES.md +43 -3
  5. data/Gemfile +2 -5
  6. data/ISSUE_TEMPLATE.md +1 -1
  7. data/LICENSE.txt +1 -1
  8. data/README.md +7 -9
  9. data/Rakefile +6 -10
  10. data/lib/reform/contract.rb +7 -7
  11. data/lib/reform/contract/custom_error.rb +41 -0
  12. data/lib/reform/contract/validate.rb +10 -6
  13. data/lib/reform/errors.rb +27 -15
  14. data/lib/reform/form.rb +22 -11
  15. data/lib/reform/form/call.rb +1 -1
  16. data/lib/reform/form/composition.rb +2 -2
  17. data/lib/reform/form/dry.rb +22 -60
  18. data/lib/reform/form/dry/input_hash.rb +37 -0
  19. data/lib/reform/form/populator.rb +9 -11
  20. data/lib/reform/form/prepopulate.rb +3 -2
  21. data/lib/reform/form/validate.rb +19 -12
  22. data/lib/reform/result.rb +36 -9
  23. data/lib/reform/validation.rb +10 -8
  24. data/lib/reform/validation/groups.rb +2 -4
  25. data/lib/reform/version.rb +1 -1
  26. data/reform.gemspec +9 -9
  27. data/test/benchmarking.rb +10 -11
  28. data/test/call_test.rb +8 -8
  29. data/test/changed_test.rb +13 -13
  30. data/test/coercion_test.rb +56 -24
  31. data/test/composition_test.rb +49 -51
  32. data/test/contract/custom_error_test.rb +55 -0
  33. data/test/contract_test.rb +18 -18
  34. data/test/default_test.rb +3 -3
  35. data/test/deserialize_test.rb +14 -17
  36. data/test/docs/validation_test.rb +134 -0
  37. data/test/errors_test.rb +131 -86
  38. data/test/feature_test.rb +9 -11
  39. data/test/fixtures/dry_error_messages.yml +65 -52
  40. data/test/form_option_test.rb +3 -3
  41. data/test/form_test.rb +6 -6
  42. data/test/from_test.rb +17 -21
  43. data/test/inherit_test.rb +28 -35
  44. data/test/module_test.rb +23 -28
  45. data/test/parse_option_test.rb +12 -12
  46. data/test/parse_pipeline_test.rb +3 -3
  47. data/test/populate_test.rb +146 -93
  48. data/test/populator_skip_test.rb +3 -4
  49. data/test/prepopulator_test.rb +20 -21
  50. data/test/read_only_test.rb +12 -1
  51. data/test/readable_test.rb +7 -7
  52. data/test/reform_test.rb +38 -42
  53. data/test/save_test.rb +16 -19
  54. data/test/setup_test.rb +15 -15
  55. data/test/skip_if_test.rb +30 -19
  56. data/test/skip_setter_and_getter_test.rb +8 -9
  57. data/test/test_helper.rb +12 -5
  58. data/test/validate_test.rb +160 -140
  59. data/test/validation/dry_validation_test.rb +407 -236
  60. data/test/validation/result_test.rb +29 -31
  61. data/test/validation_library_provided_test.rb +3 -3
  62. data/test/virtual_test.rb +46 -6
  63. data/test/writeable_test.rb +13 -13
  64. metadata +32 -29
  65. data/test/readonly_test.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 833e85f6efec80dfacc227371dfb5ec4fe5d1fdd
4
- data.tar.gz: e084df9fc8f080155d3af4398a80c78347db5b78
2
+ SHA256:
3
+ metadata.gz: e0b6b3c1dbb48304ae8b36f5fbc12d9f7149edc61b40757017472b3eb01cf04f
4
+ data.tar.gz: 689811ffa73a062d4e598d8a36b6258571350d1ce4c5c87c85db9707313dde04
5
5
  SHA512:
6
- metadata.gz: dba35a83313b4f5a2cecde6d85800b19c83f5b55553d2600e448ca042f1a8429a85b26ff3d522aa95b308bc1fadba731bb7a550cbe81e3ebab02d4c2052a4de8
7
- data.tar.gz: cac8a4bddbbbef87379fd823bcfccce351fb9a0af36448d36d092393abaebcabc5343cc1665a68652a570bc7e765593ae0c873912033166d870e729962bddf70
6
+ metadata.gz: a797c79e164c4c1791661a97163d4e89b1b2e49ae4992e1c0a59eb427d98ddfdbdae96292f455f843acacc869b5053916afa7131018843c66f4d886e6872c372
7
+ data.tar.gz: 82fe647e1ab34dcc24e85250d5876b88b84ea3a991cdfb163bf0dd616be701105ab1ca87065285bc0e32abe9f67ccdb3cda103c7f70143e49d0a8c3024baa75e
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*
19
+ .byebug_history
20
+ .idea
21
+ *.iml
22
+ gemfiles/*.gemfile.lock
data/.travis.yml CHANGED
@@ -2,16 +2,12 @@ language: ruby
2
2
  cache: bundler
3
3
  bundler_args: --without benchmarks tools
4
4
  rvm:
5
- - 2.1.10
6
- - 2.2.5
7
- - 2.3.1
8
- - 2.4.0
5
+ - ruby-head
6
+ - 2.7
7
+ - 2.6
8
+ - 2.5
9
+ - 2.4
9
10
  matrix:
10
11
  fast_finish: true
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
12
+ allow_failures:
13
+ - rvm: ruby-head
data/CHANGES.md CHANGED
@@ -1,8 +1,25 @@
1
- ## 3.0.0
1
+ ## 2.5.0
2
+ * fix memory leak with Dry validation (#525)
2
3
 
4
+ ## 2.4.0
5
+
6
+ * [BREAKING] Dropping compatibility of dry-validation < 1.x
3
7
  [* Removed `Reform::Contract` ?]
4
8
  [* Move Form#deserializer to Form::deserializer]
5
9
 
10
+ ## 2.3.3
11
+
12
+ * Rename validation option for dry-v 1+ to `contract` instead of `schema`
13
+
14
+ ## 2.3.2
15
+
16
+ * Fix Validation block option :form incorrectly memoized between tests
17
+
18
+ ## 2.3.1
19
+ * With dry-validation 1.5 the form is always injected. Just add option :form to access it in the schema.
20
+ * Removed global monkey patching of Dry::Schema::DSL
21
+ * Tests in ruby 2.7
22
+
6
23
  ## 2.3.0
7
24
 
8
25
  You can upgrade from 2.2.0 without worries.
@@ -17,10 +34,33 @@ You can upgrade from 2.2.0 without worries.
17
34
  * Reform now maintains a generic `Dry::Schema` class for global schema configuration. Can be overridden via `::validation`.
18
35
  * 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
36
  * [private] `Group#call` API now is: `call(form, errors)`.
20
- * Removed `Form#valid?`.
21
-
37
+ * Modify `Form#valid?` - simply calls `validate({})`.
22
38
  * In `:if` for validation groups, you now get a hash of result objects, not just true/false.
39
+ * Allow adding a custom error AFTER validate has been already called
40
+
41
+ Compatibility with `dry-validation` with 1.x:
42
+ * [CHANGE] seems like "custom" predicate are not supported by `dry-schema` anymore or better the same result is reached using the `rule` method:
43
+ Something like this:
44
+ ```ruby
45
+ validation do
46
+ def a_song?(value)
47
+ value == :really_cool_song
48
+ end
23
49
 
50
+ required(:songs).filled(:a_song?)
51
+ end
52
+ ```
53
+ will be something like:
54
+ ```ruby
55
+ validation do
56
+ required(:songs).filled
57
+
58
+ rule(:songs) do
59
+ key.failure(:a_song?) unless value == :really_cool_song
60
+ end
61
+ end
62
+ ```
63
+ * [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
24
64
 
25
65
  ## 2.2.4
26
66
 
data/Gemfile CHANGED
@@ -1,8 +1,5 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem "minitest-line"
6
- gem 'byebug'
7
- # gem "disposable", path: "../disposable"
8
-
5
+ gem 'dry-validation', '~> 1.5.0'
data/ISSUE_TEMPLATE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  Note: If you have a question about Reform, would like help using
2
2
  Reform, want to request a feature, or do anything else other than
3
- submit a bug report, please use the Trailblazer gitter channel.
3
+ submit a bug report, please use the [Trailblazer gitter channel](https://gitter.im/trailblazer/chat).
4
4
 
5
5
  Note: Rails/ ActiveRecord/ ActiveModel support.
6
6
  As of Reform 2.2.0 all Rails/ Active-* code was moved to the [reform-rails](https://github.com/trailblazer/reform-rails) gem.
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 - 2016 Nick Sutterer
1
+ Copyright (c) 2013 - 2021 Nick Sutterer
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -10,11 +10,11 @@ _Form objects decoupled from your models._
10
10
 
11
11
  Reform gives you a form object with validations and nested setup of models. It is completely framework-agnostic and doesn't care about your database.
12
12
 
13
- Although reform can be used in any Ruby framework, it comes with [Rails support](#rails-integration), works with [simple_form and other form gems](#formbuilder-support), allows nesting forms to implement [has_one](#nesting-forms-1-1-relations) and [has_many](#nesting-forms-1-n-relations) relationships, can [compose a form](#compositions) from multiple objects and gives you [coercion](#coercion).
13
+ Although reform can be used in any Ruby framework, it comes with [Rails support](#rails-integration), works with simple_form and other form gems, allows nesting forms to implement has_one and has_many relationships, can [compose a form](#compositions) from multiple objects and gives you coercion.
14
14
 
15
15
  ## Full Documentation
16
16
 
17
- Reform is part of the [Trailblazer](http://trailblazer.to) framework. [Full documentation](http://trailblazer.to/gems/reform) is available on the project site.
17
+ Reform is part of the [Trailblazer](http://trailblazer.to) framework. [Full documentation](http://trailblazer.to/2.1/docs/reform.html) is available on the project site.
18
18
 
19
19
  ## Reform 2.2
20
20
 
@@ -250,7 +250,7 @@ The manual saving with block is not encouraged. You should rather check the Disp
250
250
 
251
251
  ## Populating Forms
252
252
 
253
- Very often, you need to give Reform some information how to create or find nested objects when `validate`ing. This directive is called _populator_ and [documented here](http://trailblazer.to/gems/reform/populator.html).
253
+ Very often, you need to give Reform some information how to create or find nested objects when `validate`ing. This directive is called _populator_ and [documented here](http://trailblazer.to/2.1/docs/reform.html#reform-populators).
254
254
 
255
255
  ## Installation
256
256
 
@@ -313,18 +313,16 @@ AlbumForm.new(album: album, cd: CD.find(1))
313
313
 
314
314
  ## More
315
315
 
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).
316
+ Reform comes many more optional features, like hash fields, coercion, virtual fields, and so on. Check the [full documentation here](http://trailblazer.to/2.1/docs/reform.html).
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
 
@@ -332,7 +330,7 @@ Temporary note: This is the README and API for Reform 2. On the public API, only
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
 
335
- [Full documentation for Reform](http://trailblazer.to/gems/reform) is available online, or support us and grab the [Trailblazer book](https://leanpub.com/trailblazer). There is an [Upgrading Guide](http://trailblazer.to/gems/reform/upgrading-guide.html) to help you migrate from Reform 1.x.
333
+ [Full documentation for Reform](http://trailblazer.to/2.1/docs/reform.html) is available online, or support us and grab the [Trailblazer book](https://leanpub.com/trailblazer). There is an [Upgrading Guide](http://trailblazer.to/2.1/docs/reform.html#reform-upgrading-guide) to help you migrate through versions.
336
334
 
337
335
  ### Attributions!!!
338
336
 
data/Rakefile CHANGED
@@ -1,15 +1,11 @@
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
9
- end
5
+ task default: %i[test]
10
6
 
11
- Rake::TestTask.new(:test_rails) do |test|
12
- test.libs << 'test'
13
- test.test_files = FileList['test/rails/*_test.rb']
7
+ Rake::TestTask.new(:test) do |test|
8
+ test.libs << "test"
9
+ test.test_files = FileList["test/**/*_test.rb"]
14
10
  test.verbose = true
15
11
  end
@@ -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
@@ -33,7 +32,7 @@ module Reform
33
32
  end
34
33
 
35
34
  require "reform/result"
36
- require 'reform/contract/validate'
35
+ require "reform/contract/validate"
37
36
  include Reform::Contract::Validate
38
37
 
39
38
  require "reform/validation"
@@ -43,14 +42,16 @@ module Reform
43
42
  require "disposable/twin/sync"
44
43
  include Disposable::Twin::Sync
45
44
 
46
- private
45
+ private
46
+
47
47
  # DISCUSS: separate file?
48
48
  module Readonly
49
49
  def readonly?(name)
50
50
  options_for(name)[:writeable] == false
51
51
  end
52
+
52
53
  def options_for(name)
53
- self.class.options_for(name)
54
+ self.class.options_for(name)
54
55
  end
55
56
  end
56
57
 
@@ -59,7 +60,6 @@ module Reform
59
60
  end
60
61
  include Readonly
61
62
 
62
-
63
63
  def self.clone # TODO: test. THIS IS ONLY FOR Trailblazer when contract gets cloned in suboperation.
64
64
  Class.new(self)
65
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
@@ -11,7 +11,7 @@ class Reform::Contract < Disposable::Twin
11
11
  validate!(nil).success?
12
12
  end
13
13
 
14
- # The #errors method will be removed in Reform 2.4/3.0 core.
14
+ # The #errors method will be removed in Reform 3.0 core.
15
15
  def errors(*args)
16
16
  Result::Errors.new(@result, self)
17
17
  end
@@ -22,7 +22,11 @@ class Reform::Contract < Disposable::Twin
22
22
  @result
23
23
  end
24
24
 
25
- def validate!(name, pointers=[])
25
+ def custom_errors
26
+ @result.to_results.select { |result| result.is_a? Reform::Contract::CustomError }
27
+ end
28
+
29
+ def validate!(name, pointers = [])
26
30
  # run local validations. this could be nested schemas, too.
27
31
  local_errors_by_group = Reform::Validation::Groups::Validate.(self.class.validation_groups, self).compact # TODO: discss compact
28
32
 
@@ -33,10 +37,10 @@ class Reform::Contract < Disposable::Twin
33
37
  nested_errors = validate_nested!(pointers_for_nested)
34
38
 
35
39
  # Result: unified interface #success?, #messages, etc.
36
- @result = Result.new(local_errors_by_group + pointers, nested_errors)
40
+ @result = Result.new(custom_errors + local_errors_by_group + pointers, nested_errors)
37
41
  end
38
42
 
39
- private
43
+ private
40
44
 
41
45
  # Recursively call validate! on nested forms.
42
46
  # A pointer keeps an entire result object (e.g. Dry result) and
@@ -46,11 +50,11 @@ class Reform::Contract < Disposable::Twin
46
50
 
47
51
  schema.each(twin: true) do |dfn|
48
52
  # on collections, this calls validate! on each item form.
49
- Disposable::Twin::PropertyProcessor.new(dfn, self).() { |form, i|
53
+ Disposable::Twin::PropertyProcessor.new(dfn, self).() do |form, i|
50
54
  nested_pointers = pointers.collect { |pointer| pointer.advance(dfn[:name].to_sym, i) }.compact # pointer contains fragment for us, so go deeper
51
55
 
52
56
  arr << form.validate!(dfn[:name], nested_pointers)
53
- }
57
+ end
54
58
  end
55
59
 
56
60
  arr
data/lib/reform/errors.rb CHANGED
@@ -12,15 +12,14 @@ class Reform::Contract::Result::Errors
12
12
  # PROTOTYPING. THIS WILL GO TO A SEPARATE GEM IN REFORM 2.4/3.0.
13
13
  DottedErrors = ->(form, prefix, hash) do
14
14
  result = form.to_result
15
- result.messages.collect { |k,v| hash[ [*prefix, k].join(".").to_sym] = v }
15
+ result.messages.collect { |k, v| hash[[*prefix, k].join(".").to_sym] = v }
16
16
 
17
- form.schema.each(twin: true) { |dfn|
17
+ form.schema.each(twin: true) do |dfn|
18
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)
19
+ form_obj = i ? form.send(dfn[:name])[i] : form.send(dfn[:name])
20
+ DottedErrors.(form_obj, [*prefix, dfn[:name]], hash)
22
21
  end
23
- }
22
+ end
24
23
  end
25
24
 
26
25
  def messages(*args)
@@ -28,22 +27,35 @@ class Reform::Contract::Result::Errors
28
27
  end
29
28
 
30
29
  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
30
+ @dotted_errors.collect { |path, errors|
31
+ human_field = path.to_s.gsub(/([\.\_])+/, " ").gsub(/(\b\w)+/) { |s| s.capitalize }
32
+ errors.collect { |message| "#{human_field} #{message}" }
33
+ }.flatten
35
34
  end
36
35
 
37
36
  def [](name)
38
- @dotted_errors[name]
37
+ @dotted_errors[name] || []
39
38
  end
40
39
 
41
40
  def size
42
41
  messages.size
43
42
  end
43
+
44
+ # needed for rails form helpers
45
+ def empty?
46
+ messages.empty?
47
+ end
48
+
49
+ # we need to delegate adding error to result because every time we call form.errors
50
+ # a new instance of this class is created so we need to update the @results array
51
+ # to be able to add custom errors here.
52
+ # This method will actually work only AFTER a validate call has been made
53
+ def add(key, error_test)
54
+ @result.add_error(key, error_test)
55
+ end
44
56
  end
45
57
 
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()
58
+ # Ensure that we can return Active Record compliant full messages when using dry
59
+ # we only want unique messages in our array
60
+ #
61
+ # @full_errors.add()
data/lib/reform/form.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  module Reform
2
2
  class Form < Contract
3
+ class InvalidOptionsCombinationError < StandardError; end
4
+
3
5
  def self.default_nested_class
4
6
  Form
5
7
  end
@@ -15,23 +17,32 @@ module Reform
15
17
 
16
18
  module Property
17
19
  # Add macro logic, e.g. for :populator.
18
- def property(name, options={}, &block)
20
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
21
+ def property(name, options = {}, &block)
22
+ if (options.keys & %i[skip_if populator]).size == 2
23
+ raise InvalidOptionsCombinationError.new(
24
+ "[Reform] #{self}:property:#{name} Do not use skip_if and populator together, use populator with skip! instead"
25
+ )
26
+ end
27
+
19
28
  # if composition and inherited we also need this setting
20
29
  # to correctly inherit modules
21
- if options.key?(:on) && options.key?(:inherit)
22
- options[:_inherited] = options[:inherit]
23
- end
30
+ options[:_inherited] = options[:inherit] if options.key?(:on) && options.key?(:inherit)
24
31
 
25
32
  if options.key?(:parse)
26
33
  options[:deserializer] ||= {}
27
34
  options[:deserializer][:writeable] = options.delete(:parse)
28
35
  end
29
36
 
30
- if options.key?(:writable)
31
- options[:writeable] ||= options.delete(:writable)
37
+ options[:writeable] ||= options.delete(:writable) if options.key?(:writable)
38
+
39
+ # for virtual collection we need at least to have the collection equal to [] to
40
+ # avoid issue when the populator
41
+ if (options.keys & %i[collection virtual]).size == 2
42
+ options = { default: [] }.merge(options)
32
43
  end
33
44
 
34
- definition = super # let disposable and declarative gems sort out inheriting of properties, and so on.
45
+ definition = super # letdisposable and declarative gems sort out inheriting of properties, and so on.
35
46
  definition.merge!(deserializer: {}) unless definition[:deserializer] # always keep :deserializer per property.
36
47
 
37
48
  deserializer_options = definition[:deserializer]
@@ -48,7 +59,7 @@ module Reform
48
59
  external_populator = Populator::External.new
49
60
 
50
61
  # always compute a parse_pipeline for each property of the deserializer and inject it via :parse_pipeline.
51
- # first, let representable compute the pipeline functions by invoking #parse_functions.
62
+ # first, letrepresentable compute the pipeline functions by invoking #parse_functions.
52
63
  if definition[:nested]
53
64
  parse_pipeline = ->(input, opts) do
54
65
  functions = opts[:binding].send(:parse_functions)
@@ -65,7 +76,7 @@ module Reform
65
76
  pipeline = Representable::Pipeline[*functions] # Pipeline[StopOnExcluded, AssignName, ReadFragment, StopOnNotFound, OverwriteOnNil, Collect[#<Representable::Function::CreateObject:0xa6148ec>, #<Representable::Function::Decorate:0xa6148b0>, Deserialize], Set]
66
77
 
67
78
  # FIXME: this won't work with property :name, inherit: true (where there is a populator set already).
68
- pipeline = Representable::Pipeline::Insert.(pipeline, external_populator, replace: Representable::SetValue) if definition[:populator] # FIXME: only diff to options without :populator
79
+ pipeline = Representable::Pipeline::Insert.(pipeline, external_populator, replace: Representable::SetValue) if definition[:populator] # FIXME: only diff to options without :populator
69
80
  pipeline
70
81
  end
71
82
  end
@@ -77,12 +88,12 @@ module Reform
77
88
  deserializer_options.merge!(skip_parse: proc) # TODO: same with skip_parse ==> External
78
89
  end
79
90
 
80
-
81
91
  # per default, everything should be writeable for the deserializer (we're only writing on the form). however, allow turning it off.
82
- deserializer_options.merge!(writeable: true) unless deserializer_options.has_key?(:writeable)
92
+ deserializer_options.merge!(writeable: true) unless deserializer_options.key?(:writeable)
83
93
 
84
94
  definition
85
95
  end
96
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
86
97
  end
87
98
  extend Property
88
99