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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -1
  3. data/README.md +4 -4
  4. data/lib/active_interaction.rb +2 -0
  5. data/lib/active_interaction/base.rb +26 -14
  6. data/lib/active_interaction/errors.rb +7 -10
  7. data/lib/active_interaction/filter.rb +10 -10
  8. data/lib/active_interaction/filters.rb +2 -0
  9. data/lib/active_interaction/filters/abstract_date_time_filter.rb +4 -2
  10. data/lib/active_interaction/filters/abstract_numeric_filter.rb +3 -1
  11. data/lib/active_interaction/filters/array_filter.rb +5 -3
  12. data/lib/active_interaction/filters/boolean_filter.rb +2 -0
  13. data/lib/active_interaction/filters/date_filter.rb +2 -0
  14. data/lib/active_interaction/filters/date_time_filter.rb +2 -0
  15. data/lib/active_interaction/filters/file_filter.rb +2 -0
  16. data/lib/active_interaction/filters/float_filter.rb +2 -0
  17. data/lib/active_interaction/filters/hash_filter.rb +7 -6
  18. data/lib/active_interaction/filters/integer_filter.rb +2 -0
  19. data/lib/active_interaction/filters/model_filter.rb +2 -0
  20. data/lib/active_interaction/filters/string_filter.rb +2 -0
  21. data/lib/active_interaction/filters/symbol_filter.rb +2 -0
  22. data/lib/active_interaction/filters/time_filter.rb +2 -0
  23. data/lib/active_interaction/modules/active_model.rb +2 -0
  24. data/lib/active_interaction/modules/core.rb +4 -1
  25. data/lib/active_interaction/modules/method_missing.rb +2 -0
  26. data/lib/active_interaction/modules/overload_hash.rb +2 -0
  27. data/lib/active_interaction/modules/validation.rb +10 -5
  28. data/lib/active_interaction/version.rb +4 -1
  29. data/spec/active_interaction/base_spec.rb +60 -35
  30. data/spec/active_interaction/errors_spec.rb +48 -14
  31. data/spec/active_interaction/filter_spec.rb +4 -2
  32. data/spec/active_interaction/filters/array_filter_spec.rb +13 -6
  33. data/spec/active_interaction/filters/boolean_filter_spec.rb +2 -0
  34. data/spec/active_interaction/filters/date_filter_spec.rb +6 -4
  35. data/spec/active_interaction/filters/date_time_filter_spec.rb +6 -4
  36. data/spec/active_interaction/filters/file_filter_spec.rb +2 -0
  37. data/spec/active_interaction/filters/float_filter_spec.rb +4 -2
  38. data/spec/active_interaction/filters/hash_filter_spec.rb +16 -4
  39. data/spec/active_interaction/filters/integer_filter_spec.rb +4 -2
  40. data/spec/active_interaction/filters/model_filter_spec.rb +4 -2
  41. data/spec/active_interaction/filters/string_filter_spec.rb +2 -0
  42. data/spec/active_interaction/filters/symbol_filter_spec.rb +2 -0
  43. data/spec/active_interaction/filters/time_filter_spec.rb +6 -4
  44. data/spec/active_interaction/filters_spec.rb +2 -0
  45. data/spec/active_interaction/i18n_spec.rb +9 -9
  46. data/spec/active_interaction/integration/array_interaction_spec.rb +10 -8
  47. data/spec/active_interaction/integration/boolean_interaction_spec.rb +2 -0
  48. data/spec/active_interaction/integration/date_interaction_spec.rb +2 -0
  49. data/spec/active_interaction/integration/date_time_interaction_spec.rb +2 -0
  50. data/spec/active_interaction/integration/file_interaction_spec.rb +2 -0
  51. data/spec/active_interaction/integration/float_interaction_spec.rb +2 -0
  52. data/spec/active_interaction/integration/hash_interaction_spec.rb +10 -8
  53. data/spec/active_interaction/integration/integer_interaction_spec.rb +2 -0
  54. data/spec/active_interaction/integration/model_interaction_spec.rb +3 -1
  55. data/spec/active_interaction/integration/string_interaction_spec.rb +2 -0
  56. data/spec/active_interaction/integration/symbol_interaction_spec.rb +2 -0
  57. data/spec/active_interaction/integration/time_interaction_spec.rb +9 -7
  58. data/spec/active_interaction/modules/active_model_spec.rb +2 -0
  59. data/spec/active_interaction/modules/core_spec.rb +12 -7
  60. data/spec/active_interaction/modules/method_missing_spec.rb +12 -10
  61. data/spec/active_interaction/modules/overload_hash_spec.rb +7 -5
  62. data/spec/active_interaction/modules/validation_spec.rb +5 -2
  63. data/spec/spec_helper.rb +4 -0
  64. data/spec/support/filters.rb +18 -16
  65. data/spec/support/interactions.rb +13 -11
  66. metadata +20 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1139d1df0d150418f22b231b09423c894ba98c41
4
- data.tar.gz: c53767591c098f28c46ffa8ae77fb31ac714e310
3
+ metadata.gz: 0449466d5aadcc60fb21146f2f69cad47097698a
4
+ data.tar.gz: d88a740e6c885e713a2829b7a26902e9712a57c8
5
5
  SHA512:
6
- metadata.gz: 5524134f37f76b6252d1ee82a2fe9d879aae1debec5d46679b66eb608e42bd49668c59532c5e2fda8f64999b5582649fb090c78e0c9c7048df2d05cd7e729474
7
- data.tar.gz: 591d0b74feed33a7a7d39ebe67f61adea4c7f5721541d9c570ef39c8dd06b53f4cfd74ee8bac8dc8a78508c447d942a7e0cacbb80813392640ddce8d79f9b512
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.0...master
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.0'
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 options are provided and that those
47
- options are in the format you want them in. If the options are valid
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 options validate. It
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
@@ -1,3 +1,5 @@
1
+ # coding: utf-8
2
+
1
3
  require 'active_model'
2
4
 
3
5
  require 'active_interaction/version'
@@ -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 options [Hash{Symbol => Object}] Attribute values to set.
38
+ # @param inputs [Hash{Symbol => Object}] Attribute values to set.
37
39
  #
38
40
  # @private
39
- def initialize(options = {})
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
- options = options.symbolize_keys
45
- options.each do |key, value|
48
+ inputs.each do |key, value|
46
49
  if key.to_s.start_with?('_interaction_')
47
- raise InvalidValueError, key.inspect
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(options[filter.name]))
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.reduce({}) do |h, filter|
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
- raise NotImplementedError
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
- raise InvalidFilterError, 'missing attribute name' if names.empty?
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
- raise InvalidFilterError, attribute.inspect
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, options = {})
177
- outcome = interaction.run(options)
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
- raise Interrupt
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.dup
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 do |symbol|
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 do |message|
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
- raise InvalidClassError, name unless match
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
- begin
86
- CLASSES[klass.slug] = klass
87
- rescue InvalidClassError
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
- raise NoDefaultError, name unless has_default?
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.has_key?(:default)
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
- raise MissingValueError, name unless has_default?
203
+ fail MissingValueError, name unless has_default?
204
204
 
205
205
  nil
206
206
  else
207
- raise InvalidValueError, "#{name}: #{value.inspect}"
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
  # A collection of {Filter}s.
3
5
  #
@@ -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.has_key?(:format)
32
+ options.key?(:format)
31
33
  end
32
34
 
33
35
  def klass
34
- raise NotImplementedError
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
  # @private
3
5
  class AbstractNumericFilter < Filter
@@ -19,7 +21,7 @@ module ActiveInteraction
19
21
  private
20
22
 
21
23
  def klass
22
- raise NotImplementedError
24
+ fail NotImplementedError
23
25
  end
24
26
  end
25
27
  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
- raise InvalidFilterError, 'multiple filters in array block'
50
+ fail InvalidFilterError, 'multiple filters in array block'
49
51
  end
50
52
  unless names.empty?
51
- raise InvalidFilterError, 'attribute names in array block'
53
+ fail InvalidFilterError, 'attribute names in array block'
52
54
  end
53
55
  if filter.has_default?
54
- raise InvalidDefaultError, 'default values in array block'
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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.reduce(strip? ? {} : value) do |h, f|
35
- k = f.name
36
- h[k] = f.clean(value[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
- raise InvalidDefaultError, "#{name}: #{options[:default].inspect}"
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
- raise InvalidFilterError, 'missing attribute name' if names.empty?
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))
@@ -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
@@ -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
@@ -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
@@ -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