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