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
@@ -1,22 +1,22 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
3
|
module ActiveInteraction
|
4
|
+
# @abstract
|
5
|
+
#
|
6
|
+
# Common logic for filters that handle `Date`, `DateTime`, and `Time`
|
7
|
+
# objects.
|
8
|
+
#
|
4
9
|
# @private
|
5
|
-
class AbstractDateTimeFilter <
|
10
|
+
class AbstractDateTimeFilter < AbstractFilter
|
11
|
+
alias_method :_cast, :cast
|
12
|
+
private :_cast
|
13
|
+
|
6
14
|
def cast(value)
|
7
15
|
case value
|
8
16
|
when *klasses
|
9
17
|
value
|
10
18
|
when String
|
11
|
-
|
12
|
-
if has_format?
|
13
|
-
klass.strptime(value, format)
|
14
|
-
else
|
15
|
-
klass.parse(value)
|
16
|
-
end
|
17
|
-
rescue ArgumentError
|
18
|
-
super
|
19
|
-
end
|
19
|
+
convert(value)
|
20
20
|
else
|
21
21
|
super
|
22
22
|
end
|
@@ -24,18 +24,27 @@ module ActiveInteraction
|
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
|
+
def convert(value)
|
28
|
+
if format?
|
29
|
+
klass.strptime(value, format)
|
30
|
+
else
|
31
|
+
klass.parse(value)
|
32
|
+
end
|
33
|
+
rescue ArgumentError
|
34
|
+
_cast(value)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [String]
|
27
38
|
def format
|
28
39
|
options.fetch(:format)
|
29
40
|
end
|
30
41
|
|
31
|
-
|
42
|
+
# @return [Boolean]
|
43
|
+
def format?
|
32
44
|
options.key?(:format)
|
33
45
|
end
|
34
46
|
|
35
|
-
|
36
|
-
self.class.slug.to_s.camelize.constantize
|
37
|
-
end
|
38
|
-
|
47
|
+
# @return [Array<Class>]
|
39
48
|
def klasses
|
40
49
|
[klass]
|
41
50
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module ActiveInteraction
|
4
|
+
# @abstract
|
5
|
+
#
|
6
|
+
# Common logic for filters that can guess the class they operator on based on
|
7
|
+
# their name.
|
8
|
+
#
|
9
|
+
# @private
|
10
|
+
class AbstractFilter < Filter
|
11
|
+
private
|
12
|
+
|
13
|
+
# @return [Class]
|
14
|
+
def klass
|
15
|
+
self.class.slug.to_s.camelize.constantize
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,18 +1,21 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
3
|
module ActiveInteraction
|
4
|
+
# @abstract
|
5
|
+
#
|
6
|
+
# Common logic for filters that handle numeric objects.
|
7
|
+
#
|
4
8
|
# @private
|
5
|
-
class AbstractNumericFilter <
|
9
|
+
class AbstractNumericFilter < AbstractFilter
|
10
|
+
alias_method :_cast, :cast
|
11
|
+
private :_cast
|
12
|
+
|
6
13
|
def cast(value)
|
7
14
|
case value
|
8
15
|
when klass
|
9
16
|
value
|
10
17
|
when Numeric, String
|
11
|
-
|
12
|
-
send(klass.name, value)
|
13
|
-
rescue ArgumentError
|
14
|
-
super
|
15
|
-
end
|
18
|
+
convert(value)
|
16
19
|
else
|
17
20
|
super
|
18
21
|
end
|
@@ -20,8 +23,10 @@ module ActiveInteraction
|
|
20
23
|
|
21
24
|
private
|
22
25
|
|
23
|
-
def
|
24
|
-
|
26
|
+
def convert(value)
|
27
|
+
Kernel.public_send(klass.name, value)
|
28
|
+
rescue ArgumentError
|
29
|
+
_cast(value)
|
25
30
|
end
|
26
31
|
end
|
27
32
|
end
|
@@ -2,61 +2,68 @@
|
|
2
2
|
|
3
3
|
module ActiveInteraction
|
4
4
|
class Base
|
5
|
-
#
|
6
|
-
# the attributes
|
5
|
+
# @!method self.array(*attributes, options = {}, &block)
|
6
|
+
# Creates accessors for the attributes and ensures that values passed to
|
7
|
+
# the attributes are Arrays.
|
7
8
|
#
|
8
|
-
#
|
9
|
-
#
|
9
|
+
# @!macro filter_method_params
|
10
|
+
# @param block [Proc] filter method to apply to each element
|
10
11
|
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
# integer default: nil
|
22
|
-
# end
|
23
|
-
#
|
24
|
-
# @since 0.1.0
|
25
|
-
#
|
26
|
-
# @method self.array(*attributes, options = {}, &block)
|
12
|
+
# @example
|
13
|
+
# array :ids
|
14
|
+
# @example
|
15
|
+
# array :ids do
|
16
|
+
# integer
|
17
|
+
# end
|
18
|
+
# @example
|
19
|
+
# array :ids do
|
20
|
+
# integer default: nil
|
21
|
+
# end
|
27
22
|
end
|
28
23
|
|
29
24
|
# @private
|
30
25
|
class ArrayFilter < Filter
|
31
|
-
include
|
26
|
+
include Missable
|
32
27
|
|
33
28
|
def cast(value)
|
34
29
|
case value
|
35
30
|
when Array
|
36
|
-
return value if filters.
|
31
|
+
return value if filters.empty?
|
37
32
|
|
38
|
-
filter = filters.first
|
33
|
+
filter = filters.values.first
|
39
34
|
value.map { |e| filter.clean(e) }
|
40
35
|
else
|
41
36
|
super
|
42
37
|
end
|
43
38
|
end
|
44
39
|
|
45
|
-
def method_missing(
|
40
|
+
def method_missing(*, &block)
|
46
41
|
super do |klass, names, options|
|
47
42
|
filter = klass.new(name, options, &block)
|
48
43
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
44
|
+
validate(filter, names)
|
45
|
+
|
46
|
+
filters[name] = filter
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# @param filter [Filter]
|
53
|
+
# @param names [Array<Symbol>]
|
54
|
+
#
|
55
|
+
# @raise [InvalidFilterError]
|
56
|
+
def validate(filter, names)
|
57
|
+
unless filters.empty?
|
58
|
+
fail InvalidFilterError, 'multiple filters in array block'
|
59
|
+
end
|
60
|
+
|
61
|
+
unless names.empty?
|
62
|
+
fail InvalidFilterError, 'attribute names in array block'
|
63
|
+
end
|
64
|
+
|
65
|
+
if filter.default?
|
66
|
+
fail InvalidDefaultError, 'default values in array block'
|
60
67
|
end
|
61
68
|
end
|
62
69
|
end
|
@@ -2,19 +2,16 @@
|
|
2
2
|
|
3
3
|
module ActiveInteraction
|
4
4
|
class Base
|
5
|
-
#
|
6
|
-
# the attributes
|
7
|
-
#
|
8
|
-
#
|
5
|
+
# @!method self.boolean(*attributes, options = {})
|
6
|
+
# Creates accessors for the attributes and ensures that values passed to
|
7
|
+
# the attributes are Booleans. The strings `"1"` and `"true"`
|
8
|
+
# (case-insensitive) are converted to `true` while the strings `"0"`
|
9
|
+
# and `"false"` are converted to `false`.
|
9
10
|
#
|
10
|
-
#
|
11
|
+
# @!macro filter_method_params
|
11
12
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
# @since 0.1.0
|
16
|
-
#
|
17
|
-
# @method self.boolean(*attributes, options = {})
|
13
|
+
# @example
|
14
|
+
# boolean :subscribed
|
18
15
|
end
|
19
16
|
|
20
17
|
# @private
|
@@ -2,23 +2,19 @@
|
|
2
2
|
|
3
3
|
module ActiveInteraction
|
4
4
|
class Base
|
5
|
-
#
|
6
|
-
# the attributes
|
7
|
-
#
|
8
|
-
#
|
5
|
+
# @!method self.date(*attributes, options = {})
|
6
|
+
# Creates accessors for the attributes and ensures that values passed to
|
7
|
+
# the attributes are Dates. String values are processed using `parse`
|
8
|
+
# unless the format option is given, in which case they will be
|
9
|
+
# processed with `strptime`.
|
9
10
|
#
|
10
|
-
#
|
11
|
-
#
|
11
|
+
# @!macro filter_method_params
|
12
|
+
# @option options [String] :format parse strings using this format string
|
12
13
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# date :birthday, format: '%Y-%m-%d'
|
18
|
-
#
|
19
|
-
# @since 0.1.0
|
20
|
-
#
|
21
|
-
# @method self.date(*attributes, options = {})
|
14
|
+
# @example
|
15
|
+
# date :birthday
|
16
|
+
# @example
|
17
|
+
# date :birthday, format: '%Y-%m-%d'
|
22
18
|
end
|
23
19
|
|
24
20
|
# @private
|
@@ -2,23 +2,19 @@
|
|
2
2
|
|
3
3
|
module ActiveInteraction
|
4
4
|
class Base
|
5
|
-
#
|
6
|
-
# the attributes
|
7
|
-
#
|
8
|
-
#
|
5
|
+
# @!method self.date_time(*attributes, options = {})
|
6
|
+
# Creates accessors for the attributes and ensures that values passed to
|
7
|
+
# the attributes are DateTimes. String values are processed using
|
8
|
+
# `parse` unless the format option is given, in which case they will be
|
9
|
+
# processed with `strptime`.
|
9
10
|
#
|
10
|
-
#
|
11
|
-
#
|
11
|
+
# @!macro filter_method_params
|
12
|
+
# @option options [String] :format parse strings using this format string
|
12
13
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# date_time :start_date, format: '%Y-%m-%dT%H:%M:%S%:z'
|
18
|
-
#
|
19
|
-
# @since 0.1.0
|
20
|
-
#
|
21
|
-
# @method self.date_time(*attributes, options = {})
|
14
|
+
# @example
|
15
|
+
# date_time :start_date
|
16
|
+
# @example
|
17
|
+
# date_time :start_date, format: '%Y-%m-%dT%H:%M:%SZ'
|
22
18
|
end
|
23
19
|
|
24
20
|
# @private
|
@@ -2,19 +2,16 @@
|
|
2
2
|
|
3
3
|
module ActiveInteraction
|
4
4
|
class Base
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
5
|
+
# @!method self.file(*attributes, options = {})
|
6
|
+
# Creates accessors for the attributes and ensures that values passed to
|
7
|
+
# the attributes are Files or Tempfiles. It will also extract a file
|
8
|
+
# from any object with a `tempfile` method. This is useful when passing
|
9
|
+
# in Rails params that include a file upload.
|
9
10
|
#
|
10
|
-
#
|
11
|
+
# @!macro filter_method_params
|
11
12
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
# @since 0.1.0
|
16
|
-
#
|
17
|
-
# @method self.file(*attributes, options = {})
|
13
|
+
# @example
|
14
|
+
# file :image
|
18
15
|
end
|
19
16
|
|
20
17
|
# @private
|
@@ -32,6 +29,9 @@ module ActiveInteraction
|
|
32
29
|
|
33
30
|
private
|
34
31
|
|
32
|
+
# @param value [File, #tempfile]
|
33
|
+
#
|
34
|
+
# @return [File]
|
35
35
|
def extract_file(value)
|
36
36
|
if value.respond_to?(:tempfile)
|
37
37
|
value.tempfile
|
@@ -2,26 +2,18 @@
|
|
2
2
|
|
3
3
|
module ActiveInteraction
|
4
4
|
class Base
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
5
|
+
# @!method self.float(*attributes, options = {})
|
6
|
+
# Creates accessors for the attributes and ensures that values passed to
|
7
|
+
# the attributes are Floats. Integer and String values are converted
|
8
|
+
# into Floats.
|
8
9
|
#
|
9
|
-
#
|
10
|
+
# @!macro filter_method_params
|
10
11
|
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# @since 0.1.0
|
15
|
-
#
|
16
|
-
# @method self.float(*attributes, options = {})
|
12
|
+
# @example
|
13
|
+
# float :amount
|
17
14
|
end
|
18
15
|
|
19
16
|
# @private
|
20
17
|
class FloatFilter < AbstractNumericFilter
|
21
|
-
private
|
22
|
-
|
23
|
-
def klass
|
24
|
-
Float
|
25
|
-
end
|
26
18
|
end
|
27
19
|
end
|
@@ -2,40 +2,33 @@
|
|
2
2
|
|
3
3
|
module ActiveInteraction
|
4
4
|
class Base
|
5
|
-
#
|
6
|
-
# the attributes
|
5
|
+
# @!method self.hash(*attributes, options = {}, &block)
|
6
|
+
# Creates accessors for the attributes and ensures that values passed to
|
7
|
+
# the attributes are Hashes.
|
7
8
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
9
|
+
# @!macro filter_method_params
|
10
|
+
# @param block [Proc] filter methods to apply for select keys
|
11
|
+
# @option options [Boolean] :strip (true) strip unknown keys
|
11
12
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# integer :quantity
|
20
|
-
# boolean :delivered
|
21
|
-
# end
|
22
|
-
#
|
23
|
-
# @since 0.1.0
|
24
|
-
#
|
25
|
-
# @method self.hash(*attributes, options = {}, &block)
|
13
|
+
# @example
|
14
|
+
# hash :order
|
15
|
+
# @example
|
16
|
+
# hash :order do
|
17
|
+
# model :item
|
18
|
+
# integer :quantity, default: 1
|
19
|
+
# end
|
26
20
|
end
|
27
21
|
|
28
22
|
# @private
|
29
23
|
class HashFilter < Filter
|
30
|
-
include
|
24
|
+
include Missable
|
31
25
|
|
32
26
|
def cast(value)
|
33
27
|
case value
|
34
28
|
when Hash
|
35
29
|
value = value.symbolize_keys
|
36
|
-
filters.each_with_object(strip? ? {} : value) do |filter, h|
|
37
|
-
|
38
|
-
h[k] = filter.clean(value[k])
|
30
|
+
filters.each_with_object(strip? ? {} : value) do |(name, filter), h|
|
31
|
+
h[name] = filter.clean(value[name])
|
39
32
|
end
|
40
33
|
else
|
41
34
|
super
|
@@ -55,13 +48,14 @@ module ActiveInteraction
|
|
55
48
|
fail InvalidFilterError, 'missing attribute name' if names.empty?
|
56
49
|
|
57
50
|
names.each do |name|
|
58
|
-
filters
|
51
|
+
filters[name] = klass.new(name, options, &block)
|
59
52
|
end
|
60
53
|
end
|
61
54
|
end
|
62
55
|
|
63
56
|
private
|
64
57
|
|
58
|
+
# @return [Boolean]
|
65
59
|
def strip?
|
66
60
|
options.fetch(:strip, true)
|
67
61
|
end
|