loom-core 0.0.6 → 0.0.7
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/.rubocop.yml +31 -0
- data/Gemfile.lock +19 -1
- data/Rakefile +2 -0
- data/bin/loom +3 -0
- data/docs/architecture.jpg +0 -0
- data/gentags.sh +2 -0
- data/lib/loom/all.rb +1 -1
- data/lib/loom/config.rb +1 -1
- data/lib/loom/mods/mod_loader.rb +7 -2
- data/lib/loom/pattern/all.rb +1 -0
- data/lib/loom/pattern/definition_context.rb +4 -4
- data/lib/loom/pattern/dsl.rb +176 -119
- data/lib/loom/pattern/expanding_reference.rb +88 -6
- data/lib/loom/pattern/loader.rb +1 -17
- data/lib/loom/pattern/pattern.rb +52 -0
- data/lib/loom/pattern/reference.rb +17 -13
- data/lib/loom/pattern/reference_set.rb +71 -50
- data/lib/loom/runner.rb +46 -33
- data/lib/loom/runner/all.rb +2 -0
- data/lib/loom/runner/execution_context.rb +9 -0
- data/lib/loom/{dsl.rb → runner/sshkit_connector.rb} +5 -7
- data/lib/loom/shell.rb +4 -2
- data/lib/loom/shell/core.rb +16 -16
- data/lib/loom/version.rb +1 -1
- data/lib/loomext/coremods/exec.rb +1 -0
- data/loom.TAGS +797 -0
- data/loom.gemspec +1 -0
- data/spec/.loom/error_handling.loom +1 -0
- data/spec/.loom/fail.loom +27 -13
- data/spec/.loom/files.loom +1 -0
- data/spec/.loom/inventory.yml +3 -0
- data/spec/.loom/net.loom +1 -0
- data/spec/.loom/pattern_context.loom +1 -0
- data/spec/.loom/pkg.loom +1 -0
- data/spec/.loom/shell.loom +1 -0
- data/spec/.loom/test.loom +17 -4
- data/spec/.loom/user.loom +1 -0
- data/spec/.loom/vms.loom +1 -0
- data/spec/loom/pattern/dsl_spec.rb +3 -2
- data/spec/shared/loom_internals_helper.rb +1 -1
- data/spec/test_loom_spec.rb +102 -42
- data/test +15 -0
- metadata +25 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c95f594a71ab9a9a776b8f83cceb04224d17d7527546237f019f018971be6ad
|
4
|
+
data.tar.gz: d105521e8666b8bc50b7fd504e13534c02d3c216f1d99bdf72ef484602a54efb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71bce721c3d6168173522d90e56988cf415daef5bf6dcb97033a4fac8d9fa9f7ae9ae422c27b2dcd31027de60f3935c02f8cd12926dffd251272698ffbfa5314
|
7
|
+
data.tar.gz: 3196d487c91a75cb327b4b2050fbdbce95c5c1e1ea0f8890429999e16baf6a5349f127b0579eea362395ffeda7a19ebac5bb03fe05d5fab08e332c29576161ab
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# https://rubocop.readthedocs.io/en/latest/cops_layout/
|
2
|
+
Layout/EmptyLineAfterGuardClause:
|
3
|
+
Enabled: false
|
4
|
+
Layout/EmptyLinesAroundModuleBody:
|
5
|
+
Enabled: false
|
6
|
+
Layout/EmptyLinesAroundClassBody:
|
7
|
+
Enabled: false
|
8
|
+
Layout/MultilineMethodCallBrace:
|
9
|
+
Enabled: false
|
10
|
+
Layout/MultilineMethodCallIndentation:
|
11
|
+
EnforcedStyle: indented
|
12
|
+
|
13
|
+
Lint/BooleanSymbol:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
# https://rubocop.readthedocs.io/en/latest/cops_metrics/
|
17
|
+
Metrics/LineLength:
|
18
|
+
Max: 100
|
19
|
+
|
20
|
+
Naming/HeredocDelimiterNaming:
|
21
|
+
Enable: false
|
22
|
+
|
23
|
+
# https://rubocop.readthedocs.io/en/latest/cops_style/
|
24
|
+
Style/Alias:
|
25
|
+
Enabled: false
|
26
|
+
Style/ClassAndModuleChildren:
|
27
|
+
Enabled: false
|
28
|
+
Style/Encoding:
|
29
|
+
Enabled: false
|
30
|
+
Sylte/IfUnlessModifier:
|
31
|
+
Enabled: false
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
loom-core (0.0.
|
4
|
+
loom-core (0.0.7)
|
5
5
|
bcrypt_pbkdf (>= 1.0, < 2.0)
|
6
6
|
commander (~> 4.4)
|
7
7
|
ed25519 (>= 1.0, < 2.0)
|
@@ -12,6 +12,7 @@ PATH
|
|
12
12
|
GEM
|
13
13
|
remote: https://rubygems.org/
|
14
14
|
specs:
|
15
|
+
ast (2.4.0)
|
15
16
|
bcrypt_pbkdf (1.0.0)
|
16
17
|
byebug (9.0.6)
|
17
18
|
coderay (1.1.1)
|
@@ -36,6 +37,7 @@ GEM
|
|
36
37
|
guard-compat (~> 1.1)
|
37
38
|
rspec (>= 2.99.0, < 4.0)
|
38
39
|
highline (1.7.10)
|
40
|
+
jaro_winkler (1.5.1)
|
39
41
|
listen (3.1.5)
|
40
42
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
41
43
|
rb-inotify (~> 0.9, >= 0.9.7)
|
@@ -49,6 +51,10 @@ GEM
|
|
49
51
|
notiffany (0.1.1)
|
50
52
|
nenv (~> 0.1)
|
51
53
|
shellany (~> 0.0)
|
54
|
+
parallel (1.12.1)
|
55
|
+
parser (2.5.1.2)
|
56
|
+
ast (~> 2.4.0)
|
57
|
+
powerpack (0.1.2)
|
52
58
|
pry (0.10.4)
|
53
59
|
coderay (~> 1.1.0)
|
54
60
|
method_source (~> 0.8.1)
|
@@ -56,6 +62,7 @@ GEM
|
|
56
62
|
pry-byebug (3.4.0)
|
57
63
|
byebug (~> 9.0)
|
58
64
|
pry (~> 0.10)
|
65
|
+
rainbow (3.0.0)
|
59
66
|
rake (11.3.0)
|
60
67
|
rb-fsevent (0.9.8)
|
61
68
|
rb-inotify (0.9.7)
|
@@ -77,6 +84,15 @@ GEM
|
|
77
84
|
diff-lcs (>= 1.2.0, < 2.0)
|
78
85
|
rspec-support (~> 3.5.0)
|
79
86
|
rspec-support (3.5.0)
|
87
|
+
rubocop (0.60.0)
|
88
|
+
jaro_winkler (~> 1.5.1)
|
89
|
+
parallel (~> 1.10)
|
90
|
+
parser (>= 2.5, != 2.5.1.1)
|
91
|
+
powerpack (~> 0.1)
|
92
|
+
rainbow (>= 2.2.2, < 4.0)
|
93
|
+
ruby-progressbar (~> 1.7)
|
94
|
+
unicode-display_width (~> 1.4.0)
|
95
|
+
ruby-progressbar (1.10.0)
|
80
96
|
ruby_dep (1.5.0)
|
81
97
|
shellany (0.0.1)
|
82
98
|
slop (3.6.0)
|
@@ -84,6 +100,7 @@ GEM
|
|
84
100
|
net-scp (>= 1.1.2)
|
85
101
|
net-ssh (>= 2.8.0)
|
86
102
|
thor (0.19.1)
|
103
|
+
unicode-display_width (1.4.0)
|
87
104
|
|
88
105
|
PLATFORMS
|
89
106
|
ruby
|
@@ -96,6 +113,7 @@ DEPENDENCIES
|
|
96
113
|
pry-byebug
|
97
114
|
rake (~> 11.3)
|
98
115
|
rspec (~> 3.5)
|
116
|
+
rubocop
|
99
117
|
|
100
118
|
BUNDLED WITH
|
101
119
|
1.16.2
|
data/Rakefile
CHANGED
data/bin/loom
CHANGED
@@ -158,6 +158,9 @@ EOS
|
|
158
158
|
end
|
159
159
|
end
|
160
160
|
end
|
161
|
+
# TODO: p and patterns produce different output on failure, p is a
|
162
|
+
# truncated stack. Figure out why, and fix that. This is going to be
|
163
|
+
# somewhere in Commander.
|
161
164
|
alias_command :"p", :"patterns"
|
162
165
|
|
163
166
|
command :"config" do |c|
|
Binary file
|
data/gentags.sh
ADDED
data/lib/loom/all.rb
CHANGED
@@ -9,12 +9,12 @@ require_relative "config"
|
|
9
9
|
|
10
10
|
require_relative "shell"
|
11
11
|
require_relative "host_spec"
|
12
|
-
require_relative "dsl"
|
13
12
|
|
14
13
|
require_relative "inventory"
|
15
14
|
require_relative "facts"
|
16
15
|
require_relative "pattern"
|
17
16
|
require_relative "mods"
|
17
|
+
require_relative "runner/all"
|
18
18
|
require_relative "runner"
|
19
19
|
|
20
20
|
require_relative "version"
|
data/lib/loom/config.rb
CHANGED
@@ -77,7 +77,7 @@ module Loom
|
|
77
77
|
class FileManager
|
78
78
|
|
79
79
|
def initialize(config)
|
80
|
-
@loom_search_paths = config.loom_search_paths
|
80
|
+
@loom_search_paths = [config.loom_search_paths].flatten
|
81
81
|
@loom_files = config.loom_files
|
82
82
|
@loom_file_patterns = config.loom_file_patterns
|
83
83
|
end
|
data/lib/loom/mods/mod_loader.rb
CHANGED
@@ -10,7 +10,12 @@ module Loom::Mods
|
|
10
10
|
@loom_config = loom_config
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
13
|
+
def load_mod_klass(mod_klass, shell)
|
14
|
+
verify_shell_cmds mod_klass, shell
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def verify_shell_cmds(mod_klass, shell)
|
14
19
|
Loom.log.debug2(self) { "verifying cmds for mod => #{mod_klass}" }
|
15
20
|
mod_klass.required_commands.each do |cmd|
|
16
21
|
begin
|
@@ -53,7 +58,7 @@ module Loom::Mods
|
|
53
58
|
Loom.log.debug3(self) do
|
54
59
|
"handling mod call => #{mod_klass}##{name} #{args} #{pattern_block}"
|
55
60
|
end
|
56
|
-
|
61
|
+
load_mod_klass mod_klass, shell
|
57
62
|
|
58
63
|
mod = mod_klass.new shell, @loom_config
|
59
64
|
mod.execute *args, &pattern_block
|
data/lib/loom/pattern/all.rb
CHANGED
@@ -8,11 +8,11 @@ module Loom::Pattern
|
|
8
8
|
|
9
9
|
NilLetValueError = Class.new Loom::LoomError
|
10
10
|
|
11
|
-
def initialize(
|
12
|
-
@fact_map =
|
13
|
-
@let_map =
|
11
|
+
def initialize(dsl_builder, parent_context=nil)
|
12
|
+
@fact_map = dsl_builder.facts.dup
|
13
|
+
@let_map = dsl_builder.let_map.dup
|
14
14
|
|
15
|
-
@hooks =
|
15
|
+
@hooks = dsl_builder.hooks.dup
|
16
16
|
@parent_context = parent_context
|
17
17
|
|
18
18
|
@merged_fact_map = merged_fact_map
|
data/lib/loom/pattern/dsl.rb
CHANGED
@@ -1,13 +1,53 @@
|
|
1
|
+
# IN Progress:
|
2
|
+
# [wip-patternbuilder]
|
3
|
+
# * Add a phase to the pattern execution sequence to collect calls to factset and loom
|
4
|
+
# objects. Results collected from this ""pre-execute"" can be analyzed for errors, optimization,
|
5
|
+
# success assertions, verification, etc. The loom file then executes in 2 passes, analyze &
|
6
|
+
# execute.
|
7
|
+
# -- pre-fact collection - inject the facts (or loom) object as a recorder
|
8
|
+
# -- (like a mock in record mode) # instead of the factual fact set. no need
|
9
|
+
# -- to change any loom files.
|
10
|
+
# -- only run fact providers which are accessed in the pattern set
|
11
|
+
# -- only load modules accessed in the pattern set
|
12
|
+
#
|
13
|
+
# * Replace Pattern "mods" and "mod specs" in 80 Loom::Pattern::ReferenceSet
|
14
|
+
# with usages of a builder instead. Currently the internal data model in
|
15
|
+
# ReferenceSet is confusing, but luckily it's the only client of pattern
|
16
|
+
# modules, that have used Loom::Pattern::DSL. Change calls to
|
17
|
+
# DSL#pattern/report/weave (anythin else that creates a pattern) to add a new
|
18
|
+
# PatternBuilder to the module. Use the builder to implement the TODO above
|
19
|
+
# ("Add a phase..."). Implement analysis on the builder.
|
20
|
+
# [master]
|
21
|
+
# * ... ongoing ... ways to test and +verify+ pattern execution
|
22
|
+
|
1
23
|
# TODO: DSL extensions:
|
2
|
-
# -
|
3
|
-
#
|
24
|
+
# - More Mods! .... ondeck:
|
25
|
+
# * bkblz
|
26
|
+
# * cassandra
|
27
|
+
# * apache (nginx?)
|
28
|
+
# * digital ocean
|
29
|
+
# * systemd-nspawj
|
30
|
+
|
31
|
+
# - Models Future:
|
32
|
+
# Notice each ondeck module above (bkblz, cassanadra, apache, digital ocean,
|
33
|
+
# system-nspawn): covers a unique infrastructure area, i.e.: bkblz:object and
|
34
|
+
# cold storage, cassandra:hyper-scale data storage, apache/nginx:content
|
35
|
+
# serving, digital-ocean/aws/gcp:remote virtual hosting, containers:local
|
36
|
+
# virtual hosting
|
37
|
+
# I could generalize each of the above infrastructure area into a high level
|
38
|
+
# mod. Should I? Maybe not.
|
39
|
+
|
40
|
+
# - auto mod documentation in the CLI
|
41
|
+
|
4
42
|
# - Pattern+non_idempotent+ marks a pattern as explicitly not idempotent, this
|
5
43
|
# let's additional warnings and checks to be added
|
44
|
+
|
6
45
|
# - A history module, store a log of each executed command, a hash of the .loom
|
7
46
|
# file, and the requisite facts (the let declarations) for each executed pattern
|
8
47
|
# on the host it executes. /var/log/loom/history? Create this log on startup.
|
9
48
|
# -- add a new set of history commands through the CLI and a history
|
10
49
|
# FactProvider exposing host/loom/pattern_slug execution stats.
|
50
|
+
|
11
51
|
# - Provide automatic command reversion support with a =Module= DSL that ties in
|
12
52
|
# with local revision history.
|
13
53
|
# -- allow Module actions/mods to define an "undo" command of itself given the
|
@@ -24,6 +64,7 @@
|
|
24
64
|
# accesses to fact_set be done in let expressions (enforce this maybe?)
|
25
65
|
# -- Later... before/after hooks can ensure the entire loom execution sequence
|
26
66
|
# was "revertable"
|
67
|
+
|
27
68
|
# - A mechanism to allow mods to register CLI flags and actions. Using an action predicate
|
28
69
|
# mechanisms can add flags at the global or action levels. All CLI flags set config values.
|
29
70
|
# -- Mods can also register for action namespaces similar to git. This is consistent with mod
|
@@ -31,33 +72,20 @@
|
|
31
72
|
# -- Best way is to migrate loom/mods/module and loom/mods/action_proxy into base classes of
|
32
73
|
# themselves. The isolate the shell specific behavior into a subclass of each to preserve the
|
33
74
|
# current behavior. A new "cli" module and "cli" action proxy would enable the implementation.
|
34
|
-
# - Add a phase to the pattern execution sequence to collect calls to factset and loom
|
35
|
-
# objects. Results collected from this ""pre-execute"" can be analyzed for errors, optimization,
|
36
|
-
# success assertions, verification, etc. The loom file then executes in 2 passes, analyze &
|
37
|
-
# execute.
|
38
|
-
# -- pre-fact collection - inject the facts (or loom) object as a recorder (like a mock in record mode)
|
39
|
-
# instead of the factual fact set. no need to change any loom files.
|
40
|
-
# -- only run fact providers which are accessed in the pattern set
|
41
|
-
# -- only load modules accessed in the pattern set
|
42
|
-
# - Replace Pattern "mods" and "mod specs" in Loom::Pattern::ReferenceSet with usages of a builder
|
43
|
-
# instead. Currently the internal data model in ReferenceSet is confusing, but luckily it's the
|
44
|
-
# only client of pattern modules, that have used Loom::Pattern::DSL. Change calls to
|
45
|
-
# DSL#pattern/report/weave (anythin else that creates a pattern) to add a new PatternBuilder to
|
46
|
-
# the module. Use the builder to implement the TODO above ("Add a phase..."). Implement analysis
|
47
|
-
# on the builder.
|
48
75
|
|
49
76
|
=begin
|
50
77
|
|
51
78
|
## .loom File DSL
|
52
79
|
|
53
80
|
See:
|
54
|
-
* spec
|
81
|
+
* spec/.loom/*.loom for a lots of valid .loom files.
|
82
|
+
* run these specs with `rspec spec/test_loom_spec.rb`
|
55
83
|
* spec/loom/pattern/dsl_spec.rb for other examples
|
56
84
|
|
57
85
|
|
58
|
-
I've tried to take inspriation from several ruby DSLs, including
|
59
|
-
|
60
|
-
|
86
|
+
I've tried to take inspriation from several ruby DSLs, including RSpec, Thor,
|
87
|
+
Commander, Sinatra... so hopefully it feels comfortable. Thank you to all of
|
88
|
+
those projects.
|
61
89
|
|
62
90
|
Loom::Pattern::DSL is the mixin that defines the declarative API for all .loom
|
63
91
|
file defined modules. It is included into Loom::Pattern by default. The outer
|
@@ -83,15 +111,19 @@ end
|
|
83
111
|
|
84
112
|
It declares a reference set with slugs:
|
85
113
|
|
86
|
-
*
|
114
|
+
* cmd
|
87
115
|
* outer:first
|
88
116
|
* outer:inner:second
|
89
117
|
|
90
|
-
Defining the same pattern slug twice raises a
|
118
|
+
Defining the same pattern slug twice raises a `DuplicatePatternRef` error.
|
91
119
|
|
92
120
|
#### Code Details
|
93
121
|
|
94
|
-
|
122
|
+
Loom::DSL is a facade available to all .loom file ::Modules that include
|
123
|
+
Loom::Pattern. It provides Module singleton methods listed at
|
124
|
+
Loom::DSL::DSL_METHODS.
|
125
|
+
|
126
|
+
Code path for .loom file loading:
|
95
127
|
|
96
128
|
Loom::Runner#load
|
97
129
|
-> Loom::Pattern::Loader.load
|
@@ -103,12 +135,48 @@ file. A ReferenseSet being a collection of references with uniquely named
|
|
103
135
|
slugs. The slug of a reference is computed from the module namespace and
|
104
136
|
instance method name.
|
105
137
|
|
138
|
+
### `weave`
|
139
|
+
|
140
|
+
The `weave` creates a specialized pattern, that allows aliasing a sequence of
|
141
|
+
pattern slugs as a single pattern name. Pattern execution will be flattened and
|
142
|
+
run sequentially before or after any other patterns in the `$ loom` invocation.
|
143
|
+
|
144
|
+
``` ~ruby
|
145
|
+
pattern :step_1 { ... }
|
146
|
+
pattern :step_2 { ... }
|
147
|
+
|
148
|
+
module OtherTasks
|
149
|
+
...
|
150
|
+
end
|
151
|
+
|
152
|
+
weave :do_it, [ :step_1, :step_2, :other_tasks:* ]
|
153
|
+
```
|
154
|
+
|
155
|
+
This creates pattern :do_it, which when run `$ loom do_it` will run :step_1,
|
156
|
+
:step_2, and all slugs that match /other_tasks.*/. Recursive expansion is
|
157
|
+
explicitly disallowed, only pattern names (not weaves), are allowed in the list
|
158
|
+
of weave pattern slugs.
|
159
|
+
|
160
|
+
#### Code Details
|
161
|
+
|
162
|
+
Weave expansion to pattern slugs is accomplished by creating a
|
163
|
+
Loom::Pattern::ExpandingReference via the Loom::Pattern::Loader+load+ path
|
164
|
+
invoked via Loom::Runner+load+. Expansion happens on read via
|
165
|
+
Loom::Pattern::Loader+patterns+, thus the list of patterns is constant
|
166
|
+
throughout all phases of pattern execution.
|
167
|
+
|
106
168
|
### `report`
|
107
169
|
|
108
|
-
Use `report` to create
|
109
|
-
a block to yaml, json, or any other format.
|
170
|
+
Use `report` to create another specialized pattern that prints a fact, value,
|
171
|
+
or result of a block to yaml, json, or any other format to STDOUT.
|
110
172
|
|
111
|
-
### `let`, `before`, and `after
|
173
|
+
### `let`, `before`, and `after`: for examples spec/.loom/parent_context.loom
|
174
|
+
|
175
|
+
`let`, does the same as in RSpec (including the context details). It creates an
|
176
|
+
alias for a value, available in all patterns.
|
177
|
+
|
178
|
+
`before` and `after` are similar to the same in RSpec. Each before/after block
|
179
|
+
is run before/after respectively to EACH pattern.
|
112
180
|
|
113
181
|
Module::Inner, from above, inherits all +let+ declarations from its outer
|
114
182
|
contexts, both :: (root) and ::Outer. +before+ hooks are run from a top-down
|
@@ -179,31 +247,6 @@ Loom::Facts::FactSet as parameters.
|
|
179
247
|
See Loom::Pattern::DefinitionContext for evaluation of `let` blocks and
|
180
248
|
before/after context ordering.
|
181
249
|
|
182
|
-
### `weave`
|
183
|
-
|
184
|
-
The `weave` keyword allows aliasing a sequence of patterns a single
|
185
|
-
name. Pattern execution will be flattened and run sequentially before or after
|
186
|
-
any other patterns in the `$ loom` invocation.
|
187
|
-
|
188
|
-
``` ~ruby
|
189
|
-
pattern :step_1 { ... }
|
190
|
-
pattern :step_2 { ... }
|
191
|
-
|
192
|
-
weave :do_it, [ :step_1, :step_2 ]
|
193
|
-
```
|
194
|
-
|
195
|
-
This creates pattern :do_it, which when run `$ loom do_it` will run :step_1,
|
196
|
-
:step_2. Recursive expansion is explicitly disallowed, only pattern names (not
|
197
|
-
weaves), are allowed in the 2nd param of `weave`.
|
198
|
-
|
199
|
-
#### Code Details
|
200
|
-
|
201
|
-
Weave expansion to pattern slugs is accomplished by creating a
|
202
|
-
Loom::Pattern::ExpandingReference via the Loom::Pattern::Loader+load+ path
|
203
|
-
invoked via Loom::Runner+load+. Expansion happens on read via
|
204
|
-
Loom::Pattern::Loader+patterns+, thus the list of patterns is constant
|
205
|
-
throughout all phases of pattern execution.
|
206
|
-
|
207
250
|
#### Pattern Execution Sequence
|
208
251
|
|
209
252
|
Once hosts and patterns are identified in earlier Loom::Runner phases,
|
@@ -250,14 +293,55 @@ module Loom::Pattern
|
|
250
293
|
|
251
294
|
PatternDefinitionError = Class.new Loom::LoomError
|
252
295
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
296
|
+
DSL_METHODS = [
|
297
|
+
:desc,
|
298
|
+
:description,
|
299
|
+
|
300
|
+
:pattern,
|
301
|
+
:weave,
|
302
|
+
:report,
|
303
|
+
|
304
|
+
:with_facts,
|
305
|
+
:let,
|
306
|
+
:before,
|
307
|
+
:after,
|
308
|
+
|
309
|
+
:namespace
|
310
|
+
]
|
311
|
+
|
312
|
+
##
|
313
|
+
# The Loom DSL definition. See documentation above.
|
259
314
|
module DSL
|
315
|
+
DSL_METHODS.each do |m|
|
316
|
+
define_method m do |*args, &block|
|
317
|
+
Loom.log.debug1(self) { "delegating Pattern::DSL call to DSLBuilder+#{m}+" }
|
318
|
+
@dsl_builder.send(m, *args, &block)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
attr_reader :dsl_builder
|
323
|
+
|
324
|
+
class << self
|
325
|
+
def extended(receiving_mod)
|
326
|
+
# NB: Using Forwardable was awkward here due to the scope of extended, and
|
327
|
+
# the scope of where the fordwardable instance variable would live.
|
328
|
+
dsl_builder = PatternBuilder.new
|
329
|
+
receiving_mod.instance_variable_set :@dsl_builder, dsl_builder
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
class DSL::PatternBuilder
|
335
|
+
|
336
|
+
def initialize
|
337
|
+
@pattern_map = {}
|
338
|
+
@fact_map = {}
|
339
|
+
@let_map = {}
|
340
|
+
@hooks = []
|
341
|
+
@next_description = nil
|
342
|
+
end
|
260
343
|
|
344
|
+
# BEGIN DSL Implementation
|
261
345
|
loom_accessor :namespace
|
262
346
|
|
263
347
|
def description(description)
|
@@ -266,37 +350,33 @@ module Loom::Pattern
|
|
266
350
|
alias_method :desc, :description
|
267
351
|
|
268
352
|
def with_facts(**new_facts, &block)
|
269
|
-
@
|
270
|
-
@
|
271
|
-
|
272
|
-
@facts = yield_result if yield_result.is_a? Hash
|
353
|
+
@fact_map.merge! new_facts
|
354
|
+
yield_result = yield @fact_map if block_given?
|
355
|
+
@fact_map = yield_result if yield_result.is_a? Hash
|
273
356
|
end
|
274
357
|
|
275
358
|
def let(name, default: nil, &block)
|
276
359
|
raise "malformed let expression: missing block" unless block_given?
|
277
|
-
|
278
|
-
@let_map ||= {}
|
279
360
|
@let_map[name.to_sym] = LetMapEntry.new default, &block
|
280
361
|
end
|
281
362
|
|
282
363
|
def pattern(name, &block)
|
283
|
-
|
284
|
-
define_pattern_internal(name, &block)
|
364
|
+
define_pattern_internal(name, kind: :pattern, &block)
|
285
365
|
end
|
286
366
|
|
287
367
|
##
|
288
368
|
# @param format[:yaml|:json|:raw] default is :yaml
|
289
369
|
def report(name, format: :yaml, &block)
|
290
|
-
|
291
|
-
|
292
|
-
#
|
370
|
+
define_pattern_internal(name, kind: :report) do |loom, facts|
|
371
|
+
# TODO: I don't like all of this logic here. It feels like it belongs in
|
372
|
+
# a mod.
|
293
373
|
result = if block_given?
|
294
374
|
Loom.log.debug(self) { "report[#{name}] from block" }
|
295
|
-
|
375
|
+
instance_exec(loom, facts, &block)
|
296
376
|
elsif !Loom::Facts.is_empty?(facts[name])
|
297
377
|
Loom.log.debug(self) { "report[#{name}] from facts[#{name}]" }
|
298
378
|
facts[name]
|
299
|
-
elsif
|
379
|
+
elsif respond_to?(name) && !self.send(name).nil?
|
300
380
|
Loom.log.debug(self) { "report[#{name}] from let{#{name}}" }
|
301
381
|
self.send name
|
302
382
|
else
|
@@ -318,15 +398,10 @@ module Loom::Pattern
|
|
318
398
|
end
|
319
399
|
|
320
400
|
def weave(name, pattern_slugs)
|
321
|
-
Loom.log.debug1(self) { "defining weave => #{name}" }
|
322
|
-
@weave_slugs ||= {}
|
323
|
-
@weave_slugs[name.to_sym] = pattern_slugs.map { |s| s.to_s }
|
324
|
-
|
325
401
|
unless @next_description
|
326
402
|
@next_description = "Weave runs patterns: %s" % pattern_slugs.join(", ")
|
327
403
|
end
|
328
|
-
|
329
|
-
define_pattern_internal(name) { |_, _| true }
|
404
|
+
define_pattern_internal(name, kind: :weave, expanded_slugs: pattern_slugs) { true }
|
330
405
|
end
|
331
406
|
|
332
407
|
def before(&block)
|
@@ -336,68 +411,50 @@ module Loom::Pattern
|
|
336
411
|
def after(&block)
|
337
412
|
hook :after, &block
|
338
413
|
end
|
414
|
+
# END DSL Implementation
|
339
415
|
|
340
|
-
def
|
341
|
-
@
|
342
|
-
end
|
343
|
-
|
344
|
-
def is_weave?(name)
|
345
|
-
!!weave_slugs[name]
|
346
|
-
end
|
347
|
-
|
348
|
-
def pattern_methods
|
349
|
-
@pattern_methods || []
|
350
|
-
end
|
351
|
-
|
352
|
-
def pattern_description(name)
|
353
|
-
@pattern_descriptions[name]
|
354
|
-
end
|
355
|
-
|
356
|
-
def pattern_method(name)
|
357
|
-
raise UnknownPatternMethod, name unless @pattern_method_map[name]
|
358
|
-
instance_method name
|
416
|
+
def patterns
|
417
|
+
@pattern_map.values
|
359
418
|
end
|
360
419
|
|
361
420
|
def hooks
|
362
|
-
@hooks
|
421
|
+
@hooks
|
363
422
|
end
|
364
423
|
|
365
424
|
def facts
|
366
|
-
@
|
425
|
+
@fact_map
|
367
426
|
end
|
368
427
|
|
369
428
|
def let_map
|
370
|
-
@let_map
|
429
|
+
@let_map
|
371
430
|
end
|
372
431
|
|
373
432
|
private
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
433
|
+
# TODO: Let mods introduce new pattern handlers. A pattern is effectively a
|
434
|
+
# named wrapper around a pattern execution block. This would be an advanced
|
435
|
+
# usage when before and after blocks aren't scalable. It could also provided
|
436
|
+
# additional filtering for pattern selection at weave time.
|
437
|
+
def define_pattern_internal(name, kind: :pattern, **kwargs, &loom_file_block)
|
438
|
+
unless block_given?
|
439
|
+
raise PatternDefinitionError, "missing block for pattern[#{name}]"
|
440
|
+
end
|
441
|
+
unless Pattern::KINDS[kind]
|
442
|
+
raise "unknown pattern kind: #{kind}"
|
443
|
+
end
|
380
444
|
|
381
|
-
|
382
|
-
|
383
|
-
|
445
|
+
desc = @next_description
|
446
|
+
unless desc.is_a?(String) || desc.nil?
|
447
|
+
raise PatternDefinitionError, "description must be a string: #{desc.class}"
|
448
|
+
end
|
384
449
|
@next_description = nil
|
450
|
+
name = name.intern
|
385
451
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
# loom.x :dostuff
|
390
|
-
# end
|
391
|
-
# ```
|
392
|
-
# Patterns declared in the .loom file are defined here:
|
393
|
-
define_method method_name do |loom, facts|
|
394
|
-
Loom.log.debug2(self) { "calling .loom file pattern: #{name}" }
|
395
|
-
self.instance_exec(loom, facts, &loom_file_block)
|
396
|
-
end
|
452
|
+
Loom.log.debug(self) { "defined .loom pattern[kind:#{kind}]: #{name}" }
|
453
|
+
@pattern_map[name] = Pattern.new(
|
454
|
+
name: name, description: desc, kind: kind, **kwargs, &loom_file_block)
|
397
455
|
end
|
398
456
|
|
399
457
|
def hook(scope, &block)
|
400
|
-
@hooks ||= []
|
401
458
|
@hooks << Hook.new(scope, &block)
|
402
459
|
end
|
403
460
|
end # DSL
|