active_interaction-extras 0.2.3 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a892408995ae88c12ca11175ebdb5cd2289fdcd28227fc731351300a6ac23415
4
- data.tar.gz: 594a1ed75fb4431e341931f6492ff0137dd8fbe8fc2a1562e73ec5c3a95d97fd
3
+ metadata.gz: ebddd04a27db967f1796d98c4571c9a49233cb8aeb2c0af53124804eca42813d
4
+ data.tar.gz: 51cd957db282cf73fcdac076a7493430e40d329d15706565af67dbf9111c604a
5
5
  SHA512:
6
- metadata.gz: 4fc7920ac168a7e481ef26d573416f0b52ddbf81eb5089cda0202b1cea548e01f2cb38160272604991da5f38f1f0c305bf3b3f62254b0e6850f16046564b46e8
7
- data.tar.gz: d16a91ed85b5d3d267b93031422fe2721cde11c3481a68ffd42ac35edb5a530f3c114fdc491616d3fa0044af94d4bfdc19f0ec9dfa08fddee06cac839bf988a5
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,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
@@ -149,6 +237,10 @@ UpdateUserForm.run
149
237
  Comment.count # => 0
150
238
  ```
151
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
+
152
244
  ### ActiveJob
153
245
 
154
246
  ```ruby
@@ -169,10 +261,15 @@ class DoubleService < ApplicationInteraction
169
261
  end
170
262
 
171
263
  DoubleService.delay.run(x: 2) # queues to run in background
264
+ DoubleService.delay(queue: 'low_priority', wait: 1.minute).run(x: 2)
172
265
  ```
173
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
+
174
269
  ### Sidekiq
175
270
 
271
+ You can use sidekiq directly if you need more control. Sidekiq integration comes with default GlobalID support.
272
+
176
273
  ```ruby
177
274
  class ApplicationInteraction < ActiveInteraction::Base
178
275
  include ActiveInteraction::Extras::Sidekiq
@@ -185,7 +282,7 @@ end
185
282
 
186
283
  class DoubleService < ApplicationInteraction
187
284
  job do
188
- sidekiq_options retry: 1
285
+ sidekiq_options retry: 1 # configure sidekiq options
189
286
  end
190
287
 
191
288
  integer :x
@@ -196,9 +293,53 @@ class DoubleService < ApplicationInteraction
196
293
  end
197
294
 
198
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
199
340
  ```
200
341
 
201
- ### Rspec
342
+ ## Rspec
202
343
 
203
344
  ```ruby
204
345
  class SomeService < ActiveInteraction::Base
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
@@ -13,6 +13,9 @@ Gem::Specification.new do |spec|
13
13
  spec.description = %q{Extensions for active_interaction gem}
14
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", ">= 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"
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,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
@@ -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,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
- 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
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
- def run_in_transaction!
25
- set_callback :execute, :around, :run_in_transaction!, prepend: true
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
- skip_callback :execute, :around, :run_in_transaction!
23
+ self.run_in_transaction_options = nil
30
24
  end
31
25
  end
32
26
  end
@@ -1,5 +1,5 @@
1
1
  module ActiveInteraction
2
2
  module Extras
3
- VERSION = "0.2.3"
3
+ VERSION = "1.0.0"
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.3
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Katunin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-04 00:00:00.000000000 Z
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: 3.0.0
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: 3.0.0
26
+ version: '4'
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
@@ -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
@@ -115,8 +101,10 @@ 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,7 +116,16 @@ 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
@@ -139,7 +136,8 @@ files:
139
136
  homepage: https://github.com/antulik/active_interaction-extras
140
137
  licenses:
141
138
  - MIT
142
- metadata: {}
139
+ metadata:
140
+ changelog_uri: https://github.com/antulik/active_interaction-extras/blob/master/CHANGELOG.md
143
141
  post_install_message:
144
142
  rdoc_options: []
145
143
  require_paths:
@@ -155,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
155
153
  - !ruby/object:Gem::Version
156
154
  version: '0'
157
155
  requirements: []
158
- rubygems_version: 3.0.3
156
+ rubygems_version: 3.1.4
159
157
  signing_key:
160
158
  specification_version: 4
161
159
  summary: Extensions for active_interaction gem