active_interaction 3.7.1 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af64e2c9d517eda9f5f86be8927f2d8d82b53f905112379ca49bb6e42e487e99
4
- data.tar.gz: 9f59937f4722d923d79611a9c99b81a0ee920e405352ee48cf01050000bc06d3
3
+ metadata.gz: 46534b15fa7a2aa90e5dcaa3917d63ac4c5aa2b1b06b265dfd284437c1d9de0e
4
+ data.tar.gz: f9c562732b0bc2335876a6325a959b9d7b440825224359eecfe6446cb32e92f9
5
5
  SHA512:
6
- metadata.gz: eb7c7b81e0105785bba793aa3ca5466daea5f49e197a5461689c45b1b5f637fdd18f1d58ed394afa7f23a28a1d8a39d16c7c146ad05055e577f89760508ee879
7
- data.tar.gz: 277a4bebd07bb0c8465851104a95b0ea9c3d7d07de4386c20082551b4bd2ce09a822687d9d891322825625daf76ec5e4606b19b97308475ec735a5ad73f42356
6
+ metadata.gz: ec99ac3c5b256f9645902a467b838567a57172d97712e2eeebf1ef6952bed1c48d48706080b122d168a682182ad8999ada7d26a5964946e3d2718dad5ad1fabe
7
+ data.tar.gz: 2749a5436350022dc15e625e24159f05e8d3205adf7a6568754aaea2c3c93aab62f82d8848549ea6a2c96611130fbb64ad424a79dd9368a696101705dad4468c
@@ -1,3 +1,10 @@
1
+ # [3.8.0][] (2020-02-28)
2
+
3
+ ## Added
4
+
5
+ - [#477][] `InvalidInteractionError` now provides access to the failing interaction by calling `interaction`.
6
+ - [#476][] Update `given?` to check for items in an array by passing an index.
7
+
1
8
  # [3.7.1][] (2019-03-20)
2
9
 
3
10
  ## Fixed
@@ -752,6 +759,7 @@ Example.run
752
759
 
753
760
  - Initial release.
754
761
 
762
+ [3.8.0]: https://github.com/AaronLasseigne/active_interaction/compare/v3.7.1...v3.8.0
755
763
  [3.7.1]: https://github.com/AaronLasseigne/active_interaction/compare/v3.7.0...v3.7.1
756
764
  [3.7.0]: https://github.com/AaronLasseigne/active_interaction/compare/v3.6.2...v3.7.0
757
765
  [3.6.2]: https://github.com/AaronLasseigne/active_interaction/compare/v3.6.1...v3.6.2
@@ -946,3 +954,5 @@ Example.run
946
954
  [#454]: https://github.com/AaronLasseigne/active_interaction/pull/454
947
955
  [#455]: https://github.com/AaronLasseigne/active_interaction/pull/455
948
956
  [#457]: https://github.com/AaronLasseigne/active_interaction/issues/457
957
+ [#477]: https://github.com/AaronLasseigne/active_interaction/issues/477
958
+ [#476]: https://github.com/AaronLasseigne/active_interaction/issues/476
data/README.md CHANGED
@@ -66,13 +66,13 @@ handles your verbs.
66
66
  Add it to your Gemfile:
67
67
 
68
68
  ``` rb
69
- gem 'active_interaction', '~> 3.7'
69
+ gem 'active_interaction', '~> 3.8'
70
70
  ```
71
71
 
72
72
  Or install it manually:
73
73
 
74
74
  ``` sh
75
- $ gem install active_interaction --version '~> 3.7'
75
+ $ gem install active_interaction --version '~> 3.8'
76
76
  ```
77
77
 
78
78
  This project uses [Semantic Versioning][]. Check out [GitHub releases][] for a
@@ -378,6 +378,25 @@ InterfaceInteraction.run!(serializer: JSON)
378
378
  # => "{\"is_json\":true}"
379
379
  ```
380
380
 
381
+ NOTE: The `methods` option is optional.
382
+
383
+ ```rb
384
+ class InterfaceInteraction < ActiveInteraction::Base
385
+ interface :anything
386
+
387
+ def execute
388
+ anything.class
389
+ end
390
+ end
391
+
392
+ require 'json'
393
+
394
+ InterfaceInteraction.run!(anything: Hash.new)
395
+ # => Hash
396
+ InterfaceInteraction.run!
397
+ # => NilClass
398
+ ```
399
+
381
400
  ### Object
382
401
 
383
402
  Object filters allow you to require an instance of a particular class. It
@@ -668,7 +687,7 @@ sensible (e.g. `base: 10`).
668
687
  class IntegerInteraction < ActiveInteraction::Base
669
688
  integer :limit1, base: 10
670
689
  integer :limit2
671
-
690
+
672
691
  def execute
673
692
  [limit1, limit2]
674
693
  end
@@ -1322,7 +1341,8 @@ whether a value was passed to `run` or the result of a filter default. In
1322
1341
  particular, it is useful when `nil` is an acceptable value. For example, you
1323
1342
  may optionally track your users' birthdays. You can use the `given?` predicate
1324
1343
  to see if an input was even passed to `run`. With `given?` you can also check
1325
- the input of a hash filter by passing a series of keys to check.
1344
+ the input of a hash or array filter by passing a series of keys or indexes to
1345
+ check.
1326
1346
 
1327
1347
  ``` rb
1328
1348
  class UpdateUser < ActiveInteraction::Base
@@ -28,7 +28,7 @@ module ActiveInteraction
28
28
  # else
29
29
  # outcome.errors
30
30
  # end
31
- class Base
31
+ class Base # rubocop:disable Metrics/ClassLength
32
32
  include ActiveModelable
33
33
  include ActiveRecordable
34
34
  include Runnable
@@ -203,7 +203,8 @@ module ActiveInteraction
203
203
  # Returns `true` if the given key was in the hash passed to {.run}.
204
204
  # Otherwise returns `false`. Use this to figure out if an input was given,
205
205
  # even if it was `nil`. Keys within nested hash filter can also be checked
206
- # by passing them in series.
206
+ # by passing them in series. Arrays can be checked in the same manor as
207
+ # hashes by passing an index.
207
208
  #
208
209
  # @example
209
210
  # class Example < ActiveInteraction::Base
@@ -219,32 +220,49 @@ module ActiveInteraction
219
220
  # hash :x, default: {} do
220
221
  # integer :y, default: nil
221
222
  # end
222
- # def execute; given?(:x, :y) end
223
+ # array :a, default: [] do
224
+ # integer
225
+ # end
226
+ # def execute; given?(:x, :y) || given?(:a, 2) end
223
227
  # end
224
228
  # Example.run!() # => false
225
229
  # Example.run!(x: nil) # => false
226
230
  # Example.run!(x: {}) # => false
227
231
  # Example.run!(x: { y: nil }) # => true
228
232
  # Example.run!(x: { y: rand }) # => true
233
+ # Example.run!(a: [1, 2]) # => false
234
+ # Example.run!(a: [1, 2, 3]) # => true
229
235
  #
230
236
  # @param input [#to_sym]
231
237
  #
232
238
  # @return [Boolean]
233
239
  #
234
240
  # @since 2.1.0
235
- def given?(input, *rest) # rubocop:disable Metrics/CyclomaticComplexity
241
+ # rubocop:disable all
242
+ def given?(input, *rest)
236
243
  filter_level = self.class
237
244
  input_level = @_interaction_inputs
238
245
 
239
- [input, *rest].map(&:to_sym).each do |key|
240
- filter_level = filter_level.filters[key]
246
+ [input, *rest].each do |key_or_index|
247
+ if key_or_index.is_a?(Symbol) || key_or_index.is_a?(String)
248
+ key_or_index = key_or_index.to_sym
249
+ filter_level = filter_level.filters[key_or_index]
250
+
251
+ break false if filter_level.nil? || input_level.nil?
252
+ break false unless input_level.key?(key_or_index) || input_level.key?(key_or_index.to_s)
241
253
 
242
- break false if filter_level.nil? || input_level.nil?
243
- break false unless input_level.key?(key) || input_level.key?(key.to_s)
254
+ input_level = input_level[key_or_index] || input_level[key_or_index.to_s]
255
+ else
256
+ filter_level = filter_level.filters.first.last
244
257
 
245
- input_level = input_level[key] || input_level[key.to_s]
258
+ break false if filter_level.nil? || input_level.nil?
259
+ break false unless key_or_index.between?(-input_level.size, input_level.size - 1)
260
+
261
+ input_level = input_level[key_or_index]
262
+ end
246
263
  end && true
247
264
  end
265
+ # rubocop:enable all
248
266
 
249
267
  protected
250
268
 
@@ -97,11 +97,9 @@ module ActiveInteraction
97
97
  def run!
98
98
  run
99
99
 
100
- unless valid?
101
- raise InvalidInteractionError, errors.full_messages.join(', ')
102
- end
100
+ return result if valid?
103
101
 
104
- result
102
+ raise InvalidInteractionError, self
105
103
  end
106
104
 
107
105
  #
@@ -31,7 +31,15 @@ module ActiveInteraction
31
31
  # Raised if an interaction is invalid.
32
32
  #
33
33
  # @return [Class]
34
- InvalidInteractionError = Class.new(Error)
34
+ class InvalidInteractionError < Error
35
+ attr_reader :interaction
36
+
37
+ def initialize(interaction)
38
+ @interaction = interaction
39
+
40
+ super(interaction.errors.full_messages.join(', '))
41
+ end
42
+ end
35
43
 
36
44
  # Raised if a user-supplied value is invalid.
37
45
  #
@@ -0,0 +1,24 @@
1
+ ja:
2
+ active_interaction:
3
+ errors:
4
+ messages:
5
+ invalid: は無効です
6
+ invalid_nested: は無効なネストされた値です (%{name} => %{value})
7
+ invalid_type: は無効な %{type} です
8
+ missing: が必要です
9
+ types:
10
+ array: 配列
11
+ boolean: ブール
12
+ date: 日付
13
+ date_time: 日時
14
+ decimal: 10進数
15
+ file: ファイル
16
+ float: 浮動小数点数
17
+ hash: ハッシュ
18
+ integer: 整数
19
+ interface: インターフェース
20
+ object: オブジェクト
21
+ record: レコード
22
+ string: 文字
23
+ symbol: シンボル
24
+ time: 時間
@@ -17,8 +17,14 @@ module ActiveInteraction
17
17
  def reserved?(name)
18
18
  name.to_s.start_with?('_interaction_') ||
19
19
  name == :syscall ||
20
- Base.method_defined?(name) ||
21
- Base.private_method_defined?(name)
20
+ (
21
+ Base.method_defined?(name) &&
22
+ !Object.method_defined?(name)
23
+ ) ||
24
+ (
25
+ Base.private_method_defined?(name) &&
26
+ !Object.private_method_defined?(name)
27
+ )
22
28
  end
23
29
 
24
30
  def process(inputs)
@@ -6,5 +6,5 @@ module ActiveInteraction
6
6
  # The version number.
7
7
  #
8
8
  # @return [Gem::Version]
9
- VERSION = Gem::Version.new('3.7.1')
9
+ VERSION = Gem::Version.new('3.8.0')
10
10
  end
@@ -505,6 +505,79 @@ describe ActiveInteraction::Base do
505
505
  expect(result).to be false
506
506
  end
507
507
  end
508
+
509
+ context 'nested array values' do
510
+ let(:described_class) do
511
+ Class.new(TestInteraction) do
512
+ array :x do
513
+ hash do
514
+ boolean :y, default: true
515
+ end
516
+ end
517
+
518
+ def execute; end
519
+ end
520
+ end
521
+
522
+ context 'has a positive index' do
523
+ it 'returns true if found' do
524
+ described_class.class_exec do
525
+ def execute
526
+ given?(:x, 0, :y)
527
+ end
528
+ end
529
+
530
+ inputs[:x] = [{ y: true }]
531
+ expect(result).to be true
532
+ end
533
+
534
+ it 'returns false if not found' do
535
+ described_class.class_exec do
536
+ def execute
537
+ given?(:x, 0, :y)
538
+ end
539
+ end
540
+
541
+ inputs[:x] = []
542
+ expect(result).to be false
543
+ end
544
+ end
545
+
546
+ context 'has a negative index' do
547
+ it 'returns true if found' do
548
+ described_class.class_exec do
549
+ def execute
550
+ given?(:x, -1, :y)
551
+ end
552
+ end
553
+
554
+ inputs[:x] = [{ y: true }]
555
+ expect(result).to be true
556
+ end
557
+
558
+ it 'returns false if not found' do
559
+ described_class.class_exec do
560
+ def execute
561
+ given?(:x, -1, :y)
562
+ end
563
+ end
564
+
565
+ inputs[:x] = []
566
+ expect(result).to be false
567
+ end
568
+ end
569
+
570
+ it 'returns false if you go too far' do
571
+ described_class.class_exec do
572
+ def execute
573
+ given?(:x, 10, :y)
574
+ end
575
+ end
576
+
577
+ inputs[:x] = [{}]
578
+ expect(result).to be false
579
+ end
580
+ end
508
581
  end
509
582
 
510
583
  context 'inheritance' do
@@ -107,11 +107,11 @@ describe ActiveInteraction::Runnable do
107
107
  context 'using if' do
108
108
  it 'yields errors to the if' do
109
109
  has_run = false
110
- # rubocop:disable Metic/LineLength
110
+ # rubocop:disable Metrics/LineLength
111
111
  WithFailingCompose.set_callback :execute, :after, if: -> { errors.any? } do
112
112
  has_run = true
113
113
  end
114
- # rubocop:enable Metic/LineLength
114
+ # rubocop:enable Metrics/LineLength
115
115
 
116
116
  WithFailingCompose.run
117
117
  expect(has_run).to be_truthy
@@ -369,6 +369,12 @@ describe ActiveInteraction::Runnable do
369
369
  result
370
370
  end.to raise_error ActiveInteraction::InvalidInteractionError
371
371
  end
372
+
373
+ it 'adds interaction instance to this error' do
374
+ expect { result }.to raise_error do |error|
375
+ expect(error.interaction).to be_a klass
376
+ end
377
+ end
372
378
  end
373
379
  end
374
380
  end
@@ -1,6 +1,17 @@
1
1
  # coding: utf-8
2
2
 
3
3
  require 'spec_helper'
4
+ require 'active_record'
5
+ unless defined?(JRUBY_VERSION) # rubocop:disable Style/IfUnlessModifier
6
+ require 'sqlite3'
7
+ end
8
+
9
+ unless defined?(JRUBY_VERSION)
10
+ ActiveRecord::Base.establish_connection(
11
+ adapter: 'sqlite3',
12
+ database: ':memory:'
13
+ )
14
+ end
4
15
 
5
16
  describe ActiveInteraction::Errors do
6
17
  let(:klass) do
@@ -148,5 +159,42 @@ describe ActiveInteraction::Errors do
148
159
  expect(errors.messages[:base]).to include message
149
160
  end
150
161
  end
162
+
163
+ unless defined?(JRUBY_VERSION)
164
+ context 'with nested errors' do
165
+ before do
166
+ # suppress create_table output
167
+ allow($stdout).to receive(:puts)
168
+ ActiveRecord::Schema.define do
169
+ create_table(:as)
170
+ create_table(:bs) do |t|
171
+ t.column :a_id, :integer
172
+ t.column :name, :string
173
+ end
174
+ end
175
+
176
+ class A < ActiveRecord::Base
177
+ has_one :b
178
+ accepts_nested_attributes_for :b
179
+ end
180
+
181
+ class B < ActiveRecord::Base
182
+ belongs_to :a
183
+
184
+ validates :name, presence: true
185
+ end
186
+ end
187
+
188
+ let(:a) { A.create(b_attributes: { name: nil }) }
189
+
190
+ it 'merges the nested errors' do
191
+ a.valid?
192
+ expect(a.errors.messages).to eql(:'b.name' => ["can't be blank"])
193
+ expect(a.errors.size).to eql 1
194
+ expect { errors.merge!(a.errors) }.to_not raise_error
195
+ expect(errors.size).to eql 1
196
+ end
197
+ end
198
+ end
151
199
  end
152
200
  end
@@ -1,14 +1,28 @@
1
1
  # coding: utf-8
2
2
 
3
3
  require 'spec_helper'
4
+ require 'active_record'
5
+ unless defined?(JRUBY_VERSION) # rubocop:disable Style/IfUnlessModifier
6
+ require 'sqlite3'
7
+ end
8
+
9
+ unless defined?(JRUBY_VERSION)
10
+ ActiveRecord::Base.establish_connection(
11
+ adapter: 'sqlite3',
12
+ database: ':memory:'
13
+ )
4
14
 
5
- module ActiveRecord
6
- class Relation
15
+ ActiveRecord::Schema.define do
16
+ create_table(:lists)
17
+ create_table(:elements) { |t| t.column(:list_id, :integer) }
7
18
  end
8
19
 
9
- module Associations
10
- class CollectionProxy
11
- end
20
+ class List < ActiveRecord::Base
21
+ has_many :elements
22
+ end
23
+
24
+ class Element < ActiveRecord::Base
25
+ belongs_to :list
12
26
  end
13
27
  end
14
28
 
@@ -24,9 +38,10 @@ end
24
38
  describe ArrayInteraction do
25
39
  include_context 'interactions'
26
40
  it_behaves_like 'an interaction', :array, -> { [] }
27
- it_behaves_like 'an interaction', :array, -> { ActiveRecord::Relation.new }
28
- it_behaves_like 'an interaction', :array,
29
- -> { ActiveRecord::Associations::CollectionProxy.new }
41
+ unless defined?(JRUBY_VERSION)
42
+ it_behaves_like 'an interaction', :array, -> { Element.where('1 = 1') }
43
+ it_behaves_like 'an interaction', :array, -> { List.create!.elements }
44
+ end
30
45
 
31
46
  context 'with inputs[:a]' do
32
47
  let(:a) { [[]] }
@@ -8,14 +8,16 @@ describe ActiveInteraction::InputProcessor do
8
8
  expect(described_class.reserved?('_interaction_')).to be_truthy
9
9
  end
10
10
 
11
+ # rubocop:disable Metrics/LineLength
11
12
  it 'returns true for existing instance methods' do
12
13
  (
13
- ActiveInteraction::Base.instance_methods +
14
- ActiveInteraction::Base.private_instance_methods
14
+ (ActiveInteraction::Base.instance_methods - Object.instance_methods) +
15
+ (ActiveInteraction::Base.private_instance_methods - Object.private_instance_methods)
15
16
  ).each do |method|
16
17
  expect(described_class.reserved?(method)).to be_truthy
17
18
  end
18
19
  end
20
+ # rubocop:enable Metrics/LineLength
19
21
 
20
22
  it 'returns false for anything else' do
21
23
  expect(described_class.reserved?(SecureRandom.hex)).to be_falsey
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_interaction
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.7.1
4
+ version: 3.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Lasseigne
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-03-20 00:00:00.000000000 Z
12
+ date: 2020-02-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
@@ -45,6 +45,20 @@ dependencies:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
+ - !ruby/object:Gem::Dependency
49
+ name: activerecord
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
48
62
  - !ruby/object:Gem::Dependency
49
63
  name: benchmark-ips
50
64
  requirement: !ruby/object:Gem::Requirement
@@ -143,6 +157,20 @@ dependencies:
143
157
  - - "~>"
144
158
  - !ruby/object:Gem::Version
145
159
  version: '0.9'
160
+ - !ruby/object:Gem::Dependency
161
+ name: sqlite3
162
+ requirement: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '='
165
+ - !ruby/object:Gem::Version
166
+ version: 1.4.2
167
+ type: :development
168
+ prerelease: false
169
+ version_requirements: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '='
172
+ - !ruby/object:Gem::Version
173
+ version: 1.4.2
146
174
  description: |2
147
175
  ActiveInteraction manages application-specific business logic. It is an
148
176
  implementation of the command pattern in Ruby.
@@ -190,6 +218,7 @@ files:
190
218
  - lib/active_interaction/locale/en.yml
191
219
  - lib/active_interaction/locale/fr.yml
192
220
  - lib/active_interaction/locale/it.yml
221
+ - lib/active_interaction/locale/ja.yml
193
222
  - lib/active_interaction/locale/pt-BR.yml
194
223
  - lib/active_interaction/modules/input_processor.rb
195
224
  - lib/active_interaction/modules/validation.rb
@@ -264,7 +293,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
264
293
  - !ruby/object:Gem::Version
265
294
  version: '0'
266
295
  requirements: []
267
- rubygems_version: 3.0.3
296
+ rubygems_version: 3.1.2
268
297
  signing_key:
269
298
  specification_version: 4
270
299
  summary: Manage application specific business logic.