pathway 0.11.1 → 0.12.0

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: 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"