active_interaction 0.1.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 (53) hide show
  1. data/CHANGELOG.md +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.md +202 -0
  4. data/lib/active_interaction.rb +23 -0
  5. data/lib/active_interaction/base.rb +162 -0
  6. data/lib/active_interaction/errors.rb +6 -0
  7. data/lib/active_interaction/filter.rb +41 -0
  8. data/lib/active_interaction/filter_method.rb +17 -0
  9. data/lib/active_interaction/filter_methods.rb +26 -0
  10. data/lib/active_interaction/filters/array_filter.rb +56 -0
  11. data/lib/active_interaction/filters/boolean_filter.rb +30 -0
  12. data/lib/active_interaction/filters/date_filter.rb +31 -0
  13. data/lib/active_interaction/filters/date_time_filter.rb +31 -0
  14. data/lib/active_interaction/filters/file_filter.rb +38 -0
  15. data/lib/active_interaction/filters/float_filter.rb +32 -0
  16. data/lib/active_interaction/filters/hash_filter.rb +47 -0
  17. data/lib/active_interaction/filters/integer_filter.rb +31 -0
  18. data/lib/active_interaction/filters/model_filter.rb +40 -0
  19. data/lib/active_interaction/filters/string_filter.rb +25 -0
  20. data/lib/active_interaction/filters/time_filter.rb +44 -0
  21. data/lib/active_interaction/overload_hash.rb +11 -0
  22. data/lib/active_interaction/version.rb +3 -0
  23. data/spec/active_interaction/base_spec.rb +175 -0
  24. data/spec/active_interaction/filter_method_spec.rb +48 -0
  25. data/spec/active_interaction/filter_methods_spec.rb +30 -0
  26. data/spec/active_interaction/filter_spec.rb +29 -0
  27. data/spec/active_interaction/filters/array_filter_spec.rb +54 -0
  28. data/spec/active_interaction/filters/boolean_filter_spec.rb +40 -0
  29. data/spec/active_interaction/filters/date_filter_spec.rb +32 -0
  30. data/spec/active_interaction/filters/date_time_filter_spec.rb +32 -0
  31. data/spec/active_interaction/filters/file_filter_spec.rb +32 -0
  32. data/spec/active_interaction/filters/float_filter_spec.rb +40 -0
  33. data/spec/active_interaction/filters/hash_filter_spec.rb +57 -0
  34. data/spec/active_interaction/filters/integer_filter_spec.rb +32 -0
  35. data/spec/active_interaction/filters/model_filter_spec.rb +40 -0
  36. data/spec/active_interaction/filters/string_filter_spec.rb +16 -0
  37. data/spec/active_interaction/filters/time_filter_spec.rb +66 -0
  38. data/spec/active_interaction/integration/array_interaction_spec.rb +69 -0
  39. data/spec/active_interaction/integration/boolean_interaction_spec.rb +5 -0
  40. data/spec/active_interaction/integration/date_interaction_spec.rb +5 -0
  41. data/spec/active_interaction/integration/date_time_interaction_spec.rb +5 -0
  42. data/spec/active_interaction/integration/file_interaction_spec.rb +5 -0
  43. data/spec/active_interaction/integration/float_interaction_spec.rb +5 -0
  44. data/spec/active_interaction/integration/hash_interaction_spec.rb +69 -0
  45. data/spec/active_interaction/integration/integer_interaction_spec.rb +5 -0
  46. data/spec/active_interaction/integration/model_interaction_spec.rb +5 -0
  47. data/spec/active_interaction/integration/string_interaction_spec.rb +5 -0
  48. data/spec/active_interaction/integration/time_interaction_spec.rb +5 -0
  49. data/spec/active_interaction/overload_hash_spec.rb +41 -0
  50. data/spec/spec_helper.rb +6 -0
  51. data/spec/support/filters.rb +37 -0
  52. data/spec/support/interactions.rb +79 -0
  53. metadata +278 -0
@@ -0,0 +1,11 @@
1
+ module ActiveInteraction
2
+ module OverloadHash
3
+ def hash(*args, &block)
4
+ if args.empty? && !block_given?
5
+ super
6
+ else
7
+ method_missing(:hash, *args, &block)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveInteraction
2
+ VERSION = Gem::Version.new('0.1.1')
3
+ end
@@ -0,0 +1,175 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveInteraction::Base do
4
+ let(:options) { {} }
5
+ subject(:interaction) { described_class.new(options) }
6
+
7
+ class InteractionWithAttribute < described_class
8
+ attr_reader :thing
9
+
10
+ validates :thing, presence: true
11
+
12
+ def execute
13
+ thing
14
+ end
15
+ end
16
+
17
+ class InteractionWithFilter < described_class
18
+ float :thing
19
+
20
+ def execute
21
+ thing
22
+ end
23
+ end
24
+
25
+ class InteractionWithFilters < described_class
26
+ float :thing1, :thing2
27
+
28
+ def execute; end
29
+ end
30
+
31
+ describe '.new(options = {})' do
32
+ it 'does not allow :result as an option' do
33
+ options.merge!(result: nil)
34
+ expect { interaction }.to raise_error ArgumentError
35
+ end
36
+
37
+ it 'does not allow "result" as an option' do
38
+ options.merge!('result' => nil)
39
+ expect { interaction }.to raise_error ArgumentError
40
+ end
41
+
42
+ describe InteractionWithAttribute do
43
+ let(:described_class) { InteractionWithAttribute }
44
+ let(:thing) { SecureRandom.hex }
45
+
46
+ context 'failing validations' do
47
+ before { options.merge!(thing: nil) }
48
+
49
+ it 'returns an invalid outcome' do
50
+ expect(interaction).to be_invalid
51
+ end
52
+ end
53
+
54
+ context 'passing validations' do
55
+ before { options.merge!(thing: thing) }
56
+
57
+ it 'returns a valid outcome' do
58
+ expect(interaction).to be_valid
59
+ end
60
+
61
+ it 'sets the attribute' do
62
+ expect(interaction.thing).to eq thing
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ describe '.method_missing(filter_type, *args, &block)' do
69
+ it 'raises an error for invalid filter types' do
70
+ expect {
71
+ class InteractionWithInvalidFilter < described_class
72
+ not_a_valid_filter_type :thing
73
+ def execute; end
74
+ end
75
+ }.to raise_error NoMethodError
76
+ end
77
+
78
+ describe InteractionWithFilter do
79
+ let(:described_class) { InteractionWithFilter }
80
+
81
+ it 'adds an attr_reader' do
82
+ expect(interaction).to respond_to :thing
83
+ end
84
+
85
+ it 'adds an attr_writer' do
86
+ expect(interaction).to respond_to :thing=
87
+ end
88
+ end
89
+
90
+ describe InteractionWithFilters do
91
+ let(:described_class) { InteractionWithFilters }
92
+
93
+ %w(thing1 thing2).each do |thing|
94
+ it "adds an attr_reader for #{thing}" do
95
+ expect(interaction).to respond_to thing
96
+ end
97
+
98
+ it "adds an attr_writer for #{thing}" do
99
+ expect(interaction).to respond_to "#{thing}="
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ describe InteractionWithFilter do
106
+ let(:described_class) { InteractionWithFilter }
107
+ let(:thing) { rand }
108
+
109
+ describe '.run(options = {})' do
110
+ subject(:outcome) { described_class.run(options) }
111
+
112
+ it "returns an instance of #{described_class}" do
113
+ expect(outcome).to be_a described_class
114
+ end
115
+
116
+ context 'failing validations' do
117
+ it 'returns an invalid outcome' do
118
+ expect(outcome).to be_invalid
119
+ end
120
+
121
+ it 'sets the result to nil' do
122
+ expect(outcome.result).to be_nil
123
+ end
124
+ end
125
+
126
+ context 'passing validations' do
127
+ before { options.merge!(thing: thing) }
128
+
129
+ it 'returns a valid outcome' do
130
+ expect(outcome).to be_valid
131
+ end
132
+
133
+ it 'sets the result' do
134
+ expect(outcome.result).to eq thing
135
+ end
136
+ end
137
+ end
138
+
139
+ describe '.run!(options = {})' do
140
+ subject(:result) { described_class.run!(options) }
141
+
142
+ context 'failing validations' do
143
+ it 'raises an error' do
144
+ expect { result }.to raise_error ActiveInteraction::InteractionInvalid
145
+ end
146
+ end
147
+
148
+ context 'passing validations' do
149
+ before { options.merge!(thing: thing) }
150
+
151
+ it 'returns the result' do
152
+ expect(result).to eq thing
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ describe '#execute' do
159
+ it 'raises an error' do
160
+ expect { interaction.execute }.to raise_error NotImplementedError
161
+ end
162
+ end
163
+
164
+ describe '#new_record?' do
165
+ it 'returns true' do
166
+ expect(interaction).to be_new_record
167
+ end
168
+ end
169
+
170
+ describe '#persisted?' do
171
+ it 'returns false' do
172
+ expect(interaction).to_not be_persisted
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveInteraction::FilterMethod do
4
+ describe '.new(method_name, *args, &block)' do
5
+ let(:method_name) { SecureRandom.hex }
6
+ let(:attribute) { nil }
7
+ let(:options) { {} }
8
+ let(:args) { [] }
9
+ let(:block) { nil }
10
+ subject(:filter_method) { described_class.new(method_name, *args, &block) }
11
+
12
+ shared_examples 'instance variable assignment' do
13
+ its(:method_name) { should equal method_name }
14
+ its(:attribute) { should equal attribute }
15
+ its(:options) { should eq options }
16
+ its(:block) { should equal block }
17
+ end
18
+
19
+ include_examples 'instance variable assignment'
20
+
21
+ context 'with an attribute' do
22
+ let(:attribute) { SecureRandom.hex.to_sym }
23
+
24
+ before { args << attribute }
25
+
26
+ include_examples 'instance variable assignment'
27
+ end
28
+
29
+ context 'with options' do
30
+ let(:options) { { nil => nil } }
31
+
32
+ before { args << options }
33
+
34
+ include_examples 'instance variable assignment'
35
+
36
+ it 'does not allow :default' do
37
+ options.merge!(default: nil)
38
+ expect { filter_method }.to raise_error ArgumentError
39
+ end
40
+ end
41
+
42
+ context 'with a block' do
43
+ let(:block) { Proc.new {} }
44
+
45
+ include_examples 'instance variable assignment'
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveInteraction::FilterMethods do
4
+ let(:block) { Proc.new {} }
5
+ subject(:filter_methods) { described_class.evaluate(&block) }
6
+
7
+ describe '.evaluate(&block)' do
8
+ it "returns an instance of #{described_class}" do
9
+ expect(filter_methods).to be_a described_class
10
+ end
11
+
12
+ it 'does not add any filter methods' do
13
+ expect(filter_methods.count).to eq 0
14
+ end
15
+
16
+ context 'with a filter method' do
17
+ let(:block) { Proc.new { boolean } }
18
+
19
+ it 'adds a filter method' do
20
+ expect(filter_methods.count).to eq 1
21
+ end
22
+ end
23
+ end
24
+
25
+ describe '#each' do
26
+ it 'returns an Enumerator' do
27
+ expect(filter_methods.each).to be_an Enumerator
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ module ActiveInteraction
4
+ class TestFilter < Filter; end
5
+ end
6
+
7
+ describe ActiveInteraction::Filter do
8
+ it_behaves_like 'a filter'
9
+
10
+ describe '.factory(type)' do
11
+ let(:result) { described_class.factory(type) }
12
+
13
+ context 'with a valid type' do
14
+ let(:type) { :test }
15
+
16
+ it 'returns the Class' do
17
+ expect(result).to eql ActiveInteraction::TestFilter
18
+ end
19
+ end
20
+
21
+ context 'with an invalid type' do
22
+ let(:type) { :not_a_valid_type }
23
+
24
+ it 'raises an error' do
25
+ expect { result }.to raise_error NoMethodError
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveInteraction::ArrayFilter do
4
+ include_context 'filters'
5
+ it_behaves_like 'a filter'
6
+
7
+ describe '.prepare(key, value, options = {}, &block)' do
8
+ context 'with an Array' do
9
+ let(:value) { [] }
10
+
11
+ it 'returns the Array' do
12
+ expect(result).to eql value
13
+ end
14
+ end
15
+
16
+ context 'with block as a valid block' do
17
+ let(:block) { Proc.new { array } }
18
+
19
+ context 'with an Array of Arrays' do
20
+ let(:value) { [[]] }
21
+
22
+ it 'returns the Array' do
23
+ expect(result).to eql value
24
+ end
25
+ end
26
+
27
+ context 'with an Array of anything else' do
28
+ let(:value) { [Object.new] }
29
+
30
+ it 'raises an error' do
31
+ expect { result }.to raise_error ActiveInteraction::InvalidValue
32
+ end
33
+ end
34
+ end
35
+
36
+ context 'with block as a nested block' do
37
+ let(:block) { Proc.new { array { array } } }
38
+ let(:value) { [[[]]] }
39
+
40
+ it 'returns the Array' do
41
+ expect(result).to eql value
42
+ end
43
+ end
44
+
45
+ context 'with block as an invalid block' do
46
+ let(:block) { Proc.new { array; array } }
47
+ let(:value) { [] }
48
+
49
+ it 'raises an error' do
50
+ expect { result }.to raise_error ArgumentError
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveInteraction::BooleanFilter do
4
+ include_context 'filters'
5
+ it_behaves_like 'a filter'
6
+
7
+ describe '.prepare(key, value, options = {}, &block)' do
8
+ context 'with true' do
9
+ let(:value) { true }
10
+
11
+ it 'returns true' do
12
+ expect(result).to eql true
13
+ end
14
+ end
15
+
16
+ context 'with false' do
17
+ let(:value) { false }
18
+
19
+ it 'returns false' do
20
+ expect(result).to eql false
21
+ end
22
+ end
23
+
24
+ context 'with "1"' do
25
+ let(:value) { '1' }
26
+
27
+ it 'returns true' do
28
+ expect(result).to eql true
29
+ end
30
+ end
31
+
32
+ context 'with "0"' do
33
+ let(:value) { '0' }
34
+
35
+ it 'returns false' do
36
+ expect(result).to eql false
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveInteraction::DateFilter do
4
+ include_context 'filters'
5
+ it_behaves_like 'a filter'
6
+
7
+ describe '.prepare(key, value, options = {}, &block)' do
8
+ context 'with a Date' do
9
+ let(:value) { Date.today }
10
+
11
+ it 'returns the Date' do
12
+ expect(result).to eql value
13
+ end
14
+ end
15
+
16
+ context 'with a valid String' do
17
+ let(:value) { '2001-01-01' }
18
+
19
+ it 'parses the String' do
20
+ expect(result).to eql Date.parse(value)
21
+ end
22
+ end
23
+
24
+ context 'with an invalid String' do
25
+ let(:value) { 'not a valid Date' }
26
+
27
+ it 'raises an error' do
28
+ expect { result }.to raise_error ActiveInteraction::InvalidValue
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveInteraction::DateTimeFilter do
4
+ include_context 'filters'
5
+ it_behaves_like 'a filter'
6
+
7
+ describe '.prepare(key, value, options = {}, &block)' do
8
+ context 'with a DateTime' do
9
+ let(:value) { DateTime.now }
10
+
11
+ it 'returns the DateTime' do
12
+ expect(result).to eql value
13
+ end
14
+ end
15
+
16
+ context 'with a valid String' do
17
+ let(:value) { '2001-01-01T01:01:01+01:01' }
18
+
19
+ it 'parses the String' do
20
+ expect(result).to eql DateTime.parse(value)
21
+ end
22
+ end
23
+
24
+ context 'with an invalid String' do
25
+ let(:value) { 'not a valid DateTine' }
26
+
27
+ it 'raises an error' do
28
+ expect { result }.to raise_error ActiveInteraction::InvalidValue
29
+ end
30
+ end
31
+ end
32
+ end