pathway 0.11.1 → 0.12.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb77df296a81bb4d0d277c4c802e990727f58e9c6fd5dee20ca3e0986aa954f2
4
- data.tar.gz: 6921ad015f25c29bf669d815b86518eba9c11938da78f8f37aec9ab6d9a3ed4f
3
+ metadata.gz: a2a1f9207d8b2db81dbf655234010e4cb8c622299cb69c1dfaa9486f824a7e86
4
+ data.tar.gz: 25e380e764a1b1585b86ae847b2cbc107a83fa23f1a1205e4e1e5e1a2c021c50
5
5
  SHA512:
6
- metadata.gz: 1f1d59e04131f194731a6890aa40cf498df3bd15fbe65818ed6e90b43e3a96a1ef94d647737bd94fa8e1e43eef3583a3bcc04df03c7e45ea155f7e6f998610a1
7
- data.tar.gz: 222112d0f9a8a7c23499afa2d656f961fdef7e32df3de6c165e55fb81357d2db27605ea3882d1d7d2c1886165f25b27d9d272d6c493cd5fd5bfe5894ff25ae18
6
+ metadata.gz: 393014838fece86b27409c08ef0f5ccb9d7dacf5f5012d956bbf0e7ca3869175e4090c6dd23cf6c8f61bfcecd15839a738b87afcf911c226a06ea36e8cb91dc3
7
+ data.tar.gz: 1dd63c594d299837fb1b595f14fd2056ac39f81fdd508254c331fb9ef4b3a67262e0f47683bb5e0296708076d88c12aef0dd981c8864391d43f0bc6083ad9422
@@ -0,0 +1,28 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ ruby-version: [2.6, 2.7, 3.0, 3.1]
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+ - name: Set up Ruby
18
+ uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: ${{ matrix.ruby-version }}
21
+ bundler-cache: true
22
+ - name: Run tests
23
+ run: bundle exec rake
24
+ - name: Coveralls GitHub Action
25
+ if: matrix.ruby-version == '3.1'
26
+ uses: coverallsapp/github-action@1.1.3
27
+ with:
28
+ github-token: ${{ secrets.GITHUB_TOKEN }}
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## [0.12.0] - 2022-05-31
2
+ ### Changed
3
+ - Improve compatibility with Ruby 3.0
4
+ - Add plugin `:auto_deconstruct_state` to help migrating old apps to Ruby 3.0
5
+
6
+ ## [0.11.3] - 2020-07-22
7
+ ### Changed
8
+ - Use default error message on `:fetch_model` step, at `:sequel_models` plugin, if model type cannot be determined
9
+
10
+ ## [0.11.2] - 2020-07-22
11
+ ### Changed
12
+ - Improve `from:` option for `:fetch_model` step, at `:sequel_models` plugin, to also accept a Sequel Dataset
13
+
1
14
  ## [0.11.1] - 2020-01-09
2
15
  ### Changed
3
16
  - Improve custom `rspec` matchers for testing field presence on schemas
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Pathway
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/pathway.svg)](https://badge.fury.io/rb/pathway)
4
- [![CircleCI](https://circleci.com/gh/pabloh/pathway/tree/master.svg?style=shield)](https://circleci.com/gh/pabloh/pathway/tree/master)
4
+ [![Tests](https://github.com/pabloh/pathway/workflows/Tests/badge.svg)](https://github.com/oabloh/pathway/actions?query=workflow%3ATests)
5
5
  [![Coverage Status](https://coveralls.io/repos/github/pabloh/pathway/badge.svg?branch=master)](https://coveralls.io/github/pabloh/pathway?branch=master)
6
6
 
7
7
  Pathway encapsulates your business logic into simple operation objects (AKA application services on the [DDD](https://en.wikipedia.org/wiki/Domain-driven_design) lingo).
@@ -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,17 +267,17 @@ 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
 
273
274
  def create_nugget(:params, **)
274
275
  Nugget.create(owner: current_user, **params)
275
- def
276
+ end
276
277
 
277
278
  def notify(:nugget, **)
278
279
  Notifier.notify(:new_nugget, nugget)
279
- else
280
+ end
280
281
  end
281
282
  ```
282
283
 
@@ -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
414
+
415
+ params do
416
+ required(:owner).filled(:string)
417
+ required(:price).filled(:integer)
418
+ end
389
419
 
390
- required(:owner).filled(:str?, :eql?: user_name)
391
- required(:price).filled(:int?)
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
 
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pathway
4
+ module Plugins
5
+ module AutoDeconstructState
6
+ module DSLMethods
7
+ private
8
+
9
+ def _callable(callable)
10
+ if callable.is_a?(Symbol) && @operation.respond_to?(callable, true) &&
11
+ @operation.method(callable).parameters.all? { _1 in [:key|:keyreq|:keyrest|:block,*] }
12
+
13
+ -> state { @operation.send(callable, **state) }
14
+ else
15
+ super
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ if RUBY_VERSION =~ /^3\./
4
+ require 'pathway/plugins/auto_deconstruct_state/ruby3'
5
+ end
6
+
7
+ module Pathway
8
+ module Plugins
9
+ module AutoDeconstructState
10
+ end
11
+ end
12
+ end
@@ -19,7 +19,7 @@ module Pathway
19
19
  end
20
20
  end
21
21
 
22
- def params(*args, &block)
22
+ ruby2_keywords def params(*args, &block)
23
23
  contract { params(*args, &block) }
24
24
  end
25
25
 
@@ -29,8 +29,8 @@ module Pathway
29
29
  @builded_contract = @contract_options.empty? && klass.schema ? klass.new : nil
30
30
  end
31
31
 
32
- def build_contract(opts = {})
33
- @builded_contract || contract_class.new(opts)
32
+ def build_contract(**opts)
33
+ @builded_contract || contract_class.new(**opts)
34
34
  end
35
35
 
36
36
  def inherited(subclass)
@@ -57,12 +57,12 @@ module Pathway
57
57
  with ||= contract_options.zip(contract_options).to_h
58
58
  end
59
59
  opts = Hash(with).map { |to, from| [to, state[from]] }.to_h
60
- validate_with(state[:input], opts)
60
+ validate_with(state[:input], **opts)
61
61
  .then { |params| state.update(params: params) }
62
62
  end
63
63
 
64
- def validate_with(input, opts = {})
65
- result = contract(opts).call(input)
64
+ def validate_with(input, **opts)
65
+ result = contract(**opts).call(input)
66
66
 
67
67
  result.success? ? wrap(result.values.to_h) : error(:validation, details: result.errors.to_h)
68
68
  end
@@ -4,8 +4,8 @@ module Pathway
4
4
  module Plugins
5
5
  module Responder
6
6
  module ClassMethods
7
- def call(ctx, *params, &bl)
8
- result = super(ctx, *params)
7
+ ruby2_keywords def call(*args, &bl)
8
+ result = super(*args)
9
9
  block_given? ? Responder.respond(result, &bl) : result
10
10
  end
11
11
  end
@@ -63,10 +63,11 @@ 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'
70
71
  end
71
72
 
72
73
  if state[to].nil? || overwrite
@@ -83,8 +84,8 @@ module Pathway
83
84
  end
84
85
  end
85
86
 
86
- def self.apply(operation, model: nil, **args)
87
- operation.model(model, args) if model
87
+ def self.apply(operation, model: nil, **kwargs)
88
+ operation.model(model, **kwargs) if model
88
89
  end
89
90
  end
90
91
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pathway
4
- VERSION = '0.11.1'
4
+ VERSION = '0.12.0'
5
5
  end
data/lib/pathway.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'ruby2_keywords'
3
4
  require 'forwardable'
4
5
  require 'dry/inflector'
5
6
  require 'contextualizer'
@@ -9,21 +10,23 @@ require 'pathway/result'
9
10
  module Pathway
10
11
  Inflector = Dry::Inflector.new
11
12
  class Operation
12
- def self.plugin(name, *args)
13
- require "pathway/plugins/#{Inflector.underscore(name)}" if name.is_a?(Symbol)
13
+ class << self
14
+ ruby2_keywords def plugin(name, *args)
15
+ require "pathway/plugins/#{Inflector.underscore(name)}" if name.is_a?(Symbol)
14
16
 
15
- plugin = name.is_a?(Module) ? name : Plugins.const_get(Inflector.camelize(name))
17
+ plugin = name.is_a?(Module) ? name : Plugins.const_get(Inflector.camelize(name))
16
18
 
17
- self.extend plugin::ClassMethods if plugin.const_defined? :ClassMethods
18
- self.include plugin::InstanceMethods if plugin.const_defined? :InstanceMethods
19
- self::DSL.include plugin::DSLMethods if plugin.const_defined? :DSLMethods
19
+ self.extend plugin::ClassMethods if plugin.const_defined? :ClassMethods
20
+ self.include plugin::InstanceMethods if plugin.const_defined? :InstanceMethods
21
+ self::DSL.include plugin::DSLMethods if plugin.const_defined? :DSLMethods
20
22
 
21
- plugin.apply(self, *args) if plugin.respond_to?(:apply)
22
- end
23
+ plugin.apply(self, *args) if plugin.respond_to?(:apply)
24
+ end
23
25
 
24
- def self.inherited(subclass)
25
- super
26
- subclass.const_set :DSL, Class.new(self::DSL)
26
+ def inherited(subclass)
27
+ super
28
+ subclass.const_set :DSL, Class.new(self::DSL)
29
+ end
27
30
  end
28
31
 
29
32
  class DSL
@@ -90,7 +93,7 @@ module Pathway
90
93
  end
91
94
  end
92
95
 
93
- def call(ctx, *params)
96
+ ruby2_keywords def call(ctx, *params)
94
97
  new(ctx).call(*params)
95
98
  end
96
99
 
@@ -137,7 +140,7 @@ module Pathway
137
140
  end
138
141
 
139
142
  # Execute step and preserve the former state
140
- def step(callable, *args)
143
+ ruby2_keywords def step(callable, *args)
141
144
  bl = _callable(callable)
142
145
 
143
146
  @result = @result.tee { |state| bl.call(state, *args) }
@@ -190,9 +193,9 @@ module Pathway
190
193
  def _callable(callable)
191
194
  case callable
192
195
  when Proc
193
- -> *args { @operation.instance_exec(*args, &callable) }
196
+ -> *args { @operation.instance_exec(*args, &callable) }.ruby2_keywords
194
197
  when Symbol
195
- -> *args { @operation.send(callable, *args) }
198
+ -> *args { @operation.send(callable, *args) }.ruby2_keywords
196
199
  else
197
200
  callable
198
201
  end
data/pathway.gemspec CHANGED
@@ -31,13 +31,15 @@ Gem::Specification.new do |spec|
31
31
 
32
32
  spec.add_dependency "dry-inflector", ">= 0.1.0"
33
33
  spec.add_dependency "contextualizer", "~> 0.0.4"
34
+ spec.add_dependency "ruby2_keywords"
34
35
 
35
36
  spec.add_development_dependency "dry-validation", ">= 0.11"
36
- spec.add_development_dependency "bundler", ">= 1.14.0"
37
- spec.add_development_dependency "sequel", "~> 5.25.0"
37
+ spec.add_development_dependency "bundler", "~> 2.3.7"
38
+ spec.add_development_dependency "sequel", "~> 5.0"
38
39
  spec.add_development_dependency "rake", "~> 13.0"
39
- spec.add_development_dependency "rspec", "~> 3.0"
40
- spec.add_development_dependency "coveralls"
40
+ spec.add_development_dependency "rspec", "~> 3.11"
41
+ spec.add_development_dependency "simplecov-lcov", '~> 0.8.0'
42
+ spec.add_development_dependency "simplecov"
41
43
  spec.add_development_dependency "pry"
42
44
  spec.add_development_dependency "pry-byebug"
43
45
  spec.add_development_dependency "pry-doc"
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.12.0
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: 2022-06-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-inflector
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: 0.0.4
41
+ - !ruby/object:Gem::Dependency
42
+ name: ruby2_keywords
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: dry-validation
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -56,30 +70,30 @@ dependencies:
56
70
  name: bundler
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
- - - ">="
73
+ - - "~>"
60
74
  - !ruby/object:Gem::Version
61
- version: 1.14.0
75
+ version: 2.3.7
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
- - - ">="
80
+ - - "~>"
67
81
  - !ruby/object:Gem::Version
68
- version: 1.14.0
82
+ version: 2.3.7
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: sequel
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
87
  - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: 5.25.0
89
+ version: '5.0'
76
90
  type: :development
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
94
  - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: 5.25.0
96
+ version: '5.0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: rake
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -100,16 +114,30 @@ dependencies:
100
114
  requirements:
101
115
  - - "~>"
102
116
  - !ruby/object:Gem::Version
103
- version: '3.0'
117
+ version: '3.11'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.11'
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov-lcov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.8.0
104
132
  type: :development
105
133
  prerelease: false
106
134
  version_requirements: !ruby/object:Gem::Requirement
107
135
  requirements:
108
136
  - - "~>"
109
137
  - !ruby/object:Gem::Version
110
- version: '3.0'
138
+ version: 0.8.0
111
139
  - !ruby/object:Gem::Dependency
112
- name: coveralls
140
+ name: simplecov
113
141
  requirement: !ruby/object:Gem::Requirement
114
142
  requirements:
115
143
  - - ">="
@@ -171,7 +199,7 @@ executables: []
171
199
  extensions: []
172
200
  extra_rdoc_files: []
173
201
  files:
174
- - ".circleci/config.yml"
202
+ - ".github/workflows/tests.yml"
175
203
  - ".gitignore"
176
204
  - ".rspec"
177
205
  - CHANGELOG.md
@@ -185,6 +213,8 @@ files:
185
213
  - bin/rspec
186
214
  - bin/setup
187
215
  - lib/pathway.rb
216
+ - lib/pathway/plugins/auto_deconstruct_state.rb
217
+ - lib/pathway/plugins/auto_deconstruct_state/ruby3.rb
188
218
  - lib/pathway/plugins/dry_validation.rb
189
219
  - lib/pathway/plugins/dry_validation/v0_11.rb
190
220
  - lib/pathway/plugins/dry_validation/v0_12.rb
@@ -225,7 +255,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
225
255
  - !ruby/object:Gem::Version
226
256
  version: '0'
227
257
  requirements: []
228
- rubygems_version: 3.0.6
258
+ rubygems_version: 3.3.7
229
259
  signing_key:
230
260
  specification_version: 4
231
261
  summary: Define your business logic in simple steps.
data/.circleci/config.yml DELETED
@@ -1,38 +0,0 @@
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"