active_interaction 0.10.2 → 1.0.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 +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
|