pathway 0.11.1 → 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: bb77df296a81bb4d0d277c4c802e990727f58e9c6fd5dee20ca3e0986aa954f2
4
- data.tar.gz: 6921ad015f25c29bf669d815b86518eba9c11938da78f8f37aec9ab6d9a3ed4f
3
+ metadata.gz: f5a986bfc55b57eb310d22891427f8d26473f34f9704d639f164cdba1e2318e7
4
+ data.tar.gz: 88fada6e7d1fd07ab3be7c2383c48984b38efa478721fd23d966a36b4fc3677c
5
5
  SHA512:
6
- metadata.gz: 1f1d59e04131f194731a6890aa40cf498df3bd15fbe65818ed6e90b43e3a96a1ef94d647737bd94fa8e1e43eef3583a3bcc04df03c7e45ea155f7e6f998610a1
7
- data.tar.gz: 222112d0f9a8a7c23499afa2d656f961fdef7e32df3de6c165e55fb81357d2db27605ea3882d1d7d2c1886165f25b27d9d272d6c493cd5fd5bfe5894ff25ae18
6
+ metadata.gz: c7c7294f4b8731a06a3dfd5649391f53526181d3e5d18333534e450d531a56206e9ff19cf7a91ef76238955c9c994c19acf10128d1ac12ec37ea17f7bfc5ca2d
7
+ data.tar.gz: 374f040438a17db333211d852a5e0e742d9eedd067deebfdbed5bc7f4a6a1c6898451ca31c7ab7d61d666723fcfdfd5e735440d846113d0e9a4fe6864983c526
@@ -1,3 +1,7 @@
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
+
1
5
  ## [0.11.1] - 2020-01-09
2
6
  ### Changed
3
7
  - Improve custom `rspec` matchers for testing field presence on schemas
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,25 +431,33 @@ 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
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.
406
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:
439
+
407
440
  ```ruby
408
441
  class CreateNugget < Pathway::Operation
409
442
  plugin :dry_validation
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.
@@ -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
 
@@ -63,10 +63,13 @@ module Pathway
63
63
  delegate :db => :model_class
64
64
 
65
65
  def fetch_model(state, from: model_class, search_by: search_field, using: search_by, to: result_key, overwrite: false, error_message: nil)
66
- error_message ||= if from != model_class
67
- Inflector.humanize(Inflector.underscore(Inflector.demodulize(from.name))) + ' not found'
68
- else
66
+ error_message ||= if (from == model_class)
69
67
  model_not_found
68
+ elsif from.respond_to?(:name) || from.respond_to?(:model)
69
+ from_name = (from.respond_to?(:name) ? from : from.model).name
70
+ Inflector.humanize(Inflector.underscore(Inflector.demodulize(from_name))) + ' not found'
71
+ else
72
+ 'Register not found'
70
73
  end
71
74
 
72
75
  if state[to].nil? || overwrite
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pathway
4
- VERSION = '0.11.1'
4
+ VERSION = '0.11.2'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pathway
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.1
4
+ version: 0.11.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pablo Herrero
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-01-09 00:00:00.000000000 Z
11
+ date: 2020-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-inflector
@@ -225,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
225
225
  - !ruby/object:Gem::Version
226
226
  version: '0'
227
227
  requirements: []
228
- rubygems_version: 3.0.6
228
+ rubygems_version: 3.0.8
229
229
  signing_key:
230
230
  specification_version: 4
231
231
  summary: Define your business logic in simple steps.