active_interaction-extras 0.2.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c7fe435178207a247b888536be48aa6e64ce9440
4
- data.tar.gz: 54a41e41ff3127f1b6220ca87d742f7a1ea52f46
2
+ SHA256:
3
+ metadata.gz: 388877d30b968d835dd575246ce422c76869152aa48e5c5d092d77b43f35764f
4
+ data.tar.gz: da305c4468eaacca6ec6cf9a5c81f7bec3b2cceee1cd1e295fe06d654bdd8ea4
5
5
  SHA512:
6
- metadata.gz: 63d32e869429369af8f3184091aeb8b90cd5b411931a99b8b951fbc2ca91fc9950eeae0595cc847fc44a2e9d59330f6278c5c37ad50955548f2b8ecd14a0f5dd
7
- data.tar.gz: 18b826e7e0300318093a6ff13d4a208b128d84ef072c597caf9e0b02b1de525cac8f3baa155c5124f72850880c8ab8a1ce935d24d811bc2f9a286cee0c6a1126
6
+ metadata.gz: c93f2d6aefa7015c27b34ec33c18d26c912099e85894987a9bbaca137cd52d3ff40842f9343a2c4ca3d4b79ef7bf672a03f8f328e0aaddf94943907a19c2db40
7
+ data.tar.gz: 69756c3fd8da5ac3e9bbbc209b8c4b7456b99c1203161f3df5cb15e7c0b2f1a40eb258b1b61663f4fc5fba2b5d639c97c2a6c516ae71ac5d588d42c8df674e41
@@ -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,29 @@
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.2] - 2021-07-07
8
+
9
+ - Requires active_interaction v4.0.2 or higher
10
+ - Fixed `run_in_transaction!` to rollback when interaction finished with errors
11
+
12
+ ## [1.0.1] - 2021-05-13
13
+
14
+ - Fix `run_in_transaction` in ruby 3 [#8](https://github.com/antulik/active_interaction-extras/pull/8)
15
+
16
+ ## [1.0.0] - 2021-05-12
17
+
18
+ - Requires active_interaction v4
19
+ - New filters: `anything` and `uuid`
20
+ - New filter extensions
21
+ - object: support for multiple classes
22
+ - hash: disable auto strip when no keys are listed
23
+ - New extension:
24
+ - filter alias
25
+ - Changed `transaction` extension
26
+ - It requires new transaction by default
27
+ - Include order is important now
28
+ - Removed `active_interaction-active_job` gem dependency
29
+ - Added changelog
data/README.md CHANGED
@@ -1,26 +1,115 @@
1
1
  # ActiveInteraction::Extras
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/active_interaction-extras.svg)](https://badge.fury.io/rb/active_interaction-extras) ![CI build](https://github.com/antulik/active_interaction-extras/actions/workflows/ci.yml/badge.svg)
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::Halt
20
- # include ActiveInteraction::Extras::ModelFields
21
- # include ActiveInteraction::Extras::RunCallback
22
- # include ActiveInteraction::Extras::StrongParams
23
- # include ActiveInteraction::Extras::Transaction
39
+ end
40
+ ```
41
+
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.
49
+
50
+ ```ruby
51
+ class Service < ActiveInteraction::Base
52
+ anything :model
53
+ end
54
+ ```
55
+
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
85
+ end
86
+ end
87
+ ```
88
+
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
109
+
110
+ def execute
111
+ user_attributes == params # => true
112
+ end
24
113
  end
25
114
  ```
26
115
 
@@ -50,7 +139,7 @@ end
50
139
  class UserForm < ActiveInteraction::Base
51
140
  include ActiveInteraction::Extras::ModelFields
52
141
 
53
- interface :user
142
+ anything :user
54
143
 
55
144
  model_fields(:user) do
56
145
  string :first_name
@@ -96,7 +185,6 @@ end
96
185
 
97
186
  ### StrongParams
98
187
 
99
-
100
188
  ```ruby
101
189
  class UpdateUserForm < ActiveInteraction::Base
102
190
  include ActiveInteraction::Extras::StrongParams
@@ -120,6 +208,14 @@ form_params = ActionController::Parameters.new(
120
208
  )
121
209
 
122
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)
123
219
  ```
124
220
 
125
221
  ### Transaction
@@ -141,6 +237,10 @@ UpdateUserForm.run
141
237
  Comment.count # => 0
142
238
  ```
143
239
 
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
+
144
244
  ### ActiveJob
145
245
 
146
246
  ```ruby
@@ -161,10 +261,15 @@ class DoubleService < ApplicationInteraction
161
261
  end
162
262
 
163
263
  DoubleService.delay.run(x: 2) # queues to run in background
264
+ DoubleService.delay(queue: 'low_priority', wait: 1.minute).run(x: 2)
164
265
  ```
165
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
+
166
269
  ### Sidekiq
167
270
 
271
+ You can use sidekiq directly if you need more control. Sidekiq integration comes with default GlobalID support.
272
+
168
273
  ```ruby
169
274
  class ApplicationInteraction < ActiveInteraction::Base
170
275
  include ActiveInteraction::Extras::Sidekiq
@@ -177,7 +282,7 @@ end
177
282
 
178
283
  class DoubleService < ApplicationInteraction
179
284
  job do
180
- sidekiq_options retry: 1
285
+ sidekiq_options retry: 1 # configure sidekiq options
181
286
  end
182
287
 
183
288
  integer :x
@@ -188,9 +293,53 @@ class DoubleService < ApplicationInteraction
188
293
  end
189
294
 
190
295
  DoubleService.delay.run(x: 2) # queues to run in background
296
+ DoubleService.delay(queue: 'low_priority', wait: 1.minute).run(x: 2)
191
297
  ```
192
298
 
193
- ### Rspec
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
194
343
 
195
344
  ```ruby
196
345
  class SomeService < ActiveInteraction::Base
@@ -219,7 +368,7 @@ RSpec.describe SomeService do
219
368
 
220
369
  # expect_to_run / expect_not_to_run / expect_to_not_run
221
370
  # expect_to_execute
222
- # expect_to_delay_run / expect_to_not_run_delayed
371
+ # expect_to_delay_run / expect_not_to_run_delayed / expect_to_not_run_delayed
223
372
  # expect_to_delay_execute
224
373
  end
225
374
  end
@@ -239,6 +388,11 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/antuli
239
388
 
240
389
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
241
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
+ * Further improvements to this gem brought to you by [Anton Katunin](https://github.com/antulik) once again and the [Split Payments team](https://github.com/splitpayments/split).
395
+
242
396
  ## Code of Conduct
243
397
 
244
398
  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
@@ -1,2 +1,10 @@
1
1
  require "bundler/gem_tasks"
2
- task :default => :spec
2
+
3
+ begin
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
8
+ rescue LoadError
9
+ # no rspec available
10
+ end
@@ -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{Extension for active_interaction gem}
13
- spec.description = %q{Extension for active_interaction gem}
14
- spec.homepage = "https://github.com/antulik/xxxx"
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,11 @@ 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", ">= 3.0.0"
25
- spec.add_dependency "active_interaction-active_job", ">= 0"
26
- spec.add_development_dependency "bundler", "~> 1.16"
27
- spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_dependency "active_interaction", ">= 4.0.2"
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"
33
+ spec.add_development_dependency "sqlite3"
31
34
  end
@@ -5,18 +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
18
  autoload(:All, "active_interaction/extras/all")
10
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
23
  autoload(:RunCallback, "active_interaction/extras/run_callback")
14
24
  autoload(:StrongParams, "active_interaction/extras/strong_params")
15
25
  autoload(:Transaction, "active_interaction/extras/transaction")
16
26
 
27
+ autoload(:TimezoneSupport, "active_interaction/extras/timezone_support")
28
+ autoload(:Rspec, "active_interaction/extras/rspec")
29
+
17
30
  autoload(:ActiveJob, "active_interaction/extras/active_job")
18
31
  autoload(:Sidekiq, "active_interaction/extras/sidekiq")
19
-
20
- autoload(:Rspec, "active_interaction/extras/rspec")
21
32
  end
22
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,13 +1,35 @@
1
- require 'active_interaction/active_job'
1
+ require 'active_job'
2
2
 
3
3
  module ActiveInteraction::Extras::ActiveJob
4
4
  extend ActiveSupport::Concern
5
5
 
6
- include ActiveInteraction::ActiveJob::Core
6
+ include ActiveInteraction::Extras::Jobs::Core
7
+
8
+ class_methods do
9
+ def configured_job_class
10
+ ConfiguredJob
11
+ end
12
+ end
7
13
 
8
14
  module Perform
9
15
  extend ActiveSupport::Concern
10
16
 
11
- include ActiveInteraction::ActiveJob::JobHelper
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
12
34
  end
13
35
  end
@@ -1,9 +1,12 @@
1
1
  module ActiveInteraction::Extras::All
2
2
  extend ActiveSupport::Concern
3
3
 
4
+ # order dependant, include first so around callback includes other modules
5
+ include ActiveInteraction::Extras::Transaction
6
+
7
+ include ActiveInteraction::Extras::FilterAlias
4
8
  include ActiveInteraction::Extras::Halt
5
9
  include ActiveInteraction::Extras::ModelFields
6
10
  include ActiveInteraction::Extras::RunCallback
7
11
  include ActiveInteraction::Extras::StrongParams
8
- include ActiveInteraction::Extras::Transaction
9
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,2 @@
1
+ require 'active_interaction/extras/filter_extensions/hash_auto_strip'
2
+ require 'active_interaction/extras/filter_extensions/object_classes'
@@ -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,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ActiveInteraction::Extras::Filters::AnythingFilter < ActiveInteraction::Filter
4
+ register :anything
5
+
6
+ def matches?(_object)
7
+ true
8
+ end
9
+ end
@@ -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,5 @@
1
+ en:
2
+ active_interaction:
3
+ types:
4
+ uuid: UUID
5
+ anything: anything
@@ -46,6 +46,10 @@ module ActiveInteraction::Extras::ModelFields
46
46
  alias file custom_filter_attribute
47
47
  alias boolean custom_filter_attribute
48
48
  alias array custom_filter_attribute
49
+ alias record custom_filter_attribute
50
+
51
+ alias anything custom_filter_attribute
52
+ alias uuid custom_filter_attribute
49
53
  end
50
54
 
51
55
  # checks if value was given to the service and the value is different from
@@ -64,7 +68,7 @@ module ActiveInteraction::Extras::ModelFields
64
68
  end
65
69
 
66
70
  # overwritten to pre-populate model fields
67
- def populate_filters(_inputs)
71
+ def populate_filters_and_inputs(_inputs)
68
72
  super.tap do
69
73
  self.class.filters.each do |name, filter|
70
74
  next if given?(name)
@@ -67,6 +67,8 @@ module ActiveInteraction::Extras::Rspec
67
67
  expect(klass).to_not receive(:delay).with(*with)
68
68
  end
69
69
 
70
+ alias expect_not_to_run_delayed expect_to_not_run_delayed
71
+
70
72
  # see expect_to_run
71
73
  #
72
74
  # additional params
@@ -1,13 +1,71 @@
1
- require 'active_interaction/active_job'
1
+ require 'active_job'
2
2
 
3
3
  module ActiveInteraction::Extras::Sidekiq
4
4
  extend ActiveSupport::Concern
5
5
 
6
- include ActiveInteraction::ActiveJob::Sidekiq::Core
6
+ include ActiveInteraction::Extras::Jobs::Core
7
+
8
+ class_methods do
9
+ def configured_job_class
10
+ ConfiguredJob
11
+ end
12
+ end
7
13
 
8
14
  module Perform
9
15
  extend ActiveSupport::Concern
10
16
 
11
- include ActiveInteraction::ActiveJob::Sidekiq::JobHelper
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
12
70
  end
13
71
  end
@@ -1,7 +1,7 @@
1
1
  module ActiveInteraction::Extras::StrongParams
2
2
  extend ActiveSupport::Concern
3
3
 
4
- def process_inputs(inputs)
4
+ def initialize(inputs = {})
5
5
  # TODO: whitelist :params and :form_params, so they could not be used as filters
6
6
  return super if self.class.filters.key?(:params) || self.class.filters.key?(:form_params)
7
7
 
@@ -1,32 +1,27 @@
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
- def run_in_transaction!
5
- result_or_errors = nil
6
- ActiveRecord::Base.transaction do
7
- result_or_errors = yield
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
12
+ raise ActiveRecord::Rollback if _interaction.errors.any?
16
13
  end
17
-
18
- raise ActiveRecord::Rollback if errors.any?
19
- end
20
- result_or_errors
14
+ }, if: :run_in_transaction_options
21
15
  end
22
16
 
23
17
  class_methods do
24
- def run_in_transaction!
25
- set_callback :execute, :around, :run_in_transaction!, prepend: true
18
+ # https://pragtob.wordpress.com/2017/12/12/surprises-with-nested-transactions-rollbacks-and-activerecord/
19
+ def run_in_transaction!(requires_new: true)
20
+ self.run_in_transaction_options = {requires_new: requires_new}
26
21
  end
27
22
 
28
23
  def skip_run_in_transaction!
29
- skip_callback :execute, :around, :run_in_transaction!
24
+ self.run_in_transaction_options = nil
30
25
  end
31
26
  end
32
27
  end
@@ -1,5 +1,5 @@
1
1
  module ActiveInteraction
2
2
  module Extras
3
- VERSION = "0.2.1"
3
+ VERSION = "1.0.2"
4
4
  end
5
5
  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: 0.2.1
4
+ version: 1.0.2
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: 2018-05-25 00:00:00.000000000 Z
11
+ date: 2021-07-07 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: 3.0.0
19
+ version: 4.0.2
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: 3.0.0
26
+ version: 4.0.2
27
27
  - !ruby/object:Gem::Dependency
28
- name: active_interaction-active_job
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: '1.16'
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: '1.16'
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: '10.0'
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: '10.0'
68
+ version: 12.3.3
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -81,21 +81,21 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '3.7'
83
83
  - !ruby/object:Gem::Dependency
84
- name: rails
84
+ name: pry
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: '4.0'
89
+ version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: '4.0'
96
+ version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: pry
98
+ name: sqlite3
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
@@ -108,15 +108,17 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
- description: Extension for active_interaction gem
111
+ description: Extensions for active_interaction gem
112
112
  email:
113
113
  - antulik@gmail.com
114
114
  executables: []
115
115
  extensions: []
116
116
  extra_rdoc_files: []
117
117
  files:
118
+ - ".github/workflows/ci.yml"
118
119
  - ".gitignore"
119
120
  - ".rspec"
121
+ - CHANGELOG.md
120
122
  - CODE_OF_CONDUCT.md
121
123
  - Gemfile
122
124
  - LICENSE.txt
@@ -128,7 +130,16 @@ files:
128
130
  - lib/active_interaction/extras.rb
129
131
  - lib/active_interaction/extras/active_job.rb
130
132
  - lib/active_interaction/extras/all.rb
133
+ - lib/active_interaction/extras/filter_alias.rb
134
+ - lib/active_interaction/extras/filter_extensions.rb
135
+ - lib/active_interaction/extras/filter_extensions/hash_auto_strip.rb
136
+ - lib/active_interaction/extras/filter_extensions/object_classes.rb
137
+ - lib/active_interaction/extras/filter_extensions/timezone_support.rb
138
+ - lib/active_interaction/extras/filters/anything_filter.rb
139
+ - lib/active_interaction/extras/filters/uuid_filter.rb
131
140
  - lib/active_interaction/extras/halt.rb
141
+ - lib/active_interaction/extras/jobs/core.rb
142
+ - lib/active_interaction/extras/locale/en.yml
132
143
  - lib/active_interaction/extras/model_fields.rb
133
144
  - lib/active_interaction/extras/rspec.rb
134
145
  - lib/active_interaction/extras/run_callback.rb
@@ -136,11 +147,12 @@ files:
136
147
  - lib/active_interaction/extras/strong_params.rb
137
148
  - lib/active_interaction/extras/transaction.rb
138
149
  - lib/active_interaction/extras/version.rb
139
- homepage: https://github.com/antulik/xxxx
150
+ homepage: https://github.com/antulik/active_interaction-extras
140
151
  licenses:
141
152
  - MIT
142
- metadata: {}
143
- post_install_message:
153
+ metadata:
154
+ changelog_uri: https://github.com/antulik/active_interaction-extras/blob/master/CHANGELOG.md
155
+ post_install_message:
144
156
  rdoc_options: []
145
157
  require_paths:
146
158
  - lib
@@ -155,9 +167,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
155
167
  - !ruby/object:Gem::Version
156
168
  version: '0'
157
169
  requirements: []
158
- rubyforge_project:
159
- rubygems_version: 2.6.8
160
- signing_key:
170
+ rubygems_version: 3.1.4
171
+ signing_key:
161
172
  specification_version: 4
162
- summary: Extension for active_interaction gem
173
+ summary: Extensions for active_interaction gem
163
174
  test_files: []