active_interaction 0.5.0 → 0.6.1

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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -3
  3. data/README.md +8 -6
  4. data/lib/active_interaction.rb +5 -3
  5. data/lib/active_interaction/active_model.rb +29 -0
  6. data/lib/active_interaction/base.rb +82 -116
  7. data/lib/active_interaction/errors.rb +79 -5
  8. data/lib/active_interaction/filter.rb +195 -21
  9. data/lib/active_interaction/filters.rb +26 -0
  10. data/lib/active_interaction/filters/array_filter.rb +22 -25
  11. data/lib/active_interaction/filters/boolean_filter.rb +12 -12
  12. data/lib/active_interaction/filters/date_filter.rb +32 -5
  13. data/lib/active_interaction/filters/date_time_filter.rb +34 -7
  14. data/lib/active_interaction/filters/file_filter.rb +12 -9
  15. data/lib/active_interaction/filters/float_filter.rb +13 -11
  16. data/lib/active_interaction/filters/hash_filter.rb +36 -17
  17. data/lib/active_interaction/filters/integer_filter.rb +13 -11
  18. data/lib/active_interaction/filters/model_filter.rb +15 -15
  19. data/lib/active_interaction/filters/string_filter.rb +19 -8
  20. data/lib/active_interaction/filters/symbol_filter.rb +29 -0
  21. data/lib/active_interaction/filters/time_filter.rb +38 -16
  22. data/lib/active_interaction/method_missing.rb +18 -0
  23. data/lib/active_interaction/overload_hash.rb +1 -0
  24. data/lib/active_interaction/validation.rb +19 -0
  25. data/lib/active_interaction/version.rb +1 -1
  26. data/spec/active_interaction/active_model_spec.rb +33 -0
  27. data/spec/active_interaction/base_spec.rb +54 -48
  28. data/spec/active_interaction/errors_spec.rb +99 -0
  29. data/spec/active_interaction/filter_spec.rb +12 -20
  30. data/spec/active_interaction/filters/array_filter_spec.rb +50 -28
  31. data/spec/active_interaction/filters/boolean_filter_spec.rb +15 -15
  32. data/spec/active_interaction/filters/date_filter_spec.rb +30 -18
  33. data/spec/active_interaction/filters/date_time_filter_spec.rb +31 -19
  34. data/spec/active_interaction/filters/file_filter_spec.rb +7 -7
  35. data/spec/active_interaction/filters/float_filter_spec.rb +13 -11
  36. data/spec/active_interaction/filters/hash_filter_spec.rb +38 -29
  37. data/spec/active_interaction/filters/integer_filter_spec.rb +18 -8
  38. data/spec/active_interaction/filters/model_filter_spec.rb +24 -20
  39. data/spec/active_interaction/filters/string_filter_spec.rb +14 -8
  40. data/spec/active_interaction/filters/symbol_filter_spec.rb +24 -0
  41. data/spec/active_interaction/filters/time_filter_spec.rb +33 -69
  42. data/spec/active_interaction/filters_spec.rb +21 -0
  43. data/spec/active_interaction/i18n_spec.rb +0 -15
  44. data/spec/active_interaction/integration/array_interaction_spec.rb +2 -22
  45. data/spec/active_interaction/integration/hash_interaction_spec.rb +5 -25
  46. data/spec/active_interaction/integration/symbol_interaction_spec.rb +5 -0
  47. data/spec/active_interaction/method_missing_spec.rb +69 -0
  48. data/spec/active_interaction/validation_spec.rb +55 -0
  49. data/spec/spec_helper.rb +6 -0
  50. data/spec/support/filters.rb +168 -14
  51. data/spec/support/interactions.rb +11 -13
  52. metadata +31 -13
  53. data/lib/active_interaction/filter_method.rb +0 -13
  54. data/lib/active_interaction/filter_methods.rb +0 -26
  55. data/lib/active_interaction/filters/abstract_date_time_filter.rb +0 -25
  56. data/spec/active_interaction/filter_method_spec.rb +0 -43
  57. data/spec/active_interaction/filter_methods_spec.rb +0 -30
@@ -1,58 +1,67 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ActiveInteraction::HashFilter do
3
+ describe ActiveInteraction::HashFilter, :filter do
4
4
  include_context 'filters'
5
5
  it_behaves_like 'a filter'
6
6
 
7
- describe '.prepare(key, value, options = {}, &block)' do
7
+ context 'with a nested nameless filter' do
8
+ let(:block) { Proc.new { hash } }
9
+
10
+ it 'raises an error' do
11
+ expect { filter }.to raise_error ActiveInteraction::InvalidFilterError
12
+ end
13
+ end
14
+
15
+ describe '#cast' do
8
16
  context 'with a Hash' do
9
17
  let(:value) { {} }
10
18
 
11
19
  it 'returns the Hash' do
12
- expect(result).to eql value
20
+ expect(filter.cast(value)).to eq value
13
21
  end
14
22
  end
15
23
 
16
- context 'with a block' do
24
+ context 'with a non-empty Hash' do
25
+ let(:value) { { a: {} } }
26
+
27
+ it 'returns an empty Hash' do
28
+ expect(filter.cast(value)).to eq({})
29
+ end
30
+ end
31
+
32
+ context 'with a nested filter' do
17
33
  let(:block) { Proc.new { hash :a } }
18
34
 
19
- context 'with a Hash containing a Hash' do
35
+ context 'with a Hash' do
20
36
  let(:value) { { a: {} } }
21
37
 
22
38
  it 'returns the Hash' do
23
- expect(result).to eql value
24
- end
25
- end
26
-
27
- context 'with a Hash containing anything else' do
28
- let(:value) { { a: Object.new } }
29
-
30
- it 'raises an error' do
31
- expect {
32
- result
33
- }.to raise_error ActiveInteraction::InvalidNestedValue
39
+ expect(filter.cast(value)).to eq value
34
40
  end
35
41
  end
36
42
  end
43
+ end
37
44
 
38
- context 'with a block with multiple filters' do
39
- let(:block) { Proc.new { hash :a; hash :b } }
40
-
41
- context 'with a Hash containing Hashes' do
42
- let(:value) { { a: {}, b: {} } }
45
+ describe '#default' do
46
+ context 'with a Hash' do
47
+ before do
48
+ options.merge!(default: {})
49
+ end
43
50
 
44
- it 'returns the Hash' do
45
- expect(result).to eql value
46
- end
51
+ it 'returns the Hash' do
52
+ expect(filter.default).to eq options[:default]
47
53
  end
48
54
  end
49
55
 
50
- context 'with a nested block' do
51
- let(:block) { Proc.new { hash :a do; hash :b end } }
52
- let(:value) { { a: { b: {} } } }
56
+ context 'with a non-empty Hash' do
57
+ before do
58
+ options.merge!(default: { a: {} })
59
+ end
53
60
 
54
- it 'returns the Hash' do
55
- expect(result).to eql value
61
+ it 'raises an error' do
62
+ expect {
63
+ filter.default
64
+ }.to raise_error ActiveInteraction::InvalidDefaultError
56
65
  end
57
66
  end
58
67
  end
@@ -1,31 +1,41 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ActiveInteraction::IntegerFilter do
3
+ describe ActiveInteraction::IntegerFilter, :filter do
4
4
  include_context 'filters'
5
5
  it_behaves_like 'a filter'
6
6
 
7
- describe '.prepare(key, value, options = {}, &block)' do
7
+ describe '#cast' do
8
8
  context 'with an Integer' do
9
9
  let(:value) { rand(1 << 16) }
10
10
 
11
11
  it 'returns the Integer' do
12
- expect(result).to eql value
12
+ expect(filter.cast(value)).to eq value
13
13
  end
14
14
  end
15
15
 
16
- context 'with a valid String' do
16
+ context 'with a Numeric' do
17
+ let(:value) { rand(1 << 16) + rand }
18
+
19
+ it 'returns an Integer' do
20
+ expect(filter.cast(value)).to eq value.to_i
21
+ end
22
+ end
23
+
24
+ context 'with a String' do
17
25
  let(:value) { rand(1 << 16).to_s }
18
26
 
19
- it 'converts the String' do
20
- expect(result).to eql Integer(value)
27
+ it 'returns an Integer' do
28
+ expect(filter.cast(value)).to eq Integer(value)
21
29
  end
22
30
  end
23
31
 
24
32
  context 'with an invalid String' do
25
- let(:value) { 'not a valid Integer' }
33
+ let(:value) { 'invalid' }
26
34
 
27
35
  it 'raises an error' do
28
- expect { result }.to raise_error ActiveInteraction::InvalidValue
36
+ expect {
37
+ filter.cast(value)
38
+ }.to raise_error ActiveInteraction::InvalidValueError
29
39
  end
30
40
  end
31
41
  end
@@ -1,39 +1,43 @@
1
1
  require 'spec_helper'
2
2
 
3
- TestModel = Class.new
3
+ class Model; end
4
4
 
5
- describe ActiveInteraction::ModelFilter do
5
+ describe ActiveInteraction::ModelFilter, :filter do
6
6
  include_context 'filters'
7
7
  it_behaves_like 'a filter'
8
8
 
9
- before { options.merge!(class: TestModel) }
9
+ before do
10
+ options.merge!(class: Model)
11
+ end
10
12
 
11
- describe '.prepare(key, value, options = {}, &block)' do
12
- shared_examples 'type checking' do
13
- context 'with the right class' do
14
- let(:value) { TestModel.new }
13
+ describe '#cast' do
14
+ let(:value) { Model.new }
15
15
 
16
- it 'returns the instance' do
17
- expect(result).to eql value
18
- end
16
+ context 'with class as a Class' do
17
+ it 'returns the instance' do
18
+ expect(filter.cast(value)).to eq value
19
19
  end
20
20
  end
21
21
 
22
- context 'with options[:class] as a Class' do
23
- include_examples 'type checking'
24
- end
25
-
26
- context 'with options[:class] as a valid String' do
27
- include_examples 'type checking'
22
+ context 'with class as a String' do
23
+ before do
24
+ options.merge!(class: Model.name)
25
+ end
28
26
 
29
- before { options.merge!(class: options[:class].to_s) }
27
+ it 'returns the instance' do
28
+ expect(filter.cast(value)).to eq value
29
+ end
30
30
  end
31
31
 
32
- context 'with options[:class] as an invalid String' do
33
- before { options.merge!(class: 'not a valid Class') }
32
+ context 'with class as an invalid String' do
33
+ before do
34
+ options.merge!(class: 'invalid')
35
+ end
34
36
 
35
37
  it 'raises an error' do
36
- expect { result }.to raise_error NameError
38
+ expect {
39
+ filter.cast(value)
40
+ }.to raise_error ActiveInteraction::InvalidClassError
37
41
  end
38
42
  end
39
43
  end
@@ -1,30 +1,36 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ActiveInteraction::StringFilter do
3
+ describe ActiveInteraction::StringFilter, :filter do
4
4
  include_context 'filters'
5
5
  it_behaves_like 'a filter'
6
6
 
7
- describe '.prepare(key, value, options = {}, &block)' do
7
+ shared_context 'without strip' do
8
+ before do
9
+ options.merge!(strip: false)
10
+ end
11
+ end
12
+
13
+ describe '#cast' do
8
14
  context 'with a String' do
9
15
  let(:value) { SecureRandom.hex }
10
16
 
11
17
  it 'returns the String' do
12
- expect(result).to eql value
18
+ expect(filter.cast(value)).to eq value
13
19
  end
14
20
  end
15
21
 
16
22
  context 'with a strippable String' do
17
23
  let(:value) { " #{SecureRandom.hex} " }
18
24
 
19
- it 'returns the stripped String' do
20
- expect(result).to eql value.strip
25
+ it 'returns the stripped string' do
26
+ expect(filter.cast(value)).to eq value.strip
21
27
  end
22
28
 
23
- context 'with options[:strip] as false' do
24
- before { options.merge!(strip: false) }
29
+ context 'without strip' do
30
+ include_context 'without strip'
25
31
 
26
32
  it 'returns the String' do
27
- expect(result).to eql value
33
+ expect(filter.cast(value)).to eq value
28
34
  end
29
35
  end
30
36
  end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveInteraction::SymbolFilter, :filter do
4
+ include_context 'filters'
5
+ it_behaves_like 'a filter'
6
+
7
+ describe '#cast' do
8
+ context 'with a Symbol' do
9
+ let(:value) { SecureRandom.hex.to_sym }
10
+
11
+ it 'returns the Symbol' do
12
+ expect(filter.cast(value)).to eq value
13
+ end
14
+ end
15
+
16
+ context 'with a String' do
17
+ let(:value) { SecureRandom.hex }
18
+
19
+ it 'returns a Symbol' do
20
+ expect(filter.cast(value)).to eq value.to_sym
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,96 +1,60 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ActiveInteraction::TimeFilter do
3
+ describe ActiveInteraction::TimeFilter, :filter do
4
4
  include_context 'filters'
5
5
  it_behaves_like 'a filter'
6
6
 
7
- describe '.prepare(key, value, options = {}, &block)' do
8
- context 'with a Time' do
9
- let(:value) { Time.now }
7
+ shared_context 'with format' do
8
+ let(:format) { '%d/%m/%Y %H:%M:%S %z' }
10
9
 
11
- it 'returns the Time' do
12
- expect(result).to eql value
13
- end
10
+ before do
11
+ options.merge!(format: format)
14
12
  end
13
+ end
15
14
 
16
- shared_examples 'conversion' do
17
- context 'with a Float' do
18
- let(:value) { rand }
19
-
20
- it 'converts the Float' do
21
- expect(result).to eql Time.at(value)
22
- end
23
- end
24
-
25
- context 'with an Integer' do
26
- let(:value) { rand(1 << 16) }
15
+ describe '#cast' do
16
+ context 'with a Time' do
17
+ let(:value) { Time.new }
27
18
 
28
- it 'converts the Integer' do
29
- expect(result).to eql Time.at(value)
30
- end
19
+ it 'returns the Time' do
20
+ expect(filter.cast(value)).to eq value
31
21
  end
22
+ end
32
23
 
33
- context 'with a valid String' do
34
- let(:value) { '2001-01-01T01:01:01+01:01' }
35
-
36
- it 'parses the String' do
37
- expect(result).to eql Time.parse(value)
38
- end
39
-
40
- context 'with options[:format]' do
41
- let(:value) { '01010101012001' }
42
-
43
- before { options.merge!(format: '%S%M%H%d%m%Y') }
24
+ context 'with a String' do
25
+ let(:value) { '2011-12-13 14:15:16 +1718' }
44
26
 
45
- it 'parses the String' do
46
- expect(result).to eql Time.strptime(value, options[:format])
47
- end
48
- end
27
+ it 'returns a Time' do
28
+ expect(filter.cast(value)).to eq Time.parse(value)
49
29
  end
50
30
 
51
- context 'with an invalid String' do
52
- let(:value) { 'not a valid Time' }
31
+ context 'with format' do
32
+ include_context 'with format'
53
33
 
54
- it 'raises an error' do
55
- expect { result }.to raise_error ActiveInteraction::InvalidValue
56
- end
57
-
58
- context 'with options[:format]' do
59
- before { options.merge!(format: '%S%M%H%d%m%Y') }
34
+ let(:value) { '13/12/2011 14:15:16 +1718' }
60
35
 
61
- it 'raises an error' do
62
- expect { result }.to raise_error ActiveInteraction::InvalidValue
63
- end
36
+ it 'returns a Time' do
37
+ expect(filter.cast(value)).to eq Time.strptime(value, format)
64
38
  end
65
39
  end
66
40
  end
67
41
 
68
- context 'without Time.zone' do
69
- include_examples 'conversion'
70
- end
42
+ context 'with an invalid String' do
43
+ let(:value) { 'invalid' }
71
44
 
72
- context 'with Time.zone' do
73
- context 'as nil' do
74
- include_examples 'conversion'
75
-
76
- before do
77
- allow(Time).to receive(:zone).and_return(nil)
78
- end
79
-
80
- after do
81
- expect(Time).to have_received(:zone).once.with(no_args)
82
- end
45
+ it 'raises an error' do
46
+ expect {
47
+ filter.cast(value)
48
+ }.to raise_error ActiveInteraction::InvalidValueError
83
49
  end
84
50
 
85
- context 'as Time' do
86
- include_examples 'conversion'
87
-
88
- before do
89
- allow(Time).to receive(:zone).and_return(Time)
90
- end
51
+ context 'with format' do
52
+ include_context 'with format'
91
53
 
92
- after do
93
- expect(Time).to have_received(:zone).twice.with(no_args)
54
+ it do
55
+ expect {
56
+ filter.cast(value)
57
+ }.to raise_error ActiveInteraction::InvalidValueError
94
58
  end
95
59
  end
96
60
  end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveInteraction::Filters do
4
+ describe '#each' do
5
+ it 'returns an Enumerator' do
6
+ expect(subject.each).to be_an Enumerator
7
+ end
8
+ end
9
+
10
+ describe '#add(filter)' do
11
+ it 'returns self' do
12
+ expect(subject.add(double)).to equal subject
13
+ end
14
+
15
+ it 'adds the filter' do
16
+ filter = double
17
+
18
+ expect(subject.add(filter).to_a).to eql [filter]
19
+ end
20
+ end
21
+ end
@@ -52,21 +52,6 @@ describe I18nInteraction do
52
52
  end
53
53
  end
54
54
 
55
- context ':invalid_nested' do
56
- let(:key) {
57
- "#{described_class.i18n_scope}.errors.messages.invalid_nested"
58
- }
59
-
60
- it 'has a translation' do
61
- expect { translation }.to_not raise_error
62
- end
63
-
64
- it 'returns the translation' do
65
- options.merge!(a: { x: Object.new })
66
- expect(outcome.errors[:a]).to eq [translation]
67
- end
68
- end
69
-
70
55
  context ':missing' do
71
56
  let(:key) { "#{described_class.i18n_scope}.errors.messages.missing" }
72
57