rulebow 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|