active_interaction-extras 0.2.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0701c28aa2dd9c001e1be9c5525b39b6403caabd0024a3d6424f983fe27580f
4
- data.tar.gz: 33f90613a5e4d9c9624a8c1e15bd33d297a3df76c9858ddaf5c57d634a5d54ad
3
+ metadata.gz: c3637e9bc034e39019c7a5273730c9c66e39257fcb7006f8cb153318c2345acf
4
+ data.tar.gz: 51d7d883e0cfab7d0eae0f15b764f2b9e3f0868372cc4329fe47785014669e98
5
5
  SHA512:
6
- metadata.gz: 1098d113ec98b8a0cf1e26e438eab2c29b00cfb4915a230b0892104382d265837fdc53cdba21542743e98f750e54f317b0857e6ef443ad54efe084f0f4118607
7
- data.tar.gz: f1a3457c4ef8bdf3ad814833427694598967934887f89beadbd2ce7b44c3a64c7b6bb018d60f780cfae995e0ea479e749f105829cc7e63d61f300c49c8c4ae38
6
+ metadata.gz: 798047a1473aec22f030540ee4af4f7d074c7372cde93b9ceedcec996cc368d88c2e48743f7a0868da2029549f1e155c70037be36e252e7b5877caf64268bbbb
7
+ data.tar.gz: 38890669f04e15239b5ace15841dbb39e2a048eb9881ce127d80ad5b33126396417b5be918d8bef7a8b5a67bb1973fde82e2378baa0ce8b8dc00973d44546558
@@ -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,33 @@
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.3] - 2021-07-11
8
+
9
+ - Loosen the gem requirements
10
+
11
+ ## [1.0.2] - 2021-07-07
12
+
13
+ - Requires active_interaction v4.0.2 or higher
14
+ - Fixed `run_in_transaction!` to rollback when interaction finished with errors
15
+
16
+ ## [1.0.1] - 2021-05-13
17
+
18
+ - Fix `run_in_transaction` in ruby 3 [#8](https://github.com/antulik/active_interaction-extras/pull/8)
19
+
20
+ ## [1.0.0] - 2021-05-12
21
+
22
+ - Requires active_interaction v4
23
+ - New filters: `anything` and `uuid`
24
+ - New filter extensions
25
+ - object: support for multiple classes
26
+ - hash: disable auto strip when no keys are listed
27
+ - New extension:
28
+ - filter alias
29
+ - Changed `transaction` extension
30
+ - It requires new transaction by default
31
+ - Include order is important now
32
+ - Removed `active_interaction-active_job` gem dependency
33
+ - 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
@@ -249,7 +390,8 @@ The gem is available as open source under the terms of the [MIT License](https:/
249
390
 
250
391
  ## Credits
251
392
 
252
- ActiveInteraction::Extras is brought to you by [Anton Katunin](https://github.com/antulik) and was originally built at [CarNextDoor](https://www.carnextdoor.com.au/).
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).
253
395
 
254
396
  ## Code of Conduct
255
397
 
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,14 @@ 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 "activemodel", ">= 6.0"
29
+ spec.add_dependency "activesupport", ">= 6.0"
30
+
31
+ spec.add_development_dependency "bundler", "~> 2.2"
32
+ spec.add_development_dependency "rake", ">= 12.3.3"
28
33
  spec.add_development_dependency "rspec", "~> 3.7"
29
- spec.add_development_dependency "rails", ">= 4.0"
30
34
  spec.add_development_dependency "pry"
35
+ spec.add_development_dependency "sqlite3"
36
+ spec.add_development_dependency "activerecord"
31
37
  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,7 +67,7 @@ module ActiveInteraction::Extras::Rspec
67
67
  expect(klass).to_not receive(:delay).with(*with)
68
68
  end
69
69
 
70
- alias expect_to_not_run_delayed expect_not_to_run_delayed
70
+ alias expect_not_to_run_delayed expect_to_not_run_delayed
71
71
 
72
72
  # see expect_to_run
73
73
  #
@@ -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.2"
3
+ VERSION = "1.0.3"
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.2
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Katunin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-30 00:00:00.000000000 Z
11
+ date: 2021-07-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: active_interaction
@@ -16,56 +16,70 @@ 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: activemodel
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
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '6.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '6.0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: '1.16'
61
+ version: '2.2'
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: '1.16'
68
+ version: '2.2'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rake
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
- - - "~>"
73
+ - - ">="
60
74
  - !ruby/object:Gem::Version
61
- version: '10.0'
75
+ version: 12.3.3
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
- - - "~>"
80
+ - - ">="
67
81
  - !ruby/object:Gem::Version
68
- version: '10.0'
82
+ version: 12.3.3
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rspec
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -81,21 +95,35 @@ dependencies:
81
95
  - !ruby/object:Gem::Version
82
96
  version: '3.7'
83
97
  - !ruby/object:Gem::Dependency
84
- name: rails
98
+ name: pry
85
99
  requirement: !ruby/object:Gem::Requirement
86
100
  requirements:
87
101
  - - ">="
88
102
  - !ruby/object:Gem::Version
89
- version: '4.0'
103
+ version: '0'
90
104
  type: :development
91
105
  prerelease: false
92
106
  version_requirements: !ruby/object:Gem::Requirement
93
107
  requirements:
94
108
  - - ">="
95
109
  - !ruby/object:Gem::Version
96
- version: '4.0'
110
+ version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
- name: pry
112
+ name: sqlite3
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: activerecord
99
127
  requirement: !ruby/object:Gem::Requirement
100
128
  requirements:
101
129
  - - ">="
@@ -115,8 +143,10 @@ executables: []
115
143
  extensions: []
116
144
  extra_rdoc_files: []
117
145
  files:
146
+ - ".github/workflows/ci.yml"
118
147
  - ".gitignore"
119
148
  - ".rspec"
149
+ - CHANGELOG.md
120
150
  - CODE_OF_CONDUCT.md
121
151
  - Gemfile
122
152
  - LICENSE.txt
@@ -128,7 +158,16 @@ files:
128
158
  - lib/active_interaction/extras.rb
129
159
  - lib/active_interaction/extras/active_job.rb
130
160
  - lib/active_interaction/extras/all.rb
161
+ - lib/active_interaction/extras/filter_alias.rb
162
+ - lib/active_interaction/extras/filter_extensions.rb
163
+ - lib/active_interaction/extras/filter_extensions/hash_auto_strip.rb
164
+ - lib/active_interaction/extras/filter_extensions/object_classes.rb
165
+ - lib/active_interaction/extras/filter_extensions/timezone_support.rb
166
+ - lib/active_interaction/extras/filters/anything_filter.rb
167
+ - lib/active_interaction/extras/filters/uuid_filter.rb
131
168
  - lib/active_interaction/extras/halt.rb
169
+ - lib/active_interaction/extras/jobs/core.rb
170
+ - lib/active_interaction/extras/locale/en.yml
132
171
  - lib/active_interaction/extras/model_fields.rb
133
172
  - lib/active_interaction/extras/rspec.rb
134
173
  - lib/active_interaction/extras/run_callback.rb
@@ -139,7 +178,8 @@ files:
139
178
  homepage: https://github.com/antulik/active_interaction-extras
140
179
  licenses:
141
180
  - MIT
142
- metadata: {}
181
+ metadata:
182
+ changelog_uri: https://github.com/antulik/active_interaction-extras/blob/master/CHANGELOG.md
143
183
  post_install_message:
144
184
  rdoc_options: []
145
185
  require_paths:
@@ -155,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
155
195
  - !ruby/object:Gem::Version
156
196
  version: '0'
157
197
  requirements: []
158
- rubygems_version: 3.0.3
198
+ rubygems_version: 3.1.4
159
199
  signing_key:
160
200
  specification_version: 4
161
201
  summary: Extensions for active_interaction gem