rulebow 0.4.0
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 +7 -0
- data/.index +61 -0
- data/.yardopts +10 -0
- data/HISTORY.md +62 -0
- data/LICENSE.txt +25 -0
- data/README.md +168 -0
- data/bin/bow +4 -0
- data/demo/03_runner/01_applying_rules.md +51 -0
- data/demo/applique/ae.rb +1 -0
- data/demo/applique/rulebow.rb +11 -0
- data/demo/overview.md +0 -0
- data/lib/rulebow.rb +21 -0
- data/lib/rulebow.yml +61 -0
- data/lib/rulebow/cli.rb +181 -0
- data/lib/rulebow/core_ext.rb +3 -0
- data/lib/rulebow/core_ext/boolean.rb +10 -0
- data/lib/rulebow/core_ext/cli.rb +56 -0
- data/lib/rulebow/core_ext/true_class.rb +61 -0
- data/lib/rulebow/digest.rb +240 -0
- data/lib/rulebow/fact.rb +118 -0
- data/lib/rulebow/ignore.rb +136 -0
- data/lib/rulebow/match.rb +26 -0
- data/lib/rulebow/rule.rb +63 -0
- data/lib/rulebow/ruleset.rb +308 -0
- data/lib/rulebow/runner.rb +445 -0
- data/lib/rulebow/shellutils.rb +84 -0
- data/lib/rulebow/system.rb +153 -0
- data/lib/rulebow/watchlist.rb +203 -0
- data/man/.gitignore +2 -0
- data/man/ergo.1.ronn +50 -0
- metadata +151 -0
data/lib/rulebow/fact.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
module Rulebow
|
2
|
+
|
3
|
+
##
|
4
|
+
# Rulebow's logic system is a *set logic* system. That means an empty set, `[]`
|
5
|
+
# is treated as `false` and a non-empty set is `true`.
|
6
|
+
#
|
7
|
+
# Rulebow handles complex logic by building-up lazy logic constructs. It's logical
|
8
|
+
# operators are defined using single charcter symbols, e.g. `&` and `|`.
|
9
|
+
#
|
10
|
+
class Fact
|
11
|
+
#
|
12
|
+
def initialize(&procedure)
|
13
|
+
@procedure = procedure
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
def call(digest)
|
18
|
+
set @procedure.call(digest)
|
19
|
+
end
|
20
|
+
|
21
|
+
# set or
|
22
|
+
def |(other)
|
23
|
+
Fact.new{ |d| set(self.call(d)) | set(other.call(d)) }
|
24
|
+
end
|
25
|
+
|
26
|
+
# set and
|
27
|
+
def &(other)
|
28
|
+
Fact.new{ |d| set(self.call(d)) & set(other.call(d)) }
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
#
|
34
|
+
def set(value)
|
35
|
+
case value
|
36
|
+
when Array
|
37
|
+
value.compact
|
38
|
+
when Boolean
|
39
|
+
value ? true : []
|
40
|
+
else
|
41
|
+
[value]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# This subclass of Fact is specialized for file change conditions.
|
49
|
+
#
|
50
|
+
class FileFact < Fact
|
51
|
+
# Initialize new instance of FileFact.
|
52
|
+
#
|
53
|
+
# pattern - File glob or regular expression. [String,Regexp]
|
54
|
+
# digest - The system digest. [Digest]
|
55
|
+
#
|
56
|
+
def initialize(pattern, &coerce)
|
57
|
+
@pattern = pattern
|
58
|
+
@coerce = coerce
|
59
|
+
end
|
60
|
+
|
61
|
+
# File glob or regular expression.
|
62
|
+
attr :pattern
|
63
|
+
|
64
|
+
#
|
65
|
+
attr :coerce
|
66
|
+
|
67
|
+
# Process logic.
|
68
|
+
def call(digest)
|
69
|
+
result = []
|
70
|
+
|
71
|
+
case pattern
|
72
|
+
when Regexp
|
73
|
+
list = Dir.glob('**/*', File::FNM_PATHNAME)
|
74
|
+
list = digest.filter(list) # excludes ignored files (odd way to do it but if should work)
|
75
|
+
list.each do |fname|
|
76
|
+
if md = pattern.match(fname)
|
77
|
+
if digest.current[fname] != digest.saved[fname]
|
78
|
+
result << Match.new(fname, md)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
# NOTE: The problem with using the digest list, is that if a rule
|
83
|
+
# adds a new file to the project, then a subsequent rule needs
|
84
|
+
# to be able to see it.
|
85
|
+
#@digest.current.keys.each do |fname|
|
86
|
+
# if md = pattern.match(fname)
|
87
|
+
# if @digest.current[fname] != @digest.saved[fname]
|
88
|
+
# result << Match.new(fname, md)
|
89
|
+
# end
|
90
|
+
# end
|
91
|
+
#end
|
92
|
+
else
|
93
|
+
list = Dir.glob(pattern, File::FNM_PATHNAME)
|
94
|
+
list = digest.filter(list) # excludes ignored files (odd way to do it but if should work)
|
95
|
+
list.each do |fname|
|
96
|
+
if digest.current[fname] != digest.saved[fname]
|
97
|
+
result << fname
|
98
|
+
end
|
99
|
+
end
|
100
|
+
#@digest.current.keys.each do |fname|
|
101
|
+
# if md = File.fnmatch?(pattern, fname, File::FNM_PATHNAME | File::FNM_EXTGLOB)
|
102
|
+
# if @digest.current[fname] != @digest.saved[fname]
|
103
|
+
# result << Match.new(fname, md)
|
104
|
+
# end
|
105
|
+
# end
|
106
|
+
#end
|
107
|
+
end
|
108
|
+
|
109
|
+
if coerce
|
110
|
+
return [coerce.call(result)].flatten.compact
|
111
|
+
else
|
112
|
+
return result
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module Rulebow
|
2
|
+
|
3
|
+
##
|
4
|
+
# Deprecated: Encapsulates list of file globs to be ignored.
|
5
|
+
#
|
6
|
+
class Ignore
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
# Initialize new instance of Ignore.
|
10
|
+
#
|
11
|
+
# Returns nothing.
|
12
|
+
def initialize(ignore)
|
13
|
+
@ignore = ignore.to_a
|
14
|
+
end
|
15
|
+
|
16
|
+
# Filter a list of files in accordance with the
|
17
|
+
# ignore list.
|
18
|
+
#
|
19
|
+
# files - The list of files. [Array<String>]
|
20
|
+
#
|
21
|
+
# Returns [Array<String>]
|
22
|
+
def filter(files)
|
23
|
+
list = []
|
24
|
+
files.each do |file|
|
25
|
+
hit = any? do |pattern|
|
26
|
+
match?(pattern, file)
|
27
|
+
end
|
28
|
+
list << file unless hit
|
29
|
+
end
|
30
|
+
list
|
31
|
+
end
|
32
|
+
|
33
|
+
# Ignore file.
|
34
|
+
#def file
|
35
|
+
# @file ||= (
|
36
|
+
# Dir["{.gitignore,.hgignore}"].first
|
37
|
+
# )
|
38
|
+
#end
|
39
|
+
|
40
|
+
#
|
41
|
+
def each
|
42
|
+
to_a.each{ |g| yield g }
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
def size
|
47
|
+
to_a.size
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
def to_a
|
52
|
+
@ignore #||= load_ignore
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
def replace(*globs)
|
57
|
+
@ignore = globs.flatten
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
def concat(*globs)
|
62
|
+
@ignore.concat(globs.flatten)
|
63
|
+
end
|
64
|
+
|
65
|
+
#private
|
66
|
+
|
67
|
+
#def all_ignored_files
|
68
|
+
# list = []
|
69
|
+
# ignore.each do |glob|
|
70
|
+
# if glob.start_with?('/')
|
71
|
+
# list.concat Dir[File.join(@root, glob)]
|
72
|
+
# else
|
73
|
+
# list.concat Dir[File.join(@root, '**', glob)]
|
74
|
+
# end
|
75
|
+
# end
|
76
|
+
# list
|
77
|
+
#end
|
78
|
+
|
79
|
+
=begin
|
80
|
+
# Load ignore file. Removes blank lines and line starting with `#`.
|
81
|
+
#
|
82
|
+
# Returns [Array<String>]
|
83
|
+
def load_ignore
|
84
|
+
f = file
|
85
|
+
i = []
|
86
|
+
if f && File.exist?(f)
|
87
|
+
File.read(f).lines.each do |line|
|
88
|
+
glob = line.strip
|
89
|
+
next if glob.empty?
|
90
|
+
next if glob.start_with?('#')
|
91
|
+
i << glob
|
92
|
+
end
|
93
|
+
end
|
94
|
+
i
|
95
|
+
end
|
96
|
+
=end
|
97
|
+
|
98
|
+
# Given a pattern and a file, does the file match the
|
99
|
+
# pattern? This code is based on the rules used by
|
100
|
+
# git's .gitignore file.
|
101
|
+
#
|
102
|
+
# TODO: The code is probably not quite right.
|
103
|
+
#
|
104
|
+
# Returns [Boolean]
|
105
|
+
def match?(pattern, file)
|
106
|
+
if pattern.start_with?('!')
|
107
|
+
return !match?(pattern.sub('!','').strip)
|
108
|
+
end
|
109
|
+
|
110
|
+
dir = pattern.end_with?('/')
|
111
|
+
pattern = pattern.chomp('/') if dir
|
112
|
+
|
113
|
+
if pattern.start_with?('/')
|
114
|
+
fnmatch?(pattern.sub('/',''), file)
|
115
|
+
else
|
116
|
+
if dir
|
117
|
+
fnmatch?(File.join(pattern, '**', '*'), file) ||
|
118
|
+
fnmatch?(pattern, file) && File.directory?(file)
|
119
|
+
elsif pattern.include?('/')
|
120
|
+
fnmatch?(pattern, file)
|
121
|
+
else
|
122
|
+
fnmatch?(File.join('**',pattern), file)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Shortcut to `File.fnmatch?` method.
|
128
|
+
#
|
129
|
+
# Returns [Boolean]
|
130
|
+
def fnmatch?(pattern, file, mode=File::FNM_PATHNAME)
|
131
|
+
File.fnmatch?(pattern, file, File::FNM_PATHNAME)
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Rulebow
|
2
|
+
|
3
|
+
# Match is a subclass of a string that also stores the
|
4
|
+
# MatchData then matched against it in a Regexp comparison.
|
5
|
+
#
|
6
|
+
class Match < String
|
7
|
+
# Initialize a new instance of Match.
|
8
|
+
#
|
9
|
+
# string - The string. [String]
|
10
|
+
# matchdata - The match data. [MatchData]
|
11
|
+
#
|
12
|
+
def initialize(string, matchdata)
|
13
|
+
replace(string)
|
14
|
+
@matchdata = matchdata
|
15
|
+
end
|
16
|
+
|
17
|
+
# The match data that resulted from
|
18
|
+
# a successful Regexp against the string.
|
19
|
+
#
|
20
|
+
# Returns [MatchData]
|
21
|
+
def matchdata
|
22
|
+
@matchdata
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/lib/rulebow/rule.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
module Rulebow
|
2
|
+
|
3
|
+
# Rule class encapsulates a *rule* definition.
|
4
|
+
#
|
5
|
+
class Rule
|
6
|
+
# Initialize new instanance of Rule.
|
7
|
+
#
|
8
|
+
# fact - Conditional fact. [Fact,Boolean]
|
9
|
+
# action - Procedure to run if logic condition is met. [Proc]
|
10
|
+
#
|
11
|
+
def initialize(fact, options={}, &action)
|
12
|
+
@fact = fact
|
13
|
+
@action = action
|
14
|
+
end
|
15
|
+
|
16
|
+
# Access to the rule's logic condition. [Fact]
|
17
|
+
attr :fact
|
18
|
+
|
19
|
+
# Access to the rule's action procedure.
|
20
|
+
#
|
21
|
+
# Returns [Proc]
|
22
|
+
def to_proc
|
23
|
+
@action
|
24
|
+
end
|
25
|
+
|
26
|
+
# Apply rule, running the rule's procedure if the fact is true.
|
27
|
+
#
|
28
|
+
# Returns nothing.
|
29
|
+
def apply(digest)
|
30
|
+
case fact
|
31
|
+
when false, nil
|
32
|
+
when true
|
33
|
+
call()
|
34
|
+
else
|
35
|
+
result_set = fact.call(digest)
|
36
|
+
if result_set && !result_set.empty?
|
37
|
+
call(result_set)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Alias for #apply.
|
43
|
+
alias :invoke :apply
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
# Run rule procedure.
|
48
|
+
#
|
49
|
+
# result_set - The result set returned by the logic condition.
|
50
|
+
#
|
51
|
+
# Returns whatever the procedure returns. [Object]
|
52
|
+
def call(*result_set)
|
53
|
+
if @action.arity == 0
|
54
|
+
@action.call
|
55
|
+
else
|
56
|
+
#@action.call(session, *result_set)
|
57
|
+
@action.call(*result_set)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,308 @@
|
|
1
|
+
module Rulebow
|
2
|
+
|
3
|
+
##
|
4
|
+
# Rulesets provides namespace isolation for facts, rules and methods.
|
5
|
+
#
|
6
|
+
class Ruleset < Module
|
7
|
+
|
8
|
+
# Instantiate new ruleset.
|
9
|
+
#
|
10
|
+
# Arguments
|
11
|
+
# system - The system to which this ruleset belongs. [System]
|
12
|
+
# name - Name of the ruleset.
|
13
|
+
#
|
14
|
+
# Yields the script defining the ruleset rules.
|
15
|
+
def initialize(system, name, &block)
|
16
|
+
extend ShellUtils
|
17
|
+
extend system
|
18
|
+
extend self
|
19
|
+
|
20
|
+
@scripts = []
|
21
|
+
@rules = []
|
22
|
+
@docs = []
|
23
|
+
@requires = []
|
24
|
+
|
25
|
+
@name, @chain = parse_ruleset_name(name)
|
26
|
+
|
27
|
+
@session = system.session
|
28
|
+
|
29
|
+
@watchlist = WatchList.new(:ignore=>system.ignore)
|
30
|
+
|
31
|
+
module_eval(&block) if block
|
32
|
+
end
|
33
|
+
|
34
|
+
# Ruleset name
|
35
|
+
attr :name
|
36
|
+
|
37
|
+
# Description of ruleset.
|
38
|
+
attr :docs
|
39
|
+
|
40
|
+
# Chain or dependencies.
|
41
|
+
attr :chain
|
42
|
+
|
43
|
+
# Session object can be used to passing information around between rulesets.
|
44
|
+
attr :session
|
45
|
+
|
46
|
+
# Rule scripts.
|
47
|
+
attr :scripts
|
48
|
+
|
49
|
+
# Array of defined facts.
|
50
|
+
#attr :facts
|
51
|
+
|
52
|
+
# Array of defined rules.
|
53
|
+
attr :rules
|
54
|
+
|
55
|
+
# Files to watch for this ruleset.
|
56
|
+
attr :watchlist
|
57
|
+
|
58
|
+
# Import from another file, or glob of files, relative to project root.
|
59
|
+
#
|
60
|
+
# TODO: Should importing be relative to the importing file? Currently
|
61
|
+
# it is relative to the project root.
|
62
|
+
#
|
63
|
+
# Returns nothing.
|
64
|
+
def import(*globs)
|
65
|
+
globs.each do |glob|
|
66
|
+
#if File.relative?(glob)
|
67
|
+
# dir = Dir.pwd #session.root #File.dirname(caller[0])
|
68
|
+
# glob = File.join(dir, glob)
|
69
|
+
#end
|
70
|
+
Dir[glob].each do |file|
|
71
|
+
next unless File.file?(file) # add warning
|
72
|
+
next if @scripts.include?(file)
|
73
|
+
@scripts << file
|
74
|
+
module_eval(File.read(file), file)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Add paths to be watched.
|
80
|
+
#
|
81
|
+
# globs - List of file globs. [Array<String>]
|
82
|
+
#
|
83
|
+
# Returns [Array<String>]
|
84
|
+
def watch(*globs)
|
85
|
+
@watchlist.accept(globs) unless globs.empty?
|
86
|
+
@watchlist
|
87
|
+
end
|
88
|
+
|
89
|
+
# Replace paths to be watched.
|
90
|
+
#
|
91
|
+
# globs - List of file globs. [Array<String>]
|
92
|
+
#
|
93
|
+
# Returns [Array<String>]
|
94
|
+
def watch!(*globs)
|
95
|
+
@watchlist.accept!(globs)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Add paths to be ignored in file rules.
|
99
|
+
#
|
100
|
+
# globs - List of file globs. [Array<String>]
|
101
|
+
#
|
102
|
+
# Returns [Array<String>]
|
103
|
+
def ignore(*globs)
|
104
|
+
@watchlist.ignore(globs) unless globs.empty?
|
105
|
+
end
|
106
|
+
|
107
|
+
# Replace globs in ignore list.
|
108
|
+
#
|
109
|
+
# globs - List of file globs. [Array<String>]
|
110
|
+
#
|
111
|
+
# Returns [Array<String>]
|
112
|
+
def ignore!(*globs)
|
113
|
+
@watchlist.ignore!(globs)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Define a dependency chain.
|
117
|
+
#
|
118
|
+
# Returns [Array<Symbol>]
|
119
|
+
def chain=(*rulesets)
|
120
|
+
@chain = rulesets.map{ |b| b.to_sym }
|
121
|
+
end
|
122
|
+
|
123
|
+
# Provide a ruleset description.
|
124
|
+
#
|
125
|
+
# Returns [String]
|
126
|
+
def desc(description)
|
127
|
+
@docs << description
|
128
|
+
end
|
129
|
+
|
130
|
+
# Define a rule. Rules are procedures that are tiggered
|
131
|
+
# by logical facts.
|
132
|
+
#
|
133
|
+
# Examples
|
134
|
+
# rule :rdocs? do |files|
|
135
|
+
# sh "rdoc --output doc/rdoc " + files.join(" ")
|
136
|
+
# end
|
137
|
+
#
|
138
|
+
# TODO: Allow for an expression array that conjoins them with AND logic.
|
139
|
+
#
|
140
|
+
# Returns [Rule]
|
141
|
+
def rule(expression, &block)
|
142
|
+
case expression
|
143
|
+
when Hash
|
144
|
+
expression.each do |fact, task|
|
145
|
+
fact = define_fact(fact)
|
146
|
+
task = define_task(task)
|
147
|
+
@rules << Rule.new(fact, &task)
|
148
|
+
end
|
149
|
+
else
|
150
|
+
fact = define_fact(expression)
|
151
|
+
@rules << Rule.new(fact, &block)
|
152
|
+
end
|
153
|
+
|
154
|
+
#rule = Rule.new(@_facts, get_rule_options, &procedure)
|
155
|
+
#@rules << rule
|
156
|
+
#clear_rule_options
|
157
|
+
|
158
|
+
return @rules
|
159
|
+
end
|
160
|
+
|
161
|
+
# Defines a fact. Facts define conditions that are used to trigger
|
162
|
+
# rules. Named facts are defined as methods to ensure that only one fact
|
163
|
+
# is ever defined for a given name. Calling fact again with the same name
|
164
|
+
# as a previously defined fact will redefine the condition of that fact.
|
165
|
+
#
|
166
|
+
# Examples
|
167
|
+
# fact :no_doc? do
|
168
|
+
# File.directory?('doc')
|
169
|
+
# end
|
170
|
+
#
|
171
|
+
# Returns the name if name and condition is given. [Symbol]
|
172
|
+
# Returns a fact in only name or condition is given. [Fact]
|
173
|
+
def fact(name=nil, &condition)
|
174
|
+
if name && conditon
|
175
|
+
define_method(name) do
|
176
|
+
Fact.new(&condition) # TODO: maybe we don't really need the cache after all
|
177
|
+
end
|
178
|
+
name
|
179
|
+
else
|
180
|
+
define_fact(name || condition)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# Will probably be deprecated.
|
185
|
+
alias :state :fact
|
186
|
+
|
187
|
+
# Define a file fact.
|
188
|
+
#
|
189
|
+
# TODO: Probably will add this back & limit `fact` so it can't be used for file facts.
|
190
|
+
#
|
191
|
+
# Returns [FileFact]
|
192
|
+
#def file(patterns, &coerce)
|
193
|
+
# FileFact.new(patterns, &coerce)
|
194
|
+
#end
|
195
|
+
|
196
|
+
# Convenince method for defining environment variable facts.
|
197
|
+
#
|
198
|
+
# Examples
|
199
|
+
# rule env('PATH'=>/foo/) => :dosomething
|
200
|
+
#
|
201
|
+
# Returns [Fact]
|
202
|
+
def env(name_to_pattern)
|
203
|
+
Fact.new do
|
204
|
+
name_to_pattern.any? do |name, re|
|
205
|
+
re === ENV[name.to_s] # or `all?` instead?
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# TODO: Private rulesets that can't be run from the CLI?
|
211
|
+
# Hmmm... maybe instead it can work like rake, if docs is empty
|
212
|
+
# it can't be run from the command line.
|
213
|
+
#
|
214
|
+
#def privatize
|
215
|
+
# @privatized = true
|
216
|
+
#end
|
217
|
+
|
218
|
+
# Issue notification.
|
219
|
+
#
|
220
|
+
# Returns nothing.
|
221
|
+
def notify(message, options={})
|
222
|
+
title = options.delete(:title) || 'Rulebow Notification'
|
223
|
+
Notify.notify(title, message.to_s, options)
|
224
|
+
end
|
225
|
+
|
226
|
+
# Any requires made within a ruleset will not be actually
|
227
|
+
# required until a rule is run.
|
228
|
+
#
|
229
|
+
# TODO: This feature has yet to be implemented.
|
230
|
+
#
|
231
|
+
# Returns nothing.
|
232
|
+
def require(feature=nil)
|
233
|
+
@requires << feature if feature
|
234
|
+
@requires
|
235
|
+
end
|
236
|
+
|
237
|
+
# Better inspection string.
|
238
|
+
def inspect
|
239
|
+
if chain.empty?
|
240
|
+
"#<Ruleset #{name}>"
|
241
|
+
else
|
242
|
+
"#<Ruleset #{name} " + chain.join(' ') + ">"
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# TODO: Good idea?
|
247
|
+
def to_s
|
248
|
+
name.to_s
|
249
|
+
end
|
250
|
+
|
251
|
+
private
|
252
|
+
|
253
|
+
# Define a fact.
|
254
|
+
#
|
255
|
+
# Returns [Fact]
|
256
|
+
def define_fact(fact)
|
257
|
+
case fact
|
258
|
+
when Fact
|
259
|
+
fact
|
260
|
+
when String, Regexp
|
261
|
+
@watchlist.accept(fact)
|
262
|
+
FileFact.new(fact)
|
263
|
+
when Symbol
|
264
|
+
Fact.new{ send(fact) }
|
265
|
+
when true, false, nil
|
266
|
+
Fact.new{ fact }
|
267
|
+
else #when Proc
|
268
|
+
Fact.new(&fact)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
#
|
273
|
+
def define_task(task)
|
274
|
+
case task
|
275
|
+
when Symbol
|
276
|
+
Proc.new do |*a|
|
277
|
+
meth = method(task)
|
278
|
+
if meth.arity == 0
|
279
|
+
meth.call
|
280
|
+
else
|
281
|
+
meth.call(*a)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
else
|
285
|
+
task.to_proc
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# Parse out a ruleset's name from it's ruleset dependencies.
|
290
|
+
#
|
291
|
+
# name - ruleset name
|
292
|
+
#
|
293
|
+
# Returns [Array]
|
294
|
+
def parse_ruleset_name(name)
|
295
|
+
if Hash === name
|
296
|
+
raise ArgumentError if name.size > 1
|
297
|
+
list = [name.values].flatten.map{ |b| b.to_sym }
|
298
|
+
name = name.keys.first
|
299
|
+
else
|
300
|
+
list = []
|
301
|
+
end
|
302
|
+
return name.to_sym, list
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
end
|
307
|
+
|
308
|
+
|