active_interaction 0.1.3 → 0.2.0
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.
- checksums.yaml +8 -8
- data/CHANGELOG.md +6 -0
- data/README.md +4 -2
- data/lib/active_interaction.rb +1 -0
- data/lib/active_interaction/base.rb +24 -5
- data/lib/active_interaction/filter.rb +6 -16
- data/lib/active_interaction/filters/abstract_date_time_filter.rb +25 -0
- data/lib/active_interaction/filters/date_filter.rb +9 -14
- data/lib/active_interaction/filters/date_time_filter.rb +9 -15
- data/lib/active_interaction/filters/float_filter.rb +1 -1
- data/lib/active_interaction/filters/integer_filter.rb +1 -1
- data/lib/active_interaction/filters/string_filter.rb +3 -1
- data/lib/active_interaction/filters/time_filter.rb +9 -12
- data/lib/active_interaction/version.rb +1 -1
- data/spec/active_interaction/base_spec.rb +25 -0
- data/spec/active_interaction/filters/date_filter_spec.rb +18 -0
- data/spec/active_interaction/filters/date_time_filter_spec.rb +18 -0
- data/spec/active_interaction/filters/string_filter_spec.rb +16 -0
- data/spec/active_interaction/filters/time_filter_spec.rb +18 -0
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ODY0YTVjYmY2YTE4MGY3NWU1OGU5NTU1YjcwZWVmNjU1NDU1OGYwMA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MGYwNTY4Y2ZkMDRmNDMxZTIxNDYwMTc4YjA4YWQxN2I3ZmY2ZGIwMg==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
OWI3MDgwNGVlY2QxODU5ZGRhMjMyNDRkMzQ5YjI5YTRiM2VhMTU5N2JiYjAx
|
10
|
+
ZmEzNTMyODVlYjQyZGYzOWU4ZDViOGNhN2JmZWY1NDEwZTkxOGFlOTllZDAy
|
11
|
+
ZWZkZjQ0YmVjNmZhMzhjYTc1Mjk2ZmVlZTk2MjhmYTVmOTg0Mjc=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MTQ5ZGZiMTU3ZTAzMzlkNTZmNjIxMzkwN2EwZWE3ZjAzNDZhZDUzYWU3MjUy
|
14
|
+
ZGFhM2YxNTIzYzA4ODI2NDQ2YzE0NmNjYjU3MjI5NGM2NWU3NWVmZGJmMDI2
|
15
|
+
M2IxZDRlNDVjZmZkNjAxYzEyMWY5YjliNjU4NzBhZTM0ZjhmNGM=
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Master
|
2
2
|
|
3
|
+
# 0.2.0
|
4
|
+
|
5
|
+
- Wrap interactions in ActiveRecord transactions if they're available.
|
6
|
+
- Add option to strip string values, which is enabled by default.
|
7
|
+
- Add support for strptime format strings on Date, DateTime, and Time filters.
|
8
|
+
|
3
9
|
# 0.1.3
|
4
10
|
|
5
11
|
- Fix bug that prevented `attr_accessor`s from working.
|
data/README.md
CHANGED
@@ -22,7 +22,7 @@ This project uses [semantic versioning][].
|
|
22
22
|
Add it to your Gemfile:
|
23
23
|
|
24
24
|
```ruby
|
25
|
-
gem 'active_interaction', '~> 0.
|
25
|
+
gem 'active_interaction', '~> 0.2.0'
|
26
26
|
```
|
27
27
|
|
28
28
|
And then execute:
|
@@ -91,7 +91,9 @@ end
|
|
91
91
|
You may have noticed that ActiveInteraction::Base quacks like
|
92
92
|
ActiveRecord::Base. It can use validations from your Rails application
|
93
93
|
and check option validity with `valid?`. Any errors are added to
|
94
|
-
`errors` which works exactly like an ActiveRecord model.
|
94
|
+
`errors` which works exactly like an ActiveRecord model. Additionally,
|
95
|
+
everything within the `execute` method is run in a transaction if
|
96
|
+
ActiveRecord is available.
|
95
97
|
|
96
98
|
## How do I call an interaction?
|
97
99
|
|
data/lib/active_interaction.rb
CHANGED
@@ -6,6 +6,7 @@ require 'active_interaction/overload_hash'
|
|
6
6
|
require 'active_interaction/filter'
|
7
7
|
require 'active_interaction/filter_method'
|
8
8
|
require 'active_interaction/filter_methods'
|
9
|
+
require 'active_interaction/filters/abstract_date_time_filter'
|
9
10
|
require 'active_interaction/filters/array_filter'
|
10
11
|
require 'active_interaction/filters/boolean_filter'
|
11
12
|
require 'active_interaction/filters/date_filter'
|
@@ -1,5 +1,10 @@
|
|
1
1
|
require 'active_support/core_ext/hash/indifferent_access'
|
2
2
|
|
3
|
+
begin
|
4
|
+
require 'active_record'
|
5
|
+
rescue LoadError
|
6
|
+
end
|
7
|
+
|
3
8
|
module ActiveInteraction
|
4
9
|
# @abstract Subclass and override {#execute} to implement a custom
|
5
10
|
# ActiveInteraction class.
|
@@ -69,12 +74,25 @@ module ActiveInteraction
|
|
69
74
|
# Runs the business logic associated with the interaction. The method is
|
70
75
|
# only run when there are no validation errors. The return value is
|
71
76
|
# placed into {#result}. This method must be overridden in the subclass.
|
77
|
+
# This method is run in a transaction if ActiveRecord is available.
|
72
78
|
#
|
73
79
|
# @raise [NotImplementedError] if the method is not defined.
|
74
80
|
def execute
|
75
81
|
raise NotImplementedError
|
76
82
|
end
|
77
83
|
|
84
|
+
# @private
|
85
|
+
def self.transaction
|
86
|
+
return unless block_given?
|
87
|
+
|
88
|
+
if defined?(ActiveRecord)
|
89
|
+
::ActiveRecord::Base.transaction { yield }
|
90
|
+
else
|
91
|
+
yield
|
92
|
+
end
|
93
|
+
end
|
94
|
+
private_class_method :transaction
|
95
|
+
|
78
96
|
# @!macro [new] run_attributes
|
79
97
|
# @param options [Hash] Attribute values to set.
|
80
98
|
|
@@ -85,11 +103,12 @@ module ActiveInteraction
|
|
85
103
|
# @return [ActiveInteraction::Base] An instance of the class `run` is
|
86
104
|
# called on.
|
87
105
|
def self.run(options = {})
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
106
|
+
new(options).tap do |interaction|
|
107
|
+
if interaction.valid?
|
108
|
+
result = transaction { interaction.execute }
|
109
|
+
interaction.instance_variable_set(:@result, result)
|
110
|
+
end
|
111
|
+
end
|
93
112
|
end
|
94
113
|
|
95
114
|
# Like {.run} except that it returns the value of {#execute} or raises an
|
@@ -18,24 +18,14 @@ module ActiveInteraction
|
|
18
18
|
def self.prepare(key, value, options = {}, &block)
|
19
19
|
case value
|
20
20
|
when NilClass
|
21
|
-
|
21
|
+
if options[:allow_nil]
|
22
|
+
nil
|
23
|
+
else
|
24
|
+
raise MissingValue
|
25
|
+
end
|
22
26
|
else
|
23
|
-
|
27
|
+
raise InvalidValue
|
24
28
|
end
|
25
29
|
end
|
26
|
-
|
27
|
-
def self.return_nil(allow_nil)
|
28
|
-
if allow_nil
|
29
|
-
nil
|
30
|
-
else
|
31
|
-
raise MissingValue
|
32
|
-
end
|
33
|
-
end
|
34
|
-
private_class_method :return_nil
|
35
|
-
|
36
|
-
def self.bad_value
|
37
|
-
raise InvalidValue
|
38
|
-
end
|
39
|
-
private_class_method :bad_value
|
40
30
|
end
|
41
31
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ActiveInteraction
|
2
|
+
# @private
|
3
|
+
class AbstractDateTimeFilter < Filter
|
4
|
+
def self.prepare(key, value, options = {}, &block)
|
5
|
+
klass = options.delete(:class)
|
6
|
+
|
7
|
+
case value
|
8
|
+
when klass
|
9
|
+
value
|
10
|
+
when String
|
11
|
+
begin
|
12
|
+
if options.has_key?(:format)
|
13
|
+
klass.strptime(value, options[:format])
|
14
|
+
else
|
15
|
+
klass.parse(value)
|
16
|
+
end
|
17
|
+
rescue ArgumentError
|
18
|
+
super
|
19
|
+
end
|
20
|
+
else
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,31 +1,26 @@
|
|
1
1
|
module ActiveInteraction
|
2
2
|
class Base
|
3
3
|
# Creates accessors for the attributes and ensures that values passed to
|
4
|
-
# the attributes are Dates. String values are processed using `parse
|
4
|
+
# the attributes are Dates. String values are processed using `parse`
|
5
|
+
# unless the format option is given, in which case they will be processed
|
6
|
+
# with `strptime`.
|
5
7
|
#
|
6
8
|
# @macro attribute_method_params
|
9
|
+
# @option options [String] :format Parse strings using this format string.
|
7
10
|
#
|
8
11
|
# @example
|
9
12
|
# date :birthday
|
10
13
|
#
|
14
|
+
# @example
|
15
|
+
# date :birthday, format: '%Y-%m-%d'
|
16
|
+
#
|
11
17
|
# @method self.date(*attributes, options = {})
|
12
18
|
end
|
13
19
|
|
14
20
|
# @private
|
15
|
-
class DateFilter <
|
21
|
+
class DateFilter < AbstractDateTimeFilter
|
16
22
|
def self.prepare(key, value, options = {}, &block)
|
17
|
-
|
18
|
-
when Date
|
19
|
-
value
|
20
|
-
when String
|
21
|
-
begin
|
22
|
-
Date.parse(value)
|
23
|
-
rescue ArgumentError
|
24
|
-
bad_value
|
25
|
-
end
|
26
|
-
else
|
27
|
-
super
|
28
|
-
end
|
23
|
+
super(key, value, options.merge(class: Date), &block)
|
29
24
|
end
|
30
25
|
end
|
31
26
|
end
|
@@ -1,32 +1,26 @@
|
|
1
1
|
module ActiveInteraction
|
2
2
|
class Base
|
3
3
|
# Creates accessors for the attributes and ensures that values passed to
|
4
|
-
# the attributes are DateTimes. String values are processed using
|
5
|
-
#
|
4
|
+
# the attributes are DateTimes. String values are processed using `parse`
|
5
|
+
# unless the format option is given, in which case they will be processed
|
6
|
+
# with `strptime`.
|
6
7
|
#
|
7
8
|
# @macro attribute_method_params
|
9
|
+
# @option options [String] :format Parse strings using this format string.
|
8
10
|
#
|
9
11
|
# @example
|
10
12
|
# date_time :start_date
|
11
13
|
#
|
14
|
+
# @example
|
15
|
+
# date_time :start_date, format: '%Y-%m-%dT%H:%M:%S'
|
16
|
+
#
|
12
17
|
# @method self.date_time(*attributes, options = {})
|
13
18
|
end
|
14
19
|
|
15
20
|
# @private
|
16
|
-
class DateTimeFilter <
|
21
|
+
class DateTimeFilter < AbstractDateTimeFilter
|
17
22
|
def self.prepare(key, value, options = {}, &block)
|
18
|
-
|
19
|
-
when DateTime
|
20
|
-
value
|
21
|
-
when String
|
22
|
-
begin
|
23
|
-
DateTime.parse(value)
|
24
|
-
rescue ArgumentError
|
25
|
-
bad_value
|
26
|
-
end
|
27
|
-
else
|
28
|
-
super
|
29
|
-
end
|
23
|
+
super(key, value, options.merge(class: DateTime), &block)
|
30
24
|
end
|
31
25
|
end
|
32
26
|
end
|
@@ -4,6 +4,8 @@ module ActiveInteraction
|
|
4
4
|
# the attributes are Strings.
|
5
5
|
#
|
6
6
|
# @macro attribute_method_params
|
7
|
+
# @option options [Boolean] :strip (true) Strip leading and trailing
|
8
|
+
# whitespace.
|
7
9
|
#
|
8
10
|
# @example
|
9
11
|
# string :first_name
|
@@ -16,7 +18,7 @@ module ActiveInteraction
|
|
16
18
|
def self.prepare(key, value, options = {}, &block)
|
17
19
|
case value
|
18
20
|
when String
|
19
|
-
value
|
21
|
+
options.fetch(:strip, true) ? value.strip : value
|
20
22
|
else
|
21
23
|
super
|
22
24
|
end
|
@@ -2,33 +2,30 @@ module ActiveInteraction
|
|
2
2
|
class Base
|
3
3
|
# Creates accessors for the attributes and ensures that values passed to
|
4
4
|
# the attributes are Times. Numeric values are processed using `at`.
|
5
|
-
# Strings are processed using `parse
|
6
|
-
#
|
5
|
+
# Strings are processed using `parse` unless the format option is given,
|
6
|
+
# in which case they will be processed with `strptime`. If `Time.zone` is
|
7
|
+
# available it will be used so that the values are time zone aware.
|
7
8
|
#
|
8
9
|
# @macro attribute_method_params
|
10
|
+
# @option options [String] :format Parse strings using this format string.
|
9
11
|
#
|
10
12
|
# @example
|
11
13
|
# time :start_date
|
12
14
|
#
|
15
|
+
# @example
|
16
|
+
# date_time :start_date, format: '%Y-%m-%dT%H:%M:%S'
|
17
|
+
#
|
13
18
|
# @method self.time(*attributes, options = {})
|
14
19
|
end
|
15
20
|
|
16
21
|
# @private
|
17
|
-
class TimeFilter <
|
22
|
+
class TimeFilter < AbstractDateTimeFilter
|
18
23
|
def self.prepare(key, value, options = {}, &block)
|
19
24
|
case value
|
20
|
-
when Time
|
21
|
-
value
|
22
|
-
when String
|
23
|
-
begin
|
24
|
-
time.parse(value)
|
25
|
-
rescue ArgumentError
|
26
|
-
bad_value
|
27
|
-
end
|
28
25
|
when Numeric
|
29
26
|
time.at(value)
|
30
27
|
else
|
31
|
-
super
|
28
|
+
super(key, value, options.merge(class: time), &block)
|
32
29
|
end
|
33
30
|
end
|
34
31
|
|
@@ -168,6 +168,31 @@ describe ActiveInteraction::Base do
|
|
168
168
|
it 'sets the result' do
|
169
169
|
expect(outcome.result).to eq thing
|
170
170
|
end
|
171
|
+
|
172
|
+
it 'calls transaction' do
|
173
|
+
allow(described_class).to receive(:transaction)
|
174
|
+
outcome
|
175
|
+
expect(described_class).to have_received(:transaction).once.
|
176
|
+
with(no_args)
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'with ActiveRecord' do
|
180
|
+
before do
|
181
|
+
ActiveRecord = Class.new
|
182
|
+
ActiveRecord::Base = double
|
183
|
+
allow(ActiveRecord::Base).to receive(:transaction)
|
184
|
+
end
|
185
|
+
|
186
|
+
after do
|
187
|
+
Object.send(:remove_const, :ActiveRecord)
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'calls ActiveRecord::Base.transaction' do
|
191
|
+
outcome
|
192
|
+
expect(ActiveRecord::Base).to have_received(:transaction).once.
|
193
|
+
with(no_args)
|
194
|
+
end
|
195
|
+
end
|
171
196
|
end
|
172
197
|
end
|
173
198
|
|
@@ -19,6 +19,16 @@ describe ActiveInteraction::DateFilter do
|
|
19
19
|
it 'parses the String' do
|
20
20
|
expect(result).to eql Date.parse(value)
|
21
21
|
end
|
22
|
+
|
23
|
+
context 'with options[:format]' do
|
24
|
+
let(:value) { '01012001' }
|
25
|
+
|
26
|
+
before { options.merge!(format: '%m%d%Y') }
|
27
|
+
|
28
|
+
it 'parses the String' do
|
29
|
+
expect(result).to eql Date.strptime(value, options[:format])
|
30
|
+
end
|
31
|
+
end
|
22
32
|
end
|
23
33
|
|
24
34
|
context 'with an invalid String' do
|
@@ -27,6 +37,14 @@ describe ActiveInteraction::DateFilter do
|
|
27
37
|
it 'raises an error' do
|
28
38
|
expect { result }.to raise_error ActiveInteraction::InvalidValue
|
29
39
|
end
|
40
|
+
|
41
|
+
context 'with options[:format]' do
|
42
|
+
before { options.merge!(format: '%m%d%Y') }
|
43
|
+
|
44
|
+
it 'raises an error' do
|
45
|
+
expect { result }.to raise_error ActiveInteraction::InvalidValue
|
46
|
+
end
|
47
|
+
end
|
30
48
|
end
|
31
49
|
end
|
32
50
|
end
|
@@ -19,6 +19,16 @@ describe ActiveInteraction::DateTimeFilter do
|
|
19
19
|
it 'parses the String' do
|
20
20
|
expect(result).to eql DateTime.parse(value)
|
21
21
|
end
|
22
|
+
|
23
|
+
context 'with options[:format]' do
|
24
|
+
let(:value) { '01010101012001' }
|
25
|
+
|
26
|
+
before { options.merge!(format: '%S%M%H%m%d%Y') }
|
27
|
+
|
28
|
+
it 'parses the String' do
|
29
|
+
expect(result).to eql DateTime.strptime(value, options[:format])
|
30
|
+
end
|
31
|
+
end
|
22
32
|
end
|
23
33
|
|
24
34
|
context 'with an invalid String' do
|
@@ -27,6 +37,14 @@ describe ActiveInteraction::DateTimeFilter do
|
|
27
37
|
it 'raises an error' do
|
28
38
|
expect { result }.to raise_error ActiveInteraction::InvalidValue
|
29
39
|
end
|
40
|
+
|
41
|
+
context 'with options[:format]' do
|
42
|
+
before { options.merge!(format: '%S%M%H%m%d%Y') }
|
43
|
+
|
44
|
+
it 'raises an error' do
|
45
|
+
expect { result }.to raise_error ActiveInteraction::InvalidValue
|
46
|
+
end
|
47
|
+
end
|
30
48
|
end
|
31
49
|
end
|
32
50
|
end
|
@@ -12,5 +12,21 @@ describe ActiveInteraction::StringFilter do
|
|
12
12
|
expect(result).to eql value
|
13
13
|
end
|
14
14
|
end
|
15
|
+
|
16
|
+
context 'with a strippable String' do
|
17
|
+
let(:value) { " #{SecureRandom.hex} " }
|
18
|
+
|
19
|
+
it 'returns the stripped String' do
|
20
|
+
expect(result).to eql value.strip
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'with options[:strip] as false' do
|
24
|
+
before { options.merge!(strip: false) }
|
25
|
+
|
26
|
+
it 'returns the String' do
|
27
|
+
expect(result).to eql value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
15
31
|
end
|
16
32
|
end
|
@@ -36,6 +36,16 @@ describe ActiveInteraction::TimeFilter do
|
|
36
36
|
it 'parses the String' do
|
37
37
|
expect(result).to eql Time.parse(value)
|
38
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') }
|
44
|
+
|
45
|
+
it 'parses the String' do
|
46
|
+
expect(result).to eql Time.strptime(value, options[:format])
|
47
|
+
end
|
48
|
+
end
|
39
49
|
end
|
40
50
|
|
41
51
|
context 'with an invalid String' do
|
@@ -44,6 +54,14 @@ describe ActiveInteraction::TimeFilter do
|
|
44
54
|
it 'raises an error' do
|
45
55
|
expect { result }.to raise_error ActiveInteraction::InvalidValue
|
46
56
|
end
|
57
|
+
|
58
|
+
context 'with options[:format]' do
|
59
|
+
before { options.merge!(format: '%S%M%H%d%m%Y') }
|
60
|
+
|
61
|
+
it 'raises an error' do
|
62
|
+
expect { result }.to raise_error ActiveInteraction::InvalidValue
|
63
|
+
end
|
64
|
+
end
|
47
65
|
end
|
48
66
|
end
|
49
67
|
|
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: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Lasseigne
|
@@ -150,6 +150,7 @@ files:
|
|
150
150
|
- lib/active_interaction/filter.rb
|
151
151
|
- lib/active_interaction/filter_method.rb
|
152
152
|
- lib/active_interaction/filter_methods.rb
|
153
|
+
- lib/active_interaction/filters/abstract_date_time_filter.rb
|
153
154
|
- lib/active_interaction/filters/array_filter.rb
|
154
155
|
- lib/active_interaction/filters/boolean_filter.rb
|
155
156
|
- lib/active_interaction/filters/date_filter.rb
|