reform 2.3.0.rc1 → 2.5.0

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