active_interaction 0.5.0 → 0.6.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 +27 -3
- data/README.md +8 -6
- data/lib/active_interaction.rb +5 -3
- data/lib/active_interaction/active_model.rb +29 -0
- data/lib/active_interaction/base.rb +82 -116
- data/lib/active_interaction/errors.rb +79 -5
- data/lib/active_interaction/filter.rb +195 -21
- data/lib/active_interaction/filters.rb +26 -0
- data/lib/active_interaction/filters/array_filter.rb +22 -25
- data/lib/active_interaction/filters/boolean_filter.rb +12 -12
- data/lib/active_interaction/filters/date_filter.rb +32 -5
- data/lib/active_interaction/filters/date_time_filter.rb +34 -7
- data/lib/active_interaction/filters/file_filter.rb +12 -9
- data/lib/active_interaction/filters/float_filter.rb +13 -11
- data/lib/active_interaction/filters/hash_filter.rb +36 -17
- data/lib/active_interaction/filters/integer_filter.rb +13 -11
- data/lib/active_interaction/filters/model_filter.rb +15 -15
- data/lib/active_interaction/filters/string_filter.rb +19 -8
- data/lib/active_interaction/filters/symbol_filter.rb +29 -0
- data/lib/active_interaction/filters/time_filter.rb +38 -16
- data/lib/active_interaction/method_missing.rb +18 -0
- data/lib/active_interaction/overload_hash.rb +1 -0
- data/lib/active_interaction/validation.rb +19 -0
- data/lib/active_interaction/version.rb +1 -1
- data/spec/active_interaction/active_model_spec.rb +33 -0
- data/spec/active_interaction/base_spec.rb +54 -48
- data/spec/active_interaction/errors_spec.rb +99 -0
- data/spec/active_interaction/filter_spec.rb +12 -20
- data/spec/active_interaction/filters/array_filter_spec.rb +50 -28
- data/spec/active_interaction/filters/boolean_filter_spec.rb +15 -15
- data/spec/active_interaction/filters/date_filter_spec.rb +30 -18
- data/spec/active_interaction/filters/date_time_filter_spec.rb +31 -19
- data/spec/active_interaction/filters/file_filter_spec.rb +7 -7
- data/spec/active_interaction/filters/float_filter_spec.rb +13 -11
- data/spec/active_interaction/filters/hash_filter_spec.rb +38 -29
- data/spec/active_interaction/filters/integer_filter_spec.rb +18 -8
- data/spec/active_interaction/filters/model_filter_spec.rb +24 -20
- data/spec/active_interaction/filters/string_filter_spec.rb +14 -8
- data/spec/active_interaction/filters/symbol_filter_spec.rb +24 -0
- data/spec/active_interaction/filters/time_filter_spec.rb +33 -69
- data/spec/active_interaction/filters_spec.rb +21 -0
- data/spec/active_interaction/i18n_spec.rb +0 -15
- data/spec/active_interaction/integration/array_interaction_spec.rb +2 -22
- data/spec/active_interaction/integration/hash_interaction_spec.rb +5 -25
- data/spec/active_interaction/integration/symbol_interaction_spec.rb +5 -0
- data/spec/active_interaction/method_missing_spec.rb +69 -0
- data/spec/active_interaction/validation_spec.rb +55 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/support/filters.rb +168 -14
- data/spec/support/interactions.rb +11 -13
- metadata +31 -13
- data/lib/active_interaction/filter_method.rb +0 -13
- data/lib/active_interaction/filter_methods.rb +0 -26
- data/lib/active_interaction/filters/abstract_date_time_filter.rb +0 -25
- data/spec/active_interaction/filter_method_spec.rb +0 -43
- data/spec/active_interaction/filter_methods_spec.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddc5d64fad3052b09b16259d6aae6760411b384b
|
4
|
+
data.tar.gz: 1c3569cfc819b0153c2c875cf5674c29382fc956
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 399f01cd79b55412083c29364afa80e53660e28ccbfc9d01788a138e30152ee61f4e6307e7a8f496a1b058557555ece132bf496b9e7d623b580e9414e203b030
|
7
|
+
data.tar.gz: c25734f3428395f3c063c7a6b1f867b65107dc806a7e5a391b38d8692fcd46a391cc8f3259aca4df2911bfb377fe45adb96393f7738eb2cfc7acde95cf401d86
|
data/CHANGELOG.md
CHANGED
@@ -1,11 +1,33 @@
|
|
1
1
|
# [Master][]
|
2
2
|
|
3
|
-
# [0.
|
3
|
+
# [0.6.1][] (2013-11-14)
|
4
|
+
|
5
|
+
- Re-release. Forgot to merge into master.
|
6
|
+
|
7
|
+
# [0.6.0][] (2013-11-14)
|
8
|
+
|
9
|
+
- Error class now end with `Error`.
|
10
|
+
- By default, strip unlisted keys from hashes. To retain the old behavior, set
|
11
|
+
`strip: false` on a hash filter.
|
12
|
+
- Prevent specifying defaults (other than `nil` or `{}`) on hash filters. Set
|
13
|
+
defaults on the nested filters instead.
|
14
|
+
- Add ability to introspect interactions with `filters`.
|
15
|
+
- Fix bug that prevented listing multiple attributes in a hash filter.
|
16
|
+
- Allow getting all of the user-supplied inputs in an interaction with
|
17
|
+
`inputs`.
|
18
|
+
- Fix bug that prevented hash filters from being nested in array filters.
|
19
|
+
- Replace `allow_nil: true` with `default: nil`.
|
20
|
+
- Refactor internals.
|
21
|
+
- Add a symbol filter.
|
22
|
+
- Allow adding symbolic errors with `errors.add_sym` and retrieving them with
|
23
|
+
`errors.symbolic`.
|
24
|
+
|
25
|
+
# [0.5.0][] (2013-10-16)
|
4
26
|
|
5
27
|
- Allow adding errors in `execute` method with `errors.add`.
|
6
28
|
- Prevent manually setting the outcome's result.
|
7
29
|
|
8
|
-
# [0.4.0][]
|
30
|
+
# [0.4.0][] (2013-08-15)
|
9
31
|
|
10
32
|
- Support i18n translations.
|
11
33
|
|
@@ -48,7 +70,9 @@
|
|
48
70
|
|
49
71
|
- Initial release.
|
50
72
|
|
51
|
-
[master]: https://github.com/orgsync/active_interaction/compare/v0.
|
73
|
+
[master]: https://github.com/orgsync/active_interaction/compare/v0.6.1...master
|
74
|
+
[0.6.1]: https://github.com/orgsync/active_interaction/compare/v0.6.0...v0.6.1
|
75
|
+
[0.6.0]: https://github.com/orgsync/active_interaction/compare/v0.5.0...v0.6.0
|
52
76
|
[0.5.0]: https://github.com/orgsync/active_interaction/compare/v0.4.0...v0.5.0
|
53
77
|
[0.4.0]: https://github.com/orgsync/active_interaction/compare/v0.3.0...v0.4.0
|
54
78
|
[0.3.0]: https://github.com/orgsync/active_interaction/compare/v0.2.2...v0.3.0
|
data/README.md
CHANGED
@@ -15,7 +15,8 @@ to this.
|
|
15
15
|
Take back control. Slim down models and wrangle monstrous controller
|
16
16
|
methods with ActiveInteraction.
|
17
17
|
|
18
|
-
|
18
|
+
Read more on the [project page][] or check out the full [documentation][]
|
19
|
+
on RubyDoc.info.
|
19
20
|
|
20
21
|
## Installation
|
21
22
|
|
@@ -24,7 +25,7 @@ This project uses [semantic versioning][].
|
|
24
25
|
Add it to your Gemfile:
|
25
26
|
|
26
27
|
```ruby
|
27
|
-
gem 'active_interaction', '~> 0.
|
28
|
+
gem 'active_interaction', '~> 0.6.1'
|
28
29
|
```
|
29
30
|
|
30
31
|
And then execute:
|
@@ -46,7 +47,7 @@ models ensure that certain options are provided and that those
|
|
46
47
|
options are in the format you want them in. If the options are valid
|
47
48
|
it will call `execute`, store the return value of that method in
|
48
49
|
`result`, and return an instance of your ActiveInteraction::Base
|
49
|
-
subclass. Let's
|
50
|
+
subclass. Let's look at a simple example:
|
50
51
|
|
51
52
|
```ruby
|
52
53
|
# Define an interaction that signs up a user.
|
@@ -55,7 +56,7 @@ class UserSignup < ActiveInteraction::Base
|
|
55
56
|
string :email, :name
|
56
57
|
|
57
58
|
# optional
|
58
|
-
boolean :newsletter_subscribe,
|
59
|
+
boolean :newsletter_subscribe, default: nil
|
59
60
|
|
60
61
|
# ActiveRecord validations
|
61
62
|
validates :email, format: EMAIL_REGEX
|
@@ -160,10 +161,10 @@ end
|
|
160
161
|
integer :age
|
161
162
|
boolean :is_special
|
162
163
|
model :account
|
163
|
-
array :tags,
|
164
|
+
array :tags, default: nil do
|
164
165
|
string
|
165
166
|
end
|
166
|
-
hash :prefs,
|
167
|
+
hash :prefs, default: nil do
|
167
168
|
boolean :smoking
|
168
169
|
boolean :view
|
169
170
|
end
|
@@ -258,4 +259,5 @@ This project was inspired by the fantastic work done in [Mutations][].
|
|
258
259
|
[documentation]: http://rubydoc.info/github/orgsync/active_interaction
|
259
260
|
[gem version]: https://badge.fury.io/rb/active_interaction.png
|
260
261
|
[mutations]: https://github.com/cypriss/mutations
|
262
|
+
[project page]: http://orgsync.github.io/active_interaction/
|
261
263
|
[semantic versioning]: http://semver.org
|
data/lib/active_interaction.rb
CHANGED
@@ -2,11 +2,10 @@ require 'active_model'
|
|
2
2
|
|
3
3
|
require 'active_interaction/version'
|
4
4
|
require 'active_interaction/errors'
|
5
|
+
require 'active_interaction/active_model'
|
6
|
+
require 'active_interaction/method_missing'
|
5
7
|
require 'active_interaction/overload_hash'
|
6
8
|
require 'active_interaction/filter'
|
7
|
-
require 'active_interaction/filter_method'
|
8
|
-
require 'active_interaction/filter_methods'
|
9
|
-
require 'active_interaction/filters/abstract_date_time_filter'
|
10
9
|
require 'active_interaction/filters/array_filter'
|
11
10
|
require 'active_interaction/filters/boolean_filter'
|
12
11
|
require 'active_interaction/filters/date_filter'
|
@@ -17,7 +16,10 @@ require 'active_interaction/filters/hash_filter'
|
|
17
16
|
require 'active_interaction/filters/integer_filter'
|
18
17
|
require 'active_interaction/filters/model_filter'
|
19
18
|
require 'active_interaction/filters/string_filter'
|
19
|
+
require 'active_interaction/filters/symbol_filter'
|
20
20
|
require 'active_interaction/filters/time_filter'
|
21
|
+
require 'active_interaction/filters'
|
22
|
+
require 'active_interaction/validation'
|
21
23
|
require 'active_interaction/base'
|
22
24
|
|
23
25
|
I18n.backend.load_translations(
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ActiveInteraction
|
2
|
+
# @private
|
3
|
+
module ActiveModel
|
4
|
+
extend ::ActiveSupport::Concern
|
5
|
+
|
6
|
+
extend ::ActiveModel::Naming
|
7
|
+
include ::ActiveModel::Conversion
|
8
|
+
include ::ActiveModel::Validations
|
9
|
+
|
10
|
+
def new_record?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
def persisted?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
def i18n_scope
|
19
|
+
self.class.i18n_scope
|
20
|
+
end
|
21
|
+
|
22
|
+
# @private
|
23
|
+
module ClassMethods
|
24
|
+
def i18n_scope
|
25
|
+
:active_interaction
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -15,7 +15,7 @@ module ActiveInteraction
|
|
15
15
|
# integer :a, :b
|
16
16
|
#
|
17
17
|
# # Optional
|
18
|
-
# integer :c,
|
18
|
+
# integer :c, default: nil
|
19
19
|
#
|
20
20
|
# def execute
|
21
21
|
# sum = a + b
|
@@ -30,53 +30,59 @@ module ActiveInteraction
|
|
30
30
|
# p outcome.errors
|
31
31
|
# end
|
32
32
|
class Base
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
include ActiveModel
|
34
|
+
extend MethodMissing
|
35
|
+
extend OverloadHash
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
validate do
|
38
|
+
Validation.validate(self.class.filters, inputs).each do |error|
|
39
|
+
errors.add_sym(*error)
|
40
|
+
end
|
40
41
|
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
false
|
45
|
-
end
|
43
|
+
validate do
|
44
|
+
return unless instance_variable_defined?(:@_interaction_runtime_errors)
|
46
45
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
46
|
+
@_interaction_runtime_errors.symbolic.each do |attribute, symbols|
|
47
|
+
symbols.each { |symbol| errors.add_sym(attribute, symbol) }
|
48
|
+
end
|
51
49
|
|
52
|
-
|
53
|
-
|
54
|
-
|
50
|
+
@_interaction_runtime_errors.messages.each do |attribute, messages|
|
51
|
+
messages.each { |message| errors.add(attribute, message) }
|
52
|
+
end
|
55
53
|
end
|
56
54
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
55
|
+
# Returns the inputs provided to {.run} or {.run!} after being cast based
|
56
|
+
# on the filters in the class.
|
57
|
+
#
|
58
|
+
# @return [Hash{Symbol => Object}] All inputs passed to {.run} or {.run!}.
|
59
|
+
#
|
60
|
+
# @since 0.6.0
|
61
|
+
def inputs
|
62
|
+
self.class.filters.reduce({}) do |h, filter|
|
63
|
+
h[filter.name] = send(filter.name)
|
64
|
+
h
|
63
65
|
end
|
64
66
|
end
|
65
67
|
|
68
|
+
# @param options [Hash{Symbol => Object}] Attribute values to set.
|
69
|
+
#
|
66
70
|
# @private
|
67
71
|
def initialize(options = {})
|
68
|
-
options = options.
|
72
|
+
options = options.symbolize_keys
|
73
|
+
|
74
|
+
options.each do |key, value|
|
75
|
+
if key.to_s.start_with?('_interaction_')
|
76
|
+
raise InvalidValueError, key.inspect
|
77
|
+
end
|
69
78
|
|
70
|
-
|
71
|
-
raise ArgumentError, ':result is reserved and can not be used'
|
79
|
+
instance_variable_set("@#{key}", value)
|
72
80
|
end
|
73
81
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
else
|
79
|
-
instance_variable_set("@#{attribute}", value)
|
82
|
+
self.class.filters.each do |filter|
|
83
|
+
begin
|
84
|
+
send("#{filter.name}=", filter.clean(options[filter.name]))
|
85
|
+
rescue InvalidValueError, MissingValueError
|
80
86
|
end
|
81
87
|
end
|
82
88
|
end
|
@@ -87,6 +93,8 @@ module ActiveInteraction
|
|
87
93
|
# This method is run in a transaction if ActiveRecord is available.
|
88
94
|
#
|
89
95
|
# @raise [NotImplementedError] if the method is not defined.
|
96
|
+
#
|
97
|
+
# @abstract
|
90
98
|
def execute
|
91
99
|
raise NotImplementedError
|
92
100
|
end
|
@@ -97,12 +105,22 @@ module ActiveInteraction
|
|
97
105
|
# @return [Nil] if there are validation errors.
|
98
106
|
# @return [Object] if there are no validation errors.
|
99
107
|
def result
|
100
|
-
@_interaction_result
|
108
|
+
symbol = :'@_interaction_result'
|
109
|
+
if instance_variable_defined?(symbol)
|
110
|
+
instance_variable_get(symbol)
|
111
|
+
else
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# @private
|
117
|
+
def errors
|
118
|
+
@_interaction_errors ||= Errors.new(self)
|
101
119
|
end
|
102
120
|
|
103
121
|
# @private
|
104
122
|
def valid?(*args)
|
105
|
-
super ||
|
123
|
+
super || @_interaction_result = nil
|
106
124
|
end
|
107
125
|
|
108
126
|
# @private
|
@@ -115,27 +133,32 @@ module ActiveInteraction
|
|
115
133
|
yield
|
116
134
|
end
|
117
135
|
end
|
118
|
-
private_class_method :transaction
|
119
136
|
|
120
|
-
#
|
121
|
-
#
|
137
|
+
# Get all the filters defined on this interaction.
|
138
|
+
#
|
139
|
+
# @return [Filters]
|
140
|
+
#
|
141
|
+
# @since 0.6.0
|
142
|
+
def self.filters
|
143
|
+
@_interaction_filters ||= Filters.new
|
144
|
+
end
|
122
145
|
|
123
146
|
# Runs validations and if there are no errors it will call {#execute}.
|
124
147
|
#
|
125
|
-
# @
|
148
|
+
# @param (see #initialize)
|
126
149
|
#
|
127
150
|
# @return [ActiveInteraction::Base] An instance of the class `run` is
|
128
151
|
# called on.
|
129
|
-
def self.run(
|
130
|
-
new(
|
152
|
+
def self.run(*args)
|
153
|
+
new(*args).tap do |interaction|
|
131
154
|
if interaction.valid?
|
132
155
|
result = transaction { interaction.execute }
|
133
156
|
|
134
157
|
if interaction.errors.empty?
|
135
158
|
interaction.instance_variable_set(:@_interaction_result, result)
|
136
159
|
else
|
137
|
-
interaction.instance_variable_set(
|
138
|
-
interaction.errors.dup)
|
160
|
+
interaction.instance_variable_set(
|
161
|
+
:@_interaction_runtime_errors, interaction.errors.dup)
|
139
162
|
end
|
140
163
|
end
|
141
164
|
end
|
@@ -144,93 +167,36 @@ module ActiveInteraction
|
|
144
167
|
# Like {.run} except that it returns the value of {#execute} or raises an
|
145
168
|
# exception if there were any validation errors.
|
146
169
|
#
|
147
|
-
# @
|
148
|
-
#
|
149
|
-
# @raise [InteractionInvalid] if there are any errors on the model.
|
170
|
+
# @param (see .run)
|
150
171
|
#
|
151
172
|
# @return The return value of {#execute}.
|
152
|
-
|
153
|
-
|
173
|
+
#
|
174
|
+
# @raise [InteractionInvalidError] if there are any errors on the model.
|
175
|
+
def self.run!(*args)
|
176
|
+
outcome = run(*args)
|
154
177
|
if outcome.invalid?
|
155
|
-
raise
|
178
|
+
raise InteractionInvalidError, outcome.errors.full_messages.join(', ')
|
156
179
|
end
|
157
180
|
outcome.result
|
158
181
|
end
|
159
182
|
|
160
183
|
# @private
|
161
|
-
def self.method_missing(
|
162
|
-
|
163
|
-
|
164
|
-
args.each do |attribute|
|
165
|
-
set_up_reader(attribute, filter, options, &block)
|
166
|
-
set_up_writer(attribute, filter, options, &block)
|
167
|
-
set_up_validator(attribute, type, filter, options, &block)
|
168
|
-
end
|
169
|
-
end
|
170
|
-
private_class_method :method_missing
|
171
|
-
|
172
|
-
# @private
|
173
|
-
def self.set_up_reader(attribute, filter, options, &block)
|
174
|
-
default = nil
|
175
|
-
if options.has_key?(:default)
|
176
|
-
begin
|
177
|
-
default = filter.
|
178
|
-
prepare(attribute, options[:default], options, &block)
|
179
|
-
rescue InvalidNestedValue, InvalidValue
|
180
|
-
raise InvalidDefaultValue
|
181
|
-
end
|
182
|
-
end
|
184
|
+
def self.method_missing(*args, &block)
|
185
|
+
super do |klass, names, options|
|
186
|
+
raise InvalidFilterError, 'no name' if names.empty?
|
183
187
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
instance_variable_get(symbol)
|
188
|
-
else
|
189
|
-
default
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
|
-
private_class_method :set_up_reader
|
194
|
-
|
195
|
-
# @private
|
196
|
-
def self.set_up_writer(attribute, filter, options, &block)
|
197
|
-
attr_writer attribute
|
198
|
-
|
199
|
-
writer = "_filter__#{attribute}="
|
200
|
-
|
201
|
-
define_method(writer) do |value|
|
202
|
-
value =
|
203
|
-
begin
|
204
|
-
filter.prepare(attribute, value, options, &block)
|
205
|
-
rescue InvalidNestedValue, InvalidValue, MissingValue
|
206
|
-
value
|
188
|
+
names.each do |attribute|
|
189
|
+
if attribute.to_s.start_with?('_interaction_')
|
190
|
+
raise InvalidFilterError, attribute.inspect
|
207
191
|
end
|
208
|
-
instance_variable_set("@#{attribute}", value)
|
209
|
-
end
|
210
|
-
private writer
|
211
|
-
end
|
212
|
-
private_class_method :set_up_writer
|
213
192
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
validate validator
|
193
|
+
filter = klass.new(attribute, options, &block)
|
194
|
+
filters.add(filter)
|
195
|
+
attr_accessor filter.name
|
219
196
|
|
220
|
-
|
221
|
-
begin
|
222
|
-
filter.prepare(attribute, send(attribute), options, &block)
|
223
|
-
rescue InvalidNestedValue
|
224
|
-
errors.add(attribute, :invalid_nested)
|
225
|
-
rescue InvalidValue
|
226
|
-
errors.add(attribute, :invalid,
|
227
|
-
type: I18n.translate("#{i18n_scope}.types.#{type.to_s}"))
|
228
|
-
rescue MissingValue
|
229
|
-
errors.add(attribute, :missing)
|
197
|
+
filter.default if filter.has_default?
|
230
198
|
end
|
231
199
|
end
|
232
|
-
private validator
|
233
200
|
end
|
234
|
-
private_class_method :set_up_validator
|
235
201
|
end
|
236
202
|
end
|