active_interaction-extras 0.2.1 → 1.0.2

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
- 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: []