active_interaction 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|