pathway 0.9.0 → 0.11.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d286f1d75ae6776baa9113bd4dae0da6f30685723173cee2a971a02d134223f6
4
- data.tar.gz: ab88b4c578bde709d3dbeff0dd6e97f5f2da884cb9151a73303e08b9888f60a6
3
+ metadata.gz: f5a986bfc55b57eb310d22891427f8d26473f34f9704d639f164cdba1e2318e7
4
+ data.tar.gz: 88fada6e7d1fd07ab3be7c2383c48984b38efa478721fd23d966a36b4fc3677c
5
5
  SHA512:
6
- metadata.gz: 41c27065b1bba5907eab6b00fc76799fa17f89a0d9e292e58b9eff2a1a06f8813a737c08ca5b13e5c8aad62a60442f8135c56df1b301b205ad6aeabe8f92189d
7
- data.tar.gz: df7cc1e6293f27135cbfa25f0aed776afd637f62128182c1caaf961b08189ffc322f1e12beb8e572e68ab1624808a6549803559582d87d4a6870d52388054df4
6
+ metadata.gz: c7c7294f4b8731a06a3dfd5649391f53526181d3e5d18333534e450d531a56206e9ff19cf7a91ef76238955c9c994c19acf10128d1ac12ec37ea17f7bfc5ca2d
7
+ data.tar.gz: 374f040438a17db333211d852a5e0e742d9eedd067deebfdbed5bc7f4a6a1c6898451ca31c7ab7d61d666723fcfdfd5e735440d846113d0e9a4fe6864983c526
@@ -0,0 +1,38 @@
1
+ version: 2.0
2
+ shared: &shared
3
+ steps:
4
+ - checkout
5
+ - run:
6
+ name: Bundle gems
7
+ command: |
8
+ echo '--no-rdoc --no-ri' > '.gemrc'
9
+ bundle install --jobs=4 --retry=3 --path vendor/bundle
10
+ - run:
11
+ name: Run tests
12
+ command: bundle exec rspec --format documentation --color --format progress spec
13
+
14
+ jobs:
15
+ "ruby-2.4":
16
+ <<: *shared
17
+ docker:
18
+ - image: circleci/ruby:2.4
19
+
20
+ "ruby-2.5":
21
+ <<: *shared
22
+ docker:
23
+ - image: circleci/ruby:2.5
24
+
25
+ "ruby-2.6":
26
+ <<: *shared
27
+ docker:
28
+ - image: circleci/ruby:2.6
29
+ environment:
30
+ REPORT_COVERAGE: 'true'
31
+
32
+ workflows:
33
+ version: 2
34
+ build:
35
+ jobs:
36
+ - "ruby-2.4"
37
+ - "ruby-2.5"
38
+ - "ruby-2.6"
@@ -1,4 +1,26 @@
1
- ## [0.9.0] - 2019-04-01
1
+ ## [0.11.2] - 2020-07-22
2
+ ### Changed
3
+ - Improve `from:` option for `:fetch_model` step, at `:sequel_models` plugin, to also accept a Sequel Dataset
4
+
5
+ ## [0.11.1] - 2020-01-09
6
+ ### Changed
7
+ - Improve custom `rspec` matchers for testing field presence on schemas
8
+
9
+ ## [0.11.0] - 2020-01-02
10
+ ### Changed
11
+ - Add support for `dry-validation` 1.0 and above
12
+
13
+ ## [0.10.0] - 2019-10-06
14
+ ### Changed
15
+ - Restrict support for `dry-validation` from 0.11.0 up to (excluding) 1.0.0
16
+ - Changed behavior for `:transaction` step wrapper, on `:sequel_models` plugin, to allow to take a single step name instead of block.
17
+ - Changed behavior for `:after_commit` step wrapper, on `:sequel_models` plugin, to allow to take a single step name instead of block.
18
+
19
+ ## [0.9.1] - 2019-02-18
20
+ ### Changed
21
+ - Various improvements on documentation and gemspec.
22
+
23
+ ## [0.9.0] - 2019-02-04
2
24
  ### Changed
3
25
  - Changed behavior for `:after_commit` step wrapper, on `:sequel_models` plugin, to capture current state and reuse it later when executing.
4
26
 
data/Gemfile CHANGED
@@ -1,5 +1,3 @@
1
- ruby '2.5.1'
2
-
3
1
  source "https://rubygems.org"
4
2
 
5
3
  # Specify your gem's dependencies in pathway.gemspec
data/README.md CHANGED
@@ -91,36 +91,37 @@ end
91
91
  #### Error objects
92
92
 
93
93
  `Pathway::Error` is a helper class to represent the error description from an failed operation execution (and can be used also for pattern matching as we'll see later).
94
- It's use is completely optional, but provides you with a basic schema to communicate what when wrong. You can instantiate it by calling `new` on the class itself or using the helper method `error` provided in the operation class:
94
+ Its use is completely optional, but provides you with a basic schema to communicate what when wrong. You can instantiate it by calling `new` on the class itself or using the helper method `error` provided by the operation class:
95
95
 
96
96
  ```ruby
97
97
  class CreateNugget < Pathway::Operation
98
98
  def call(input)
99
- validation = Form.call(input)
99
+ validation = Validator.call(input)
100
100
 
101
101
  if validation.ok?
102
102
  success(Nugget.create(validation.values))
103
103
  else
104
- error(type: :validation, message: 'Invalid input', details: validation.errors)
104
+ error(:validation, message: 'Invalid input', details: validation.errors)
105
105
  end
106
106
  end
107
107
  end
108
108
  ```
109
109
 
110
- As you can see `error(...)` expects `type:`, `message:` and `details` keyword arguments; `type:` is the only mandatory, the other ones can be omitted and have default values. Also `type` should be a `Symbol`, `message:` a `String` and `details:` can be a `Hash` or any other structure you see fit.
110
+ As you can see `error(...)` expects the `type` as the first parameter (and only the mandatory) then `message:` and `details` keyword arguments; these 2 last ones can be omitted and have default values. The type parameter must be a `Symbol`, `message:` a `String` and `details:` can be a `Hash` or any other structure you see fit.
111
111
 
112
- You then have accessors available on the error object to get the values back:
112
+ Finally, the `Error` object have three accessors available to get the values back:
113
113
 
114
114
  ```ruby
115
115
  result = CreateNugget.new.call(foo: 'foobar')
116
116
  if result.failure?
117
117
  puts "#{result.error.type} error: #{result.error.message}"
118
+ puts "Error details: #{result.error.details}"
118
119
  end
119
120
 
120
121
  ```
121
122
 
122
123
  Mind you, `error(...)` creates an `Error` object wrapped into a `Pathway::Failure` so you don't have to do it yourself.
123
- If you decide to use `Pathway::Error.new(...)` directly, the expected arguments will be the same, but you will have to wrap the object before returning it.
124
+ If you decide to use `Pathway::Error.new(...)` directly, you will have to pass all the arguments as keywords (including `type:`), and you will have to wrap the object before returning it.
124
125
 
125
126
  #### Initialization context
126
127
 
@@ -135,7 +136,7 @@ class CreateNugget < Pathway::Operation
135
136
  context :current_user, notify: false
136
137
 
137
138
  def call(input)
138
- validation = Form.call(input)
139
+ validation = Validator.call(input)
139
140
 
140
141
  if validation.valid?
141
142
  nugget = Nugget.create(owner: current_user, **validation.values)
@@ -143,7 +144,7 @@ class CreateNugget < Pathway::Operation
143
144
  Notifier.notify(:new_nugget, nugget) if @notify
144
145
  success(nugget)
145
146
  else
146
- error(type: :validation, message: 'Invalid input', details: validation.errors)
147
+ error(:validation, message: 'Invalid input', details: validation.errors)
147
148
  end
148
149
  end
149
150
  end
@@ -266,7 +267,7 @@ class CreateNugget < Pathway::Operation
266
267
  if validation.ok?
267
268
  state[:params] = validation.values
268
269
  else
269
- error(type: :validation, details: validation.errors)
270
+ error(:validation, details: validation.errors)
270
271
  end
271
272
  end
272
273
 
@@ -313,8 +314,8 @@ class SomeOperation < BaseOperation
313
314
  end
314
315
  ```
315
316
 
316
- The plugin name must be specified as a `Symbol` (or also as the `Module` where is implemented, but more on that later), and can it take parameters next to the plugin's name.
317
- When activated it will enrich your operations with new instance and class methods plus extra customs step for the process DSL.
317
+ The plugin name must be specified as a `Symbol` (or also as the `Module` where is implemented, but more on that later), and it can take parameters next to the plugin's name.
318
+ When activated it will enrich your operations with new instance and class methods plus extra customs step for the `process` DSL.
318
319
 
319
320
  Mind you, if you wish to activate a plugin for a number of operations you can activate it for all of them directly on the `Pathway::Operation` class, or you can create your own base operation and all its descendants will inherit the base class' plugins.
320
321
 
@@ -322,21 +323,23 @@ Mind you, if you wish to activate a plugin for a number of operations you can ac
322
323
 
323
324
  This plugin provides integration with the [dry-validation](http://dry-rb.org/gems/dry-validation/) gem. I won't explain in detail how to use this library since is already extensively documented on its official website, but instead I'll assume certain knowledge of it, nonetheless, as you'll see in a moment, its API pretty self-explanatory.
324
325
 
325
- `dry-validation` provides a very simple way to define form (or schema) objects to process and validate our input. The provided custom `:validate` step allows you to run your input though a form to check your data is valid before continuing. When the input is invalid it will return an error object with type `:validation` and the reasons the validation failed on the `details` attribute. Is commonly the first step any operation runs.
326
+ `dry-validation` provides a very simple way to define contract objects (conceptually very similar to form objects) to process and validate input. The provided custom `:validate` step allows you to run your input though a contract to check if your data is valid before carrying on. When the input is invalid it will return an error object of type `:validation` and the reasons the validation failed will be available at the `details` attribute. Is usually the first step an operation runs.
326
327
 
327
- When using this plugin we'll have to provide an already defined form to the step to use or we can also define one inline.
328
+ When using this plugin we can provide an already defined contract to the step to use or we can also define it within the operation.
328
329
  Let's see a few examples:
329
330
 
330
331
  ```ruby
331
- NuggetForm = Dry::Validation.Form do
332
- required(:owner).filled(:str?)
333
- required(:price).filled(:int?)
332
+ class NuggetContract < Dry::Validation::Contract
333
+ params do
334
+ required(:owner).filled(:string)
335
+ required(:price).filled(:integer)
336
+ end
334
337
  end
335
338
 
336
339
  class CreateNugget < Pathway::Operation
337
340
  plugin :dry_validation
338
341
 
339
- form NuggetForm
342
+ contract NuggetContract
340
343
 
341
344
  process do
342
345
  step :validate
@@ -347,15 +350,17 @@ class CreateNugget < Pathway::Operation
347
350
  end
348
351
  ```
349
352
 
350
- As it can be seen at the code above, the form is first created elsewhere, then is configured to be used by the operation (by calling `form NuggetForm`), and validate the input at the process block by calling `step :validate`.
353
+ As is is shown above, the contract is defined first, then is configured it will be used by the operation by calling `contract NuggetContract`, and validate the input at the process block by placing the step `step :validate` inside the `process` block.
351
354
 
352
355
  ```ruby
353
356
  class CreateNugget < Pathway::Operation
354
357
  plugin :dry_validation
355
358
 
356
- form do
357
- required(:owner).filled(:str?)
358
- required(:price).filled(:int?)
359
+ contract do
360
+ params do
361
+ required(:owner).filled(:string)
362
+ required(:price).filled(:integer)
363
+ end
359
364
  end
360
365
 
361
366
  process do
@@ -367,16 +372,36 @@ class CreateNugget < Pathway::Operation
367
372
  end
368
373
  ```
369
374
 
370
- Now, this second example is equivalent to the first one, but here we call `form` with a block instead and no parameter; this block will be use as definition body for a form object that will be stored internally. Thus keeping the form and operation at the same place, this is convenient when you have a rather simpler form and don't need to reuse it.
375
+ Now, this second example is equivalent to the first one, but here we call `contract` with a block instead and no parameter; this block will be used as definition body for a contract class that will be stored internally. Thus keeping the contract and operation code at the same place, this is convenient when you have a rather simpler contract and don't need to reuse it.
376
+
377
+ One interesting nuance to keep in mind regarding the inline block contract is that, when doing operation inheritance, if the parent operation already has a contract, the child operation will define a new one inheriting from the parent's. This is very useful to share validation logic among related operations in the same class hierarchy.
378
+
379
+ As a side note, if your contract is simple enough and only have params, you can call the `params` method directly instead, the following code is essentially equivalent to previous example:
380
+
381
+ ```ruby
382
+ class CreateNugget < Pathway::Operation
383
+ plugin :dry_validation
384
+
385
+ params do
386
+ required(:owner).filled(:string)
387
+ required(:price).filled(:integer)
388
+ end
389
+
390
+ process do
391
+ step :validate
392
+ step :create_nugget
393
+ end
371
394
 
372
- One interesting nuance to keep in mind regarding the inline block form is that, when doing operation inheritance, if the parent operation already has a form, the child operation will define a new one extending from the parent's. This is very useful to share form functionality among related operations in the same class hierarchy.
395
+ # ...
396
+ end
397
+ ```
373
398
 
374
- ##### Form options
399
+ ##### Contract options
375
400
 
376
- If you are familiar with `dry-validation` you probably know it provides a way to [inject options](http://dry-rb.org/gems/dry-validation/basics/working-with-schemas/#injecting-external-dependencies) before calling the form instance.
401
+ If you are familiar with `dry-validation` you probably know it provides a way to [inject options](https://dry-rb.org/gems/dry-validation/1.4/external-dependencies/) before calling the contract.
377
402
 
378
- On those scenarios you must either use the `auto_wire_options: true` plugin argument, or specify how to map options from the execution state to the form when calling `step :validate`.
379
- Lets see and example for each case:
403
+ On those scenarios you must either use the `auto_wire_options: true` plugin argument, or specify how to map options from the execution state to the contract when calling `step :validate`.
404
+ Lets see and example for the first case:
380
405
 
381
406
  ```ruby
382
407
  class CreateNugget < Pathway::Operation
@@ -384,11 +409,17 @@ class CreateNugget < Pathway::Operation
384
409
 
385
410
  context :user_name
386
411
 
387
- form do
388
- configure { option :user_name }
412
+ contract do
413
+ option :user_name
389
414
 
390
- required(:owner).filled(:str?, :eql?: user_name)
391
- required(:price).filled(:int?)
415
+ params do
416
+ required(:owner).filled(:string)
417
+ required(:price).filled(:integer)
418
+ end
419
+
420
+ rule(:owner) do
421
+ key.failure("invalid owner") unless user_name == values[:owner]
422
+ end
392
423
  end
393
424
 
394
425
  process do
@@ -400,9 +431,11 @@ class CreateNugget < Pathway::Operation
400
431
  end
401
432
  ```
402
433
 
403
- Here the defined form needs a `:user_name` option, so we tell the operation to grab the attribute with the same name from the state by activating `:auto_wire_options`, afterwards, when the validation runs, the form will already have the user name available.
434
+ Here the defined contract needs a `:user_name` option, so we tell the operation to grab the attribute with the same name from the state by activating `:auto_wire_options`, afterwards, when the validation runs, the contract will already have the user name available.
404
435
 
405
- Mind you, this option is `false` by default, so be sure to set it to `true` at `Pathway::Operation` if you'd rather have it for all your operations.
436
+ Mind you, this option is `false` by default, so be sure to set it to `true` at `Pathway::Operation` if you'd rather have it enabled for all your operations.
437
+
438
+ On the other hand, if for some reason the name of the contract's option and state attribute don't match, we can just pass `with: {...}` when calling to `step :validate`, indicating how to wire the attributes, the following example illustrates just that:
406
439
 
407
440
  ```ruby
408
441
  class CreateNugget < Pathway::Operation
@@ -410,15 +443,21 @@ class CreateNugget < Pathway::Operation
410
443
 
411
444
  context :current_user_name
412
445
 
413
- form do
414
- configure { option :user_name }
446
+ contract do
447
+ option :user_name
448
+
449
+ params do
450
+ required(:owner).filled(:string)
451
+ required(:price).filled(:integer)
452
+ end
415
453
 
416
- required(:owner).filled(:str?, :eql?: user_name)
417
- required(:price).filled(:int?)
454
+ rule(:owner) do
455
+ key.failure("invalid owner") unless user_name == values[:owner]
456
+ end
418
457
  end
419
458
 
420
459
  process do
421
- step :validate, with: { user_name: :current_user_name } # Inject :user_name to the form object with the state's :current_user_name
460
+ step :validate, with: { user_name: :current_user_name } # Inject :user_name to the contract object with the state's :current_user_name
422
461
  step :create_nugget
423
462
  end
424
463
 
@@ -426,10 +465,12 @@ class CreateNugget < Pathway::Operation
426
465
  end
427
466
  ```
428
467
 
429
- On the other hand, if for some reason the name of the form's option and state attribute don't match, we can just pass `with: {...}` when calling to `step :validate`, indicating how to wire the attributes, the example above illustrates just that.
430
-
431
468
  The `with:` parameter can always be specified, at `step :validate`, and allows you to override the default mapping regardless if auto-wiring is active or not.
432
469
 
470
+ ##### Older versions of `dry-validation`
471
+
472
+ Pathway supports the `dry-validation` gem down to version `0.11` (inclusive) in case you still have unmigrated code. When using versions bellow `1.0` the concept of contract is not present and instead of calling the `contract` method to setup your validation logic you must use the `form` method. Everything else remains the same except, obviously, that you would have to use `dry-definition`'s [old API](https://dry-rb.org/gems/dry-validation/0.13/) which is a bit different from the current one.
473
+
433
474
  #### `SimpleAuth` plugin
434
475
 
435
476
  This very simple plugin adds a custom step called `:authorize`, that can be used to check for permissions and halt the operation with a `:forbidden` error when they aren't fulfilled.
@@ -599,7 +640,7 @@ module Pathway
599
640
  module InstanceMethods
600
641
  delegate :model, :pk, to: :class
601
642
 
602
- # This plugin will conflict will :sequel_models so you mustn't load them in the same operation
643
+ # This method will conflict with :sequel_models so you mustn't load both plugins in the same operation
603
644
  def fetch_model(state, column: pk)
604
645
  current_pk = state[:input][column]
605
646
  result = model.first(column => current_pk)
@@ -613,7 +654,7 @@ module Pathway
613
654
  end
614
655
 
615
656
  module DSLMethods
616
- # This method also conflicts with :sequel_models, so don't use them as once.
657
+ # This method also conflicts with :sequel_models, so don't use them at once
617
658
  def transaction(&steps)
618
659
  transactional_seq = -> seq, _state do
619
660
  ActiveRecord::Base.transaction do
@@ -635,10 +676,10 @@ end
635
676
  ```
636
677
 
637
678
  The code above implements a plugin to provide basic interaction with the [ActiveRecord](http://guides.rubyonrails.org/active_record_basics.html) gem.
638
- Even though is a very simply plugin, it shows all the essentials to develop more complex plugin.
679
+ Even though is a very simple plugin, it shows all the essentials to develop more complex ones.
639
680
 
640
- First as is pointed out in the code, some of the methods implemented here (`fetch_model` and `transmission`) collide with methods defined for the `:sequel_models`, so as a consequence these two plugin's are not compatible with each other an cannot be activated at the same time on the same operation (although you can still do it for different operation within the same application).
641
- You must be mindful about colliding method names when mixing plugins, since `Pathway` can't book keep compatibility among every plugin that exists of will ever exist.
681
+ As is pointed out in the code, some of the methods implemented here (`fetch_model` and `transmission`) collide with methods defined for `:sequel_models`, so as a consequence, these two plugin's are not compatible with each other and cannot be activated for the same operation (although you can still do it for different operations within the same application).
682
+ You must be mindful about colliding method names when mixing plugins, since `Pathway` can't bookkeep compatibility among every plugin that exists of will ever exist.
642
683
  Is a good practice to document known incompatibilities on the plugin definition itself when they are known.
643
684
 
644
685
  The whole plugin is completely defined within the `ActiveRecord` module inside the `Pathway::Plugins` namespace, also the file is placed at the load path in `pathway/plugin/active_record.rb` (assuming `lib/` is listed in `$LOAD_PATH`). This will ensure, when calling `plugin :active_record` inside an operation, the correct file will be loaded and the correct plugin module will be applied to the current operation.
@@ -673,7 +714,7 @@ require 'pathway/rspec'
673
714
 
674
715
  #### Rspec matchers
675
716
 
676
- Pathway provide a few matchers in order to tests your operation easier.
717
+ Pathway provides a few matchers in order to tests your operation easier.
677
718
  Let's go through a full example and break it up in the following subsections:
678
719
 
679
720
  ```ruby
@@ -682,10 +723,10 @@ Let's go through a full example and break it up in the following subsections:
682
723
  class CreateNugget < Pathway::Operation
683
724
  plugin :dry_validation
684
725
 
685
- form do
686
- required(:owner).filled(:str?)
687
- required(:price).filled(:int?)
688
- optional(:disabled).maybe(:bool?)
726
+ params do
727
+ required(:owner).filled(:string)
728
+ required(:price).filled(:integer)
729
+ optional(:disabled).maybe(:bool)
689
730
  end
690
731
 
691
732
  process do
@@ -721,8 +762,8 @@ describe CreateNugget do
721
762
  end
722
763
  end
723
764
 
724
- describe '.form' do
725
- subject(:form) { CreateNugget.form }
765
+ describe '.contract' do
766
+ subject(:contract) { CreateNugget.build_contract }
726
767
 
727
768
  it { is_expected.to require_fields(:owner, :price) }
728
769
  it { is_expected.to accept_optional_field(:disabled) }
@@ -739,13 +780,13 @@ The assertion it performs is simply is that the operation was successful, also y
739
780
 
740
781
  This second matcher is analog to `succeed_on` but it asserts that operation execution was a failure instead. Also if you return an error object, and you need to, you can assert the error type using the `type` chain method (aliased as `and_type` and `with_type`); the error message (`and_message`, `with_message` or `message`); and the error details (`and_details`, `with_details` or `details`). Mind you, the chain methods for the message and details accept nested matchers while the `type` chain can only test by equality.
741
782
 
742
- ##### form matchers
783
+ ##### contract/form matchers
743
784
 
744
- Finally we can see that we are also testing the operation's form, implemented here with the `dry-validation` gem.
785
+ Finally we can see that we are also testing the operation's contract (or form), implemented here with the `dry-validation` gem.
745
786
 
746
- Two more matchers are provided when we use this gem: `require_fields` (aliased `require_field`) to test when form is expected to define a required set of fields, and `accept_optional_fields` (aliased `accept_optional_field`) to test when a form must define certain set of optional fields.
787
+ Two more matchers are provided: `require_fields` (aliased `require_field`) to test when a contract is expected to define a required set of fields, and `accept_optional_fields` (aliased `accept_optional_field`) to test when a contract must define a certain set of optional fields, both the contract class (at operation class method `contract_class`) or an instance (operation class method `build_contract`) can be provided.
747
788
 
748
- These matchers are only useful when using `dry-validation` and will very likely be extracted to its own gem in the future.
789
+ These matchers are only useful when using `dry-validation` (on every version newer or equal to `0.11.0`) and will probably be extracted to their own gem in the future.
749
790
 
750
791
  ## Development
751
792
 
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rspec/core/rake_task"
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
  require 'dry/inflector'
3
5
  require 'contextualizer'
@@ -107,7 +109,7 @@ module Pathway
107
109
  alias :wrap :result
108
110
 
109
111
  def call(*)
110
- fail "must implement at subclass"
112
+ fail 'must implement at subclass'
111
113
  end
112
114
 
113
115
  def error(type, message: nil, details: nil)
@@ -1,99 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/validation'
2
4
 
3
5
  module Pathway
4
6
  module Plugins
5
7
  module DryValidation
6
- module ClassMethods
7
- attr_reader :form_class, :form_options
8
- attr_accessor :auto_wire_options
9
-
10
- def form(base = nil, **opts, &block)
11
- if block_given?
12
- base ||= _base_form
13
- self.form_class = _block_definition(base, opts, &block)
14
- elsif base
15
- self.form_class = _form_class(base)
16
- else
17
- raise ArgumentError, 'Either a form class or a block must be provided'
18
- end
19
- end
20
-
21
- def form_class= klass
22
- @builded_form = klass.options.empty? ? klass.new : nil
23
- @form_class = klass
24
- @form_options = klass.options.keys
25
- end
26
-
27
- def build_form(opts = {})
28
- @builded_form || form_class.new(opts)
29
- end
30
-
31
- def inherited(subclass)
32
- super
33
- subclass.form_class = form_class
34
- subclass.auto_wire_options = auto_wire_options
35
- end
36
-
37
- private
38
-
39
- def _base_form
40
- superclass.respond_to?(:form_class) ? superclass.form_class : DefaultFormClass
41
- end
42
-
43
- def _form_class(form)
44
- form.is_a?(Class) ? form : form.class
45
- end
46
-
47
- def _form_opts(opts = {})
48
- opts.merge(build: false)
49
- end
50
- end
51
-
52
- module InstanceMethods
53
- extend Forwardable
54
-
55
- delegate %i[build_form form_options auto_wire_options] => 'self.class'
56
- alias :form :build_form
57
-
58
- def validate(state, with: nil)
59
- if auto_wire_options && form_options.any?
60
- with ||= form_options.zip(form_options).to_h
61
- end
62
- opts = Hash(with).map { |opt, key| [opt, state[key]] }.to_h
63
- validate_with(state[:input], opts)
64
- .then { |params| state.update(params: params) }
65
- end
66
-
67
- def validate_with(params, opts = {})
68
- val = form(opts).call(params)
69
-
70
- val.success? ? wrap(val.output) : error(:validation, details: val.messages)
71
- end
72
- end
73
-
74
- def self.apply(operation, auto_wire_options: false)
75
- operation.form_class = DefaultFormClass
76
- operation.auto_wire_options = auto_wire_options
77
- end
78
-
79
- if Gem.loaded_specs['dry-validation'].version >= Gem::Version.new('0.12')
80
- DefaultFormClass = Dry::Validation::Schema::Params
81
-
82
- module ClassMethods
83
- private
84
- def _block_definition(base, opts, &block)
85
- Dry::Validation.Params(_form_class(base), _form_opts(opts), &block)
86
- end
87
- end
88
- else
89
- DefaultFormClass = Dry::Validation::Schema::Form
90
-
91
- module ClassMethods
92
- private
93
- def _block_definition(base, opts, &block)
94
- Dry::Validation.Form(_form_class(base), _form_opts(opts), &block)
95
- end
96
- end
8
+ def self.apply(operation, **kwargs)
9
+ #:nocov:
10
+ if Gem.loaded_specs['dry-validation'].version < Gem::Version.new('0.11')
11
+ fail 'unsupported dry-validation gem version'
12
+ elsif Gem.loaded_specs['dry-validation'].version < Gem::Version.new('0.12')
13
+ require 'pathway/plugins/dry_validation/v0_11'
14
+ operation.plugin(Plugins::DryValidation::V0_11, **kwargs)
15
+ elsif Gem.loaded_specs['dry-validation'].version < Gem::Version.new('1.0')
16
+ require 'pathway/plugins/dry_validation/v0_12'
17
+ operation.plugin(Plugins::DryValidation::V0_12, **kwargs)
18
+ else
19
+ require 'pathway/plugins/dry_validation/v1_0'
20
+ operation.plugin(Plugins::DryValidation::V1_0, **kwargs)
21
+ end
22
+ #:nocov:
97
23
  end
98
24
  end
99
25
  end