loom-core 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|