active_interaction 0.9.0 → 0.9.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -1
- data/README.md +4 -4
- data/lib/active_interaction.rb +2 -0
- data/lib/active_interaction/base.rb +26 -14
- data/lib/active_interaction/errors.rb +7 -10
- data/lib/active_interaction/filter.rb +10 -10
- data/lib/active_interaction/filters.rb +2 -0
- data/lib/active_interaction/filters/abstract_date_time_filter.rb +4 -2
- data/lib/active_interaction/filters/abstract_numeric_filter.rb +3 -1
- data/lib/active_interaction/filters/array_filter.rb +5 -3
- data/lib/active_interaction/filters/boolean_filter.rb +2 -0
- data/lib/active_interaction/filters/date_filter.rb +2 -0
- data/lib/active_interaction/filters/date_time_filter.rb +2 -0
- data/lib/active_interaction/filters/file_filter.rb +2 -0
- data/lib/active_interaction/filters/float_filter.rb +2 -0
- data/lib/active_interaction/filters/hash_filter.rb +7 -6
- data/lib/active_interaction/filters/integer_filter.rb +2 -0
- data/lib/active_interaction/filters/model_filter.rb +2 -0
- data/lib/active_interaction/filters/string_filter.rb +2 -0
- data/lib/active_interaction/filters/symbol_filter.rb +2 -0
- data/lib/active_interaction/filters/time_filter.rb +2 -0
- data/lib/active_interaction/modules/active_model.rb +2 -0
- data/lib/active_interaction/modules/core.rb +4 -1
- data/lib/active_interaction/modules/method_missing.rb +2 -0
- data/lib/active_interaction/modules/overload_hash.rb +2 -0
- data/lib/active_interaction/modules/validation.rb +10 -5
- data/lib/active_interaction/version.rb +4 -1
- data/spec/active_interaction/base_spec.rb +60 -35
- data/spec/active_interaction/errors_spec.rb +48 -14
- data/spec/active_interaction/filter_spec.rb +4 -2
- data/spec/active_interaction/filters/array_filter_spec.rb +13 -6
- data/spec/active_interaction/filters/boolean_filter_spec.rb +2 -0
- data/spec/active_interaction/filters/date_filter_spec.rb +6 -4
- data/spec/active_interaction/filters/date_time_filter_spec.rb +6 -4
- data/spec/active_interaction/filters/file_filter_spec.rb +2 -0
- data/spec/active_interaction/filters/float_filter_spec.rb +4 -2
- data/spec/active_interaction/filters/hash_filter_spec.rb +16 -4
- data/spec/active_interaction/filters/integer_filter_spec.rb +4 -2
- data/spec/active_interaction/filters/model_filter_spec.rb +4 -2
- data/spec/active_interaction/filters/string_filter_spec.rb +2 -0
- data/spec/active_interaction/filters/symbol_filter_spec.rb +2 -0
- data/spec/active_interaction/filters/time_filter_spec.rb +6 -4
- data/spec/active_interaction/filters_spec.rb +2 -0
- data/spec/active_interaction/i18n_spec.rb +9 -9
- data/spec/active_interaction/integration/array_interaction_spec.rb +10 -8
- data/spec/active_interaction/integration/boolean_interaction_spec.rb +2 -0
- data/spec/active_interaction/integration/date_interaction_spec.rb +2 -0
- data/spec/active_interaction/integration/date_time_interaction_spec.rb +2 -0
- data/spec/active_interaction/integration/file_interaction_spec.rb +2 -0
- data/spec/active_interaction/integration/float_interaction_spec.rb +2 -0
- data/spec/active_interaction/integration/hash_interaction_spec.rb +10 -8
- data/spec/active_interaction/integration/integer_interaction_spec.rb +2 -0
- data/spec/active_interaction/integration/model_interaction_spec.rb +3 -1
- data/spec/active_interaction/integration/string_interaction_spec.rb +2 -0
- data/spec/active_interaction/integration/symbol_interaction_spec.rb +2 -0
- data/spec/active_interaction/integration/time_interaction_spec.rb +9 -7
- data/spec/active_interaction/modules/active_model_spec.rb +2 -0
- data/spec/active_interaction/modules/core_spec.rb +12 -7
- data/spec/active_interaction/modules/method_missing_spec.rb +12 -10
- data/spec/active_interaction/modules/overload_hash_spec.rb +7 -5
- data/spec/active_interaction/modules/validation_spec.rb +5 -2
- data/spec/spec_helper.rb +4 -0
- data/spec/support/filters.rb +18 -16
- data/spec/support/interactions.rb +13 -11
- metadata +20 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0449466d5aadcc60fb21146f2f69cad47097698a
|
4
|
+
data.tar.gz: d88a740e6c885e713a2829b7a26902e9712a57c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3563d4d5944e8316c3a75c9450c52a411a626436b9acc87380b92890cd393f1cd743f1b6cd2b3a7159a5e963aac06c2bc21dcdb494616337c4d2fb654cfa0ac5
|
7
|
+
data.tar.gz: 58cb5f5101acce859c491c3eb3a5d8c0117d46f31c966007f3d86a34244c87ac8810493204161dcdee1d840d5954730d88b06c6173e10cdf9366f41542feec66
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# [Master][]
|
2
2
|
|
3
|
+
# [0.9.1][] (2013-12-17)
|
4
|
+
|
5
|
+
- Fix I18n deprecation warning.
|
6
|
+
- Raise `ArgumentError` when running an interaction with non-hash inputs.
|
7
|
+
- For compatibility with `ActiveRecord::Errors`, support indifferent access of
|
8
|
+
`ActiveInteraction::Errors`.
|
9
|
+
- Fix losing filters when using inheritance.
|
10
|
+
|
3
11
|
# [0.9.0][] (2013-12-02)
|
4
12
|
|
5
13
|
- Add experimental composition implementation
|
@@ -84,7 +92,8 @@
|
|
84
92
|
|
85
93
|
- Initial release.
|
86
94
|
|
87
|
-
[master]: https://github.com/orgsync/active_interaction/compare/v0.9.
|
95
|
+
[master]: https://github.com/orgsync/active_interaction/compare/v0.9.1...master
|
96
|
+
[0.9.1]: https://github.com/orgsync/active_interaction/compare/v0.9.0...0.9.1
|
88
97
|
[0.9.0]: https://github.com/orgsync/active_interaction/compare/v0.9.0...0.9.0
|
89
98
|
[0.8.0]: https://github.com/orgsync/active_interaction/compare/v0.7.0...v0.8.0
|
90
99
|
[0.7.0]: https://github.com/orgsync/active_interaction/compare/v0.6.1...v0.7.0
|
data/README.md
CHANGED
@@ -25,7 +25,7 @@ This project uses [semantic versioning][].
|
|
25
25
|
Add it to your Gemfile:
|
26
26
|
|
27
27
|
```ruby
|
28
|
-
gem 'active_interaction', '~> 0.9.
|
28
|
+
gem 'active_interaction', '~> 0.9.1'
|
29
29
|
```
|
30
30
|
|
31
31
|
And then execute:
|
@@ -43,8 +43,8 @@ $ gem install active_interaction
|
|
43
43
|
## What do I get?
|
44
44
|
|
45
45
|
ActiveInteraction::Base lets you create interaction models. These
|
46
|
-
models ensure that certain
|
47
|
-
|
46
|
+
models ensure that certain inputs are provided and that those
|
47
|
+
inputs are in the format you want them in. If the inputs are valid
|
48
48
|
it will call `execute`, store the return value of that method in
|
49
49
|
`result`, and return an instance of your ActiveInteraction::Base
|
50
50
|
subclass. Let's look at a simple example:
|
@@ -61,7 +61,7 @@ class UserSignup < ActiveInteraction::Base
|
|
61
61
|
# ActiveRecord validations
|
62
62
|
validates :email, format: EMAIL_REGEX
|
63
63
|
|
64
|
-
# The execute method is called only if the
|
64
|
+
# The execute method is called only if the inputs validate. It
|
65
65
|
# does your business action. The return value will be stored in
|
66
66
|
# `result`.
|
67
67
|
def execute
|
data/lib/active_interaction.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
require 'active_support/core_ext/hash/indifferent_access'
|
2
4
|
|
3
5
|
module ActiveInteraction
|
@@ -33,26 +35,29 @@ module ActiveInteraction
|
|
33
35
|
|
34
36
|
validate :input_errors, :runtime_errors
|
35
37
|
|
36
|
-
# @param
|
38
|
+
# @param inputs [Hash{Symbol => Object}] Attribute values to set.
|
37
39
|
#
|
38
40
|
# @private
|
39
|
-
def initialize(
|
41
|
+
def initialize(inputs = {})
|
42
|
+
fail ArgumentError, 'inputs must be a hash' unless inputs.is_a?(Hash)
|
43
|
+
|
40
44
|
@_interaction_errors = Errors.new(self)
|
41
45
|
@_interaction_result = nil
|
42
46
|
@_interaction_runtime_errors = nil
|
43
47
|
|
44
|
-
|
45
|
-
options.each do |key, value|
|
48
|
+
inputs.each do |key, value|
|
46
49
|
if key.to_s.start_with?('_interaction_')
|
47
|
-
|
50
|
+
fail InvalidValueError, key.inspect
|
48
51
|
end
|
49
52
|
|
50
53
|
instance_variable_set("@#{key}", value)
|
51
54
|
end
|
52
55
|
|
56
|
+
inputs = inputs.symbolize_keys
|
53
57
|
self.class.filters.each do |filter|
|
54
58
|
begin
|
55
|
-
send("#{filter.name}=", filter.clean(
|
59
|
+
send("#{filter.name}=", filter.clean(inputs[filter.name]))
|
60
|
+
# rubocop: disable HandleExceptions
|
56
61
|
rescue InvalidValueError, MissingValueError
|
57
62
|
# Validators (#input_errors) will add errors if appropriate.
|
58
63
|
end
|
@@ -66,9 +71,8 @@ module ActiveInteraction
|
|
66
71
|
#
|
67
72
|
# @since 0.6.0
|
68
73
|
def inputs
|
69
|
-
self.class.filters.
|
74
|
+
self.class.filters.each_with_object({}) do |filter, h|
|
70
75
|
h[filter.name] = send(filter.name)
|
71
|
-
h
|
72
76
|
end
|
73
77
|
end
|
74
78
|
|
@@ -81,7 +85,7 @@ module ActiveInteraction
|
|
81
85
|
#
|
82
86
|
# @abstract
|
83
87
|
def execute
|
84
|
-
|
88
|
+
fail NotImplementedError
|
85
89
|
end
|
86
90
|
|
87
91
|
# Returns the output from {#execute} if there are no validation errors or
|
@@ -123,6 +127,7 @@ module ActiveInteraction
|
|
123
127
|
result = transaction do
|
124
128
|
begin
|
125
129
|
interaction.execute
|
130
|
+
# rubocop:disable HandleExceptions
|
126
131
|
rescue Interrupt
|
127
132
|
# Inner interaction failed. #compose handles merging errors.
|
128
133
|
end
|
@@ -141,11 +146,11 @@ module ActiveInteraction
|
|
141
146
|
# @private
|
142
147
|
def self.method_missing(*args, &block)
|
143
148
|
super do |klass, names, options|
|
144
|
-
|
149
|
+
fail InvalidFilterError, 'missing attribute name' if names.empty?
|
145
150
|
|
146
151
|
names.each do |attribute|
|
147
152
|
if attribute.to_s.start_with?('_interaction_')
|
148
|
-
|
153
|
+
fail InvalidFilterError, attribute.inspect
|
149
154
|
end
|
150
155
|
|
151
156
|
filter = klass.new(attribute, options, &block)
|
@@ -173,8 +178,8 @@ module ActiveInteraction
|
|
173
178
|
end
|
174
179
|
end
|
175
180
|
|
176
|
-
def compose(interaction,
|
177
|
-
outcome = interaction.run(
|
181
|
+
def compose(interaction, inputs = {})
|
182
|
+
outcome = interaction.run(inputs)
|
178
183
|
return outcome.result if outcome.valid?
|
179
184
|
|
180
185
|
# This can't use Errors#merge! because the errors have to be added to
|
@@ -183,7 +188,14 @@ module ActiveInteraction
|
|
183
188
|
errors.add(:base, message) unless errors.added?(:base, message)
|
184
189
|
end
|
185
190
|
|
186
|
-
|
191
|
+
fail Interrupt
|
192
|
+
end
|
193
|
+
|
194
|
+
def self.inherited(subclass)
|
195
|
+
instance_var = :@_interaction_filters
|
196
|
+
|
197
|
+
subclass.instance_variable_set(
|
198
|
+
instance_var, instance_variable_get(instance_var))
|
187
199
|
end
|
188
200
|
end
|
189
201
|
end
|
@@ -1,3 +1,6 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# rubocop:disable Documentation
|
1
4
|
module ActiveInteraction
|
2
5
|
# Top-level error class. All other errors subclass this.
|
3
6
|
Error = Class.new(StandardError)
|
@@ -66,13 +69,13 @@ module ActiveInteraction
|
|
66
69
|
|
67
70
|
# @private
|
68
71
|
def initialize(*args)
|
69
|
-
@symbolic = {}
|
72
|
+
@symbolic = {}.with_indifferent_access
|
70
73
|
super
|
71
74
|
end
|
72
75
|
|
73
76
|
# @private
|
74
77
|
def initialize_dup(other)
|
75
|
-
@symbolic = other.symbolic.
|
78
|
+
@symbolic = other.symbolic.with_indifferent_access
|
76
79
|
super
|
77
80
|
end
|
78
81
|
|
@@ -89,17 +92,11 @@ module ActiveInteraction
|
|
89
92
|
# @return [Errors]
|
90
93
|
def merge!(other)
|
91
94
|
other.symbolic.each do |attribute, symbols|
|
92
|
-
symbols.each
|
93
|
-
add_sym(attribute, symbol)
|
94
|
-
end
|
95
|
+
symbols.each { |s| add_sym(attribute, s) }
|
95
96
|
end
|
96
97
|
|
97
98
|
other.messages.each do |attribute, messages|
|
98
|
-
messages.each
|
99
|
-
unless added?(attribute, message)
|
100
|
-
add(attribute, message)
|
101
|
-
end
|
102
|
-
end
|
99
|
+
messages.each { |m| add(attribute, m) unless added?(attribute, m) }
|
103
100
|
end
|
104
101
|
|
105
102
|
self
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
require 'active_support/inflector'
|
2
4
|
|
3
5
|
module ActiveInteraction
|
@@ -76,17 +78,15 @@ module ActiveInteraction
|
|
76
78
|
# @see .factory
|
77
79
|
def slug
|
78
80
|
match = CLASS_REGEXP.match(name)
|
79
|
-
|
81
|
+
fail InvalidClassError, name unless match
|
80
82
|
match.captures.first.underscore.to_sym
|
81
83
|
end
|
82
84
|
|
83
85
|
# @private
|
84
86
|
def inherited(klass)
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
# Ignore classes with invalid slugs.
|
89
|
-
end
|
87
|
+
CLASSES[klass.slug] = klass
|
88
|
+
rescue InvalidClassError
|
89
|
+
CLASSES[klass.name.to_sym] = klass
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
@@ -159,7 +159,7 @@ module ActiveInteraction
|
|
159
159
|
# @raise [InvalidDefaultError] if the default value is invalid
|
160
160
|
# @raise [NoDefaultError] if there is no default value
|
161
161
|
def default
|
162
|
-
|
162
|
+
fail NoDefaultError, name unless has_default?
|
163
163
|
|
164
164
|
cast(options[:default])
|
165
165
|
rescue InvalidValueError, MissingValueError
|
@@ -193,18 +193,18 @@ module ActiveInteraction
|
|
193
193
|
#
|
194
194
|
# @return [Boolean]
|
195
195
|
def has_default?
|
196
|
-
options.
|
196
|
+
options.key?(:default)
|
197
197
|
end
|
198
198
|
|
199
199
|
# @private
|
200
200
|
def cast(value)
|
201
201
|
case value
|
202
202
|
when NilClass
|
203
|
-
|
203
|
+
fail MissingValueError, name unless has_default?
|
204
204
|
|
205
205
|
nil
|
206
206
|
else
|
207
|
-
|
207
|
+
fail InvalidValueError, "#{name}: #{value.inspect}"
|
208
208
|
end
|
209
209
|
end
|
210
210
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
module ActiveInteraction
|
2
4
|
# @private
|
3
5
|
class AbstractDateTimeFilter < Filter
|
@@ -27,11 +29,11 @@ module ActiveInteraction
|
|
27
29
|
end
|
28
30
|
|
29
31
|
def has_format?
|
30
|
-
options.
|
32
|
+
options.key?(:format)
|
31
33
|
end
|
32
34
|
|
33
35
|
def klass
|
34
|
-
|
36
|
+
fail NotImplementedError
|
35
37
|
end
|
36
38
|
end
|
37
39
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
module ActiveInteraction
|
2
4
|
class Base
|
3
5
|
# Creates accessors for the attributes and ensures that values passed to
|
@@ -45,13 +47,13 @@ module ActiveInteraction
|
|
45
47
|
filter = klass.new(name, options, &block)
|
46
48
|
|
47
49
|
if filters.any?
|
48
|
-
|
50
|
+
fail InvalidFilterError, 'multiple filters in array block'
|
49
51
|
end
|
50
52
|
unless names.empty?
|
51
|
-
|
53
|
+
fail InvalidFilterError, 'attribute names in array block'
|
52
54
|
end
|
53
55
|
if filter.has_default?
|
54
|
-
|
56
|
+
fail InvalidDefaultError, 'default values in array block'
|
55
57
|
end
|
56
58
|
|
57
59
|
filters.add(filter)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
module ActiveInteraction
|
2
4
|
class Base
|
3
5
|
# Creates accessors for the attributes and ensures that values passed to
|
@@ -31,10 +33,9 @@ module ActiveInteraction
|
|
31
33
|
case value
|
32
34
|
when Hash
|
33
35
|
value = value.symbolize_keys
|
34
|
-
filters.
|
35
|
-
k =
|
36
|
-
h[k] =
|
37
|
-
h
|
36
|
+
filters.each_with_object(strip? ? {} : value) do |filter, h|
|
37
|
+
k = filter.name
|
38
|
+
h[k] = filter.clean(value[k])
|
38
39
|
end
|
39
40
|
else
|
40
41
|
super
|
@@ -43,7 +44,7 @@ module ActiveInteraction
|
|
43
44
|
|
44
45
|
def default
|
45
46
|
if options[:default].is_a?(Hash) && !options[:default].empty?
|
46
|
-
|
47
|
+
fail InvalidDefaultError, "#{name}: #{options[:default].inspect}"
|
47
48
|
end
|
48
49
|
|
49
50
|
super
|
@@ -51,7 +52,7 @@ module ActiveInteraction
|
|
51
52
|
|
52
53
|
def method_missing(*args, &block)
|
53
54
|
super(*args) do |klass, names, options|
|
54
|
-
|
55
|
+
fail InvalidFilterError, 'missing attribute name' if names.empty?
|
55
56
|
|
56
57
|
names.each do |name|
|
57
58
|
filters.add(klass.new(name, options, &block))
|