nocode 0.0.5 → 0.0.9
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 +4 -4
- data/CHANGELOG.md +20 -1
- data/exe/nocode +2 -0
- data/lib/nocode/context.rb +16 -3
- data/lib/nocode/job_executor.rb +55 -0
- data/lib/nocode/step_registry.rb +3 -3
- data/lib/nocode/steps/each.rb +47 -0
- data/lib/nocode/steps/io/delete.rb +24 -0
- data/lib/nocode/steps/io/list.rb +25 -0
- data/lib/nocode/steps/map.rb +52 -0
- data/lib/nocode/steps/record/map.rb +25 -0
- data/lib/nocode/steps_executor.rb +68 -0
- data/lib/nocode/util/class_registry.rb +2 -2
- data/lib/nocode/util/object_template.rb +1 -3
- data/lib/nocode/util/optionable.rb +8 -0
- data/lib/nocode/version.rb +1 -1
- data/lib/nocode.rb +3 -2
- data/nocode.gemspec +2 -0
- metadata +29 -4
- data/lib/nocode/executor.rb +0 -90
- data/lib/nocode/util/string_template.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f199fcae3341fccc2e54c1c139ba2c7e65a20dda27d2be31c79de02f9ec2558a
|
4
|
+
data.tar.gz: c10d64f895d34e567b846c52727205d1394641adcd68b1b1d2707c509577700c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1a53bcec0412b9b8be57d9cb61601db583aa210542f9372c05d4cdee0e693861d05e017a697103253a2d522a2253e609481ded8e0524362ccaf6640a9d13f9d3
|
7
|
+
data.tar.gz: 131855e439116d1718cf46c53cc32597187aeae36156d71fff508fa4a0a20739a9965b055874aadc93cae4a5dd3fbf3f4b0201d5f7c75a9d1d63ed60e9150044
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,25 @@
|
|
1
|
+
#### 0.0.9 - February 18th, 2022
|
2
|
+
|
3
|
+
* Replace StringTemplate with Nay library.
|
4
|
+
#### 0.0.8 - February 14th, 2022
|
5
|
+
|
6
|
+
* Add `map` step to iterate and collect a dataset result.
|
7
|
+
* Add `record/map` step to iterate over a hash.
|
8
|
+
* Add `io/list` to populate a register with the contents of a directory.
|
9
|
+
* Add `io/delete` to delete a file specified in the path option.
|
10
|
+
#### 0.0.7 - February 13th, 2022
|
11
|
+
|
12
|
+
* Move shared logging logic to context (until a first class log writer emerges).
|
13
|
+
* Provide type_prefix option for class_registry
|
14
|
+
|
15
|
+
#### 0.0.6 - February 13th, 2022
|
16
|
+
|
17
|
+
* Expose registers to main YAML configuration.
|
18
|
+
* Add `each` step to serve as an example of an iterator.
|
19
|
+
|
1
20
|
#### 0.0.5 - February 13th, 2022
|
2
21
|
|
3
|
-
* Added initial
|
22
|
+
* Added initial `dataset` steps.
|
4
23
|
|
5
24
|
#### 0.0.4 - February 13th, 2022
|
6
25
|
|
data/exe/nocode
CHANGED
data/lib/nocode/context.rb
CHANGED
@@ -4,7 +4,12 @@ module Nocode
|
|
4
4
|
# Describes the environment for each running step. An instance is initialized when a job
|
5
5
|
# kicks off and then is passed from step to step.
|
6
6
|
class Context
|
7
|
-
|
7
|
+
PARAMETERS_KEY = 'parameters'
|
8
|
+
REGISTERS_KEY = 'registers'
|
9
|
+
|
10
|
+
attr_reader :io,
|
11
|
+
:parameters,
|
12
|
+
:registers
|
8
13
|
|
9
14
|
def initialize(io: $stdout, parameters: {}, registers: {})
|
10
15
|
@io = io || $stdout
|
@@ -24,9 +29,17 @@ module Nocode
|
|
24
29
|
|
25
30
|
def to_h
|
26
31
|
{
|
27
|
-
|
28
|
-
|
32
|
+
REGISTERS_KEY => registers,
|
33
|
+
PARAMETERS_KEY => parameters
|
29
34
|
}
|
30
35
|
end
|
36
|
+
|
37
|
+
def log_line
|
38
|
+
log('-' * 50)
|
39
|
+
end
|
40
|
+
|
41
|
+
def log(msg)
|
42
|
+
io.puts(msg)
|
43
|
+
end
|
31
44
|
end
|
32
45
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'context'
|
4
|
+
require_relative 'steps_executor'
|
5
|
+
|
6
|
+
module Nocode
|
7
|
+
# Manages the lifecycle and executes a job.
|
8
|
+
class JobExecutor
|
9
|
+
PARAMETERS_KEY = 'parameters'
|
10
|
+
REGISTERS_KEY = 'registers'
|
11
|
+
STEPS_KEY = 'steps'
|
12
|
+
|
13
|
+
attr_reader :yaml, :io
|
14
|
+
|
15
|
+
def initialize(yaml, io: $stdout)
|
16
|
+
@yaml = yaml.respond_to?(:read) ? yaml.read : yaml
|
17
|
+
@yaml = YAML.safe_load(@yaml) || {}
|
18
|
+
@io = io
|
19
|
+
|
20
|
+
freeze
|
21
|
+
end
|
22
|
+
|
23
|
+
def execute
|
24
|
+
steps = yaml[STEPS_KEY] || []
|
25
|
+
parameters = yaml[PARAMETERS_KEY] || {}
|
26
|
+
registers = yaml[REGISTERS_KEY] || {}
|
27
|
+
|
28
|
+
context = Context.new(
|
29
|
+
io: io,
|
30
|
+
parameters: parameters,
|
31
|
+
registers: registers
|
32
|
+
)
|
33
|
+
|
34
|
+
log_title(context)
|
35
|
+
|
36
|
+
StepsExecutor.new(context: context, steps: steps).execute
|
37
|
+
|
38
|
+
context.log("Ended: #{DateTime.now}")
|
39
|
+
context.log_line
|
40
|
+
|
41
|
+
context
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def log_title(context)
|
47
|
+
context.log_line
|
48
|
+
|
49
|
+
context.log('Nocode Execution')
|
50
|
+
context.log("Started: #{DateTime.now}")
|
51
|
+
|
52
|
+
context.log_line
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/nocode/step_registry.rb
CHANGED
@@ -10,8 +10,8 @@ module Nocode
|
|
10
10
|
class StepRegistry < Util::ClassRegistry
|
11
11
|
include Singleton
|
12
12
|
|
13
|
-
|
14
|
-
DIR
|
13
|
+
CLASS_PREFIX = 'Nocode::Steps::'
|
14
|
+
DIR = File.join(__dir__, 'steps')
|
15
15
|
|
16
16
|
class << self
|
17
17
|
extend Forwardable
|
@@ -27,7 +27,7 @@ module Nocode
|
|
27
27
|
files_loaded = Util::ClassLoader.new(DIR).load!
|
28
28
|
|
29
29
|
# Class the parent to load up the registry with the files we found.
|
30
|
-
load(files_loaded,
|
30
|
+
load(files_loaded, class_prefix: CLASS_PREFIX)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nocode
|
4
|
+
module Steps
|
5
|
+
# Iterate over a register. Each iteration will store the current element and index in
|
6
|
+
# special registers called: _element and _index. You can prefix these registers by setting
|
7
|
+
# the element_register_prefix option.
|
8
|
+
class Each < Step
|
9
|
+
option :element_register_prefix,
|
10
|
+
:register,
|
11
|
+
:steps
|
12
|
+
|
13
|
+
skip_options_evaluation!
|
14
|
+
|
15
|
+
def perform
|
16
|
+
entries.each_with_index do |entry, index|
|
17
|
+
registers[element_key] = entry
|
18
|
+
registers[index_key] = index
|
19
|
+
|
20
|
+
execute_steps
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def execute_steps
|
27
|
+
StepsExecutor.new(context: context, steps: steps).execute
|
28
|
+
end
|
29
|
+
|
30
|
+
def entries
|
31
|
+
array(registers[register_option])
|
32
|
+
end
|
33
|
+
|
34
|
+
def steps
|
35
|
+
array(steps_option)
|
36
|
+
end
|
37
|
+
|
38
|
+
def element_key
|
39
|
+
"#{element_register_prefix_option}_element"
|
40
|
+
end
|
41
|
+
|
42
|
+
def index_key
|
43
|
+
"#{element_register_prefix_option}_index"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nocode
|
4
|
+
module Steps
|
5
|
+
module Io
|
6
|
+
# Delete the specified path. Does nothing if the file does not exist.
|
7
|
+
class Delete < Step
|
8
|
+
option :path
|
9
|
+
|
10
|
+
def perform
|
11
|
+
return if path.to_s.empty?
|
12
|
+
|
13
|
+
FileUtils.rm_f(path) if File.exist?(path)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def path
|
19
|
+
File.join(*array(path_option))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nocode
|
4
|
+
module Steps
|
5
|
+
module Io
|
6
|
+
# List all files in the path option. Wildcards can be used.
|
7
|
+
#
|
8
|
+
# Mechanic: https://ruby-doc.org/core-2.5.0/Dir.html#method-c-glob
|
9
|
+
class List < Step
|
10
|
+
option :path,
|
11
|
+
:register
|
12
|
+
|
13
|
+
def perform
|
14
|
+
registers[register_option] = Dir[path].reject { |p| File.directory?(p) }
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def path
|
20
|
+
File.join(*array(path_option))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nocode
|
4
|
+
module Steps
|
5
|
+
# Iterate over a register. Each iteration will store the current element and index in
|
6
|
+
# special registers called: _element and _index. You can prefix these registers by setting
|
7
|
+
# the element_register_prefix option.
|
8
|
+
#
|
9
|
+
# The main difference between this and 'each' is that this will collect the iterator
|
10
|
+
# element register and set the register to this new collection.
|
11
|
+
class Map < Step
|
12
|
+
option :element_register_prefix,
|
13
|
+
:register,
|
14
|
+
:steps
|
15
|
+
|
16
|
+
skip_options_evaluation!
|
17
|
+
|
18
|
+
def perform
|
19
|
+
registers[register_option] = entries.map.with_index do |entry, index|
|
20
|
+
registers[element_key] = entry
|
21
|
+
registers[index_key] = index
|
22
|
+
|
23
|
+
execute_steps
|
24
|
+
|
25
|
+
registers[element_key]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def execute_steps
|
32
|
+
StepsExecutor.new(context: context, steps: steps).execute
|
33
|
+
end
|
34
|
+
|
35
|
+
def entries
|
36
|
+
array(registers[register_option])
|
37
|
+
end
|
38
|
+
|
39
|
+
def steps
|
40
|
+
array(steps_option)
|
41
|
+
end
|
42
|
+
|
43
|
+
def element_key
|
44
|
+
"#{element_register_prefix_option}_element"
|
45
|
+
end
|
46
|
+
|
47
|
+
def index_key
|
48
|
+
"#{element_register_prefix_option}_index"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nocode
|
4
|
+
module Steps
|
5
|
+
module Record
|
6
|
+
# Create a new hash from an existing hash mapping each key as configured by the
|
7
|
+
# key_mappings option. The key_mappings option should be in the form of:
|
8
|
+
# new_key => old_key
|
9
|
+
class Map < Step
|
10
|
+
option :key_mappings, :register
|
11
|
+
|
12
|
+
def perform
|
13
|
+
input = registers[register_option] || {}
|
14
|
+
output = {}
|
15
|
+
|
16
|
+
(key_mappings_option || {}).each do |to, from|
|
17
|
+
output[to.to_s] = input[from.to_s]
|
18
|
+
end
|
19
|
+
|
20
|
+
registers[register_option] = output
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'step_registry'
|
4
|
+
|
5
|
+
module Nocode
|
6
|
+
# Class that knows how to execute a series of steps given a context.
|
7
|
+
class StepsExecutor
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
NAME_KEY = 'name'
|
11
|
+
OPTIONS_KEY = 'options'
|
12
|
+
TYPE_KEY = 'type'
|
13
|
+
|
14
|
+
attr_reader :context, :steps
|
15
|
+
|
16
|
+
def_delegators :context, :log_line, :log
|
17
|
+
|
18
|
+
def initialize(context:, steps:)
|
19
|
+
@context = context
|
20
|
+
@steps = steps
|
21
|
+
|
22
|
+
freeze
|
23
|
+
end
|
24
|
+
|
25
|
+
def execute
|
26
|
+
steps.each do |step|
|
27
|
+
step_instance = make_step(step)
|
28
|
+
|
29
|
+
execute_step(step_instance)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def make_step(step)
|
36
|
+
evaluated_step = Util::ObjectTemplate.new(step).evaluate(context.to_h)
|
37
|
+
type = evaluated_step[TYPE_KEY].to_s
|
38
|
+
name = evaluated_step[NAME_KEY].to_s
|
39
|
+
step_class = StepRegistry.constant!(type)
|
40
|
+
|
41
|
+
options =
|
42
|
+
if step_class.skip_options_evaluation?
|
43
|
+
step[OPTIONS_KEY]
|
44
|
+
else
|
45
|
+
evaluated_step[OPTIONS_KEY]
|
46
|
+
end
|
47
|
+
|
48
|
+
step_class.new(
|
49
|
+
options: Util::Dictionary.new(options),
|
50
|
+
context: context,
|
51
|
+
name: name,
|
52
|
+
type: type
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def execute_step(step)
|
57
|
+
log(step.name) unless step.name.empty?
|
58
|
+
log("Step: #{step.type}")
|
59
|
+
log("Class: #{step.class}")
|
60
|
+
|
61
|
+
time_in_seconds = Benchmark.measure { step.perform }.real
|
62
|
+
|
63
|
+
log("Completed in #{time_in_seconds.round(3)} second(s)")
|
64
|
+
|
65
|
+
log_line
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -20,13 +20,13 @@ module Nocode
|
|
20
20
|
freeze
|
21
21
|
end
|
22
22
|
|
23
|
-
def load(types,
|
23
|
+
def load(types, class_prefix: '', type_prefix: '')
|
24
24
|
types.each do |type|
|
25
25
|
pascal_cased = type.split(File::SEPARATOR).map do |part|
|
26
26
|
part.split('_').collect(&:capitalize).join
|
27
27
|
end.join('::')
|
28
28
|
|
29
|
-
register(type, "#{
|
29
|
+
register("#{type_prefix}#{type}", "#{class_prefix}#{pascal_cased}")
|
30
30
|
end
|
31
31
|
|
32
32
|
self
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'string_template'
|
4
|
-
|
5
3
|
module Nocode
|
6
4
|
module Util
|
7
5
|
# Built on top of StringTemplate but instead of only working for a string, this will
|
@@ -34,7 +32,7 @@ module Nocode
|
|
34
32
|
[recursive_evaluate(k, values), recursive_evaluate(v, values)]
|
35
33
|
end
|
36
34
|
when String
|
37
|
-
|
35
|
+
Nay.evaluate(expression, values)
|
38
36
|
else
|
39
37
|
expression
|
40
38
|
end
|
@@ -23,6 +23,14 @@ module Nocode
|
|
23
23
|
|
24
24
|
# Class-level DSL Methods
|
25
25
|
module ClassMethods
|
26
|
+
def skip_options_evaluation?
|
27
|
+
@skip_options_evaluation || false
|
28
|
+
end
|
29
|
+
|
30
|
+
def skip_options_evaluation!
|
31
|
+
@skip_options_evaluation = true
|
32
|
+
end
|
33
|
+
|
26
34
|
def option(*values)
|
27
35
|
values.each { |v| options << v.to_s }
|
28
36
|
end
|
data/lib/nocode/version.rb
CHANGED
data/lib/nocode.rb
CHANGED
@@ -4,6 +4,7 @@ require 'benchmark'
|
|
4
4
|
require 'csv'
|
5
5
|
require 'fileutils'
|
6
6
|
require 'json'
|
7
|
+
require 'nay'
|
7
8
|
require 'singleton'
|
8
9
|
require 'time'
|
9
10
|
require 'yaml'
|
@@ -12,14 +13,14 @@ require 'yaml'
|
|
12
13
|
require 'nocode/util'
|
13
14
|
|
14
15
|
# Core
|
15
|
-
require 'nocode/
|
16
|
+
require 'nocode/job_executor'
|
16
17
|
|
17
18
|
# Establish main top-level namespace
|
18
19
|
module Nocode
|
19
20
|
# Default consumer entrypoint into the library.
|
20
21
|
class << self
|
21
22
|
def execute(yaml, io: $stdout)
|
22
|
-
|
23
|
+
JobExecutor.new(yaml, io: io).execute
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
data/nocode.gemspec
CHANGED
metadata
CHANGED
@@ -1,15 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nocode
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Ruggio
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-02-
|
11
|
+
date: 2022-02-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nay
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.0'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.0.2
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.0'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.0.2
|
13
33
|
- !ruby/object:Gem::Dependency
|
14
34
|
name: bundler-audit
|
15
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -175,7 +195,7 @@ files:
|
|
175
195
|
- exe/nocode
|
176
196
|
- lib/nocode.rb
|
177
197
|
- lib/nocode/context.rb
|
178
|
-
- lib/nocode/
|
198
|
+
- lib/nocode/job_executor.rb
|
179
199
|
- lib/nocode/step.rb
|
180
200
|
- lib/nocode/step_registry.rb
|
181
201
|
- lib/nocode/steps/copy.rb
|
@@ -188,14 +208,20 @@ files:
|
|
188
208
|
- lib/nocode/steps/deserialize/csv.rb
|
189
209
|
- lib/nocode/steps/deserialize/json.rb
|
190
210
|
- lib/nocode/steps/deserialize/yaml.rb
|
211
|
+
- lib/nocode/steps/each.rb
|
212
|
+
- lib/nocode/steps/io/delete.rb
|
213
|
+
- lib/nocode/steps/io/list.rb
|
191
214
|
- lib/nocode/steps/io/read.rb
|
192
215
|
- lib/nocode/steps/io/write.rb
|
193
216
|
- lib/nocode/steps/log.rb
|
217
|
+
- lib/nocode/steps/map.rb
|
218
|
+
- lib/nocode/steps/record/map.rb
|
194
219
|
- lib/nocode/steps/serialize/csv.rb
|
195
220
|
- lib/nocode/steps/serialize/json.rb
|
196
221
|
- lib/nocode/steps/serialize/yaml.rb
|
197
222
|
- lib/nocode/steps/set.rb
|
198
223
|
- lib/nocode/steps/sleep.rb
|
224
|
+
- lib/nocode/steps_executor.rb
|
199
225
|
- lib/nocode/util.rb
|
200
226
|
- lib/nocode/util/arrayable.rb
|
201
227
|
- lib/nocode/util/class_loader.rb
|
@@ -203,7 +229,6 @@ files:
|
|
203
229
|
- lib/nocode/util/dictionary.rb
|
204
230
|
- lib/nocode/util/object_template.rb
|
205
231
|
- lib/nocode/util/optionable.rb
|
206
|
-
- lib/nocode/util/string_template.rb
|
207
232
|
- lib/nocode/version.rb
|
208
233
|
- nocode.gemspec
|
209
234
|
homepage: https://github.com/mattruggio/nocode
|
data/lib/nocode/executor.rb
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'context'
|
4
|
-
require_relative 'step_registry'
|
5
|
-
|
6
|
-
module Nocode
|
7
|
-
# Manages the lifecycle and executes a job.
|
8
|
-
class Executor
|
9
|
-
NAME_KEY = 'name'
|
10
|
-
OPTIONS_KEY = 'options'
|
11
|
-
PARAMETERS_KEY = 'parameters'
|
12
|
-
STEPS_KEY = 'steps'
|
13
|
-
TYPE_KEY = 'type'
|
14
|
-
|
15
|
-
attr_reader :yaml, :io
|
16
|
-
|
17
|
-
def initialize(yaml, io: $stdout)
|
18
|
-
@yaml = yaml.respond_to?(:read) ? yaml.read : yaml
|
19
|
-
@yaml = YAML.safe_load(@yaml) || {}
|
20
|
-
@io = io
|
21
|
-
|
22
|
-
freeze
|
23
|
-
end
|
24
|
-
|
25
|
-
def execute
|
26
|
-
steps = yaml[STEPS_KEY] || []
|
27
|
-
parameters = yaml[PARAMETERS_KEY] || {}
|
28
|
-
context = Context.new(io: io, parameters: parameters)
|
29
|
-
|
30
|
-
log_title
|
31
|
-
|
32
|
-
steps.each do |step|
|
33
|
-
step_instance = make_step(step, context)
|
34
|
-
|
35
|
-
execute_step(step_instance)
|
36
|
-
end
|
37
|
-
|
38
|
-
log("Ended: #{DateTime.now}")
|
39
|
-
log_line
|
40
|
-
|
41
|
-
context
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def make_step(step, context)
|
47
|
-
step = Util::ObjectTemplate.new(step).evaluate(context.to_h)
|
48
|
-
type = step[TYPE_KEY].to_s
|
49
|
-
name = step[NAME_KEY].to_s
|
50
|
-
options = step[OPTIONS_KEY] || {}
|
51
|
-
step_class = StepRegistry.constant!(type)
|
52
|
-
|
53
|
-
step_class.new(
|
54
|
-
options: Util::Dictionary.new(options),
|
55
|
-
context: context,
|
56
|
-
name: name,
|
57
|
-
type: type
|
58
|
-
)
|
59
|
-
end
|
60
|
-
|
61
|
-
def execute_step(step)
|
62
|
-
log(step.name) unless step.name.empty?
|
63
|
-
log("Step: #{step.type}")
|
64
|
-
log("Class: #{step.class}")
|
65
|
-
|
66
|
-
time_in_seconds = Benchmark.measure { step.perform }.real
|
67
|
-
|
68
|
-
log("Completed in #{time_in_seconds.round(3)} second(s)")
|
69
|
-
|
70
|
-
log_line
|
71
|
-
end
|
72
|
-
|
73
|
-
def log_title
|
74
|
-
log_line
|
75
|
-
|
76
|
-
log('Nocode Execution')
|
77
|
-
log("Started: #{DateTime.now}")
|
78
|
-
|
79
|
-
log_line
|
80
|
-
end
|
81
|
-
|
82
|
-
def log_line
|
83
|
-
log('-' * 50)
|
84
|
-
end
|
85
|
-
|
86
|
-
def log(msg)
|
87
|
-
io.puts(msg)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Nocode
|
4
|
-
module Util
|
5
|
-
# Takes in an expression and interpolates in any parameters using << >> notation.
|
6
|
-
# For example:
|
7
|
-
# input = { 'person' => 'hops' }
|
8
|
-
# Nocode::Util::StringTemplate.new("Hello, << person.name >>!").evaluate(input)
|
9
|
-
# Should produce: "Hello, hops!"
|
10
|
-
class StringTemplate
|
11
|
-
LEFT_TOKEN = '<<'
|
12
|
-
RIGHT_TOKEN = '>>'
|
13
|
-
SEPARATOR = '.'
|
14
|
-
REG_EXPR = /#{Regexp.quote(LEFT_TOKEN)}(.*?)#{Regexp.quote(RIGHT_TOKEN)}/.freeze
|
15
|
-
|
16
|
-
attr_reader :expression
|
17
|
-
|
18
|
-
def initialize(expression)
|
19
|
-
@expression = expression.to_s
|
20
|
-
|
21
|
-
freeze
|
22
|
-
end
|
23
|
-
|
24
|
-
def evaluate(values = {})
|
25
|
-
resolved = tokens_to_values(tokens, values)
|
26
|
-
|
27
|
-
tokens.inject(expression) do |memo, token|
|
28
|
-
memo.gsub("#{LEFT_TOKEN}#{token}#{RIGHT_TOKEN}", resolved[token].to_s)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def tokens
|
35
|
-
expression.to_s.scan(REG_EXPR).flatten
|
36
|
-
end
|
37
|
-
|
38
|
-
def tokens_to_values(tokens, values)
|
39
|
-
tokens.each_with_object({}) do |token, memo|
|
40
|
-
cleansed = token.strip
|
41
|
-
parts = cleansed.split(SEPARATOR)
|
42
|
-
value = values.dig(*parts)
|
43
|
-
|
44
|
-
memo[token] = value
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|