active_interaction-extras 0.1.0 → 1.0.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 +5 -5
- data/.github/workflows/ci.yml +35 -0
- data/CHANGELOG.md +20 -0
- data/README.md +203 -24
- data/Rakefile +9 -1
- data/active_interaction-extras.gemspec +10 -8
- data/lib/active_interaction/extras.rb +28 -2
- data/lib/active_interaction/extras/active_job.rb +26 -2
- data/lib/active_interaction/extras/all.rb +4 -2
- data/lib/active_interaction/extras/filter_alias.rb +18 -0
- data/lib/active_interaction/extras/filter_extensions.rb +2 -0
- data/lib/active_interaction/extras/filter_extensions/hash_auto_strip.rb +25 -0
- data/lib/active_interaction/extras/filter_extensions/object_classes.rb +47 -0
- data/lib/active_interaction/extras/filter_extensions/timezone_support.rb +16 -0
- data/lib/active_interaction/extras/filters/anything_filter.rb +9 -0
- data/lib/active_interaction/extras/filters/uuid_filter.rb +15 -0
- data/lib/active_interaction/extras/jobs/core.rb +32 -0
- data/lib/active_interaction/extras/locale/en.yml +5 -0
- data/lib/active_interaction/extras/rspec.rb +2 -0
- data/lib/active_interaction/extras/sidekiq.rb +71 -0
- data/lib/active_interaction/extras/transaction.rb +13 -19
- data/lib/active_interaction/extras/version.rb +1 -1
- metadata +34 -36
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ebddd04a27db967f1796d98c4571c9a49233cb8aeb2c0af53124804eca42813d
|
|
4
|
+
data.tar.gz: 51cd957db282cf73fcdac076a7493430e40d329d15706565af67dbf9111c604a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ce26d9609ccde4305df772131058bd3181de9736b7be5028210aef8905c9f07baa6b9ed9280a8c2f2dd9e0519c7e3cd84650786a8e2582ea6161cccf66b849c7
|
|
7
|
+
data.tar.gz: 3ee722c05e8d86fc2781fe6ac3283e662d8321d47e9c15e346fa58facde4833c92465e5705d213993b77012e6e25235a7b91be36a2e0bb8e6181d9eda62fafd8
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
|
2
|
+
# They are provided by a third-party and are governed by
|
|
3
|
+
# separate terms of service, privacy policy, and support
|
|
4
|
+
# documentation.
|
|
5
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
|
6
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
|
7
|
+
|
|
8
|
+
name: Tests
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
push:
|
|
12
|
+
branches: [ master ]
|
|
13
|
+
pull_request:
|
|
14
|
+
branches: [ master ]
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
test:
|
|
18
|
+
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
strategy:
|
|
21
|
+
matrix:
|
|
22
|
+
ruby-version: ['2.6', '2.7', '3.0']
|
|
23
|
+
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v2
|
|
26
|
+
- name: Set up Ruby
|
|
27
|
+
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
|
28
|
+
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
|
29
|
+
# uses: ruby/setup-ruby@v1
|
|
30
|
+
uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
|
|
31
|
+
with:
|
|
32
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
33
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
|
34
|
+
- name: Run tests
|
|
35
|
+
run: bundle exec rake
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
All notable changes to this project will be documented in this file.
|
|
3
|
+
This project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [1.0.0] - 2021-05-12
|
|
8
|
+
|
|
9
|
+
- Requires active_interaction v4
|
|
10
|
+
- New filters: `anything` and `uuid`
|
|
11
|
+
- New filter extensions
|
|
12
|
+
- object: support for multiple classes
|
|
13
|
+
- hash: disable auto strip when no keys are listed
|
|
14
|
+
- New extension:
|
|
15
|
+
- filter alias
|
|
16
|
+
- Changed `transaction` extension
|
|
17
|
+
- It requires new transaction by default
|
|
18
|
+
- Include order is important now
|
|
19
|
+
- Removed `active_interaction-active_job` gem dependency
|
|
20
|
+
- Added changelog
|
data/README.md
CHANGED
|
@@ -1,50 +1,116 @@
|
|
|
1
1
|
# ActiveInteraction::Extras
|
|
2
2
|
|
|
3
|
+
[](https://badge.fury.io/rb/active_interaction-extras) 
|
|
4
|
+
|
|
3
5
|
This gem contains the collection of useful extensions to [active_interaction](https://github.com/AaronLasseigne/active_interaction) gem.
|
|
4
6
|
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Basic Usage](#basic-usage)
|
|
9
|
+
- [Filters](#filters)
|
|
10
|
+
- [Anything](#anything)
|
|
11
|
+
- [UUID](#uuid)
|
|
12
|
+
- [Filter Extensions](#filter-extensions)
|
|
13
|
+
- [Hash: auto strip](#hash-auto-strip)
|
|
14
|
+
- [Object: multiple classes](#object-multiple-classes)
|
|
15
|
+
- [Extensions](#extensions)
|
|
16
|
+
- [Filter alias](#filter-alias)
|
|
17
|
+
- [Halt](#halt)
|
|
18
|
+
- [ModelFields](#modelfields)
|
|
19
|
+
- [RunCallback](#runcallback)
|
|
20
|
+
- [StrongParams](#strongparams)
|
|
21
|
+
- [Transaction](#transaction)
|
|
22
|
+
- [Jobs](#jobs)
|
|
23
|
+
- [ActiveJob](#activejob)
|
|
24
|
+
- [Sidekiq](#sidekiq)
|
|
25
|
+
- [RSpec](#rspec)
|
|
26
|
+
|
|
5
27
|
## Installation
|
|
6
28
|
|
|
7
29
|
```ruby
|
|
8
30
|
gem 'active_interaction-extras'
|
|
9
31
|
```
|
|
10
32
|
|
|
11
|
-
## Usage
|
|
12
|
-
|
|
13
|
-
### All
|
|
33
|
+
## Basic Usage
|
|
14
34
|
|
|
15
35
|
```ruby
|
|
36
|
+
# app/services/application_interaction.rb
|
|
16
37
|
class ApplicationInteraction < ActiveInteraction::Base
|
|
17
38
|
include ActiveInteraction::Extras::All
|
|
18
|
-
# same as
|
|
19
|
-
# include ActiveInteraction::Extras::ActiveJob
|
|
20
|
-
# include ActiveInteraction::Extras::Halt
|
|
21
|
-
# include ActiveInteraction::Extras::ModelFields
|
|
22
|
-
# include ActiveInteraction::Extras::RunCallback
|
|
23
|
-
# include ActiveInteraction::Extras::StrongParams
|
|
24
|
-
# include ActiveInteraction::Extras::Transaction
|
|
25
39
|
end
|
|
26
40
|
```
|
|
27
41
|
|
|
28
|
-
|
|
42
|
+
## Filters
|
|
43
|
+
|
|
44
|
+
These new filters are added automatically when gem is loaded.
|
|
45
|
+
|
|
46
|
+
### Anything
|
|
47
|
+
|
|
48
|
+
Anything filter accepts as you guest it - anything.
|
|
29
49
|
|
|
30
50
|
```ruby
|
|
31
|
-
class
|
|
32
|
-
|
|
51
|
+
class Service < ActiveInteraction::Base
|
|
52
|
+
anything :model
|
|
53
|
+
end
|
|
54
|
+
```
|
|
33
55
|
|
|
34
|
-
|
|
35
|
-
|
|
56
|
+
### UUID
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
class Service < ActiveInteraction::Base
|
|
60
|
+
uuid :id
|
|
61
|
+
end
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Filter Extensions
|
|
65
|
+
|
|
66
|
+
You can load all filter extensions with:
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
# config/initializers/active_interaction.rb
|
|
70
|
+
require 'active_interaction/extras/filter_extensions'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Hash: auto strip
|
|
74
|
+
|
|
75
|
+
This small extensions allows to accept full hashes without explicit `strip` option.
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
class Service < ActiveInteraction::Base
|
|
79
|
+
hash :options_a, strip: false # (Before) Accept all keys
|
|
80
|
+
|
|
81
|
+
hash :options_b # (After) Accept all keys
|
|
82
|
+
|
|
83
|
+
hash :options_c do # (Before and After) Accept only specified keys
|
|
84
|
+
string :name
|
|
36
85
|
end
|
|
37
86
|
end
|
|
87
|
+
```
|
|
38
88
|
|
|
39
|
-
|
|
40
|
-
|
|
89
|
+
### Object: multiple classes
|
|
90
|
+
|
|
91
|
+
This extension allows using `object` filter with multiple classes.
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
class Service < ActiveInteraction::Base
|
|
95
|
+
object :user, class: [User, AdminUser]
|
|
96
|
+
end
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
## Extensions
|
|
101
|
+
|
|
102
|
+
### Filter Alias
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
class Service < ActiveInteraction::Base
|
|
106
|
+
include ActiveInteraction::Extras::FilterAlias
|
|
107
|
+
|
|
108
|
+
hash :params, as: :user_attributes
|
|
41
109
|
|
|
42
110
|
def execute
|
|
43
|
-
|
|
111
|
+
user_attributes == params # => true
|
|
44
112
|
end
|
|
45
113
|
end
|
|
46
|
-
|
|
47
|
-
DoubleService.delay.run(x: 2) # queues to run in background
|
|
48
114
|
```
|
|
49
115
|
|
|
50
116
|
### Halt
|
|
@@ -73,7 +139,7 @@ end
|
|
|
73
139
|
class UserForm < ActiveInteraction::Base
|
|
74
140
|
include ActiveInteraction::Extras::ModelFields
|
|
75
141
|
|
|
76
|
-
|
|
142
|
+
anything :user
|
|
77
143
|
|
|
78
144
|
model_fields(:user) do
|
|
79
145
|
string :first_name
|
|
@@ -119,7 +185,6 @@ end
|
|
|
119
185
|
|
|
120
186
|
### StrongParams
|
|
121
187
|
|
|
122
|
-
|
|
123
188
|
```ruby
|
|
124
189
|
class UpdateUserForm < ActiveInteraction::Base
|
|
125
190
|
include ActiveInteraction::Extras::StrongParams
|
|
@@ -143,6 +208,14 @@ form_params = ActionController::Parameters.new(
|
|
|
143
208
|
)
|
|
144
209
|
|
|
145
210
|
Service.run(params: form_params)
|
|
211
|
+
|
|
212
|
+
# OR
|
|
213
|
+
form_params = ActionController::Parameters.new(
|
|
214
|
+
first_name: 'Allowed',
|
|
215
|
+
last_name: 'Not allowed',
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
Service.run(form_params: form_params)
|
|
146
219
|
```
|
|
147
220
|
|
|
148
221
|
### Transaction
|
|
@@ -164,7 +237,109 @@ UpdateUserForm.run
|
|
|
164
237
|
Comment.count # => 0
|
|
165
238
|
```
|
|
166
239
|
|
|
167
|
-
|
|
240
|
+
## Jobs
|
|
241
|
+
|
|
242
|
+
You no longer need to create a separate Job class for the each interaction. This Job extension automatically converts interactions to background jobs. By convention each interaction will have a nested `Job` class which will be inherited from the parent interaction `Job` class (e.g. `ApplicationInteraction::Job`).
|
|
243
|
+
|
|
244
|
+
### ActiveJob
|
|
245
|
+
|
|
246
|
+
```ruby
|
|
247
|
+
class ApplicationInteraction < ActiveInteraction::Base
|
|
248
|
+
include ActiveInteraction::Extras::ActiveJob
|
|
249
|
+
|
|
250
|
+
class Job < ActiveJob::Base
|
|
251
|
+
include ActiveInteraction::Extras::ActiveJob::Perform
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
class DoubleService < ApplicationInteraction
|
|
256
|
+
integer :x
|
|
257
|
+
|
|
258
|
+
def execute
|
|
259
|
+
x + x
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
DoubleService.delay.run(x: 2) # queues to run in background
|
|
264
|
+
DoubleService.delay(queue: 'low_priority', wait: 1.minute).run(x: 2)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
In ActiveJob mode `delay` method accepts anything ActiveJob `set` [method](https://edgeapi.rubyonrails.org/classes/ActiveJob/Core/ClassMethods.html#method-i-set) does. (`wait`, `wait_until`, `queue`, `priority`)
|
|
268
|
+
|
|
269
|
+
### Sidekiq
|
|
270
|
+
|
|
271
|
+
You can use sidekiq directly if you need more control. Sidekiq integration comes with default GlobalID support.
|
|
272
|
+
|
|
273
|
+
```ruby
|
|
274
|
+
class ApplicationInteraction < ActiveInteraction::Base
|
|
275
|
+
include ActiveInteraction::Extras::Sidekiq
|
|
276
|
+
|
|
277
|
+
class Job
|
|
278
|
+
include Sidekiq::Worker
|
|
279
|
+
include ActiveInteraction::Extras::Sidekiq::Perform
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
class DoubleService < ApplicationInteraction
|
|
284
|
+
job do
|
|
285
|
+
sidekiq_options retry: 1 # configure sidekiq options
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
integer :x
|
|
289
|
+
|
|
290
|
+
def execute
|
|
291
|
+
x + x
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
DoubleService.delay.run(x: 2) # queues to run in background
|
|
296
|
+
DoubleService.delay(queue: 'low_priority', wait: 1.minute).run(x: 2)
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
In Sidekiq mode `delay` method accepts anything sidekiq `set` [method](https://github.com/mperham/sidekiq/wiki/Advanced-Options#workers) does (`queue`, `retry`, `backtrace`, etc). Plus two additional `wait` and `wait_until`.
|
|
300
|
+
|
|
301
|
+
```ruby
|
|
302
|
+
# Advance usage: retry based on given params
|
|
303
|
+
class DoubleService < ApplicationInteraction
|
|
304
|
+
job do
|
|
305
|
+
sidekiq_options(retry: ->(job) {
|
|
306
|
+
params = deserialize_active_job_args(job)
|
|
307
|
+
params[:x]
|
|
308
|
+
})
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
integer :x
|
|
312
|
+
|
|
313
|
+
def execute
|
|
314
|
+
x + x
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
```ruby
|
|
320
|
+
# Advance usage: Rescue the job but not service
|
|
321
|
+
class DoubleService < ApplicationInteraction
|
|
322
|
+
job do
|
|
323
|
+
def perform(*args)
|
|
324
|
+
super
|
|
325
|
+
rescue StandardError => e
|
|
326
|
+
params = deserialize_active_job_args(args)
|
|
327
|
+
params[:x]
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
integer :x
|
|
332
|
+
|
|
333
|
+
def execute
|
|
334
|
+
raise
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
DoubleService.run # => RuntimeError
|
|
339
|
+
DoubleService.delay.perform_now(x: 2) # => returns 2
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Rspec
|
|
168
343
|
|
|
169
344
|
```ruby
|
|
170
345
|
class SomeService < ActiveInteraction::Base
|
|
@@ -193,7 +368,7 @@ RSpec.describe SomeService do
|
|
|
193
368
|
|
|
194
369
|
# expect_to_run / expect_not_to_run / expect_to_not_run
|
|
195
370
|
# expect_to_execute
|
|
196
|
-
# expect_to_delay_run / expect_to_not_run_delayed
|
|
371
|
+
# expect_to_delay_run / expect_not_to_run_delayed / expect_to_not_run_delayed
|
|
197
372
|
# expect_to_delay_execute
|
|
198
373
|
end
|
|
199
374
|
end
|
|
@@ -213,6 +388,10 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/antuli
|
|
|
213
388
|
|
|
214
389
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
215
390
|
|
|
391
|
+
## Credits
|
|
392
|
+
|
|
393
|
+
ActiveInteraction::Extras is brought to you by [Anton Katunin](https://github.com/antulik) and was originally built at [CarNextDoor](https://www.carnextdoor.com.au/).
|
|
394
|
+
|
|
216
395
|
## Code of Conduct
|
|
217
396
|
|
|
218
397
|
Everyone interacting in the ActiveInteraction::Extras project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/antulik/active_interaction-extras/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
CHANGED
|
@@ -9,10 +9,13 @@ Gem::Specification.new do |spec|
|
|
|
9
9
|
spec.authors = ["Anton Katunin"]
|
|
10
10
|
spec.email = ["antulik@gmail.com"]
|
|
11
11
|
|
|
12
|
-
spec.summary = %q{
|
|
13
|
-
spec.description = %q{
|
|
14
|
-
spec.homepage = "https://github.com/antulik/
|
|
12
|
+
spec.summary = %q{Extensions for active_interaction gem}
|
|
13
|
+
spec.description = %q{Extensions for active_interaction gem}
|
|
14
|
+
spec.homepage = "https://github.com/antulik/active_interaction-extras"
|
|
15
15
|
spec.license = "MIT"
|
|
16
|
+
spec.metadata = {
|
|
17
|
+
"changelog_uri" => "https://github.com/antulik/active_interaction-extras/blob/master/CHANGELOG.md",
|
|
18
|
+
}
|
|
16
19
|
|
|
17
20
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
18
21
|
f.match(%r{^(test|spec|features)/})
|
|
@@ -21,11 +24,10 @@ Gem::Specification.new do |spec|
|
|
|
21
24
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
22
25
|
spec.require_paths = ["lib"]
|
|
23
26
|
|
|
24
|
-
spec.add_dependency "active_interaction", ">=
|
|
25
|
-
spec.add_dependency "
|
|
26
|
-
spec.add_development_dependency "bundler", "~>
|
|
27
|
-
spec.add_development_dependency "rake", "
|
|
27
|
+
spec.add_dependency "active_interaction", ">= 4"
|
|
28
|
+
spec.add_dependency "rails", ">= 6.0"
|
|
29
|
+
spec.add_development_dependency "bundler", "~> 2.2"
|
|
30
|
+
spec.add_development_dependency "rake", ">= 12.3.3"
|
|
28
31
|
spec.add_development_dependency "rspec", "~> 3.7"
|
|
29
|
-
spec.add_development_dependency "rails", ">= 4.0"
|
|
30
32
|
spec.add_development_dependency "pry"
|
|
31
33
|
end
|
|
@@ -5,14 +5,40 @@ require 'active_interaction'
|
|
|
5
5
|
|
|
6
6
|
module ActiveInteraction
|
|
7
7
|
module Extras
|
|
8
|
+
module Filters
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module FilterExtensions
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module Jobs
|
|
15
|
+
autoload(:Core, "active_interaction/extras/jobs/core")
|
|
16
|
+
end
|
|
8
17
|
|
|
9
|
-
autoload(:ActiveJob, "active_interaction/extras/active_job")
|
|
10
18
|
autoload(:All, "active_interaction/extras/all")
|
|
19
|
+
|
|
20
|
+
autoload(:FilterAlias, "active_interaction/extras/filter_alias")
|
|
11
21
|
autoload(:Halt, "active_interaction/extras/halt")
|
|
12
22
|
autoload(:ModelFields, "active_interaction/extras/model_fields")
|
|
13
|
-
autoload(:Rspec, "active_interaction/extras/rspec")
|
|
14
23
|
autoload(:RunCallback, "active_interaction/extras/run_callback")
|
|
15
24
|
autoload(:StrongParams, "active_interaction/extras/strong_params")
|
|
16
25
|
autoload(:Transaction, "active_interaction/extras/transaction")
|
|
26
|
+
|
|
27
|
+
autoload(:TimezoneSupport, "active_interaction/extras/timezone_support")
|
|
28
|
+
autoload(:Rspec, "active_interaction/extras/rspec")
|
|
29
|
+
|
|
30
|
+
autoload(:ActiveJob, "active_interaction/extras/active_job")
|
|
31
|
+
autoload(:Sidekiq, "active_interaction/extras/sidekiq")
|
|
17
32
|
end
|
|
18
33
|
end
|
|
34
|
+
|
|
35
|
+
require 'active_interaction/extras/filters/anything_filter'
|
|
36
|
+
require 'active_interaction/extras/filters/uuid_filter'
|
|
37
|
+
|
|
38
|
+
I18n.load_path.unshift(
|
|
39
|
+
*Dir.glob(
|
|
40
|
+
File.expand_path(
|
|
41
|
+
File.join(%w[extras locale *.yml]), File.dirname(__FILE__)
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
)
|
|
@@ -1,11 +1,35 @@
|
|
|
1
|
+
require 'active_job'
|
|
2
|
+
|
|
1
3
|
module ActiveInteraction::Extras::ActiveJob
|
|
2
4
|
extend ActiveSupport::Concern
|
|
3
5
|
|
|
4
|
-
include ActiveInteraction::
|
|
6
|
+
include ActiveInteraction::Extras::Jobs::Core
|
|
7
|
+
|
|
8
|
+
class_methods do
|
|
9
|
+
def configured_job_class
|
|
10
|
+
ConfiguredJob
|
|
11
|
+
end
|
|
12
|
+
end
|
|
5
13
|
|
|
6
14
|
module Perform
|
|
7
15
|
extend ActiveSupport::Concern
|
|
8
16
|
|
|
9
|
-
|
|
17
|
+
def perform(*args)
|
|
18
|
+
if self.class.respond_to?(:module_parent)
|
|
19
|
+
self.class.module_parent.run!(*args)
|
|
20
|
+
else
|
|
21
|
+
self.class.parent.run!(*args)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class ConfiguredJob < ::ActiveJob::ConfiguredJob
|
|
27
|
+
def run(*args)
|
|
28
|
+
perform_later(*args)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def run!(*args)
|
|
32
|
+
perform_later(*args)
|
|
33
|
+
end
|
|
10
34
|
end
|
|
11
35
|
end
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
module ActiveInteraction::Extras::All
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
|
-
include
|
|
4
|
+
# order dependant, include first so around callback includes other modules
|
|
5
|
+
include ActiveInteraction::Extras::Transaction
|
|
6
|
+
|
|
7
|
+
include ActiveInteraction::Extras::FilterAlias
|
|
5
8
|
include ActiveInteraction::Extras::Halt
|
|
6
9
|
include ActiveInteraction::Extras::ModelFields
|
|
7
10
|
include ActiveInteraction::Extras::RunCallback
|
|
8
11
|
include ActiveInteraction::Extras::StrongParams
|
|
9
|
-
include ActiveInteraction::Extras::Transaction
|
|
10
12
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Add :as option, which is a read alias to filter
|
|
4
|
+
# hash :params, as: :account_attributes
|
|
5
|
+
#
|
|
6
|
+
module ActiveInteraction::Extras::FilterAlias
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
class_methods do
|
|
10
|
+
def initialize_filter(filter)
|
|
11
|
+
super.tap do
|
|
12
|
+
if filter.options[:as]
|
|
13
|
+
alias_method filter.options[:as], filter.name
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# If hash specified without structure automatically accept full hash
|
|
4
|
+
#
|
|
5
|
+
# @example Accept all keys
|
|
6
|
+
# hash :options
|
|
7
|
+
#
|
|
8
|
+
# @example Accept only specified keys
|
|
9
|
+
# hash :options do
|
|
10
|
+
# string :name
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# @example Accept all keys
|
|
14
|
+
# hash :options, strip: false do
|
|
15
|
+
# string :name
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
module ActiveInteraction::Extras::FilterExtensions::HashAutoStrip
|
|
19
|
+
def initialize(*)
|
|
20
|
+
super
|
|
21
|
+
options[:strip] = false if !block_given? && !options.key?(:strip)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
ActiveInteraction::HashFilter.prepend(ActiveInteraction::Extras::FilterExtensions::HashAutoStrip)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Add support for polymorphic objects
|
|
4
|
+
# object :account, class: [Account, AnyoneAccount]
|
|
5
|
+
#
|
|
6
|
+
module ActiveInteraction::Extras::FilterExtensions::ObjectClasses
|
|
7
|
+
def class_list
|
|
8
|
+
class_names.map do |klass_name|
|
|
9
|
+
case klass_name
|
|
10
|
+
when Class
|
|
11
|
+
klass_name
|
|
12
|
+
else
|
|
13
|
+
begin
|
|
14
|
+
Object.const_get(klass_name.to_s.camelize)
|
|
15
|
+
rescue NameError
|
|
16
|
+
raise ActiveInteraction::InvalidNameError, "class #{klass_name.inspect} does not exist"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def klass
|
|
23
|
+
if polymorphic?
|
|
24
|
+
class_list.first
|
|
25
|
+
else
|
|
26
|
+
super
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def matches?(value)
|
|
31
|
+
if polymorphic?
|
|
32
|
+
class_list.any? { |klass| value.class <= klass }
|
|
33
|
+
else
|
|
34
|
+
super
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def class_names
|
|
39
|
+
options.fetch(:class, name)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def polymorphic?
|
|
43
|
+
class_names.is_a? Array
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
ActiveInteraction::ObjectFilter.prepend(ActiveInteraction::Extras::FilterExtensions::ObjectClasses)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Implementation inspired by
|
|
4
|
+
# https://github.com/AaronLasseigne/active_interaction/blob/c9d5608c3b8aab23d463f99c832b2ac5139911de/lib/active_interaction/filters/abstract_date_time_filter.rb#L42
|
|
5
|
+
module ActiveInteraction::Extras::FilterExtensions::TimezoneSupport
|
|
6
|
+
def convert_string(value)
|
|
7
|
+
if time_with_zone?
|
|
8
|
+
Time.zone.parse(value) ||
|
|
9
|
+
raise(ArgumentError, "no time information in #{value.inspect}")
|
|
10
|
+
else
|
|
11
|
+
super
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
ActiveInteraction::TimeFilter.include(ActiveInteraction::Extras::FilterExtensions::TimezoneSupport)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class ActiveInteraction::Extras::Filters::UUIDFilter < ActiveInteraction::StringFilter
|
|
4
|
+
register :uuid
|
|
5
|
+
|
|
6
|
+
REGEX = /^[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i.freeze
|
|
7
|
+
|
|
8
|
+
def matches?(value)
|
|
9
|
+
super && REGEX.match?(value)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def convert(value)
|
|
13
|
+
super&.presence
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module ActiveInteraction::Extras::Jobs::Core
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
class_methods do
|
|
5
|
+
def define_job_class(klass)
|
|
6
|
+
unless const_defined?(:Job, false)
|
|
7
|
+
const_set(:Job, Class.new(klass))
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def job(&block)
|
|
12
|
+
job_class.class_exec(&block)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def job_class
|
|
16
|
+
const_get(:Job, false)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def inherited(subclass)
|
|
20
|
+
super
|
|
21
|
+
subclass.define_job_class(job_class)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def delay(options = {})
|
|
25
|
+
configured_job_class.new(job_class, options)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def configured_job_class
|
|
29
|
+
raise NotImplementedError
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'active_job'
|
|
2
|
+
|
|
3
|
+
module ActiveInteraction::Extras::Sidekiq
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
include ActiveInteraction::Extras::Jobs::Core
|
|
7
|
+
|
|
8
|
+
class_methods do
|
|
9
|
+
def configured_job_class
|
|
10
|
+
ConfiguredJob
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module Perform
|
|
15
|
+
extend ActiveSupport::Concern
|
|
16
|
+
|
|
17
|
+
class_methods do
|
|
18
|
+
def deserialize_active_job_args(serialized_job)
|
|
19
|
+
ActiveJob::Arguments.deserialize(serialized_job['args']).first&.with_indifferent_access || {}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def perform_later(*args)
|
|
23
|
+
ConfiguredJob.new(self).perform_later(*args)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def perform(*args)
|
|
28
|
+
# support for sidekiq encrypted params
|
|
29
|
+
if args.length > 1 && args[0].nil?
|
|
30
|
+
args.shift
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
args = ActiveJob::Arguments.deserialize(args)
|
|
34
|
+
if self.class.respond_to?(:module_parent)
|
|
35
|
+
self.class.module_parent.run!(*args)
|
|
36
|
+
else
|
|
37
|
+
self.class.parent.run!(*args)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def deserialize_active_job_args(job_arguments)
|
|
42
|
+
ActiveJob::Arguments.deserialize(job_arguments).first&.with_indifferent_access || {}
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class ConfiguredJob < ::ActiveJob::ConfiguredJob
|
|
47
|
+
def perform_now(*args)
|
|
48
|
+
@job_class.run!(*args)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def perform_later(*args)
|
|
52
|
+
args = ActiveJob::Arguments.serialize(args)
|
|
53
|
+
scope = @job_class.set(@options.except(:wait, :wait_until))
|
|
54
|
+
|
|
55
|
+
if @job_class.sidekiq_options['encrypt']
|
|
56
|
+
args.prepend(nil)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if @options[:wait]
|
|
60
|
+
scope.perform_in @options[:wait], *args
|
|
61
|
+
elsif @options[:wait_until]
|
|
62
|
+
scope.perform_at @options[:wait_until], *args
|
|
63
|
+
else
|
|
64
|
+
scope.perform_async *args
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
alias_method :run!, :perform_later
|
|
69
|
+
alias_method :run, :perform_later
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -1,32 +1,26 @@
|
|
|
1
|
+
# Add transaction wrapper
|
|
2
|
+
# run_in_transaction!
|
|
3
|
+
# skip_run_in_transaction!
|
|
1
4
|
module ActiveInteraction::Extras::Transaction
|
|
2
5
|
extend ActiveSupport::Concern
|
|
3
6
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# check by class because
|
|
10
|
-
# errors added by compose method are merged after execute,
|
|
11
|
-
# so we need to check return type ourselves
|
|
12
|
-
#
|
|
13
|
-
# see ActiveInteraction::Runnable#run
|
|
14
|
-
if result_or_errors.is_a?(ActiveInteraction::Errors) && result_or_errors.any?
|
|
15
|
-
raise ActiveRecord::Rollback
|
|
7
|
+
included do
|
|
8
|
+
class_attribute :run_in_transaction_options
|
|
9
|
+
set_callback :execute, :around, ->(_interaction, block) {
|
|
10
|
+
ActiveRecord::Base.transaction(run_in_transaction_options) do
|
|
11
|
+
block.call
|
|
16
12
|
end
|
|
17
|
-
|
|
18
|
-
raise ActiveRecord::Rollback if errors.any?
|
|
19
|
-
end
|
|
20
|
-
result_or_errors
|
|
13
|
+
}, if: :run_in_transaction_options
|
|
21
14
|
end
|
|
22
15
|
|
|
23
16
|
class_methods do
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
# https://pragtob.wordpress.com/2017/12/12/surprises-with-nested-transactions-rollbacks-and-activerecord/
|
|
18
|
+
def run_in_transaction!(requires_new: true)
|
|
19
|
+
self.run_in_transaction_options = {requires_new: requires_new}
|
|
26
20
|
end
|
|
27
21
|
|
|
28
22
|
def skip_run_in_transaction!
|
|
29
|
-
|
|
23
|
+
self.run_in_transaction_options = nil
|
|
30
24
|
end
|
|
31
25
|
end
|
|
32
26
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_interaction-extras
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Anton Katunin
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-05-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: active_interaction
|
|
@@ -16,56 +16,56 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
19
|
+
version: '4'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
26
|
+
version: '4'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
28
|
+
name: rails
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '0'
|
|
33
|
+
version: '6.0'
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '0'
|
|
40
|
+
version: '6.0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: bundler
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
45
|
- - "~>"
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
47
|
+
version: '2.2'
|
|
48
48
|
type: :development
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
54
|
+
version: '2.2'
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: rake
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
|
-
- - "
|
|
59
|
+
- - ">="
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version:
|
|
61
|
+
version: 12.3.3
|
|
62
62
|
type: :development
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
|
-
- - "
|
|
66
|
+
- - ">="
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version:
|
|
68
|
+
version: 12.3.3
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: rspec
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -80,20 +80,6 @@ dependencies:
|
|
|
80
80
|
- - "~>"
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '3.7'
|
|
83
|
-
- !ruby/object:Gem::Dependency
|
|
84
|
-
name: rails
|
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
|
86
|
-
requirements:
|
|
87
|
-
- - ">="
|
|
88
|
-
- !ruby/object:Gem::Version
|
|
89
|
-
version: '4.0'
|
|
90
|
-
type: :development
|
|
91
|
-
prerelease: false
|
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
-
requirements:
|
|
94
|
-
- - ">="
|
|
95
|
-
- !ruby/object:Gem::Version
|
|
96
|
-
version: '4.0'
|
|
97
83
|
- !ruby/object:Gem::Dependency
|
|
98
84
|
name: pry
|
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -108,15 +94,17 @@ dependencies:
|
|
|
108
94
|
- - ">="
|
|
109
95
|
- !ruby/object:Gem::Version
|
|
110
96
|
version: '0'
|
|
111
|
-
description:
|
|
97
|
+
description: Extensions for active_interaction gem
|
|
112
98
|
email:
|
|
113
99
|
- antulik@gmail.com
|
|
114
100
|
executables: []
|
|
115
101
|
extensions: []
|
|
116
102
|
extra_rdoc_files: []
|
|
117
103
|
files:
|
|
104
|
+
- ".github/workflows/ci.yml"
|
|
118
105
|
- ".gitignore"
|
|
119
106
|
- ".rspec"
|
|
107
|
+
- CHANGELOG.md
|
|
120
108
|
- CODE_OF_CONDUCT.md
|
|
121
109
|
- Gemfile
|
|
122
110
|
- LICENSE.txt
|
|
@@ -128,18 +116,29 @@ files:
|
|
|
128
116
|
- lib/active_interaction/extras.rb
|
|
129
117
|
- lib/active_interaction/extras/active_job.rb
|
|
130
118
|
- lib/active_interaction/extras/all.rb
|
|
119
|
+
- lib/active_interaction/extras/filter_alias.rb
|
|
120
|
+
- lib/active_interaction/extras/filter_extensions.rb
|
|
121
|
+
- lib/active_interaction/extras/filter_extensions/hash_auto_strip.rb
|
|
122
|
+
- lib/active_interaction/extras/filter_extensions/object_classes.rb
|
|
123
|
+
- lib/active_interaction/extras/filter_extensions/timezone_support.rb
|
|
124
|
+
- lib/active_interaction/extras/filters/anything_filter.rb
|
|
125
|
+
- lib/active_interaction/extras/filters/uuid_filter.rb
|
|
131
126
|
- lib/active_interaction/extras/halt.rb
|
|
127
|
+
- lib/active_interaction/extras/jobs/core.rb
|
|
128
|
+
- lib/active_interaction/extras/locale/en.yml
|
|
132
129
|
- lib/active_interaction/extras/model_fields.rb
|
|
133
130
|
- lib/active_interaction/extras/rspec.rb
|
|
134
131
|
- lib/active_interaction/extras/run_callback.rb
|
|
132
|
+
- lib/active_interaction/extras/sidekiq.rb
|
|
135
133
|
- lib/active_interaction/extras/strong_params.rb
|
|
136
134
|
- lib/active_interaction/extras/transaction.rb
|
|
137
135
|
- lib/active_interaction/extras/version.rb
|
|
138
|
-
homepage: https://github.com/antulik/
|
|
136
|
+
homepage: https://github.com/antulik/active_interaction-extras
|
|
139
137
|
licenses:
|
|
140
138
|
- MIT
|
|
141
|
-
metadata:
|
|
142
|
-
|
|
139
|
+
metadata:
|
|
140
|
+
changelog_uri: https://github.com/antulik/active_interaction-extras/blob/master/CHANGELOG.md
|
|
141
|
+
post_install_message:
|
|
143
142
|
rdoc_options: []
|
|
144
143
|
require_paths:
|
|
145
144
|
- lib
|
|
@@ -154,9 +153,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
154
153
|
- !ruby/object:Gem::Version
|
|
155
154
|
version: '0'
|
|
156
155
|
requirements: []
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
signing_key:
|
|
156
|
+
rubygems_version: 3.1.4
|
|
157
|
+
signing_key:
|
|
160
158
|
specification_version: 4
|
|
161
|
-
summary:
|
|
159
|
+
summary: Extensions for active_interaction gem
|
|
162
160
|
test_files: []
|