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.
- data/CHANGELOG.md +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +202 -0
- data/lib/active_interaction.rb +23 -0
- data/lib/active_interaction/base.rb +162 -0
- data/lib/active_interaction/errors.rb +6 -0
- data/lib/active_interaction/filter.rb +41 -0
- data/lib/active_interaction/filter_method.rb +17 -0
- data/lib/active_interaction/filter_methods.rb +26 -0
- data/lib/active_interaction/filters/array_filter.rb +56 -0
- data/lib/active_interaction/filters/boolean_filter.rb +30 -0
- data/lib/active_interaction/filters/date_filter.rb +31 -0
- data/lib/active_interaction/filters/date_time_filter.rb +31 -0
- data/lib/active_interaction/filters/file_filter.rb +38 -0
- data/lib/active_interaction/filters/float_filter.rb +32 -0
- data/lib/active_interaction/filters/hash_filter.rb +47 -0
- data/lib/active_interaction/filters/integer_filter.rb +31 -0
- data/lib/active_interaction/filters/model_filter.rb +40 -0
- data/lib/active_interaction/filters/string_filter.rb +25 -0
- data/lib/active_interaction/filters/time_filter.rb +44 -0
- data/lib/active_interaction/overload_hash.rb +11 -0
- data/lib/active_interaction/version.rb +3 -0
- data/spec/active_interaction/base_spec.rb +175 -0
- data/spec/active_interaction/filter_method_spec.rb +48 -0
- data/spec/active_interaction/filter_methods_spec.rb +30 -0
- data/spec/active_interaction/filter_spec.rb +29 -0
- data/spec/active_interaction/filters/array_filter_spec.rb +54 -0
- data/spec/active_interaction/filters/boolean_filter_spec.rb +40 -0
- data/spec/active_interaction/filters/date_filter_spec.rb +32 -0
- data/spec/active_interaction/filters/date_time_filter_spec.rb +32 -0
- data/spec/active_interaction/filters/file_filter_spec.rb +32 -0
- data/spec/active_interaction/filters/float_filter_spec.rb +40 -0
- data/spec/active_interaction/filters/hash_filter_spec.rb +57 -0
- data/spec/active_interaction/filters/integer_filter_spec.rb +32 -0
- data/spec/active_interaction/filters/model_filter_spec.rb +40 -0
- data/spec/active_interaction/filters/string_filter_spec.rb +16 -0
- data/spec/active_interaction/filters/time_filter_spec.rb +66 -0
- data/spec/active_interaction/integration/array_interaction_spec.rb +69 -0
- data/spec/active_interaction/integration/boolean_interaction_spec.rb +5 -0
- data/spec/active_interaction/integration/date_interaction_spec.rb +5 -0
- data/spec/active_interaction/integration/date_time_interaction_spec.rb +5 -0
- data/spec/active_interaction/integration/file_interaction_spec.rb +5 -0
- data/spec/active_interaction/integration/float_interaction_spec.rb +5 -0
- data/spec/active_interaction/integration/hash_interaction_spec.rb +69 -0
- data/spec/active_interaction/integration/integer_interaction_spec.rb +5 -0
- data/spec/active_interaction/integration/model_interaction_spec.rb +5 -0
- data/spec/active_interaction/integration/string_interaction_spec.rb +5 -0
- data/spec/active_interaction/integration/time_interaction_spec.rb +5 -0
- data/spec/active_interaction/overload_hash_spec.rb +41 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/support/filters.rb +37 -0
- data/spec/support/interactions.rb +79 -0
- metadata +278 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveInteraction::FileFilter do
|
4
|
+
include_context 'filters'
|
5
|
+
it_behaves_like 'a filter'
|
6
|
+
|
7
|
+
describe '.prepare(key, value, options = {}, &block)' do
|
8
|
+
context 'with a File' do
|
9
|
+
let(:value) { File.open(__FILE__) }
|
10
|
+
|
11
|
+
it 'returns the File' do
|
12
|
+
expect(result).to equal value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'with a Tempfile' do
|
17
|
+
let(:value) { Tempfile.new('temp') }
|
18
|
+
|
19
|
+
it 'returns the Tempfile' do
|
20
|
+
expect(result).to equal value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with a object that responds to `tempfile`' do
|
25
|
+
let(:value) { double(tempfile: Tempfile.new('temp')) }
|
26
|
+
|
27
|
+
it 'returns the Tempfile' do
|
28
|
+
expect(result).to equal value.tempfile
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveInteraction::FloatFilter do
|
4
|
+
include_context 'filters'
|
5
|
+
it_behaves_like 'a filter'
|
6
|
+
|
7
|
+
describe '.prepare(key, value, options = {}, &block)' do
|
8
|
+
context 'with a Float' do
|
9
|
+
let(:value) { rand }
|
10
|
+
|
11
|
+
it 'returns the Float' do
|
12
|
+
expect(result).to eql value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'with an Integer' do
|
17
|
+
let(:value) { rand(1 << 16) }
|
18
|
+
|
19
|
+
it 'converts the Integer' do
|
20
|
+
expect(result).to eql Float(value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with a valid String' do
|
25
|
+
let(:value) { rand.to_s }
|
26
|
+
|
27
|
+
it 'converts the String' do
|
28
|
+
expect(result).to eql Float(value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'with an invalid String' do
|
33
|
+
let(:value) { 'not a valid Float' }
|
34
|
+
|
35
|
+
it 'raises an error' do
|
36
|
+
expect { result }.to raise_error ActiveInteraction::InvalidValue
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveInteraction::HashFilter do
|
4
|
+
include_context 'filters'
|
5
|
+
it_behaves_like 'a filter'
|
6
|
+
|
7
|
+
describe '.prepare(key, value, options = {}, &block)' do
|
8
|
+
context 'with a Hash' do
|
9
|
+
let(:value) { {} }
|
10
|
+
|
11
|
+
it 'returns the Hash' do
|
12
|
+
expect(result).to eql value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'with block as a block' do
|
17
|
+
let(:block) { Proc.new { hash :a } }
|
18
|
+
|
19
|
+
context 'with a Hash containing a Hash' do
|
20
|
+
let(:value) { { a: {} } }
|
21
|
+
|
22
|
+
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 { result }.to raise_error ActiveInteraction::InvalidValue
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'with block as a block with multiple filters' do
|
37
|
+
let(:block) { Proc.new { hash :a; hash :b } }
|
38
|
+
|
39
|
+
context 'with a Hash containing Hashes' do
|
40
|
+
let(:value) { { a: {}, b: {} } }
|
41
|
+
|
42
|
+
it 'returns the Hash' do
|
43
|
+
expect(result).to eql value
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'with block as a nested block' do
|
49
|
+
let(:block) { Proc.new { hash :a do; hash :b end } }
|
50
|
+
let(:value) { { a: { b: {} } } }
|
51
|
+
|
52
|
+
it 'returns the Hash' do
|
53
|
+
expect(result).to eql value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveInteraction::IntegerFilter do
|
4
|
+
include_context 'filters'
|
5
|
+
it_behaves_like 'a filter'
|
6
|
+
|
7
|
+
describe '.prepare(key, value, options = {}, &block)' do
|
8
|
+
context 'with an Integer' do
|
9
|
+
let(:value) { rand(1 << 16) }
|
10
|
+
|
11
|
+
it 'returns the Integer' do
|
12
|
+
expect(result).to eql value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'with a valid String' do
|
17
|
+
let(:value) { rand(1 << 16).to_s }
|
18
|
+
|
19
|
+
it 'converts the String' do
|
20
|
+
expect(result).to eql Integer(value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with an invalid String' do
|
25
|
+
let(:value) { 'not a valid Integer' }
|
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,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
TestModel = Class.new
|
4
|
+
|
5
|
+
describe ActiveInteraction::ModelFilter do
|
6
|
+
include_context 'filters'
|
7
|
+
it_behaves_like 'a filter'
|
8
|
+
|
9
|
+
before { options.merge!(class: TestModel) }
|
10
|
+
|
11
|
+
describe '.prepare(key, value, options = {}, &block)' do
|
12
|
+
shared_examples 'typechecking' do
|
13
|
+
context 'with the right class' do
|
14
|
+
let(:value) { TestModel.new }
|
15
|
+
|
16
|
+
it 'returns the instance' do
|
17
|
+
expect(result).to eql value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'with options[:class] as a Class' do
|
23
|
+
include_examples 'typechecking'
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with options[:class] as a valid String' do
|
27
|
+
include_examples 'typechecking'
|
28
|
+
|
29
|
+
before { options.merge!(class: options[:class].to_s) }
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'with options[:class] as an invalid String' do
|
33
|
+
before { options.merge!(class: 'not a valid Class') }
|
34
|
+
|
35
|
+
it 'raises an error' do
|
36
|
+
expect { result }.to raise_error NameError
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveInteraction::StringFilter do
|
4
|
+
include_context 'filters'
|
5
|
+
it_behaves_like 'a filter'
|
6
|
+
|
7
|
+
describe '.prepare(key, value, options = {}, &block)' do
|
8
|
+
context 'with a String' do
|
9
|
+
let(:value) { SecureRandom.hex }
|
10
|
+
|
11
|
+
it 'returns the String' do
|
12
|
+
expect(result).to eql value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveInteraction::TimeFilter do
|
4
|
+
include_context 'filters'
|
5
|
+
it_behaves_like 'a filter'
|
6
|
+
|
7
|
+
describe '.prepare(key, value, options = {}, &block)' do
|
8
|
+
context 'with a Time' do
|
9
|
+
let(:value) { Time.now }
|
10
|
+
|
11
|
+
it 'returns the Time' do
|
12
|
+
expect(result).to eql value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
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) }
|
27
|
+
|
28
|
+
it 'converts the Integer' do
|
29
|
+
expect(result).to eql Time.at(value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
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
|
+
end
|
40
|
+
|
41
|
+
context 'with an invalid String' do
|
42
|
+
let(:value) { 'not a valid Time' }
|
43
|
+
|
44
|
+
it 'raises an error' do
|
45
|
+
expect { result }.to raise_error ActiveInteraction::InvalidValue
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'without Time.zone' do
|
51
|
+
include_examples 'conversion'
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with Time.zone' do
|
55
|
+
include_examples 'conversion'
|
56
|
+
|
57
|
+
before do
|
58
|
+
allow(Time).to receive(:zone).and_return(Time)
|
59
|
+
end
|
60
|
+
|
61
|
+
after do
|
62
|
+
expect(Time).to have_received(:zone).with(no_args)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class ArrayInteraction < ActiveInteraction::Base
|
4
|
+
array :a do
|
5
|
+
array
|
6
|
+
end
|
7
|
+
array :b, default: [[]] do
|
8
|
+
array
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
{ a: a, b: b }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe ArrayInteraction do
|
17
|
+
include_context 'interactions'
|
18
|
+
it_behaves_like 'an interaction', :array, -> { [] }
|
19
|
+
|
20
|
+
context 'with options[:a]' do
|
21
|
+
let(:a) { [[]] }
|
22
|
+
|
23
|
+
before { options.merge!(a: a) }
|
24
|
+
|
25
|
+
it 'returns the correct value for :a' do
|
26
|
+
expect(result[:a]).to eq a
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns the correct value for :b' do
|
30
|
+
expect(result[:b]).to eq [[]]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'with an invalid default' do
|
35
|
+
it 'raises an error' do
|
36
|
+
expect {
|
37
|
+
Class.new(ActiveInteraction::Base) do
|
38
|
+
array :a, default: Object.new
|
39
|
+
end
|
40
|
+
}.to raise_error ActiveInteraction::InvalidDefaultValue
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'with an invalid nested default' do
|
45
|
+
it 'raises an error' do
|
46
|
+
expect {
|
47
|
+
Class.new(ActiveInteraction::Base) do
|
48
|
+
array :a, default: [Object.new] do
|
49
|
+
array
|
50
|
+
end
|
51
|
+
end
|
52
|
+
}.to raise_error ActiveInteraction::InvalidDefaultValue
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'with an invalidly nested default' do
|
57
|
+
it 'raises an error' do
|
58
|
+
expect {
|
59
|
+
klass = Class.new(ActiveInteraction::Base) do
|
60
|
+
array :a do
|
61
|
+
array default: []
|
62
|
+
end
|
63
|
+
end
|
64
|
+
# TODO: We should fail when defining the class, not when trying to run it.
|
65
|
+
klass.run(a: [])
|
66
|
+
}.to raise_error ArgumentError
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class HashInteraction < ActiveInteraction::Base
|
4
|
+
hash :a do
|
5
|
+
hash :x
|
6
|
+
end
|
7
|
+
hash :b, default: { x: {} } do
|
8
|
+
hash :x
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
{ a: a, b: b }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe HashInteraction do
|
17
|
+
include_context 'interactions'
|
18
|
+
it_behaves_like 'an interaction', :hash, -> { {} }
|
19
|
+
|
20
|
+
context 'with options[:a]' do
|
21
|
+
let(:a) { { 'x' => {} } }
|
22
|
+
|
23
|
+
before { options.merge!(a: a) }
|
24
|
+
|
25
|
+
it 'returns the correct value for :a' do
|
26
|
+
expect(result[:a]).to eq a
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns the correct value for :b' do
|
30
|
+
expect(result[:b]).to eq(x: {})
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'with an invalid default' do
|
35
|
+
it 'raises an error' do
|
36
|
+
expect {
|
37
|
+
Class.new(ActiveInteraction::Base) do
|
38
|
+
hash :a, default: Object.new
|
39
|
+
end
|
40
|
+
}.to raise_error ActiveInteraction::InvalidDefaultValue
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'with an invalid nested default' do
|
45
|
+
it 'raises an error' do
|
46
|
+
expect {
|
47
|
+
Class.new(ActiveInteraction::Base) do
|
48
|
+
hash :a, default: { x: Object.new } do
|
49
|
+
hash :x
|
50
|
+
end
|
51
|
+
end
|
52
|
+
}.to raise_error ActiveInteraction::InvalidDefaultValue
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'with an invalidly nested default' do
|
57
|
+
it 'raises an error' do
|
58
|
+
expect {
|
59
|
+
klass = Class.new(ActiveInteraction::Base) do
|
60
|
+
hash :a do
|
61
|
+
hash :x, default: {}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
# TODO: We should fail when defining the class, not when trying to run it.
|
65
|
+
klass.run(a: {})
|
66
|
+
}.to raise_error ArgumentError
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|