nodepile 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/.rubocop.yml +1 -1
- data/BACKLOG.md +34 -0
- data/Rakefile +92 -2
- data/lib/nodepile/base_structs.rb +62 -0
- data/lib/nodepile/colspecs.rb +562 -0
- data/lib/nodepile/gross_actions.rb +38 -0
- data/lib/nodepile/gviz.rb +108 -0
- data/lib/nodepile/keyed_array.rb +386 -0
- data/lib/nodepile/pile_organizer.rb +258 -0
- data/lib/nodepile/pragmas.rb +97 -0
- data/lib/nodepile/rec_source.rb +329 -0
- data/lib/nodepile/rule_eval.rb +155 -0
- data/lib/nodepile/version.rb +1 -1
- data/nodepile.gemspec +53 -0
- data/tmp/.gitignore +1 -0
- metadata +136 -19
@@ -0,0 +1,155 @@
|
|
1
|
+
|
2
|
+
module Nodepile
|
3
|
+
|
4
|
+
# Represents a single rule record as represernted by a KeyedArrayAccessor object.
|
5
|
+
# Note that this class does not rely on metadata of the KeyedArrayAccessor and doesn't
|
6
|
+
# even verify that the object it represents actually contains any formulas.
|
7
|
+
#
|
8
|
+
# Generally speaking, any field whose first character is a question mark is considered
|
9
|
+
# to have a calculation/formula in it. Formulas (not counting the question mark) are simply
|
10
|
+
# ruby expressions. The formulas in the id fields ('_id', '_links_to', and '_links_from')
|
11
|
+
# are given special treatment as described below.
|
12
|
+
#
|
13
|
+
# Dynamic calculations must start with the question mark character '?'. They
|
14
|
+
# will use the Ruby language itself with a tightly constrained binding that
|
15
|
+
# defines one primary object simply named "v" (standing for values). That object
|
16
|
+
# only supports a handful of operations including:
|
17
|
+
# v['fielaname'] to evaluate a field
|
18
|
+
# v.include?('fieldname') to determine whether the field exists
|
19
|
+
# v[:this] to evaluate this field without having to explicitly name it
|
20
|
+
# of fields. Note that blank "fields" will often return nil and also non-existent
|
21
|
+
# fields will be nil.
|
22
|
+
#
|
23
|
+
# Note, that some exceptional calculation rules may be triggered if this
|
24
|
+
# calculation is for an id field. See the #uses_id_calcs?() method.
|
25
|
+
class RuleRecordEvaluator
|
26
|
+
EDGE_ID_FIELDS = ['_links_from','_links_to'].freeze
|
27
|
+
NODE_ID_FIELDS = ['_id'].freeze
|
28
|
+
ID_FIELD_NAMES = (NODE_ID_FIELDS + EDGE_ID_FIELDS).freeze
|
29
|
+
|
30
|
+
# @param rule_record_kaa [KeyedArrayAccessor] This ia a rule record that
|
31
|
+
# contains one or more formulas in its fields as indicated
|
32
|
+
# by a leading question mark in either _id, _links_from, or _links_to
|
33
|
+
def initialize(rule_record_kaa)
|
34
|
+
@kaa = rule_record_kaa
|
35
|
+
@match_fields = @kaa['_id'].nil? ? EDGE_ID_FIELDS : NODE_ID_FIELDS # assuming it's well formed
|
36
|
+
@match_type = nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# Confirm that this particular rule applies to the given node or edge
|
40
|
+
# @param otr [KeyedArrayAccessor] confirms that
|
41
|
+
# @return [Boolean] true if the identifying fields match. For node entities
|
42
|
+
# the '_id' field is the key. For edges, the '_links_from'
|
43
|
+
# and '_links_to' fields are the identifying fields.
|
44
|
+
def match_record?(otr)
|
45
|
+
return @match_fields.all?{|key|
|
46
|
+
myval = @kaa[key]
|
47
|
+
if myval.start_with?('?')
|
48
|
+
self.class.eval_calc(myval,key,otr)
|
49
|
+
else
|
50
|
+
myval == otr[key]
|
51
|
+
end
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns true if the calculation used by #match_record?() uses complex
|
56
|
+
# calculation logic
|
57
|
+
def uses_dynamic_match?
|
58
|
+
@match_type ||= @match_fields.any?{|k|
|
59
|
+
@kaa[k].then{|v| v.start_with?('?') && v.include?('v[')}
|
60
|
+
} ? :dynamic : :static
|
61
|
+
return @match_type == :dynamic
|
62
|
+
end
|
63
|
+
|
64
|
+
# Calculates the "value" of the rule when applied to a specific record
|
65
|
+
# NOTE: this does not test for #match_record?() which you probably want
|
66
|
+
# to do first. Note that the three id fields are ALWAYS left as nil after this
|
67
|
+
# method. Calculated values will be coerced to string via #to_s so if the
|
68
|
+
# default behavior isn't the right one for you, you should convert to string yourself
|
69
|
+
# This also doesn't test whether the records conform.
|
70
|
+
#
|
71
|
+
# @param otr [KeyedArrayAccessor] Rules must be calculated against a given
|
72
|
+
# record
|
73
|
+
# @return [KeyedArrayAccessor] Calculates an appropriate overlay from the rule
|
74
|
+
def calculate_rule(otr)
|
75
|
+
kaa = @kaa.dup
|
76
|
+
kaa.kv_map!{|k,v|
|
77
|
+
if v.nil?
|
78
|
+
#no-op
|
79
|
+
elsif ID_FIELD_NAMES.include?(k)
|
80
|
+
nil # we never overlay key fields
|
81
|
+
elsif v.start_with?('?')
|
82
|
+
self.class.eval_calc(v,k,otr)&.to_s
|
83
|
+
else
|
84
|
+
v # leave field unaltered by calculation logic
|
85
|
+
end
|
86
|
+
}
|
87
|
+
return kaa
|
88
|
+
end
|
89
|
+
|
90
|
+
# Evaluate a calculation using standard logic. Formulas may use syntax to
|
91
|
+
# reference the values of the eval_against_key_value_map object. For example
|
92
|
+
# "?v['column X'].to_f > 17.2 ? 'red' : 'black'" calcs based on 'column X' contents
|
93
|
+
def self.eval_calc(rule_field_defn,this_field_name,eval_against_key_value_map)
|
94
|
+
return rule_field_defn unless rule_field_defn.start_with?('?')
|
95
|
+
begin
|
96
|
+
ruby_code = rule_field_defn.dup.tap{|s| s[0] = ' '} # get rid of leading question mark
|
97
|
+
val = EvalFrame.evaluate(ruby_code,this_field_name,eval_against_key_value_map)
|
98
|
+
rescue StandardError => e
|
99
|
+
#TODO: Probably will need to remove this and replace with more fault
|
100
|
+
# tolerant strategy that fails gracefully (perhaps by nil-valuing the field)
|
101
|
+
raise "Error attempting to evaluate this formula { #{rule_field_defn} } : #{e.message}"
|
102
|
+
end
|
103
|
+
case val
|
104
|
+
when true,false,nil
|
105
|
+
#no-op
|
106
|
+
when Regexp
|
107
|
+
if /^?\s*\//.match?(rule_field_defn) && ID_FIELD_NAMES.include?(this_field_name)
|
108
|
+
val = val.match?(eval_against_key_value_map[this_field_name])
|
109
|
+
else
|
110
|
+
raise "Rule should evaluate to a 'boolean' except when triggering Regex/glob matching on an id field."
|
111
|
+
end
|
112
|
+
when String
|
113
|
+
if /^?\s*['"]/.match?(rule_field_defn) && ID_FIELD_NAMES.include?(this_field_name)
|
114
|
+
val = File.fnmatch?(val,eval_against_key_value_map[this_field_name])
|
115
|
+
else
|
116
|
+
#no-op... returning a string is a good behavior for calculations
|
117
|
+
end
|
118
|
+
else
|
119
|
+
raise "For field [#{this_field_name}] the rule expression must evaluate to true, false, or nil except when triggering regex/glob matching on an id field."
|
120
|
+
end #case
|
121
|
+
return val
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
# Utility class
|
127
|
+
class HashMask
|
128
|
+
def initialize(hashlike,this_key)
|
129
|
+
@h = hashlike
|
130
|
+
@tk = this_key
|
131
|
+
end
|
132
|
+
|
133
|
+
def include?(k) = @h.include?(k)
|
134
|
+
def [](k)
|
135
|
+
raise "Unable to use v[:this] because :this was not set" if k == :this && @tk.nil?
|
136
|
+
@h[k == :this ? @tk : k]
|
137
|
+
end
|
138
|
+
end #class HashMask
|
139
|
+
|
140
|
+
# Utility class
|
141
|
+
class EvalFrame
|
142
|
+
def initialize(test_hashlike) = @__hm = test_hashlike
|
143
|
+
def v = @__hm
|
144
|
+
|
145
|
+
def self.evaluate(defn,this_fieldname,test_hashlike)
|
146
|
+
frame = new(HashMask.new(test_hashlike,this_fieldname)) #restrict access as tightly as reasonable
|
147
|
+
frame.instance_eval(defn) # return the result
|
148
|
+
end
|
149
|
+
end #class EvalFrame
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
end #class RuleRecordEvaluator
|
154
|
+
|
155
|
+
end # module Nodepile
|
data/lib/nodepile/version.rb
CHANGED
data/nodepile.gemspec
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/nodepile/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "nodepile"
|
7
|
+
spec.version = Nodepile::VERSION
|
8
|
+
spec.authors = ["David Foster"]
|
9
|
+
spec.email = ["david.t.foster.01@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "Translate flat text file data into connected graph specs suitable for graphviz. NOT WORKING CODE."
|
12
|
+
spec.description = "NOT WORKING CODE. Designed to make it easy to take a spreadsheet or other tabular data and use it to generate, manipulate, and style connected graphs. Can be used from the command line or as an object model. This gem does not itself perform any rendering, although it contains facilities to drive the Graphviz familly of open source tools to generate images (e.g. gif, jpg, svm)."
|
13
|
+
spec.required_ruby_version = ">= 3.2.2"
|
14
|
+
spec.homepage = "https://rubygems.org/gems/nodepile"
|
15
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
16
|
+
spec.metadata["source_code_uri"] = "https://gitlab.com/fosterd42/nodepile.git"
|
17
|
+
spec.license = "MIT"
|
18
|
+
#spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
19
|
+
#spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
+
spec.files = Dir.chdir(__dir__) do
|
26
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)})
|
28
|
+
end
|
29
|
+
end
|
30
|
+
spec.bindir = "exe"
|
31
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ["lib"]
|
33
|
+
|
34
|
+
# Uncomment to register a new dependency of your gem
|
35
|
+
# spec.add_dependency "example-gem", "~> 1.0"
|
36
|
+
spec.add_dependency 'ruby-graphviz', "~> 1.2.5"
|
37
|
+
|
38
|
+
# DEVELOPMENT DEPENDENCIES BELOW
|
39
|
+
spec.add_development_dependency "rake" , ">= 13.0"
|
40
|
+
spec.add_development_dependency "rspec", ">= 3.0"
|
41
|
+
spec.add_development_dependency 'rspec-debug', ">= 0.2.0"
|
42
|
+
spec.add_development_dependency "rubocop", ">= 1.21"
|
43
|
+
|
44
|
+
spec.add_development_dependency 'codecov','>= 0.6.0'
|
45
|
+
spec.add_development_dependency 'dotenv','>= 2.8.1'
|
46
|
+
spec.add_development_dependency 'simplecov', '>= 0.15', '< 0.22'
|
47
|
+
spec.add_development_dependency 'yard', '>= 0.9.34'
|
48
|
+
spec.add_development_dependency 'webrick', ">= 1.3.1"
|
49
|
+
|
50
|
+
|
51
|
+
# For more information and examples about making a new gem, check out our
|
52
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
53
|
+
end
|
data/tmp/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*
|
metadata
CHANGED
@@ -1,62 +1,166 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nodepile
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Foster
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: ruby-graphviz
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.2.5
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.2.5
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
18
32
|
- !ruby/object:Gem::Version
|
19
33
|
version: '13.0'
|
20
34
|
type: :development
|
21
35
|
prerelease: false
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
23
37
|
requirements:
|
24
|
-
- - "
|
38
|
+
- - ">="
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: '13.0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rspec
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
|
-
- - "
|
45
|
+
- - ">="
|
32
46
|
- !ruby/object:Gem::Version
|
33
47
|
version: '3.0'
|
34
48
|
type: :development
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
|
-
- - "
|
52
|
+
- - ">="
|
39
53
|
- !ruby/object:Gem::Version
|
40
54
|
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-debug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.2.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.2.0
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
70
|
name: rubocop
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
44
72
|
requirements:
|
45
|
-
- - "
|
73
|
+
- - ">="
|
46
74
|
- !ruby/object:Gem::Version
|
47
75
|
version: '1.21'
|
48
76
|
type: :development
|
49
77
|
prerelease: false
|
50
78
|
version_requirements: !ruby/object:Gem::Requirement
|
51
79
|
requirements:
|
52
|
-
- - "
|
80
|
+
- - ">="
|
53
81
|
- !ruby/object:Gem::Version
|
54
82
|
version: '1.21'
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: codecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.6.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.6.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: dotenv
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 2.8.1
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 2.8.1
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.15'
|
118
|
+
- - "<"
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0.22'
|
121
|
+
type: :development
|
122
|
+
prerelease: false
|
123
|
+
version_requirements: !ruby/object:Gem::Requirement
|
124
|
+
requirements:
|
125
|
+
- - ">="
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0.15'
|
128
|
+
- - "<"
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0.22'
|
131
|
+
- !ruby/object:Gem::Dependency
|
132
|
+
name: yard
|
133
|
+
requirement: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: 0.9.34
|
138
|
+
type: :development
|
139
|
+
prerelease: false
|
140
|
+
version_requirements: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: 0.9.34
|
145
|
+
- !ruby/object:Gem::Dependency
|
146
|
+
name: webrick
|
147
|
+
requirement: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: 1.3.1
|
152
|
+
type: :development
|
153
|
+
prerelease: false
|
154
|
+
version_requirements: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: 1.3.1
|
159
|
+
description: NOT WORKING CODE. Designed to make it easy to take a spreadsheet or other
|
160
|
+
tabular data and use it to generate, manipulate, and style connected graphs. Can
|
161
|
+
be used from the command line or as an object model. This gem does not itself perform
|
162
|
+
any rendering, although it contains facilities to drive the Graphviz familly of
|
163
|
+
open source tools to generate images (e.g. gif, jpg, svm).
|
60
164
|
email:
|
61
165
|
- david.t.foster.01@gmail.com
|
62
166
|
executables: []
|
@@ -65,20 +169,32 @@ extra_rdoc_files: []
|
|
65
169
|
files:
|
66
170
|
- ".rspec"
|
67
171
|
- ".rubocop.yml"
|
172
|
+
- BACKLOG.md
|
68
173
|
- CHANGELOG.md
|
69
174
|
- Gemfile
|
70
175
|
- README.md
|
71
176
|
- Rakefile
|
72
177
|
- lib/nodepile.rb
|
178
|
+
- lib/nodepile/base_structs.rb
|
179
|
+
- lib/nodepile/colspecs.rb
|
180
|
+
- lib/nodepile/gross_actions.rb
|
181
|
+
- lib/nodepile/gviz.rb
|
182
|
+
- lib/nodepile/keyed_array.rb
|
183
|
+
- lib/nodepile/pile_organizer.rb
|
184
|
+
- lib/nodepile/pragmas.rb
|
185
|
+
- lib/nodepile/rec_source.rb
|
186
|
+
- lib/nodepile/rule_eval.rb
|
73
187
|
- lib/nodepile/version.rb
|
188
|
+
- nodepile.gemspec
|
74
189
|
- sig/nodepile.rbs
|
190
|
+
- tmp/.gitignore
|
75
191
|
homepage: https://rubygems.org/gems/nodepile
|
76
192
|
licenses:
|
77
193
|
- MIT
|
78
194
|
metadata:
|
79
195
|
homepage_uri: https://rubygems.org/gems/nodepile
|
80
196
|
source_code_uri: https://gitlab.com/fosterd42/nodepile.git
|
81
|
-
post_install_message:
|
197
|
+
post_install_message:
|
82
198
|
rdoc_options: []
|
83
199
|
require_paths:
|
84
200
|
- lib
|
@@ -86,15 +202,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
202
|
requirements:
|
87
203
|
- - ">="
|
88
204
|
- !ruby/object:Gem::Version
|
89
|
-
version: 2.
|
205
|
+
version: 3.2.2
|
90
206
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
207
|
requirements:
|
92
208
|
- - ">="
|
93
209
|
- !ruby/object:Gem::Version
|
94
210
|
version: '0'
|
95
211
|
requirements: []
|
96
|
-
rubygems_version: 3.4.
|
97
|
-
signing_key:
|
212
|
+
rubygems_version: 3.4.10
|
213
|
+
signing_key:
|
98
214
|
specification_version: 4
|
99
|
-
summary: Translate flat text file data into connected graph specs suitable for graphviz.
|
215
|
+
summary: Translate flat text file data into connected graph specs suitable for graphviz. NOT
|
216
|
+
WORKING CODE.
|
100
217
|
test_files: []
|