snfoil-context 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96734bc3c562904649028a48af86b3bfce1c6afd8273bdf5112dd5e5d608013c
4
- data.tar.gz: c380bc1234066ff9d29d6f969214b9d72a57f713ff98a11d64cf969e5310ee02
3
+ metadata.gz: 865d3b34caed7991d357df2f115087b8c99b84c5e1a0050e691e9bf751149647
4
+ data.tar.gz: 28c589b8f0fd88f7828c91678de7c58051d1489107ff2c96ad3c94e4c1cfba02
5
5
  SHA512:
6
- metadata.gz: e6c4262868a512ea479115a6dc709425d7e264b9fbebaabab3134e299ced2e7b089833573d109627e670cffe2edd17cc92ffb9ff19e00f4341bf902b9cdb69e5
7
- data.tar.gz: f9eb6db011db1d21437e95c6881a89b9e361310a16952cccf7a57b58ed6342533643622b6379a2eae1cc8a5ab2413a6e69228308cde9b0e58a6ef98da0cd2710
6
+ metadata.gz: 7115fd9f9bbb8de53f6008fccaa4018f41fc2ce2c13066a7a00e08192d1054589d3c0a471e3be837ac9d26d62cd32088ccc5e39700d7842d954d98d24fb9b9cc
7
+ data.tar.gz: a7d880bbff1e6cb9fa1fcb39a9f865ebb171d940ec3804ae0ae05a92c97fce9007b480876dcb1b05fab188635f88d28e3e65ea2f37f39d1b8819a3f4e144aacc
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ![build](https://github.com/limited-effort/snfoil-context/actions/workflows/main.yml/badge.svg) [![maintainability](https://api.codeclimate.com/v1/badges/6a7a2f643707c17cb879/maintainability)](https://codeclimate.com/github/limited-effort/snfoil-context/maintainability)
4
4
 
5
- SnFoil Contexts are a simple way to insure a workflow pipeline can be easily established end extended. It helps by creating workflow, allowing additional in steps at specific intervals, and reacting to a success or failure, you should find your code being more maintainable and testable.
5
+ SnFoil Contexts are a simple way to ensure a workflow pipeline can be easily established end extended. It helps by creating a workflow, allowing additional steps at specific intervals, and reacting to success or failure, you should find your code being more maintainable and testable.
6
6
 
7
7
  ## Installation
8
8
 
@@ -15,7 +15,47 @@ gem 'snfoil-context'
15
15
  ## Usage
16
16
  While contexts are powerful, they aren't a magic bullet. Each function should strive to only contain a single purpose. This also has the added benefit of outlining some basic tests - if it is in a function it should have a related test.
17
17
 
18
- ### Action
18
+
19
+ ### Quickstart Example
20
+
21
+ ```ruby
22
+ require 'snfoil/context'
23
+
24
+ class TokenContext
25
+ include SnFoil::Context
26
+
27
+ action(:create) { |options| options[:object].save }
28
+ action(:expire) { |options| options[:object].update(expired_at: Time.current) }
29
+
30
+ # inject created_by
31
+ setup_create { |options| options[:params][:created_by] = entity }
32
+
33
+ # initialize token
34
+ before_create do |options|
35
+ options[:object] = Token.create(options[:params])
36
+ options
37
+ end
38
+
39
+ # send token email
40
+ after_create_success { |options| TokenMailer.new(token: option[:object]) }
41
+
42
+ # log expiration error
43
+ after_expire_failure { |options| ErrorLogger.notify(error: options[:object].errors) }
44
+ end
45
+ ```
46
+
47
+
48
+ ### Initialize
49
+
50
+ When you `new` up a SnFoil Context you should provide the entity running the actions. This will usually be a user but you can pass in anything. This will be accessible from within the context as `entity`.
51
+
52
+ ```ruby
53
+ TokenContext.new(current_user)
54
+ ```
55
+
56
+ ### Actions
57
+ Actions are a group of hookable intervals that create a workflow around a single primary function.
58
+
19
59
  To start you will need to define an action.
20
60
 
21
61
  Arguments:
@@ -30,18 +70,32 @@ require 'snfoil/context'
30
70
  class TokenContext
31
71
  include SnFoil::Context
32
72
 
33
- action :expire { |options| options[:object].update(expired_at: Time.current) }
73
+ ...
74
+
75
+ action(:expire) { |options| options[:object].update(expired_at: Time.current) }
34
76
  end
35
77
  ```
36
78
 
37
- This will generate the methods and hooks of the pipeline. In this example the following get made:
79
+ This will generate the intervals of the pipeline. In this example the following get made:
38
80
  * setup_expire
39
81
  * before_expire
40
82
  * after_expire_success
41
83
  * after_expire_failure
42
84
  * after_expire
43
85
 
44
- If you want to reuse the primary action or just prefer methods, you can pass in the method name you would like to call, rather than providing a block. If a method name and a block is provided, the block is ignored.
86
+ Now you can trigger the workflow by calling the action as a method on an instance of the context.
87
+
88
+ ```ruby
89
+ class TokenContext
90
+ include SnFoil::Context
91
+
92
+ action(:expire) { |options| options[:object].update(expired_at: Time.current) }
93
+ end
94
+
95
+ TokenContext.new(current_user).expire(object: current_token)
96
+ ```
97
+
98
+ If you want to reuse the primary action or just prefer methods, you can pass in the method name you would like to call, rather than providing a block. If a method name and a block are provided, the block is ignored.
45
99
 
46
100
 
47
101
  ```ruby
@@ -59,10 +113,10 @@ class TokenContext
59
113
  end
60
114
  ```
61
115
 
62
- #### Primary Actions
63
- The primary action is the function that determine whether or not the action is successful. To do this, the primary action must always return a truthy value if the action was successful, or a falsey one if it failed.
116
+ #### Primary Function
117
+ The primary function is the function that determines whether or not the action is successful. To do this, the primary function must always return a truthy value if the action was successful, or a falsey one if it failed.
64
118
 
65
- The primary action is passed one argument which is the return value of the closest preceeding interval function.
119
+ The primary function is passed one argument which is the return value of the closest preceding interval function.
66
120
 
67
121
  ```ruby
68
122
  # lib/contexts/token_context
@@ -85,67 +139,67 @@ class TokenContext
85
139
  end
86
140
  ```
87
141
 
88
- #### Intervals
89
- The following are the intervals SnFoil Contexts sets up in the order they occur. The suggested uses are just very simply examples. You can chain contexts to setup very complex interactions in a very easy to manage workflow.
142
+ #### Action Intervals
143
+ The following are the intervals SnFoil Contexts set up in the order they occur. The suggested uses are just very simple examples. You can chain contexts to setup very complex interactions in a very easy-to-manage workflow.
90
144
 
91
145
  <table>
92
- <thead>
93
- <th>Name</th>
94
- <th>Suggested Use</th>
95
- </thead>
96
- <tbody>
97
- <tr>
98
- <td>setup_&lt;action&gt;</td>
99
- <td>
100
- <div>* find or create a model</div>
101
- <div>* setup params needed later in the action</div>
102
- <div>* set scoping </div>
103
- </td>
104
- </tr>
105
- <tr>
106
- <td>before_&lt;action&gt;</td>
107
- <td>
108
- <div>* alter model or set attributes</div>
109
- </td>
110
- </tr>
111
- <tr>
112
- <td>primary action</td>
113
- <td>
114
- <div>* persist database changes</div>
115
- <div>* make primary network call</div>
116
- </td>
117
- </tr>
118
- <tr>
119
- <td>after_&lt;action&gt;_success</td>
120
- <td>
121
- <div>* setup additional relationships</div>
122
- <div>* success specific logging</div>
123
- </td>
124
- </tr>
125
- <tr>
126
- <td>after_&lt;action&gt;_failure</td>
127
- <td>
128
- <div>* cleanup failed remenants</div>
129
- <div>* call bug tracker</div>
130
- <div>* failure specific logging</div>
131
- </td>
132
- </tr>
133
- <tr>
134
- <td>after_&lt;action&gt;</td>
135
- <td>
136
- <div>* perform necessary required cleanup</div>
137
- <div>* log outcome</div>
138
- </td>
139
- </tr>
140
- </tbody>
146
+ <thead>
147
+ <th>Name</th>
148
+ <th>Suggested Use</th>
149
+ </thead>
150
+ <tbody>
151
+ <tr>
152
+ <td>setup_&lt;action&gt;</td>
153
+ <td>
154
+ <div>* find or create a model</div>
155
+ <div>* setup params needed later in the action</div>
156
+ <div>* set scoping </div>
157
+ </td>
158
+ </tr>
159
+ <tr>
160
+ <td>before_&lt;action&gt;</td>
161
+ <td>
162
+ <div>* alter model or set attributes</div>
163
+ </td>
164
+ </tr>
165
+ <tr>
166
+ <td>primary action</td>
167
+ <td>
168
+ <div>* persist database changes</div>
169
+ <div>* make primary network call</div>
170
+ </td>
171
+ </tr>
172
+ <tr>
173
+ <td>after_&lt;action&gt;_success</td>
174
+ <td>
175
+ <div>* setup additional relationships</div>
176
+ <div>* success specific logging</div>
177
+ </td>
178
+ </tr>
179
+ <tr>
180
+ <td>after_&lt;action&gt;_failure</td>
181
+ <td>
182
+ <div>* cleanup failed remenants</div>
183
+ <div>* call bug tracker</div>
184
+ <div>* failure specific logging</div>
185
+ </td>
186
+ </tr>
187
+ <tr>
188
+ <td>after_&lt;action&gt;</td>
189
+ <td>
190
+ <div>* perform necessary required cleanup</div>
191
+ <div>* log outcome</div>
192
+ </td>
193
+ </tr>
194
+ </tbody>
141
195
  <table>
142
196
 
143
197
 
144
198
  #### Hook and Method Design
145
199
 
146
- SnFoil Contexts try hard to not store variables longer than necessary. To facilitate this we have choosen to pass an object (we normally use a hash called options) to each hook and method, and the return from the hook or method is passed down the chain to the next hook or method.
200
+ SnFoil Contexts try hard to not store variables longer than necessary. To facilitate this we have chosen to pass an object (we normally use a hash called options) to each hook and method, and the return from the hook or method is passed down the chain to the next hook or method.
147
201
 
148
- The only method or block that does not get its value passwed down the chain is the primary action - which must always return a truthy value of whether or not the action was successful.
202
+ The only method or block that does not get its value passed down the chain is the primary action - which must always return a truthy value of whether or not the action was successful.
149
203
 
150
204
  #### Hooks
151
205
  Hooks make it very easy to compose multiple actions that need to occur in a specific order. You can have as many repeated hooks as you would like. This makes defining single responsibility hooks very simple, and they will get called in the order they are defined.
@@ -156,29 +210,29 @@ Hooks make it very easy to compose multiple actions that need to occur in a spec
156
210
  ```ruby
157
211
  # Call the webhooks for third party integrations
158
212
  after_expire_success do |options|
159
- call_webhook_for_model(options[:object])
160
- options
213
+ call_webhook_for_model(options[:object])
214
+ options
161
215
  end
162
216
 
163
217
  # Commit business logic to internal process
164
218
  after_expire_success do |options|
165
- finalize_business_logic(options[:object])
166
- options
219
+ finalize_business_logic(options[:object])
220
+ options
167
221
  end
168
222
 
169
223
  # notify error tracker
170
224
  after_expire_error do |options|
171
- notify_errors(options[:object].errors)
172
- options
225
+ notify_errors(options[:object].errors)
226
+ options
173
227
  end
174
228
  ```
175
229
 
176
230
  #### Methods
177
- Methods allow users to create inheritable actions that occur in a specific order. Methods will always run after their hook counterpart. Since these are inheritable, you can chain needed actions all the way through the parent heirarchy by using the `super` keyword.
231
+ Methods allow users to create inheritable actions that occur in a specific order. Methods will always run after their hook counterpart. Since these are inheritable, you can chain needed actions through the parent hierarchy by using the `super` keyword. These are very useful when you need to have something always happen at the end of an Interval.
178
232
 
179
233
  <strong>Important Note</strong> Methods <u>always</u> need to return the options hash at the end.
180
234
 
181
- <i>Author's opinion:</i> While simplier than hooks, they do not allow for as clean of a composition as hooks.
235
+ <i>Author's opinion:</i> While simpler than hooks, they do not allow for as clean of a composition as hooks.
182
236
 
183
237
  ##### Example
184
238
 
@@ -186,21 +240,21 @@ Methods allow users to create inheritable actions that occur in a specific order
186
240
  # Call the webhooks for third party integrations
187
241
  # Commit business logic to internal process
188
242
  def after_expire_success(**options)
189
- options = super
243
+ options = super
190
244
 
191
- call_webhook_for_model(options[:object])
192
- finalize_business_logic(options[:object])
245
+ call_webhook_for_model(options[:object])
246
+ finalize_business_logic(options[:object])
193
247
 
194
- options
248
+ options
195
249
  end
196
250
 
197
251
  # notify error tracker
198
252
  def after_expire_error(**options)
199
- options = super
253
+ options = super
200
254
 
201
- notify_errors(options[:object].errors)
255
+ notify_errors(options[:object].errors)
202
256
 
203
- options
257
+ options
204
258
  end
205
259
  ```
206
260
 
@@ -209,10 +263,10 @@ The original purpose of all of SnFoil was to ensure there was a good consistent
209
263
 
210
264
  These authorization hooks are always called twice. Once after `setup_<action>` and once after `before_<action>`
211
265
 
212
- The `authorize` method functions much like primary action except the first argument is usually the name of action you are authorizing.
266
+ The `authorize` method functions much like primary action except the first argument is usually the name of the action you are authorizing.
213
267
 
214
268
  Arguments:
215
- * `name` - The name of this action to be authorized. If ommited, all actions without a specific associated authorize will use this one..
269
+ * `name` - The name of this action to be authorized. If omitted, all actions without a specific associated authorize will use this
216
270
  * `with` - Keyword Param - The method name of the primary action. Either this or a block is required
217
271
  * `block` - Block - The block of the primary action. Either this or with is required
218
272
 
@@ -225,13 +279,13 @@ class TokenContext
225
279
 
226
280
  action :expire, with: :expire_token
227
281
 
228
- authorize :expire { |options| options[:entity].is_admin? }
282
+ authorize(:expire) { |_options| :entity.is_admin? }
229
283
 
230
284
  ...
231
285
  end
232
286
  ```
233
287
 
234
- You can also call authorize without an action name. This will have all action authorize with the provided method or block unless there is a more specific authorize action configured. Its probably easier explained with an example
288
+ You can also call authorize without an action name. This will have all action authorize with the provided method or block unless there is a more specific authorize action configured. It's probably easier explained with an example
235
289
 
236
290
 
237
291
  ```ruby
@@ -245,15 +299,44 @@ class TokenContext
245
299
  action :search, with: :query_tokens #=> will authorize by checking the entity is a user
246
300
  action :show, with: :find_token #=> will authorize by checking the entity is a user
247
301
 
248
- authorize :expire { |options| options[:entity].is_admin? }
249
- authorize { |options| options[:entity].is_user? }
302
+ authorize(:expire) { |_options| entity.is_admin? }
303
+ authorize { |_options| entity.is_user? }
250
304
 
251
305
  ...
252
306
  end
253
307
  ```
254
308
 
255
309
  #### Why before and after?
256
- Simply to make sure the entity it actually allowed access the primary target and is allowed to make the requested alterations/interactions.
310
+ Simply to make sure the entity is allowed access to the primary target and is allowed to make the requested alterations/interactions.
311
+
312
+ ### Intervals
313
+ There might be a situation where you don't need a before, after, success or failure, and just need a single name pipeline you can hook into. `interval` allows you to create a single action-like segment.
314
+
315
+ ```ruby
316
+ class TokenContext
317
+ include SnFoil::Context
318
+
319
+ interval :demo
320
+
321
+ demo do |options|
322
+ ... # Logic Here
323
+
324
+ options
325
+ end
326
+
327
+ demo do |options|
328
+ ... # Additional Steps here
329
+
330
+ options
331
+ end
332
+ end
333
+ ```
334
+
335
+ Just like for an action SnFoil allows you to define both hooks and a method. To run this interval you call it using the `run_interval` method.
336
+
337
+ ```ruby
338
+ TokenContext.new(entity).run_interval(:demo, **options)
339
+ ```
257
340
 
258
341
  ## Development
259
342
 
@@ -271,4 +354,4 @@ The gem is available as open source under the terms of the [Apache 2 License](ht
271
354
 
272
355
  ## Code of Conduct
273
356
 
274
- Everyone interacting in the Snfoil::Context project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/limited-effort/snfoil-context/blob/main/CODE_OF_CONDUCT.md).
357
+ Everyone interacting in the Snfoil::Context project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the [code of conduct](https://github.com/limited-effort/snfoil-context/blob/main/CODE_OF_CONDUCT.md).
@@ -28,51 +28,63 @@ module SnFoil
28
28
  extend ActiveSupport::Concern
29
29
 
30
30
  class_methods do
31
- attr_reader :i_authorizations
31
+ attr_accessor :snfoil_authorizations
32
32
 
33
33
  def authorize(action_name = nil, with: nil, &block)
34
- @i_authorizations ||= {}
34
+ @snfoil_authorizations ||= {}
35
35
  action_name = action_name&.to_sym
36
36
 
37
- raise SnFoil::Context::Error, "#{name} already has authorize defined for #{action_name || ':default'}" if @i_authorizations[action_name]
37
+ if @snfoil_authorizations[action_name]
38
+ raise SnFoil::Context::Error, "#{name} already has authorize defined for #{action_name || ':default'}"
39
+ end
38
40
 
39
- @i_authorizations[action_name] = { method: with, block: block }
41
+ @snfoil_authorizations[action_name] = { method: with, block: block }
40
42
  end
41
- end
42
43
 
43
- attr_reader :entity
44
+ def inherited(subclass)
45
+ super
44
46
 
45
- def initialize(entity = nil)
46
- @entity = entity
47
+ instance_variables.grep(/@snfoil_.+/).each do |i|
48
+ subclass.instance_variable_set(i, instance_variable_get(i).dup)
49
+ end
50
+ end
47
51
  end
48
52
 
49
- def authorize(name, **options)
50
- configured_call = self.class.i_authorizations&.fetch(name.to_sym, nil)
51
- configured_call ||= self.class.i_authorizations&.fetch(nil, nil)
53
+ included do
54
+ attr_reader :entity
52
55
 
53
- if configured_call
54
- run_hook(configured_call, **options)
55
- else
56
- SnFoil.logger.info "No configuration for #{name} in #{self.class.name}. Authorize not called" if SnFoil.respond_to?(:logger)
57
- true
56
+ def initialize(**keywords)
57
+ @entity = keywords[:entity]
58
58
  end
59
- end
60
59
 
61
- private
60
+ def authorize(name, **options)
61
+ configured_call = self.class.snfoil_authorizations&.fetch(name.to_sym, nil)
62
+ configured_call ||= self.class.snfoil_authorizations&.fetch(nil, nil)
62
63
 
63
- def run_hook(hook, **options)
64
- return options unless hook && hook_valid?(hook, **options)
64
+ if configured_call
65
+ run_hook(configured_call, **options)
66
+ else
67
+ SnFoil.logger.info "No configuration for #{name} in #{self.class.name}. Authorize not called" if SnFoil.respond_to?(:logger)
68
+ true
69
+ end
70
+ end
65
71
 
66
- return send(hook[:method], **options) if hook[:method]
72
+ private
67
73
 
68
- instance_exec options, &hook[:block]
69
- end
74
+ def run_hook(hook, **options)
75
+ return options unless hook && hook_valid?(hook, **options)
70
76
 
71
- def hook_valid?(hook, **options)
72
- return false if !hook[:if].nil? && hook[:if].call(options) == false
73
- return false if !hook[:unless].nil? && hook[:unless].call(options) == true
77
+ return send(hook[:method], **options) if hook[:method]
74
78
 
75
- true
79
+ instance_exec(**options, &hook[:block])
80
+ end
81
+
82
+ def hook_valid?(hook, **options)
83
+ return false if !hook[:if].nil? && hook[:if].call(**options) == false
84
+ return false if !hook[:unless].nil? && hook[:unless].call(**options) == true
85
+
86
+ true
87
+ end
76
88
  end
77
89
  end
78
90
  end
@@ -16,6 +16,6 @@
16
16
 
17
17
  module SnFoil
18
18
  module Context
19
- VERSION = '0.0.1'
19
+ VERSION = '1.0.0'
20
20
  end
21
21
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2021 Matthew Howes
3
+ # Copyright 2021 Matthew Howes, Cliff Campbell
4
4
 
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
6
6
  # you may not use this file except in compliance with the License.
@@ -35,12 +35,27 @@ module SnFoil
35
35
 
36
36
  class_methods do
37
37
  def action(name, with: nil, &block)
38
- raise SnFoil::Context::Error, "action #{name} already defined for #{self.name}" if (@defined_actions ||= []).include?(name)
38
+ raise SnFoil::Context::Error, "action #{name} already defined for #{self.name}" if (@snfoil_actions ||= []).include?(name)
39
39
 
40
- @defined_actions << name
41
- setup_hooks(name)
40
+ @snfoil_actions << name
41
+ define_workflow(name)
42
42
  define_action_primary(name, with, block)
43
43
  end
44
+
45
+ def interval(name)
46
+ define_singleton_methods(name)
47
+ define_instance_methods(name)
48
+ end
49
+
50
+ def intervals(*names)
51
+ names.each { |name| interval(name) }
52
+ end
53
+ end
54
+
55
+ def run_interval(interval, **options)
56
+ hooks = self.class.instance_variable_get("@snfoil_#{interval}_hooks") || []
57
+ options = hooks.reduce(options) { |opts, hook| run_hook(hook, **opts) }
58
+ send(interval, **options)
44
59
  end
45
60
 
46
61
  private
@@ -48,63 +63,58 @@ module SnFoil
48
63
  # rubocop:disable reason: These are builder/mapping methods that are just too complex to simplify without
49
64
  # making them more complex. If anyone has a better way please let me know
50
65
  class_methods do # rubocop:disable Metrics/BlockLength
66
+ def define_workflow(name)
67
+ interval "setup_#{name}"
68
+ interval "before_#{name}"
69
+ interval "after_#{name}_success"
70
+ interval "after_#{name}_failure"
71
+ interval "after_#{name}"
72
+ end
73
+
51
74
  def define_action_primary(name, method, block) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
52
- define_method(name) do |**options| # rubocop:disable Metrics/MethodLength
53
- options[:action] = name.to_sym
54
- options = run_action_group(format('setup_%s', name), **options)
75
+ define_method(name) do |*_args, **options| # rubocop:disable Metrics/MethodLength
76
+ options[:action] ||= name.to_sym
77
+
78
+ options = run_interval(format('setup_%s', name), **options)
55
79
  authorize(name, **options) if respond_to?(:authorize)
56
80
 
57
- options = run_action_group(format('before_%s', name), **options)
81
+ options = run_interval(format('before_%s', name), **options)
58
82
  authorize(name, **options) if respond_to?(:authorize)
59
83
 
60
84
  options = if run_action_primary(method, block, **options)
61
- run_action_group(format('after_%s_success', name), **options)
85
+ run_interval(format('after_%s_success', name), **options)
62
86
  else
63
- run_action_group(format('after_%s_failure', name), **options)
87
+ run_interval(format('after_%s_failure', name), **options)
64
88
  end
65
- run_action_group(format('after_%s', name), **options)
89
+ run_interval(format('after_%s', name), **options)
66
90
  end
67
91
  end
68
92
 
69
- def setup_hooks(name)
70
- hook_builder('setup_%s', name)
71
- hook_builder('before_%s', name)
72
- hook_builder('after_%s_success', name)
73
- hook_builder('after_%s_failure', name)
74
- hook_builder('after_%s', name)
75
- end
76
-
77
- def hook_builder(name_format, name)
78
- assign_singleton_methods format(name_format, name),
79
- format("#{name_format}_hooks", name)
80
- end
81
-
82
- def assign_singleton_methods(method_name, singleton_var)
93
+ def define_singleton_methods(method_name)
94
+ singleton_var = "snfoil_#{method_name}_hooks"
83
95
  instance_variable_set("@#{singleton_var}", [])
84
96
  define_singleton_method(singleton_var) { instance_variable_get("@#{singleton_var}") }
85
- define_singleton_method(method_name) do |method = nil, **options, &block|
86
- raise SnFoil::Context::ArgumentError, "\##{method_name} requires either a method name or a block" if method.nil? && block.nil?
97
+ define_singleton_method(method_name) do |with: nil, **options, &block|
98
+ raise SnFoil::Context::ArgumentError, "\##{method_name} requires either a method name or a block" if with.nil? && block.nil?
87
99
 
88
- instance_variable_get("@#{singleton_var}") << { method: method,
100
+ instance_variable_get("@#{singleton_var}") << { method: with,
89
101
  block: block,
90
102
  if: options[:if],
91
103
  unless: options[:unless] }
92
104
  end
93
105
  end
106
+
107
+ def define_instance_methods(method_name)
108
+ define_method(method_name) do |**options|
109
+ options
110
+ end
111
+ end
94
112
  end
95
113
 
96
114
  def run_action_primary(method, block, **options)
97
115
  return send(method, **options) if method
98
116
 
99
- instance_exec options, &block
100
- end
101
-
102
- def run_action_group(group_name, **options)
103
- options = self.class.instance_variable_get("@#{group_name}_hooks")
104
- .reduce(options) { |opts, hook| run_hook(hook, opts) }
105
- options = send(group_name, **options) if respond_to?(group_name)
106
-
107
- options
117
+ instance_exec(**options, &block)
108
118
  end
109
119
  end
110
120
  end
@@ -5,24 +5,24 @@ require_relative 'lib/snfoil/context/version'
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'snfoil-context'
7
7
  spec.version = SnFoil::Context::VERSION
8
- spec.authors = ['Matthew Howes']
9
- spec.email = ['howeszy@gmail.com']
8
+ spec.authors = ['Matthew Howes', 'Cliff Campbell']
9
+ spec.email = ['howeszy@gmail.com', 'cliffcampbell@hey.com']
10
10
 
11
- spec.summary = ''
12
- spec.description = ''
11
+ spec.summary = 'Setup Simple Pipelined Workflows'
12
+ spec.description = 'An easy way to make extensible workflows and actions'
13
13
  spec.homepage = 'https://github.com/limited-effort/snfoil-context'
14
- spec.license = 'MIT'
15
- spec.required_ruby_version = '>= 2.5.0'
14
+ spec.license = 'Apache-2.0'
15
+ spec.required_ruby_version = '>= 2.7'
16
16
 
17
17
  spec.metadata['homepage_uri'] = spec.homepage
18
18
  spec.metadata['source_code_uri'] = spec.homepage
19
19
  spec.metadata['changelog_uri'] = 'https://github.com/limited-effort/snfoil-context/blob/main/CHANGELOG.md'
20
+ spec.metadata['rubygems_mfa_required'] = 'true'
20
21
 
21
22
  # Specify which files should be added to the gem when it is released.
22
23
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
25
- end
24
+ ignore_list = %r{\A(?:test/|spec/|bin/|features/|Rakefile|\.\w)}
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) { `git ls-files -z`.split("\x0").reject { |f| f.match(ignore_list) } }
26
26
 
27
27
  spec.bindir = 'exe'
28
28
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
@@ -30,10 +30,12 @@ Gem::Specification.new do |spec|
30
30
 
31
31
  spec.add_dependency 'activesupport', '>= 5.2.6'
32
32
 
33
+ spec.add_development_dependency 'bundle-audit', '~> 0.1.0'
34
+ spec.add_development_dependency 'fasterer', '~> 0.10.0'
33
35
  spec.add_development_dependency 'pry-byebug', '~> 3.9'
34
36
  spec.add_development_dependency 'rake', '~> 13.0'
35
37
  spec.add_development_dependency 'rspec', '~> 3.10'
36
- spec.add_development_dependency 'rubocop', '~> 1.21'
38
+ spec.add_development_dependency 'rubocop', '~> 1.29'
37
39
  spec.add_development_dependency 'rubocop-performance', '~> 1.11'
38
40
  spec.add_development_dependency 'rubocop-rspec', '~> 2.5'
39
41
  end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snfoil-context
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Howes
8
- autorequire:
8
+ - Cliff Campbell
9
+ autorequire:
9
10
  bindir: exe
10
11
  cert_chain: []
11
- date: 2021-10-12 00:00:00.000000000 Z
12
+ date: 2022-05-11 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: activesupport
@@ -24,6 +25,34 @@ dependencies:
24
25
  - - ">="
25
26
  - !ruby/object:Gem::Version
26
27
  version: 5.2.6
28
+ - !ruby/object:Gem::Dependency
29
+ name: bundle-audit
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 0.1.0
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 0.1.0
42
+ - !ruby/object:Gem::Dependency
43
+ name: fasterer
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: 0.10.0
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: 0.10.0
27
56
  - !ruby/object:Gem::Dependency
28
57
  name: pry-byebug
29
58
  requirement: !ruby/object:Gem::Requirement
@@ -72,14 +101,14 @@ dependencies:
72
101
  requirements:
73
102
  - - "~>"
74
103
  - !ruby/object:Gem::Version
75
- version: '1.21'
104
+ version: '1.29'
76
105
  type: :development
77
106
  prerelease: false
78
107
  version_requirements: !ruby/object:Gem::Requirement
79
108
  requirements:
80
109
  - - "~>"
81
110
  - !ruby/object:Gem::Version
82
- version: '1.21'
111
+ version: '1.29'
83
112
  - !ruby/object:Gem::Dependency
84
113
  name: rubocop-performance
85
114
  requirement: !ruby/object:Gem::Requirement
@@ -108,39 +137,34 @@ dependencies:
108
137
  - - "~>"
109
138
  - !ruby/object:Gem::Version
110
139
  version: '2.5'
111
- description: ''
140
+ description: An easy way to make extensible workflows and actions
112
141
  email:
113
142
  - howeszy@gmail.com
143
+ - cliffcampbell@hey.com
114
144
  executables: []
115
145
  extensions: []
116
146
  extra_rdoc_files: []
117
147
  files:
118
- - ".github/workflows/main.yml"
119
- - ".gitignore"
120
- - ".rspec"
121
- - ".rubocop.yml"
122
148
  - CHANGELOG.md
123
149
  - CODE_OF_CONDUCT.md
124
150
  - Gemfile
125
151
  - LICENSE.txt
126
152
  - README.md
127
- - Rakefile
128
- - bin/console
129
- - bin/setup
130
153
  - lib/snfoil/context.rb
131
154
  - lib/snfoil/context/argument_error.rb
132
155
  - lib/snfoil/context/error.rb
133
156
  - lib/snfoil/context/structure.rb
134
157
  - lib/snfoil/context/version.rb
135
- - snfoil-policy.gemspec
158
+ - snfoil-context.gemspec
136
159
  homepage: https://github.com/limited-effort/snfoil-context
137
160
  licenses:
138
- - MIT
161
+ - Apache-2.0
139
162
  metadata:
140
163
  homepage_uri: https://github.com/limited-effort/snfoil-context
141
164
  source_code_uri: https://github.com/limited-effort/snfoil-context
142
165
  changelog_uri: https://github.com/limited-effort/snfoil-context/blob/main/CHANGELOG.md
143
- post_install_message:
166
+ rubygems_mfa_required: 'true'
167
+ post_install_message:
144
168
  rdoc_options: []
145
169
  require_paths:
146
170
  - lib
@@ -148,7 +172,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
148
172
  requirements:
149
173
  - - ">="
150
174
  - !ruby/object:Gem::Version
151
- version: 2.5.0
175
+ version: '2.7'
152
176
  required_rubygems_version: !ruby/object:Gem::Requirement
153
177
  requirements:
154
178
  - - ">="
@@ -156,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
156
180
  version: '0'
157
181
  requirements: []
158
182
  rubygems_version: 3.1.6
159
- signing_key:
183
+ signing_key:
160
184
  specification_version: 4
161
- summary: ''
185
+ summary: Setup Simple Pipelined Workflows
162
186
  test_files: []
@@ -1,46 +0,0 @@
1
- name: build
2
- on:
3
- push:
4
- branches: [ main ]
5
- pull_request:
6
-
7
- jobs:
8
- test:
9
-
10
- runs-on: ubuntu-latest
11
-
12
- strategy:
13
- matrix:
14
- ruby-version: [2.7, 2.6, 2.5]
15
-
16
- steps:
17
- - uses: actions/checkout@v2
18
- - name: Set up Ruby ${{ matrix.ruby-version }}
19
- uses: ruby/setup-ruby@v1.81.0
20
- with:
21
- ruby-version: ${{ matrix.ruby-version }}
22
- bundler-cache: true
23
- - name: Install dependencies
24
- run: bundle install
25
- - name: Run tests
26
- run: bundle exec rspec
27
- lint:
28
-
29
- runs-on: ubuntu-latest
30
-
31
- strategy:
32
- matrix:
33
- ruby-version: [2.7, 2.6, 2.5, '3.0']
34
-
35
- steps:
36
- - uses: actions/checkout@v2
37
- - name: Set up Ruby ${{ matrix.ruby-version }}
38
- uses: ruby/setup-ruby@v1.81.0
39
- with:
40
- ruby-version: ${{ matrix.ruby-version }}
41
- bundler-cache: true
42
- - name: Install dependencies
43
- run: bundle install
44
- - name: Run rubocop
45
- run: bundle exec rubocop
46
-
data/.gitignore DELETED
@@ -1,14 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
-
10
- # rspec failure tracking
11
- .rspec_status
12
-
13
- # GEMFILE
14
- Gemfile.lock
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
data/.rubocop.yml DELETED
@@ -1,39 +0,0 @@
1
- require:
2
- - rubocop-performance
3
- - rubocop-rspec
4
-
5
- AllCops:
6
- NewCops: enable
7
- SuggestExtensions: false
8
- TargetRubyVersion: 2.5.0
9
-
10
- # ================ LAYOUT ==============
11
- Layout/LineLength:
12
- Max: 150
13
-
14
- # ================ LINT ================
15
- Lint/AmbiguousBlockAssociation:
16
- Exclude:
17
- - spec/**/*_spec.rb
18
-
19
- Lint/EmptyClass:
20
- Exclude:
21
- - spec/**/*_spec.rb
22
-
23
- # ================ Metics ================
24
- Metrics/BlockLength:
25
- Exclude:
26
- - spec/**/*_spec.rb
27
-
28
- # ================ RSPEC ================
29
- RSpec/FilePath:
30
- Enabled: false
31
-
32
- RSpec/MultipleExpectations:
33
- Max: 5
34
-
35
- RSpec/MultipleMemoizedHelpers:
36
- Enabled: false
37
-
38
- RSpec/NestedGroups:
39
- Max: 5
data/Rakefile DELETED
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/gem_tasks'
4
- require 'rspec/core/rake_task'
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- require 'rubocop/rake_task'
9
-
10
- RuboCop::RakeTask.new
11
-
12
- task default: %i[spec rubocop]
data/bin/console DELETED
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'bundler/setup'
5
- require 'snfoil/policy'
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
13
-
14
- require 'irb'
15
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here