active_interaction 0.10.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/README.md +32 -35
- data/lib/active_interaction.rb +14 -6
- data/lib/active_interaction/base.rb +155 -135
- data/lib/active_interaction/concerns/active_modelable.rb +46 -0
- data/lib/active_interaction/{modules/overload_hash.rb → concerns/hashable.rb} +5 -1
- data/lib/active_interaction/concerns/missable.rb +47 -0
- data/lib/active_interaction/concerns/runnable.rb +156 -0
- data/lib/active_interaction/errors.rb +50 -14
- data/lib/active_interaction/filter.rb +33 -45
- data/lib/active_interaction/filters/abstract_date_time_filter.rb +24 -15
- data/lib/active_interaction/filters/abstract_filter.rb +18 -0
- data/lib/active_interaction/filters/abstract_numeric_filter.rb +13 -8
- data/lib/active_interaction/filters/array_filter.rb +42 -35
- data/lib/active_interaction/filters/boolean_filter.rb +8 -11
- data/lib/active_interaction/filters/date_filter.rb +11 -15
- data/lib/active_interaction/filters/date_time_filter.rb +11 -15
- data/lib/active_interaction/filters/file_filter.rb +11 -11
- data/lib/active_interaction/filters/float_filter.rb +7 -15
- data/lib/active_interaction/filters/hash_filter.rb +18 -24
- data/lib/active_interaction/filters/integer_filter.rb +7 -14
- data/lib/active_interaction/filters/model_filter.rb +13 -14
- data/lib/active_interaction/filters/string_filter.rb +11 -14
- data/lib/active_interaction/filters/symbol_filter.rb +6 -9
- data/lib/active_interaction/filters/time_filter.rb +13 -16
- data/lib/active_interaction/modules/validation.rb +9 -4
- data/lib/active_interaction/version.rb +5 -2
- data/spec/active_interaction/base_spec.rb +109 -4
- data/spec/active_interaction/{modules/active_model_spec.rb → concerns/active_modelable_spec.rb} +15 -3
- data/spec/active_interaction/{modules/overload_hash_spec.rb → concerns/hashable_spec.rb} +2 -3
- data/spec/active_interaction/{modules/method_missing_spec.rb → concerns/missable_spec.rb} +38 -3
- data/spec/active_interaction/concerns/runnable_spec.rb +192 -0
- data/spec/active_interaction/filters/abstract_filter_spec.rb +8 -0
- data/spec/active_interaction/filters/abstract_numeric_filter_spec.rb +1 -1
- data/spec/active_interaction/modules/validation_spec.rb +2 -2
- data/spec/support/concerns.rb +15 -0
- data/spec/support/filters.rb +5 -5
- data/spec/support/interactions.rb +6 -5
- metadata +47 -73
- data/lib/active_interaction/filters.rb +0 -28
- data/lib/active_interaction/modules/active_model.rb +0 -32
- data/lib/active_interaction/modules/core.rb +0 -70
- data/lib/active_interaction/modules/method_missing.rb +0 -20
- data/spec/active_interaction/filters_spec.rb +0 -23
- data/spec/active_interaction/modules/core_spec.rb +0 -114
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02f4ec454ba99aa3327ab6d539a8dd2f6e9b8622
|
4
|
+
data.tar.gz: e20fe1fb15d09040a7c00379962f53ac41d26cf1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab410e55639bb75899387091d50213f0b53b4d58de8b4cc0c3cc36d9c0f2acdc247bfe88662d276ed635a3fc695631a291c6c1f6234a7a46c38b4615127b3c7f
|
7
|
+
data.tar.gz: f48693acfcc32d503fdc03af08cad9b2ed1a4c7fb32b7dac467f30e325ab97f3647c7b79d865686d26a1cd93fb04ca9b449e52a8d247086a3bd1cb27ab85aad2
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# [Master][]
|
2
2
|
|
3
|
+
# [1.0.0][]
|
4
|
+
|
5
|
+
- **Replace `Filters` with a hash.** To iterate over `Filter` objects, use
|
6
|
+
`Interaction.filters.values`.
|
7
|
+
- Rename `Filter#has_default?` to `Filter#default?`.
|
8
|
+
- Add `respond_to_missing?` to complement `method_missing` calls.
|
9
|
+
- Add predicate methods for checking if an input was passed.
|
10
|
+
- When adding a filter that shares a name with an existing filter, it will now
|
11
|
+
replace the existing one instead of adding a duplicate.
|
12
|
+
- Allow fetching filters by name.
|
13
|
+
- Allow import filters from another interaction with `import_filters`.
|
14
|
+
|
3
15
|
# [0.10.2][] (2014-01-02)
|
4
16
|
|
5
17
|
- Fix a bug that marked Time instances as invalid if Time.zone was set.
|
@@ -106,7 +118,8 @@
|
|
106
118
|
|
107
119
|
- Initial release.
|
108
120
|
|
109
|
-
[master]: https://github.com/orgsync/active_interaction/compare/
|
121
|
+
[master]: https://github.com/orgsync/active_interaction/compare/v1.0.0...master
|
122
|
+
[1.0.0]: https://github.com/orgsync/active_interaction/compare/v0.10.2...v1.0.0
|
110
123
|
[0.10.2]: https://github.com/orgsync/active_interaction/compare/v0.10.1...v0.10.2
|
111
124
|
[0.10.1]: https://github.com/orgsync/active_interaction/compare/v0.10.0...v0.10.1
|
112
125
|
[0.10.0]: https://github.com/orgsync/active_interaction/compare/v0.9.1...v0.10.0
|
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
# ActiveInteraction
|
1
|
+
# [ActiveInteraction][0]
|
2
2
|
|
3
|
-
[![Gem Version][]][
|
4
|
-
[![Build Status][]][
|
5
|
-
[![Coverage Status][]][
|
6
|
-
[![Code Climate][]][
|
7
|
-
[![Dependency Status][]][
|
3
|
+
[![Gem Version][1]][2]
|
4
|
+
[![Build Status][3]][4]
|
5
|
+
[![Coverage Status][5]][6]
|
6
|
+
[![Code Climate][7]][8]
|
7
|
+
[![Dependency Status][9]][10]
|
8
8
|
|
9
9
|
At first it seemed alright. A little business logic in a controller
|
10
10
|
or model wasn't going to hurt anything. Then one day you wake up
|
@@ -15,17 +15,17 @@ to this.
|
|
15
15
|
Take back control. Slim down models and wrangle monstrous controller
|
16
16
|
methods with ActiveInteraction.
|
17
17
|
|
18
|
-
Read more on the [project page][] or check out the full [documentation][]
|
18
|
+
Read more on the [project page][11] or check out the full [documentation][12]
|
19
19
|
on RubyDoc.info.
|
20
20
|
|
21
21
|
## Installation
|
22
22
|
|
23
|
-
This project uses [semantic versioning][].
|
23
|
+
This project uses [semantic versioning][13].
|
24
24
|
|
25
25
|
Add it to your Gemfile:
|
26
26
|
|
27
27
|
```ruby
|
28
|
-
gem 'active_interaction', '~> 0
|
28
|
+
gem 'active_interaction', '~> 1.0'
|
29
29
|
```
|
30
30
|
|
31
31
|
And then execute:
|
@@ -198,12 +198,10 @@ end
|
|
198
198
|
end
|
199
199
|
```
|
200
200
|
|
201
|
-
Check out the [documentation][] for a full list of methods.
|
201
|
+
Check out the [documentation][12] for a full list of methods.
|
202
202
|
|
203
203
|
## How do I compose interactions?
|
204
204
|
|
205
|
-
(Note: this feature is experimental. See [#41][] & [#79][].)
|
206
|
-
|
207
205
|
You can run interactions from within other interactions by calling `compose`.
|
208
206
|
If the interaction is successful, it'll return the result (just like if you had
|
209
207
|
called it with `run!`). If something went wrong, execution will halt
|
@@ -278,26 +276,25 @@ p Interaction.run.errors.messages
|
|
278
276
|
|
279
277
|
## Credits
|
280
278
|
|
281
|
-
ActiveInteraction is brought to you by [@AaronLasseigne][] and
|
282
|
-
[@tfausak][] from [@orgsync][]. We were inspired by the fantastic
|
283
|
-
work done in [Mutations][].
|
284
|
-
|
285
|
-
[
|
286
|
-
[
|
287
|
-
[
|
288
|
-
[
|
289
|
-
[
|
290
|
-
[
|
291
|
-
[
|
292
|
-
[
|
293
|
-
[
|
294
|
-
[
|
295
|
-
[
|
296
|
-
[
|
297
|
-
[
|
298
|
-
[
|
299
|
-
[
|
300
|
-
[
|
301
|
-
[
|
302
|
-
[
|
303
|
-
[semantic versioning]: http://semver.org/spec/v2.0.0.html
|
279
|
+
ActiveInteraction is brought to you by [@AaronLasseigne][14] and
|
280
|
+
[@tfausak][15] from [@orgsync][16]. We were inspired by the fantastic
|
281
|
+
work done in [Mutations][17].
|
282
|
+
|
283
|
+
[0]: https://github.com/orgsync/active_interaction
|
284
|
+
[1]: https://badge.fury.io/rb/active_interaction.png
|
285
|
+
[2]: https://badge.fury.io/rb/active_interaction "Gem Version"
|
286
|
+
[3]: https://travis-ci.org/orgsync/active_interaction.png
|
287
|
+
[4]: https://travis-ci.org/orgsync/active_interaction "Build Status"
|
288
|
+
[5]: https://coveralls.io/repos/orgsync/active_interaction/badge.png
|
289
|
+
[6]: https://coveralls.io/r/orgsync/active_interaction "Coverage Status"
|
290
|
+
[7]: https://codeclimate.com/github/orgsync/active_interaction.png
|
291
|
+
[8]: https://codeclimate.com/github/orgsync/active_interaction "Code Climate"
|
292
|
+
[9]: https://gemnasium.com/orgsync/active_interaction.png
|
293
|
+
[10]: https://gemnasium.com/orgsync/active_interaction "Dependency Status"
|
294
|
+
[11]: http://orgsync.github.io/active_interaction/
|
295
|
+
[12]: http://rubydoc.info/github/orgsync/active_interaction
|
296
|
+
[13]: http://semver.org/spec/v2.0.0.html
|
297
|
+
[14]: https://github.com/AaronLasseigne
|
298
|
+
[15]: https://github.com/tfausak
|
299
|
+
[16]: https://github.com/orgsync
|
300
|
+
[17]: https://github.com/cypriss/mutations
|
data/lib/active_interaction.rb
CHANGED
@@ -5,14 +5,15 @@ require 'active_model'
|
|
5
5
|
require 'active_interaction/version'
|
6
6
|
require 'active_interaction/errors'
|
7
7
|
|
8
|
-
require 'active_interaction/
|
9
|
-
require 'active_interaction/
|
10
|
-
require 'active_interaction/
|
11
|
-
require 'active_interaction/
|
8
|
+
require 'active_interaction/concerns/active_modelable'
|
9
|
+
require 'active_interaction/concerns/hashable'
|
10
|
+
require 'active_interaction/concerns/missable'
|
11
|
+
require 'active_interaction/concerns/runnable'
|
12
|
+
|
12
13
|
require 'active_interaction/modules/validation'
|
13
14
|
|
14
15
|
require 'active_interaction/filter'
|
15
|
-
require 'active_interaction/filters'
|
16
|
+
require 'active_interaction/filters/abstract_filter'
|
16
17
|
require 'active_interaction/filters/abstract_date_time_filter'
|
17
18
|
require 'active_interaction/filters/abstract_numeric_filter'
|
18
19
|
require 'active_interaction/filters/array_filter'
|
@@ -34,5 +35,12 @@ I18n.backend.load_translations(
|
|
34
35
|
Dir[File.join(%w(lib active_interaction locale *.yml))]
|
35
36
|
)
|
36
37
|
|
37
|
-
#
|
38
|
+
# Manage application specific business logic.
|
39
|
+
#
|
40
|
+
# @author Aaron Lasseigne <aaron.lasseigne@gmail.com>
|
41
|
+
# @author Taylor Fausak <taylor@fausak.me>
|
42
|
+
#
|
43
|
+
# @since 1.0.0
|
44
|
+
#
|
45
|
+
# @version 1.0.0
|
38
46
|
module ActiveInteraction end
|
@@ -4,199 +4,219 @@ require 'active_support/core_ext/hash/indifferent_access'
|
|
4
4
|
|
5
5
|
module ActiveInteraction
|
6
6
|
# @abstract Subclass and override {#execute} to implement a custom
|
7
|
-
# ActiveInteraction class.
|
7
|
+
# ActiveInteraction::Base class.
|
8
|
+
#
|
9
|
+
# Provides interaction functionality. Subclass this to create an interaction.
|
8
10
|
#
|
9
11
|
# @example
|
10
12
|
# class ExampleInteraction < ActiveInteraction::Base
|
11
13
|
# # Required
|
12
|
-
#
|
14
|
+
# boolean :a
|
13
15
|
#
|
14
16
|
# # Optional
|
15
|
-
#
|
17
|
+
# boolean :b, default: false
|
16
18
|
#
|
17
19
|
# def execute
|
18
|
-
#
|
19
|
-
# c.nil? ? sum : sum + c
|
20
|
+
# a && b
|
20
21
|
# end
|
21
22
|
# end
|
22
23
|
#
|
23
|
-
# outcome = ExampleInteraction.run(a:
|
24
|
+
# outcome = ExampleInteraction.run(a: true)
|
24
25
|
# if outcome.valid?
|
25
|
-
#
|
26
|
+
# outcome.result
|
26
27
|
# else
|
27
|
-
#
|
28
|
+
# outcome.errors
|
28
29
|
# end
|
29
30
|
class Base
|
30
|
-
include
|
31
|
+
include ActiveModelable
|
32
|
+
include Runnable
|
33
|
+
|
34
|
+
validate :input_errors
|
35
|
+
|
36
|
+
class << self
|
37
|
+
include Hashable
|
38
|
+
include Missable
|
39
|
+
|
40
|
+
# Get or set the description.
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
# core.desc
|
44
|
+
# # => nil
|
45
|
+
# core.desc('Description!')
|
46
|
+
# core.desc
|
47
|
+
# # => "Description!"
|
48
|
+
#
|
49
|
+
# @param desc [String, nil] What to set the description to.
|
50
|
+
#
|
51
|
+
# @return [String, nil] The description.
|
52
|
+
def desc(desc = nil)
|
53
|
+
if desc.nil?
|
54
|
+
unless instance_variable_defined?(:@_interaction_desc)
|
55
|
+
@_interaction_desc = nil
|
56
|
+
end
|
57
|
+
else
|
58
|
+
@_interaction_desc = desc
|
59
|
+
end
|
31
60
|
|
32
|
-
|
33
|
-
|
34
|
-
extend OverloadHash
|
61
|
+
@_interaction_desc
|
62
|
+
end
|
35
63
|
|
36
|
-
|
64
|
+
# Get all the filters defined on this interaction.
|
65
|
+
#
|
66
|
+
# @return [Hash{Symbol => Filter}]
|
67
|
+
def filters
|
68
|
+
@_interaction_filters ||= {}
|
69
|
+
end
|
37
70
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
fail ArgumentError, 'inputs must be a hash' unless inputs.is_a?(Hash)
|
71
|
+
# @private
|
72
|
+
def method_missing(*args, &block)
|
73
|
+
super do |klass, names, options|
|
74
|
+
fail InvalidFilterError, 'missing attribute name' if names.empty?
|
43
75
|
|
44
|
-
|
45
|
-
|
46
|
-
|
76
|
+
names.each { |name| add_filter(klass, name, options, &block) }
|
77
|
+
end
|
78
|
+
end
|
47
79
|
|
48
|
-
|
49
|
-
|
80
|
+
# @!method run(inputs = {})
|
81
|
+
# Runs validations and if there are no errors it will call {#execute}.
|
82
|
+
#
|
83
|
+
# @param (see ActiveInteraction::Base#initialize)
|
84
|
+
#
|
85
|
+
# @return [Base]
|
86
|
+
loop
|
87
|
+
|
88
|
+
# @!method run!(inputs = {})
|
89
|
+
# Like {.run} except that it returns the value of {#execute} or raises
|
90
|
+
# an exception if there were any validation errors.
|
91
|
+
#
|
92
|
+
# @param (see ActiveInteraction::Base.run)
|
93
|
+
#
|
94
|
+
# @return (see ActiveInteraction::Runnable::ClassMethods#run!)
|
95
|
+
#
|
96
|
+
# @raise (see ActiveInteraction::Runnable::ClassMethods#run!)
|
97
|
+
loop
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
# @param klass [Class]
|
102
|
+
# @param name [Symbol]
|
103
|
+
# @param options [Hash]
|
104
|
+
def add_filter(klass, name, options, &block)
|
105
|
+
fail InvalidFilterError, name.inspect if reserved?(name)
|
106
|
+
|
107
|
+
filter = klass.new(name, options, &block)
|
108
|
+
filters[name] = filter
|
109
|
+
attr_accessor name
|
110
|
+
define_method("#{name}?") { !public_send(name).nil? }
|
111
|
+
|
112
|
+
filter.default if filter.default?
|
113
|
+
end
|
50
114
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
115
|
+
# Import filters from another interaction.
|
116
|
+
#
|
117
|
+
# @param klass [Class] The other interaction.
|
118
|
+
# @param options [Hash]
|
119
|
+
#
|
120
|
+
# @option options [Array<Symbol>, nil] :only Import only these filters.
|
121
|
+
# @option options [Array<Symbol>, nil] :except Import all filters except
|
122
|
+
# for these.
|
123
|
+
#
|
124
|
+
# @return (see .filters)
|
125
|
+
#
|
126
|
+
# @!visibility public
|
127
|
+
def import_filters(klass, options = {})
|
128
|
+
if options.key?(:only) && options.key?(:except)
|
129
|
+
fail ArgumentError, 'given both :only and :except'
|
130
|
+
end
|
131
|
+
|
132
|
+
only = options[:only]
|
133
|
+
except = options[:except]
|
134
|
+
|
135
|
+
other_filters = klass.filters.dup
|
136
|
+
other_filters.select! { |k, _| only.include?(k) } if only
|
137
|
+
other_filters.reject! { |k, _| except.include?(k) } if except
|
138
|
+
|
139
|
+
filters.merge!(other_filters)
|
60
140
|
end
|
61
|
-
end
|
62
141
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
#
|
68
|
-
# @raise [NotImplementedError] if the method is not defined.
|
69
|
-
#
|
70
|
-
# @abstract
|
71
|
-
def execute
|
72
|
-
fail NotImplementedError
|
73
|
-
end
|
142
|
+
# @param klass [Class]
|
143
|
+
def inherited(klass)
|
144
|
+
klass.instance_variable_set(:@_interaction_filters, filters.dup)
|
145
|
+
end
|
74
146
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
147
|
+
# @param symbol [Symbol]
|
148
|
+
#
|
149
|
+
# @return [Boolean]
|
150
|
+
def reserved?(symbol)
|
151
|
+
symbol.to_s.start_with?('_interaction_')
|
152
|
+
end
|
81
153
|
end
|
82
154
|
|
155
|
+
# @param inputs [Hash{Symbol => Object}] Attribute values to set.
|
156
|
+
#
|
83
157
|
# @private
|
84
|
-
def
|
85
|
-
|
86
|
-
end
|
158
|
+
def initialize(inputs = {})
|
159
|
+
fail ArgumentError, 'inputs must be a hash' unless inputs.is_a?(Hash)
|
87
160
|
|
88
|
-
|
89
|
-
def valid?(*args)
|
90
|
-
super(*args) || (@_interaction_result = nil)
|
161
|
+
process_inputs(inputs.symbolize_keys)
|
91
162
|
end
|
92
163
|
|
93
|
-
#
|
164
|
+
# @!method compose(other, inputs = {})
|
165
|
+
# Run another interaction and return its result. If the other interaction
|
166
|
+
# fails, halt execution.
|
94
167
|
#
|
95
|
-
#
|
168
|
+
# @param other (see ActiveInteraction::Runnable#compose)
|
169
|
+
# @param inputs (see ActiveInteraction::Base#initialize)
|
96
170
|
#
|
97
|
-
#
|
98
|
-
|
99
|
-
@_interaction_filters ||= Filters.new
|
100
|
-
end
|
171
|
+
# @return (see ActiveInteraction::Base.run!)
|
172
|
+
loop
|
101
173
|
|
102
|
-
#
|
174
|
+
# @!method execute
|
175
|
+
# @abstract
|
103
176
|
#
|
104
|
-
#
|
177
|
+
# Runs the business logic associated with the interaction. This method is
|
178
|
+
# only run when there are no validation errors. The return value is
|
179
|
+
# placed into {#result}. This method is run in a transaction if
|
180
|
+
# ActiveRecord is available.
|
105
181
|
#
|
106
|
-
#
|
107
|
-
|
108
|
-
def self.run(*args)
|
109
|
-
new(*args).tap do |interaction|
|
110
|
-
if interaction.valid?
|
111
|
-
result = transaction do
|
112
|
-
begin
|
113
|
-
interaction.execute
|
114
|
-
rescue Interrupt
|
115
|
-
# Inner interaction failed. #compose handles merging errors.
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
if interaction.errors.empty?
|
120
|
-
interaction.instance_variable_set(:@_interaction_result, result)
|
121
|
-
else
|
122
|
-
interaction.instance_variable_set(
|
123
|
-
:@_interaction_runtime_errors, interaction.errors.dup)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
182
|
+
# @raise (see ActiveInteraction::Runnable#execute)
|
183
|
+
loop
|
128
184
|
|
129
|
-
#
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
fail InvalidFilterError, attribute.inspect
|
137
|
-
end
|
138
|
-
|
139
|
-
filter = klass.new(attribute, options, &block)
|
140
|
-
filters.add(filter)
|
141
|
-
attr_accessor filter.name
|
142
|
-
|
143
|
-
# This isn't required, but it makes invalid defaults raise errors on
|
144
|
-
# class definition instead of on execution.
|
145
|
-
filter.default if filter.has_default?
|
146
|
-
end
|
185
|
+
# Returns the inputs provided to {.run} or {.run!} after being cast based
|
186
|
+
# on the filters in the class.
|
187
|
+
#
|
188
|
+
# @return [Hash{Symbol => Object}] All inputs passed to {.run} or {.run!}.
|
189
|
+
def inputs
|
190
|
+
self.class.filters.keys.each_with_object({}) do |name, h|
|
191
|
+
h[name] = public_send(name)
|
147
192
|
end
|
148
193
|
end
|
149
194
|
|
150
195
|
private
|
151
196
|
|
197
|
+
# @param inputs [Hash{Symbol => Object}]
|
152
198
|
def process_inputs(inputs)
|
153
199
|
inputs.each do |key, value|
|
154
|
-
|
155
|
-
fail InvalidValueError, key.inspect
|
156
|
-
end
|
200
|
+
fail InvalidValueError, key.inspect if self.class.send(:reserved?, key)
|
157
201
|
|
158
202
|
instance_variable_set("@#{key}", value)
|
159
203
|
end
|
160
204
|
|
161
|
-
self.class.filters.each do |filter|
|
205
|
+
self.class.filters.each do |name, filter|
|
162
206
|
begin
|
163
|
-
|
207
|
+
public_send("#{name}=", filter.clean(inputs[name]))
|
164
208
|
rescue InvalidValueError, MissingValueError
|
165
209
|
# Validators (#input_errors) will add errors if appropriate.
|
166
210
|
end
|
167
211
|
end
|
168
212
|
end
|
169
213
|
|
214
|
+
# @!group Validations
|
215
|
+
|
170
216
|
def input_errors
|
171
217
|
Validation.validate(self.class.filters, inputs).each do |error|
|
172
218
|
errors.add_sym(*error)
|
173
219
|
end
|
174
220
|
end
|
175
|
-
|
176
|
-
def runtime_errors
|
177
|
-
if @_interaction_runtime_errors
|
178
|
-
errors.merge!(@_interaction_runtime_errors)
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
def compose(interaction, inputs = {})
|
183
|
-
outcome = interaction.run(inputs)
|
184
|
-
return outcome.result if outcome.valid?
|
185
|
-
|
186
|
-
# This can't use Errors#merge! because the errors have to be added to
|
187
|
-
# base.
|
188
|
-
outcome.errors.full_messages.each do |message|
|
189
|
-
errors.add(:base, message) unless errors.added?(:base, message)
|
190
|
-
end
|
191
|
-
|
192
|
-
fail Interrupt
|
193
|
-
end
|
194
|
-
|
195
|
-
def self.inherited(klass)
|
196
|
-
new_filters = Filters.new
|
197
|
-
filters.each { |f| new_filters.add(f) }
|
198
|
-
|
199
|
-
klass.instance_variable_set(:@_interaction_filters, new_filters)
|
200
|
-
end
|
201
221
|
end
|
202
222
|
end
|