nocode 0.0.3 → 0.0.4

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: 4609bf6b3f082509087ab07dfbe8b96f55df01788baac74c0d7c4500f1a48f35
4
- data.tar.gz: 1acd73ef5df505964078a840362f271a8196b7f099e5f18e9f5d7f4692152e5c
3
+ metadata.gz: 884ab5737629463437ef0991555e1ea30faa8db0cd6d8c79da712942aa69011b
4
+ data.tar.gz: d8562fdf11b2a204e9ced685b434d6959bafe2dca605dd3795355aeed1ed5238
5
5
  SHA512:
6
- metadata.gz: ce4af8b6a125a1cd178d0436e32918ed246053ac8f214b7adc98f4051ab34916c60794bb915f978aa0101adca7f4d9ccd332b04f0f27efaba8dc9fb17035e310
7
- data.tar.gz: 4e2e951062a9085a6bba5d146c5672f0862e6dc92eeedcc5d08cc5e7fdcdad86c08395240034a13441b6770c3bf0d31b1383446bf9f27ed4f18ceb0cd4cda9ac
6
+ metadata.gz: 677aa00aba3a998a4cc616002420c9366ac945fabdfc336d948b5158c24eae6c9bfc1b8a7ee658718c5c55af4148c4f83656fddf8b45995002471ef7224870da
7
+ data.tar.gz: e4de53676eaebb1d9bd3738dbbbb6df777ce0fdf1674e9aa370176c2eca9d42d4672d6edd682a313b943f24d149566491d1d7322d9c30ed0cc352db7730e0f3f
@@ -25,3 +25,5 @@ jobs:
25
25
  bundler-cache: true # runs 'bundle install' and caches installed gems automatically
26
26
  - name: Run tests
27
27
  run: bundle exec rake
28
+ - name: Security audit dependencies
29
+ run: bundle exec bundler-audit check --update
data/.rubocop.yml CHANGED
@@ -14,8 +14,5 @@ Metrics/BlockLength:
14
14
  Metrics/MethodLength:
15
15
  Max: 20
16
16
 
17
- Style/Documentation:
18
- Enabled: false
19
-
20
17
  RSpec/ExampleLength:
21
18
  Max: 10
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ #### 0.0.4 - February 13th, 2022
2
+
3
+ * Increase class documentation
4
+ * Add YAML serialization/deserializationß
5
+
1
6
  #### 0.0.3 - February 12th, 2022
2
7
 
3
8
  * Add initial implementation.
data/README.md CHANGED
@@ -2,9 +2,7 @@
2
2
 
3
3
  #### Execute Ruby code through YAML
4
4
 
5
- [![Ruby Gem CI](https://github.com/mattruggio/nocode/actions/workflows/rubygem.yml/badge.svg)](https://github.com/mattruggio/nocode/actions/workflows/rubygem.yml)
6
-
7
- **Warning**: This library is currently experimental.
5
+ [![Gem Version](https://badge.fury.io/rb/nocode.svg)](https://badge.fury.io/rb/nocode) [![Ruby Gem CI](https://github.com/mattruggio/nocode/actions/workflows/rubygem.yml/badge.svg)](https://github.com/mattruggio/nocode/actions/workflows/rubygem.yml)
8
6
 
9
7
  This is a proof of concept showing how a YAML interface could be draped over arbitrary Ruby code. The YAML contains a series of steps with each step mapping to a specific Ruby class. The Ruby classes just have one responsibility: to implement #perform.
10
8
 
@@ -113,16 +111,16 @@ Note: ensure you have proper authorization before trying to publish new versions
113
111
 
114
112
  After code changes have successfully gone through the Pull Request review process then the following steps should be followed for publishing new versions:
115
113
 
116
- 1. Merge Pull Request into master
114
+ 1. Merge Pull Request into main
117
115
  2. Update `version.rb` using [semantic versioning](https://semver.org/)
118
116
  3. Install dependencies: `bundle`
119
117
  4. Update `CHANGELOG.md` with release notes
120
- 5. Commit & push master to remote and ensure CI builds master successfully
118
+ 5. Commit & push main to remote and ensure CI builds main successfully
121
119
  6. Run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
122
120
 
123
121
  ## Code of Conduct
124
122
 
125
- Everyone interacting in this codebase, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/mattruggio/nocode/blob/master/CODE_OF_CONDUCT.md).
123
+ Everyone interacting in this codebase, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/mattruggio/nocode/blob/main/CODE_OF_CONDUCT.md).
126
124
 
127
125
  ## License
128
126
 
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nocode
4
+ # Describes the environment for each running step. An instance is initialized when a job
5
+ # kicks off and then is passed from step to step.
4
6
  class Context
5
7
  attr_reader :io, :parameters, :registers
6
8
 
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'context'
4
- require_relative 'object_template'
5
4
  require_relative 'step_registry'
6
5
 
7
6
  module Nocode
7
+ # Manages the lifecycle and executes a job.
8
8
  class Executor
9
9
  attr_reader :yaml, :io
10
10
 
@@ -38,7 +38,7 @@ module Nocode
38
38
  private
39
39
 
40
40
  def make_step(step, context)
41
- step = ObjectTemplate.new(step).evaluate(context.to_h)
41
+ step = Util::ObjectTemplate.new(step).evaluate(context.to_h)
42
42
  type = step['type'].to_s
43
43
  name = step['name'].to_s
44
44
  options = step['options'] || {}
data/lib/nocode/step.rb CHANGED
@@ -1,14 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nocode
4
+ # Defines a running step. Steps should be sub-classes of this class as well as to implement
5
+ # #perform.
4
6
  class Step
5
7
  extend Forwardable
6
8
  include Util::Arrayable
7
9
  include Util::Optionable
8
10
 
9
- attr_reader :name, :context, :options, :type
11
+ attr_reader :context,
12
+ :name,
13
+ :options,
14
+ :type
10
15
 
11
- def_delegators :context, :parameters, :registers, :io
16
+ def_delegators :context,
17
+ :io,
18
+ :parameters,
19
+ :registers
12
20
 
13
21
  def initialize(
14
22
  context: Context.new,
@@ -2,7 +2,11 @@
2
2
 
3
3
  require_relative 'step'
4
4
 
5
+ # This will execute the StepRegisty's load! method upon script evaluation.
5
6
  module Nocode
7
+ # Provides a global place to register all valid steps by their types. By default the
8
+ # steps directory will be autoloaded and their paths will be used as their types. For example:
9
+ # for the class: steps/io/write, it would register as "io/write" type.
6
10
  class StepRegistry < Util::ClassRegistry
7
11
  include Singleton
8
12
 
@@ -22,9 +26,11 @@ module Nocode
22
26
  def load!
23
27
  files_loaded = Util::ClassLoader.new(DIR).load!
24
28
 
29
+ # Class the parent to load up the registry with the files we found.
25
30
  load(files_loaded, PREFIX)
26
31
  end
27
32
  end
28
33
 
34
+ # Call upon class evaluation to autoload all classes.
29
35
  StepRegistry.load!
30
36
  end
@@ -2,11 +2,12 @@
2
2
 
3
3
  module Nocode
4
4
  module Steps
5
+ # Shallow-copy one register to another.
5
6
  class Copy < Step
6
7
  option :from_register, :to_register
7
8
 
8
9
  def perform
9
- registers[to_register_option] = registers[from_register_option]
10
+ registers[to_register_option] = registers[from_register_option].dup
10
11
  end
11
12
  end
12
13
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Nocode
4
4
  module Steps
5
+ # Remove a register completely.
5
6
  class Delete < Step
6
7
  option :register
7
8
 
@@ -3,6 +3,7 @@
3
3
  module Nocode
4
4
  module Steps
5
5
  module Deserialize
6
+ # take a specified register and parse it as a CSV to produce an array of hashes.
6
7
  class Csv < Step
7
8
  option :register
8
9
 
@@ -3,6 +3,7 @@
3
3
  module Nocode
4
4
  module Steps
5
5
  module Deserialize
6
+ # take a specified register and parse it as JSON to produce Ruby object(s).
6
7
  class Json < Step
7
8
  option :register
8
9
 
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nocode
4
+ module Steps
5
+ module Deserialize
6
+ # Take a specified register and parse it as YAML to produce Ruby object(s).
7
+ #
8
+ # NOTE: This will throw an error if unsafe YAML types are used. The only allowed types are
9
+ # array, hash, strings, numbers, booleans, nil. See:
10
+ # https://ruby-doc.org/stdlib-2.6.1/libdoc/psych/rdoc/Psych.html#method-c-safe_load
11
+ class Yaml < Step
12
+ option :register
13
+
14
+ def perform
15
+ input = registers[register_option]
16
+
17
+ registers[register_option] = YAML.safe_load(input)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -3,6 +3,7 @@
3
3
  module Nocode
4
4
  module Steps
5
5
  module Io
6
+ # Read a file from disk and place its contents in a register.
6
7
  class Read < Step
7
8
  option :path,
8
9
  :register
@@ -3,6 +3,7 @@
3
3
  module Nocode
4
4
  module Steps
5
5
  module Io
6
+ # Write the contents of a register to disk.
6
7
  class Write < Step
7
8
  option :path,
8
9
  :register
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Nocode
4
4
  module Steps
5
+ # Simply output the passed in message into the outputted log.
5
6
  class Log < Step
6
7
  option :message
7
8
 
@@ -3,6 +3,8 @@
3
3
  module Nocode
4
4
  module Steps
5
5
  module Serialize
6
+ # Take the contents of a register and create a CSV out of its contents. The CSV contents
7
+ # will override the register specified.
6
8
  class Csv < Step
7
9
  option :register
8
10
 
@@ -3,6 +3,8 @@
3
3
  module Nocode
4
4
  module Steps
5
5
  module Serialize
6
+ # Take the contents of a register and serialize it as JSON. The serialized JSON
7
+ # will override the register specified.
6
8
  class Json < Step
7
9
  option :register
8
10
 
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nocode
4
+ module Steps
5
+ module Serialize
6
+ # Take the contents of a register and serialize it as YAML. The serialized YAML
7
+ # will override the register specified.
8
+ class Yaml < Step
9
+ option :register
10
+
11
+ def perform
12
+ input = registers[register_option]
13
+
14
+ registers[register_option] = input.to_yaml
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Nocode
4
4
  module Steps
5
+ # Set a register's value to the value option specified.
5
6
  class Set < Step
6
7
  option :register, :value
7
8
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Nocode
4
4
  module Steps
5
+ # Sleep for an arbitrary number of seconds.
5
6
  class Sleep < Step
6
7
  option :seconds
7
8
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Nocode
4
4
  module Util
5
+ # Hand
5
6
  module Arrayable
6
7
  def array(value)
7
8
  if value.is_a?(Hash)
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Nocode
4
4
  module Util
5
+ # Loads a directory full of Ruby classes and returns their relative paths.
5
6
  class ClassLoader
6
7
  EXTENSION = '.rb'
7
8
 
@@ -2,6 +2,9 @@
2
2
 
3
3
  module Nocode
4
4
  module Util
5
+ # Create a type -> class constant interface. Classes can be registered as types. Types
6
+ # are snake-cased while class names are stored as pascal-cased. Then constant! can be called
7
+ # to retrieve the class constant by type.
5
8
  class ClassRegistry
6
9
  extend Forwardable
7
10
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Nocode
4
4
  module Util
5
+ # A hash-like object which ensures all keys are strings.
5
6
  class Dictionary
6
7
  extend Forwardable
7
8
 
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'string_template'
4
+
5
+ module Nocode
6
+ module Util
7
+ # Built on top of StringTemplate but instead of only working for a string, this will
8
+ # recursively evaluate all strings within an object. Heuristics:
9
+ # - Strings evaluate using StringTemplate
10
+ # - Hashes will have their keys and values traversed
11
+ # - Arrays will have their entries traversed
12
+ # - All other types will simply return themselves
13
+ class ObjectTemplate
14
+ attr_reader :object
15
+
16
+ def initialize(object)
17
+ @object = object
18
+
19
+ freeze
20
+ end
21
+
22
+ def evaluate(values = {})
23
+ recursive_evaluate(object, values)
24
+ end
25
+
26
+ private
27
+
28
+ def recursive_evaluate(expression, values)
29
+ case expression
30
+ when Array
31
+ expression.map { |o| recursive_evaluate(o, values) }
32
+ when Hash
33
+ expression.to_h do |k, v|
34
+ [recursive_evaluate(k, values), recursive_evaluate(v, values)]
35
+ end
36
+ when String
37
+ Util::StringTemplate.new(expression).evaluate(values)
38
+ else
39
+ expression
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -2,6 +2,20 @@
2
2
 
3
3
  module Nocode
4
4
  module Util
5
+ # Add on a DSL for classes. The DSL allows for a new class-level keyword called 'option'
6
+ # which can be used to describe what metadata values are important. Then instances
7
+ # can reference those option's values using magic _option methods. For example:
8
+ #
9
+ # class Animal
10
+ # include Optionable
11
+ # option :type
12
+ # attr_writer :options
13
+ # end
14
+ #
15
+ # animal = Animal.new
16
+ # animal.options = { 'type' => 'dog' }
17
+ #
18
+ # animal.type_option # -> should return 'dog'
5
19
  module Optionable
6
20
  def self.included(klass)
7
21
  klass.extend(ClassMethods)
@@ -2,6 +2,11 @@
2
2
 
3
3
  module Nocode
4
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!"
5
10
  class StringTemplate
6
11
  LEFT_TOKEN = '<<'
7
12
  RIGHT_TOKEN = '>>'
@@ -11,7 +16,7 @@ module Nocode
11
16
  attr_reader :expression
12
17
 
13
18
  def initialize(expression)
14
- @expression = expression
19
+ @expression = expression.to_s
15
20
 
16
21
  freeze
17
22
  end
data/lib/nocode/util.rb CHANGED
@@ -4,5 +4,5 @@ require_relative 'util/arrayable'
4
4
  require_relative 'util/class_loader'
5
5
  require_relative 'util/class_registry'
6
6
  require_relative 'util/dictionary'
7
+ require_relative 'util/object_template'
7
8
  require_relative 'util/optionable'
8
- require_relative 'util/string_template'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nocode
4
- VERSION = '0.0.3'
4
+ VERSION = '0.0.4'
5
5
  end
data/lib/nocode.rb CHANGED
@@ -14,7 +14,9 @@ require 'nocode/util'
14
14
  # Core
15
15
  require 'nocode/executor'
16
16
 
17
+ # Establish main top-level namespace
17
18
  module Nocode
19
+ # Default consumer entrypoint into the library.
18
20
  class << self
19
21
  def execute(yaml, io: $stdout)
20
22
  Executor.new(yaml, io: io).execute
data/nocode.gemspec CHANGED
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
29
29
 
30
30
  s.required_ruby_version = '>= 2.6'
31
31
 
32
+ s.add_development_dependency('bundler-audit')
32
33
  s.add_development_dependency('guard-rspec')
33
34
  s.add_development_dependency('pry')
34
35
  s.add_development_dependency('rake')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nocode
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Ruggio
@@ -10,6 +10,20 @@ bindir: exe
10
10
  cert_chain: []
11
11
  date: 2022-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler-audit
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: guard-rspec
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -162,18 +176,19 @@ files:
162
176
  - lib/nocode.rb
163
177
  - lib/nocode/context.rb
164
178
  - lib/nocode/executor.rb
165
- - lib/nocode/object_template.rb
166
179
  - lib/nocode/step.rb
167
180
  - lib/nocode/step_registry.rb
168
181
  - lib/nocode/steps/copy.rb
169
182
  - lib/nocode/steps/delete.rb
170
183
  - lib/nocode/steps/deserialize/csv.rb
171
184
  - lib/nocode/steps/deserialize/json.rb
185
+ - lib/nocode/steps/deserialize/yaml.rb
172
186
  - lib/nocode/steps/io/read.rb
173
187
  - lib/nocode/steps/io/write.rb
174
188
  - lib/nocode/steps/log.rb
175
189
  - lib/nocode/steps/serialize/csv.rb
176
190
  - lib/nocode/steps/serialize/json.rb
191
+ - lib/nocode/steps/serialize/yaml.rb
177
192
  - lib/nocode/steps/set.rb
178
193
  - lib/nocode/steps/sleep.rb
179
194
  - lib/nocode/util.rb
@@ -181,6 +196,7 @@ files:
181
196
  - lib/nocode/util/class_loader.rb
182
197
  - lib/nocode/util/class_registry.rb
183
198
  - lib/nocode/util/dictionary.rb
199
+ - lib/nocode/util/object_template.rb
184
200
  - lib/nocode/util/optionable.rb
185
201
  - lib/nocode/util/string_template.rb
186
202
  - lib/nocode/version.rb
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Nocode
4
- class ObjectTemplate
5
- attr_reader :object
6
-
7
- def initialize(object)
8
- @object = object
9
-
10
- freeze
11
- end
12
-
13
- def evaluate(values = {})
14
- recursive_evaluate(object, values)
15
- end
16
-
17
- private
18
-
19
- def recursive_evaluate(expression, values)
20
- case expression
21
- when Array
22
- expression.map { |o| recursive_evaluate(o, values) }
23
- when Hash
24
- expression.to_h do |k, v|
25
- [recursive_evaluate(k, values), recursive_evaluate(v, values)]
26
- end
27
- when String
28
- Util::StringTemplate.new(expression).evaluate(values)
29
- else
30
- expression
31
- end
32
- end
33
- end
34
- end