arq 0.2.0 → 0.3.0

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: 37b1eabfaf83924a5d2c61b390cf9a74043860578c247b851a77bd9b7f8308ce
4
- data.tar.gz: e6bafd5efe08658bf7a6065d7026e3aa7dec67e4ead6f4621cf7c94f2df3c464
3
+ metadata.gz: bf6593b58efb70d3d2ac268f33cb8eae378fe821d13e69975c13216cdfe1ff2f
4
+ data.tar.gz: fa4401c24cd50c2d8a5b6f7adb69e3224e126d75c166fca994f816ceb27e4523
5
5
  SHA512:
6
- metadata.gz: 3b900d412628cc1da83c1f9aff8e96a058934cde992f0cbdc1524e34614fbd8c12ad5f0f5b18f25e45b991fa470ebdf0bb01d02d26073d128736aa03c0ad49cc
7
- data.tar.gz: b7e55db8850980446f857cd0d89094e95c1983077a748810607a0f8a34e97444d633bbe669ffabffe865b6621cc5ff024ab37c5aeefb125e466989e524eee624
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)
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.0"
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.0
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-06 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,90 +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
- import_context
20
- val = run_block
21
- export_context
22
-
23
- # If the block returned an array, attempt to run
24
- run_sequence(val) if val.is_a?(Array)
25
-
26
- # Only validate returns if context is successful
27
- validate_required_returns if @ctx.success?
28
-
29
- val
30
- end
31
-
32
- def run(&block)
33
- Arq::Runnable.new(@ctx, [], [], &block)
34
- end
35
-
36
- def fail!(message)
37
- @ctx.fail!(message)
38
- end
39
-
40
- def fail_now!(message)
41
- @ctx.fail_now!(message)
42
- end
43
-
44
- private
45
-
46
- def run_block
47
- instance_eval(&@block)
48
- rescue Arq::FailureError
49
- nil
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
- def import_context
64
- @ctx.each do |key, val|
65
- instance_variable_set(:"@#{key}", val)
66
- end
67
- end
68
-
69
- def export_context
70
- keys = [*@ctx.keys, *@returns]
71
-
72
- keys.each do |key|
73
- instance_key = :"@#{key}"
74
- # Must check for existence since getting non-existent
75
- # instance variables will return nil.
76
- @ctx[key] = instance_variable_get(instance_key) if instance_variables.include?(instance_key)
77
- end
78
- end
79
-
80
- def validate_required_params
81
- missing_params = (@params - @ctx.keys)
82
- raise Arq::ParametersNotInContextError, missing_params unless missing_params.empty?
83
- end
84
-
85
- def validate_required_returns
86
- missing_returns = (@returns - @ctx.keys)
87
- raise Arq::ReturnValuesNotInContextError, missing_returns unless missing_returns.empty?
88
- end
89
- end
90
- end