active_interaction 3.7.1 → 3.8.0

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: 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.