interaktor 0.1.4 → 0.1.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a783c3017cd4cb48ee61f7d68869a1b7b2e3a716657c3c1bc8ca230b8e7f483
4
- data.tar.gz: d5966a7b86cbfe7af7892002e8eee10cc10872743717b7501fee850a30ebc9a5
3
+ metadata.gz: 99cf1e62638c0d275243c18799d225ec5f6e44eb6e75ac0cb12418775f8d2ebe
4
+ data.tar.gz: 8e53e5c94af31544e37222810b123bf7817db54a3667f94e01a10c64d9536ba3
5
5
  SHA512:
6
- metadata.gz: 1f0df7c0eb75c1a3594c5f9e2c53fa7561fa8349c2cec2408f7d2a38be4e9c8ab80403fc63d6dee36f8f9e449c27fd9842912569bb80a30cf302ab398ccc5ddd
7
- data.tar.gz: f6333614072980b7bee34734ba34f8ee0d1881dc6aa756eaaa42e2ba4058cfcc85580fe2d2c83188a060dd185b1b322c62be188ae137acb9b38adbebcc9b7762
6
+ metadata.gz: 7fcd8dfeabdbacd828471bac5d80e57582e98f02c9ecf31188c2300aa24e19e2dde04453aeb82b439b8f048a4c10bb00147f2b7e9927d028aa4adbcfa22ebcf0
7
+ data.tar.gz: 1ddb2a5b49de3269b02b1c1264991ae126385ed2c22fd7f9fb953361820d9f373620fee434be064310e3aaaabe394bf3cab2d2085a9c37ac43619b8f04f92313
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "interaktor"
3
- spec.version = "0.1.4"
3
+ spec.version = "0.1.5"
4
4
 
5
5
  spec.author = "Taylor Thurlow"
6
6
  spec.email = "taylorthurlow@me.com"
@@ -33,6 +33,13 @@ module Interaktor
33
33
  @optional_attributes ||= []
34
34
  end
35
35
 
36
+ # A list of optional attributes and their default values.
37
+ #
38
+ # @return [Array<Symbol>]
39
+ def optional_defaults
40
+ @optional_defaults ||= {}
41
+ end
42
+
36
43
  # The list of attributes which are required to be passed in when calling
37
44
  # `#fail!` from within the interaktor.
38
45
  #
@@ -52,29 +59,39 @@ module Interaktor
52
59
  # A DSL method for documenting required interaktor attributes.
53
60
  #
54
61
  # @param attributes [Symbol, Array<Symbol>] the list of attribute names
62
+ # @param options [Hash]
55
63
  #
56
64
  # @return [void]
57
- def required(*attributes)
65
+ def required(*attributes, **options)
58
66
  required_attributes.concat attributes
59
67
 
60
68
  attributes.each do |attribute|
69
+ # Define getter
61
70
  define_method(attribute) { @context.send(attribute) }
71
+
72
+ # Define setter
62
73
  define_method("#{attribute}=".to_sym) do |value|
63
74
  @context.send("#{attribute}=".to_sym, value)
64
75
  end
76
+
77
+ raise "Unknown option(s): #{options.keys.join(", ")}" if options.any?
65
78
  end
66
79
  end
67
80
 
68
81
  # A DSL method for documenting optional interaktor attributes.
69
82
  #
70
83
  # @param attributes [Symbol, Array<Symbol>] the list of attribute names
84
+ # @param options [Hash]
71
85
  #
72
86
  # @return [void]
73
- def optional(*attributes)
87
+ def optional(*attributes, **options)
74
88
  optional_attributes.concat attributes
75
89
 
76
90
  attributes.each do |attribute|
91
+ # Define getter
77
92
  define_method(attribute) { @context.send(attribute) }
93
+
94
+ # Define setter
78
95
  define_method("#{attribute}=".to_sym) do |value|
79
96
  unless @context.to_h.key?(attribute)
80
97
  raise <<~ERROR
@@ -86,6 +103,12 @@ module Interaktor
86
103
 
87
104
  @context.send("#{attribute}=".to_sym, value)
88
105
  end
106
+
107
+ # Handle options
108
+ optional_defaults[attribute] = options[:default] if options[:default]
109
+ options.delete(:default)
110
+
111
+ raise "Unknown option(s): #{options.keys.join(", ")}" if options.any?
89
112
  end
90
113
  end
91
114
 
@@ -115,11 +138,10 @@ module Interaktor
115
138
  #
116
139
  # @return [Interaktor::Context] the context, following interaktor execution
117
140
  def call(context = {})
141
+ apply_default_optional_attributes(context)
118
142
  verify_attribute_presence(context)
119
143
 
120
- catch(:early_return) do
121
- new(context).tap(&:run).instance_variable_get(:@context)
122
- end
144
+ new(context).tap(&:run).instance_variable_get(:@context)
123
145
  end
124
146
 
125
147
  # Invoke an Interaktor. This method behaves identically to `#call`, with
@@ -133,18 +155,18 @@ module Interaktor
133
155
  #
134
156
  # @return [Interaktor::Context] the context, following interaktor execution
135
157
  def call!(context = {})
158
+ apply_default_optional_attributes(context)
136
159
  verify_attribute_presence(context)
137
160
 
138
- catch(:early_return) do
139
- new(context).tap(&:run!).instance_variable_get(:@context)
140
- end
161
+ new(context).tap(&:run!).instance_variable_get(:@context)
141
162
  end
142
163
 
143
164
  private
144
165
 
145
166
  # Check the provided context against the attributes defined with the DSL
146
167
  # methods, and determine if there are any attributes which are required and
147
- # have not been provided.
168
+ # have not been provided, or if there are any attributes which have been
169
+ # provided but are not listed as either required or optional.
148
170
  #
149
171
  # @param context [Interaktor::Context] the context to check
150
172
  #
@@ -157,6 +179,26 @@ module Interaktor
157
179
  Required attribute(s) were not provided when initializing #{name} interaktor:
158
180
  #{missing_attrs.join("\n ")}
159
181
  ERROR
182
+
183
+ allowed_attrs = required_attributes + optional_attributes
184
+ extra_attrs = context.to_h.keys.reject { |attr| allowed_attrs.include?(attr) }
185
+
186
+ raise <<~ERROR if extra_attrs.any?
187
+ One or more provided attributes were not recognized when initializing #{name} interaktor:
188
+ #{extra_attrs.join("\n ")}
189
+ ERROR
190
+ end
191
+
192
+ # Given the list of optional default attribute values defined by the class,
193
+ # assign those default values to the context if they were omitted.
194
+ #
195
+ # @param context [Interaktor::Context]
196
+ #
197
+ # @return [void]
198
+ def apply_default_optional_attributes(context)
199
+ optional_defaults.each do |attribute, default|
200
+ context[attribute] ||= default
201
+ end
160
202
  end
161
203
  end
162
204
 
@@ -238,7 +280,10 @@ module Interaktor
238
280
  # @return [void]
239
281
  def run!
240
282
  with_hooks do
241
- call
283
+ catch(:early_return) do
284
+ call
285
+ end
286
+
242
287
  @context.called!(self)
243
288
  end
244
289
  rescue StandardError
@@ -8,6 +8,8 @@ describe "Integration" do
8
8
  interaktor = Class.new.send(:include, Interaktor)
9
9
  interaktor.class_eval(&block) if block
10
10
  interaktor.class_eval do
11
+ optional :steps
12
+
11
13
  def unexpected_error!
12
14
  raise "foo"
13
15
  end
@@ -20,6 +22,8 @@ describe "Integration" do
20
22
  organizer.organize(options[:organize]) if options[:organize]
21
23
  organizer.class_eval(&block) if block
22
24
  organizer.class_eval do
25
+ optional :steps
26
+
23
27
  def unexpected_error!
24
28
  raise "foo"
25
29
  end
@@ -1,5 +1,12 @@
1
1
  shared_examples "lint" do
2
- let(:interaktor) { Class.new.include(described_class) }
2
+ let(:interaktor) do
3
+ klass = Class.new.include(described_class)
4
+ klass.class_eval do
5
+ optional :foo
6
+ end
7
+
8
+ klass
9
+ end
3
10
 
4
11
  describe ".call" do
5
12
  let(:context) { instance_double(Interaktor::Context) }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: interaktor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Taylor Thurlow
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-24 00:00:00.000000000 Z
11
+ date: 2020-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: zeitwerk