arq 0.2.1 → 0.3.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: 83941043c326bd9f80903aa4755c49ae3affe39584f12d3757ab8a05e0878987
4
- data.tar.gz: d94724fe95ba5dfe444c3d2587357ec60c825a61c5a0a5743a55b8c44c2b3b05
3
+ metadata.gz: bf6593b58efb70d3d2ac268f33cb8eae378fe821d13e69975c13216cdfe1ff2f
4
+ data.tar.gz: fa4401c24cd50c2d8a5b6f7adb69e3224e126d75c166fca994f816ceb27e4523
5
5
  SHA512:
6
- metadata.gz: 418c2ba2e7f9373056b1eb52503c9af47749921c8c696bdf4dce98bcf0649684eab3f2a48742f3943aed00839c1e776178e0fc4b0932409203d7c04891fa52bf
7
- data.tar.gz: 543ada56b97ebef9d58fd01e614583aca4a123707917bd3d11607e2645c6ae4688db1b942be279fd0fac86c0986d6d5f7d9c9c198d8ee5742efaffbcced27f88
6
+ metadata.gz: a9feae6ec6e4ff17c37a2af7d99b7df6ec22e1d1fce6af04e42444bdff3e250b44e2c8036d64b333620929cd0d75bd5e0f69058e09d190dc49438ca2e2d456d9
7
+ data.tar.gz: 297f51c062e329349999b9b370c1f1fc91ba17f90e2b57baf141fc44df6aae3ae97086eef293108204417b94143814ba1e8f512deb9f89d31022c7eb37e5287d
data/lib/arq/action.rb CHANGED
@@ -1,55 +1,173 @@
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 modules and dot accessors.
121
+ # IE `Foo::Bar::Action` can be called via `foo.bar.action`
122
+ def method_missing(method, *args, &block)
123
+ obj = Object.const_get(method.to_s.camelize)
124
+ case obj
125
+ when Arq::Action
126
+ call_other(obj)
127
+ when Module
128
+ Arq::ModuleHash.new(obj, self)
129
+ end
130
+ rescue NameError => _e
131
+ super
132
+ end
133
+
134
+ def respond_to_missing?(method, include_private: false)
135
+ Object.const_defined?(method.to_s.camelize) || super
136
+ end
137
+
138
+ private
139
+
140
+ # Load parameters of context as instance variables
141
+ def _import_context
142
+ @_ctx.each do |key, val|
143
+ instance_variable_set(:"@#{key}", val)
144
+ end
145
+ end
146
+
147
+ # Exports instance variables (excluding those prefixed with _) to the context
148
+ def _export_variables
149
+ # Filter out vars starting with _
150
+ vars = instance_variables.filter do |key|
151
+ !key.start_with?("@_")
152
+ end
153
+
154
+ vars.each do |var|
155
+ # Strip @ prefix
156
+ key = var[1..].to_sym
157
+
158
+ @_ctx[key] = instance_variable_get(var)
159
+ end
160
+ end
161
+
162
+ def _validate_required_params
163
+ missing_params = (self.class.params_list - @_ctx.keys)
164
+ raise Arq::ParametersNotInContextError, missing_params unless missing_params.empty?
165
+ end
166
+
167
+ def _validate_required_returns
168
+ missing_returns = (self.class.returns_list - @_ctx.keys)
169
+ raise Arq::ReturnValuesNotInContextError, missing_returns unless missing_returns.empty?
170
+ end
53
171
  end
54
172
  end
55
173
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arq
4
+ # Allows for dot-accessing of modules.
5
+ # Child modules are automatically wrapped and actions are called.
6
+ class ModuleHash < Hash
7
+ def initialize(mod, action_inst)
8
+ super()
9
+
10
+ @module = mod
11
+ @action_inst = action_inst
12
+ end
13
+
14
+ def method_missing(method, *args, &block)
15
+ wrap(@module.const_get(method.to_s.camelize))
16
+ rescue NameError => _e
17
+ super
18
+ end
19
+
20
+ def respond_to_missing?(method, include_private = false)
21
+ @module.const_defined?(method.to_s.camelize.to_sym) || super
22
+ end
23
+
24
+ private
25
+
26
+ # If the value is a Module, it's returned as a ModuleHash.
27
+ def wrap(value)
28
+ case value
29
+ when Arq::Action
30
+ @action_inst.call_other(value)
31
+ when Module
32
+ self.class.new(value, @action_inst)
33
+ else
34
+ value
35
+ end
36
+ end
37
+ end
38
+ 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.0"
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/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.0
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.
@@ -22,7 +22,7 @@ files:
22
22
  - lib/arq/action.rb
23
23
  - lib/arq/context.rb
24
24
  - lib/arq/errors.rb
25
- - lib/arq/runnable.rb
25
+ - lib/arq/module_hash.rb
26
26
  - lib/arq/version.rb
27
27
  homepage: https://github.com/kphan32/arq
28
28
  licenses:
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