active_interaction-extras 0.2.0 → 1.0.1

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: b830f72fb06c46cac4007920afe65a33c92d2548
4
- data.tar.gz: b954fa1ea9e8280a485b52da36c3f8379b2cb67e
2
+ SHA256:
3
+ metadata.gz: a71a37d918c487996272cfff3fab64da2241f0f071a659ff5f9cac97f76bf869
4
+ data.tar.gz: 15bc8f3f20329df47325103a6c5971848d0c5c22eca399a0be2e28ba0c6abc52
5
5
  SHA512:
6
- metadata.gz: 63ee6666b6b0b757f5724a3613ae38883bbb995941a32faf8627b1342b6c0114219a3efb09d1a5936d1db6d003708786b22126645aecd87c7ab633019f10195e
7
- data.tar.gz: '0413834b86cd0f6fbcf135dd0bc3d9afb4b5d086973005008ad7b6db43f51680e70e897d8e71e90776573c53be3ea4153847fead72d9abefda823b03f34cdb3b'
6
+ metadata.gz: 940a9e9195a01433b060227d3ff02450ac2b620972cf493f3760f9977b5106c071241aef5b5d9c1cef35d39937cfdae8efc2d81c508f4f68b6fe6d5b8ac5cd44
7
+ data.tar.gz: d4358a521172fbca3c27be32b41bd5ba4c9cb0df79a5f1b3a2d5539b39f7d04ea5ee73a2abaeab5585529c71701821db2de599a253aaa7ce0ee93f1d808381b4
@@ -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,24 @@
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.1] - 2021-05-13
8
+
9
+ - Fix `run_in_transaction` in ruby 3 [#8](https://github.com/antulik/active_interaction-extras/pull/8)
10
+
11
+ ## [1.0.0] - 2021-05-12
12
+
13
+ - Requires active_interaction v4
14
+ - New filters: `anything` and `uuid`
15
+ - New filter extensions
16
+ - object: support for multiple classes
17
+ - hash: disable auto strip when no keys are listed
18
+ - New extension:
19
+ - filter alias
20
+ - Changed `transaction` extension
21
+ - It requires new transaction by default
22
+ - Include order is important now
23
+ - Removed `active_interaction-active_job` gem dependency
24
+ - 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,10 @@ 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
+
242
395
  ## Code of Conduct
243
396
 
244
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
@@ -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,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,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,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
@@ -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,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.0"
3
+ VERSION = "1.0.1"
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.0
4
+ version: 1.0.1
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-05-13 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
@@ -108,15 +94,17 @@ dependencies:
108
94
  - - ">="
109
95
  - !ruby/object:Gem::Version
110
96
  version: '0'
111
- description: Extension for active_interaction gem
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,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
@@ -136,11 +133,12 @@ files:
136
133
  - lib/active_interaction/extras/strong_params.rb
137
134
  - lib/active_interaction/extras/transaction.rb
138
135
  - lib/active_interaction/extras/version.rb
139
- homepage: https://github.com/antulik/xxxx
136
+ homepage: https://github.com/antulik/active_interaction-extras
140
137
  licenses:
141
138
  - MIT
142
- metadata: {}
143
- post_install_message:
139
+ metadata:
140
+ changelog_uri: https://github.com/antulik/active_interaction-extras/blob/master/CHANGELOG.md
141
+ post_install_message:
144
142
  rdoc_options: []
145
143
  require_paths:
146
144
  - lib
@@ -155,9 +153,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
155
153
  - !ruby/object:Gem::Version
156
154
  version: '0'
157
155
  requirements: []
158
- rubyforge_project:
159
- rubygems_version: 2.6.8
160
- signing_key:
156
+ rubygems_version: 3.1.4
157
+ signing_key:
161
158
  specification_version: 4
162
- summary: Extension for active_interaction gem
159
+ summary: Extensions for active_interaction gem
163
160
  test_files: []