arq 0.2.1 → 0.3.1

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: 83941043c326bd9f80903aa4755c49ae3affe39584f12d3757ab8a05e0878987
4
- data.tar.gz: d94724fe95ba5dfe444c3d2587357ec60c825a61c5a0a5743a55b8c44c2b3b05
3
+ metadata.gz: a0ee16565eecb2e2866939a75dae3fde195f0c941bddb66f43f98ccc92c0e642
4
+ data.tar.gz: 2e4ca8c155a462706ca2668b18bac36c38fac84b025f00f4b59f54d0d478a382
5
5
  SHA512:
6
- metadata.gz: 418c2ba2e7f9373056b1eb52503c9af47749921c8c696bdf4dce98bcf0649684eab3f2a48742f3943aed00839c1e776178e0fc4b0932409203d7c04891fa52bf
7
- data.tar.gz: 543ada56b97ebef9d58fd01e614583aca4a123707917bd3d11607e2645c6ae4688db1b942be279fd0fac86c0986d6d5f7d9c9c198d8ee5742efaffbcced27f88
6
+ metadata.gz: 047d23d860d49a35f90564695578e776b35e3628d9f53681de8db4d2c4eaee407a8bae985983015c2e843e67bc70857e1ad173dfbb93d081cfeb9fdd7cf87dd8
7
+ data.tar.gz: 34f0ca90c2783f25623c1dfd464e7e4b7dce0677e187647678876aad0a5927b856bc77ed6e3d75bfd460a76f2488f0a5e9f084e6a80f3f76b23bb8ab5b5e7238
data/lib/arq/action.rb CHANGED
@@ -1,55 +1,176 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Arq
4
- # Module to extend to create an action. Exposes confing functions #call, #params, and #returns.
4
+ # Module to extend to create an action.
5
+ # Class methods are used to configure the action itself.
6
+ # Instance methods are exposed to the block passed into `run`.
5
7
  module Action
6
- def call(ctx = Arq::Context.new)
7
- ctx = transform_input_context(ctx)
8
-
9
- generate_runnable(ctx).call
10
-
11
- ctx
8
+ def self.extended(base)
9
+ base.extend(ClassMethods)
10
+ base.include(InstanceMethods)
12
11
  end
13
12
 
14
- def params(*keys)
15
- params_list.concat(keys).compact
13
+ def self.included(_base)
14
+ raise "`Arq::Action` should be `extended`"
16
15
  end
17
16
 
18
- def returns(*keys)
19
- returns_list.concat(keys).compact
20
- end
17
+ # Methods and fields exposed to configure the action.
18
+ module ClassMethods
19
+ # Runs the stored block with variables from the provided context.
20
+ def call(ctx = Arq::Context.new)
21
+ ctx = transform_input_context(ctx)
21
22
 
22
- def run(&block)
23
- @run_block = block
24
- end
23
+ inst = new(ctx)
24
+ inst.run
25
25
 
26
- private
27
-
28
- def transform_input_context(ctx)
29
- case ctx
30
- when Arq::Context
31
26
  ctx
32
- when Hash
33
- Arq::Context.new(ctx)
34
- else
35
- raise Arq::InvalidContextParameterError
36
27
  end
37
- end
38
28
 
39
- def generate_runnable(ctx)
40
- Arq::Runnable.new(ctx, params_list, returns_list, &run_block)
41
- end
29
+ def params_list
30
+ @params_list ||= []
31
+ end
42
32
 
43
- def params_list
44
- @params_list ||= []
45
- end
33
+ def returns_list
34
+ @returns_list ||= []
35
+ end
36
+
37
+ def run_block
38
+ @run_block ||= nil
39
+ end
40
+
41
+ private
46
42
 
47
- def returns_list
48
- @returns_list ||= []
43
+ # Ensures that a valid context is passed and returned.
44
+ # Acceptable values are Context or Hash objects.
45
+ def transform_input_context(ctx)
46
+ case ctx
47
+ when Arq::Context
48
+ ctx
49
+ when Hash
50
+ Arq::Context.new(ctx)
51
+ else
52
+ raise Arq::InvalidContextParameterError
53
+ end
54
+ end
55
+
56
+ def params(*keys)
57
+ params_list.concat(keys).compact
58
+ end
59
+
60
+ def returns(*keys)
61
+ returns_list.concat(keys).compact
62
+ end
63
+
64
+ def run(&block)
65
+ return "Block required" unless block_given?
66
+
67
+ @run_block = block
68
+ end
49
69
  end
50
70
 
51
- def run_block
52
- @run_block ||= nil
71
+ # Methods and fields exposed to the run block.
72
+ module InstanceMethods
73
+ def initialize(ctx)
74
+ @_ctx = ctx
75
+ end
76
+
77
+ # This is the entry-point for the action's execution.
78
+ # rubocop:disable Metrics/MethodLength
79
+ def run
80
+ return if @_ctx.failure?
81
+
82
+ _validate_required_params
83
+ _import_context
84
+
85
+ # Suppress `FailureError`s since they're used to just exit the action.
86
+ begin
87
+ # Instance eval to expose instance variables.
88
+ instance_eval(&self.class.run_block)
89
+ rescue Arq::FailureError
90
+ nil
91
+ end
92
+
93
+ _export_variables
94
+
95
+ # Only validate returns if context is successful
96
+ _validate_required_returns if @_ctx.success?
97
+
98
+ nil
99
+ end
100
+ # rubocop:enable Metrics/MethodLength
101
+
102
+ # Used to call another action using the current action's context.
103
+ # Because of this, the context is exported and then imported again.
104
+ def call_other(action)
105
+ _export_variables
106
+ action.call(@_ctx)
107
+ _import_context
108
+ end
109
+
110
+ # Fails the context without exiting the current action.
111
+ def fail!(message = nil)
112
+ @_ctx.fail!(message)
113
+ end
114
+
115
+ # Fails the context and exits the current action.
116
+ def fail_now!(message = nil)
117
+ @_ctx.fail_now!(message)
118
+ end
119
+
120
+ # Used to easily call other actions via snake-cased paths with dot accessors.
121
+ # IE `Foo::Bar::Action` can be called via `foo.bar.action`
122
+ def method_missing(method, *args, &block)
123
+ # Format method as module path.
124
+ formatted = method.to_s.camelize
125
+
126
+ # Attempt to find object.
127
+ obj = if Object.const_defined?(formatted)
128
+ Object.const_get(formatted)
129
+ else
130
+ return super
131
+ end
132
+
133
+ # Defer handling to ActionModuleHash
134
+ Arq::ActionModuleHash.from(obj, self)
135
+ end
136
+
137
+ def respond_to_missing?(method, include_private: false)
138
+ Object.const_defined?(method.to_s.camelize) || super
139
+ end
140
+
141
+ private
142
+
143
+ # Load parameters of context as instance variables
144
+ def _import_context
145
+ @_ctx.each do |key, val|
146
+ instance_variable_set(:"@#{key}", val)
147
+ end
148
+ end
149
+
150
+ # Exports instance variables (excluding those prefixed with _) to the context
151
+ def _export_variables
152
+ # Filter out vars starting with _
153
+ vars = instance_variables.filter do |key|
154
+ !key.start_with?("@_")
155
+ end
156
+
157
+ vars.each do |var|
158
+ # Strip @ prefix
159
+ key = var[1..].to_sym
160
+
161
+ @_ctx[key] = instance_variable_get(var)
162
+ end
163
+ end
164
+
165
+ def _validate_required_params
166
+ missing_params = (self.class.params_list - @_ctx.keys)
167
+ raise Arq::ParametersNotInContextError, missing_params unless missing_params.empty?
168
+ end
169
+
170
+ def _validate_required_returns
171
+ missing_returns = (self.class.returns_list - @_ctx.keys)
172
+ raise Arq::ReturnValuesNotInContextError, missing_returns unless missing_returns.empty?
173
+ end
53
174
  end
54
175
  end
55
176
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arq
4
+ # Allows for dot-accessing of modules and running of actions.
5
+ # Child modules are automatically wrapped and actions are called.
6
+ class ActionModuleHash < Hash
7
+ # Calls action or wraps in hash class if module.
8
+ def self.from(obj, action_inst)
9
+ case obj
10
+ when Arq::Action
11
+ action_inst.call_other(obj)
12
+ when Module
13
+ new(obj, action_inst)
14
+ else
15
+ raise "Object must be an Action or Module"
16
+ end
17
+ end
18
+
19
+ def initialize(mod, action_inst)
20
+ super()
21
+
22
+ @module = mod
23
+ @action_inst = action_inst
24
+ end
25
+
26
+ def method_missing(method, *args, &block)
27
+ # Format method as module path.
28
+ formatted = method.to_s.camelize
29
+
30
+ # Attempt to find object.
31
+ obj = if Object.const_defined?(formatted)
32
+ Object.const_get(formatted)
33
+ else
34
+ return super
35
+ end
36
+
37
+ self.class.from(obj)
38
+ end
39
+
40
+ def respond_to_missing?(method, include_private = false)
41
+ @module.const_defined?(method.to_s.camelize.to_sym) || super
42
+ end
43
+ end
44
+ end
data/lib/arq/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Arq
4
- VERSION = "0.2.1"
4
+ VERSION = "0.3.1"
5
5
  end
data/lib/arq.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/string"
4
+
3
5
  require_relative "arq/version"
4
6
  require_relative "arq/errors"
5
7
  require_relative "arq/context"
6
- require_relative "arq/runnable"
8
+ require_relative "arq/action_module_hash"
7
9
  require_relative "arq/action"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kavin Phan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-09 00:00:00.000000000 Z
11
+ date: 2022-09-29 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A service skeleton framework heavily inspired by LightService with the
14
14
  primary goal of being less verbose.
@@ -20,9 +20,9 @@ extra_rdoc_files: []
20
20
  files:
21
21
  - lib/arq.rb
22
22
  - lib/arq/action.rb
23
+ - lib/arq/action_module_hash.rb
23
24
  - lib/arq/context.rb
24
25
  - lib/arq/errors.rb
25
- - lib/arq/runnable.rb
26
26
  - lib/arq/version.rb
27
27
  homepage: https://github.com/kphan32/arq
28
28
  licenses:
@@ -48,7 +48,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
48
48
  - !ruby/object:Gem::Version
49
49
  version: '0'
50
50
  requirements: []
51
- rubygems_version: 3.2.32
51
+ rubygems_version: 3.3.7
52
52
  signing_key:
53
53
  specification_version: 4
54
54
  summary: A simple service skeleton framework
data/lib/arq/runnable.rb DELETED
@@ -1,109 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Arq
4
- # Represents a process to run with a given context, parameters, and returns.
5
- # A Runnable is generated from an Action.
6
- class Runnable
7
- def initialize(ctx, params = [], returns = [], &block)
8
- @ctx = ctx
9
- @params = params
10
- @returns = returns
11
- @block = block
12
- end
13
-
14
- def call
15
- return if @ctx.failure?
16
-
17
- validate_required_params
18
-
19
- val = run_block
20
-
21
- # If the block returned an array, attempt to run
22
- run_sequence(val) if val.is_a?(Array)
23
-
24
- # Only validate returns if context is successful
25
- validate_required_returns if @ctx.success?
26
-
27
- val
28
- end
29
-
30
- def run(&block)
31
- Arq::Runnable.new(@ctx, [], [], &block)
32
- end
33
-
34
- def fail!(message = nil)
35
- @ctx.fail!(message)
36
- end
37
-
38
- def fail_now!(message = nil)
39
- @ctx.fail_now!(message)
40
- end
41
-
42
- private
43
-
44
- def run_block
45
- with_context do
46
- instance_eval(&@block)
47
- rescue Arq::FailureError
48
- nil
49
- end
50
- end
51
-
52
- def run_sequence(sequence)
53
- sequence.each do |e|
54
- case e
55
- when Arq::Action
56
- e.call(@ctx)
57
- when Arq::Runnable
58
- e.call
59
- end
60
- end
61
- end
62
-
63
- # Runs the block with context parameters set as instance variables, then
64
- # imports all context parameters and any new instance vars into the context.
65
- # Returns the return value of the block.
66
- def with_context
67
- # Grab all current variables to know what is being returned
68
- before_vars = instance_variables
69
-
70
- # Load all context parameters into runnable instance
71
- import_context_to_vars
72
-
73
- # Run block
74
- val = yield
75
-
76
- # Grab all ctx + new vars
77
- ctx_vars = instance_variables - before_vars
78
-
79
- # Import instance vars into ctx
80
- import_vars_to_context(ctx_vars)
81
-
82
- # Return block value
83
- val
84
- end
85
-
86
- def import_context_to_vars
87
- @ctx.each do |key, val|
88
- instance_variable_set(:"@#{key}", val)
89
- end
90
- end
91
-
92
- def import_vars_to_context(vars)
93
- vars.each do |var|
94
- key = var[1..].to_sym
95
- @ctx[key] = instance_variable_get(var)
96
- end
97
- end
98
-
99
- def validate_required_params
100
- missing_params = (@params - @ctx.keys)
101
- raise Arq::ParametersNotInContextError, missing_params unless missing_params.empty?
102
- end
103
-
104
- def validate_required_returns
105
- missing_returns = (@returns - @ctx.keys)
106
- raise Arq::ReturnValuesNotInContextError, missing_returns unless missing_returns.empty?
107
- end
108
- end
109
- end