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
@@ -0,0 +1,153 @@
|
|
1
|
+
module Rulebow
|
2
|
+
|
3
|
+
# Master system instance.
|
4
|
+
#
|
5
|
+
# Returns [System]
|
6
|
+
#def self.system
|
7
|
+
# @system ||= System.new
|
8
|
+
#end
|
9
|
+
|
10
|
+
##
|
11
|
+
#
|
12
|
+
class System < Module
|
13
|
+
|
14
|
+
RULEBOOK_GLOB = "{,.,_}{R,r}ulebook{,.rb}"
|
15
|
+
|
16
|
+
# Initialize new System instance.
|
17
|
+
#
|
18
|
+
def initialize(options={})
|
19
|
+
extend self
|
20
|
+
extend ShellUtils
|
21
|
+
|
22
|
+
@root = options[:root] || Dir.pwd
|
23
|
+
@ignore = options[:ignore] || [] #Ignore.new
|
24
|
+
|
25
|
+
@rulebook = options[:rulebook]
|
26
|
+
@state_file = options[:statefile]
|
27
|
+
|
28
|
+
@session = OpenStruct.new
|
29
|
+
|
30
|
+
@scripts = []
|
31
|
+
@rules = []
|
32
|
+
#@facts = []
|
33
|
+
|
34
|
+
@digests = {}
|
35
|
+
@rulesets = {}
|
36
|
+
|
37
|
+
import(*rulebook)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Project's root directory. [String]
|
41
|
+
attr :root
|
42
|
+
|
43
|
+
# Session variables. [Hash]
|
44
|
+
attr :session
|
45
|
+
|
46
|
+
# Digests [Hash]
|
47
|
+
attr :digests
|
48
|
+
|
49
|
+
# Rulesets indexed by name. [Hash]
|
50
|
+
attr :rulesets
|
51
|
+
|
52
|
+
# Rulebook file.
|
53
|
+
def rulebook
|
54
|
+
@rulebook ||= Dir[File.join(root, RULEBOOK_GLOB)].first
|
55
|
+
end
|
56
|
+
|
57
|
+
# State file.
|
58
|
+
def state_file
|
59
|
+
@state_file ||= rulebook.chomp('.rb') + '.state'
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
#def default(*rulesets)
|
64
|
+
# ruleset :default => rulesets
|
65
|
+
#end
|
66
|
+
|
67
|
+
# Rulesets provide a separate space for rules which are only
|
68
|
+
# run when the ruleset name is specifically given.
|
69
|
+
#
|
70
|
+
# Return [Ruleset]
|
71
|
+
def ruleset(name_and_chain, &block)
|
72
|
+
name, chain = parse_ruleset_name(name_and_chain)
|
73
|
+
if @rulesets.key?(name)
|
74
|
+
ruleset = @rulesets[name]
|
75
|
+
ruleset.update(chain, &block)
|
76
|
+
else
|
77
|
+
ruleset = Ruleset.new(self, name_and_chain, &block)
|
78
|
+
@rulesets[name] = ruleset
|
79
|
+
end
|
80
|
+
ruleset
|
81
|
+
end
|
82
|
+
|
83
|
+
# Import from another file, or glob of files, relative to project root.
|
84
|
+
#
|
85
|
+
# TODO: Should importing be relative to the importing file?
|
86
|
+
#
|
87
|
+
# Returns nothing.
|
88
|
+
def import(*globs)
|
89
|
+
globs.each do |glob|
|
90
|
+
#if File.relative?(glob)
|
91
|
+
# dir = Dir.pwd #session.root #File.dirname(caller[0])
|
92
|
+
# glob = File.join(dir, glob)
|
93
|
+
#end
|
94
|
+
Dir[glob].each do |file|
|
95
|
+
next unless File.file?(file) # add warning
|
96
|
+
next if @scripts.include?(file)
|
97
|
+
@scripts << file
|
98
|
+
module_eval(File.read(file), file)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Add paths to be ignored in file rules.
|
104
|
+
#
|
105
|
+
# globs - List of file globs. [Array<String>]
|
106
|
+
#
|
107
|
+
# Returns [Array<String>]
|
108
|
+
def ignore(*globs)
|
109
|
+
@ignore.concat(globs) unless globs.empty?
|
110
|
+
@ignore
|
111
|
+
end
|
112
|
+
|
113
|
+
# Replace globs in ignore list.
|
114
|
+
#
|
115
|
+
# globs - List of file globs. [Array<String>]
|
116
|
+
#
|
117
|
+
# Returns [Array<String>]
|
118
|
+
def ignore!(*globs)
|
119
|
+
@ignore.replace(globs)
|
120
|
+
@ignore
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
def inspect
|
125
|
+
"#<Rulebow::System>"
|
126
|
+
end
|
127
|
+
|
128
|
+
# Home directory.
|
129
|
+
#
|
130
|
+
# Returns [String]
|
131
|
+
def home
|
132
|
+
@home ||= File.expand_path('~')
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
# Parse out a ruleset's name from it's ruleset dependencies.
|
138
|
+
#
|
139
|
+
# Returns [Array]
|
140
|
+
def parse_ruleset_name(name)
|
141
|
+
if Hash === name
|
142
|
+
raise ArgumentError if name.size > 1
|
143
|
+
list = [name.values].flatten.map{ |b| b.to_sym }
|
144
|
+
name = name.keys.first
|
145
|
+
else
|
146
|
+
list = []
|
147
|
+
end
|
148
|
+
return name.to_sym, list
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
module Rulebow
|
2
|
+
|
3
|
+
##
|
4
|
+
# Encapsulates list of file globs to be watched.
|
5
|
+
#
|
6
|
+
class WatchList
|
7
|
+
|
8
|
+
#include Enumerable
|
9
|
+
|
10
|
+
# Initialize new instance of Ignore.
|
11
|
+
#
|
12
|
+
# Returns nothing.
|
13
|
+
def initialize(options={})
|
14
|
+
@accept = options[:accept].to_a.flatten
|
15
|
+
@ignore = options[:ignore].to_a.flatten
|
16
|
+
end
|
17
|
+
|
18
|
+
# Project's root directory.
|
19
|
+
attr :root
|
20
|
+
|
21
|
+
# Filter a list of files in accordance with the
|
22
|
+
# accept and ignore lists.
|
23
|
+
#
|
24
|
+
def filter(files)
|
25
|
+
filter_ignore(filter_accept(files))
|
26
|
+
end
|
27
|
+
|
28
|
+
# Filter a list of files in accordance with the
|
29
|
+
# ignore list.
|
30
|
+
#
|
31
|
+
# files - The list of files. [Array<String>]
|
32
|
+
#
|
33
|
+
# Returns [Array<String>]
|
34
|
+
def filter_accept(files)
|
35
|
+
list = []
|
36
|
+
files.each do |file|
|
37
|
+
hit = @accept.any? do |pattern|
|
38
|
+
match?(pattern, file)
|
39
|
+
end
|
40
|
+
list << file if hit
|
41
|
+
end
|
42
|
+
list
|
43
|
+
end
|
44
|
+
|
45
|
+
# Filter a list of files in accordance with the
|
46
|
+
# ignore list.
|
47
|
+
#
|
48
|
+
# files - The list of files. [Array<String>]
|
49
|
+
#
|
50
|
+
# Returns [Array<String>]
|
51
|
+
def filter_ignore(files)
|
52
|
+
list = []
|
53
|
+
files.each do |file|
|
54
|
+
hit = @ignore.any? do |pattern|
|
55
|
+
match?(pattern, file)
|
56
|
+
end
|
57
|
+
list << file unless hit
|
58
|
+
end
|
59
|
+
list
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
#def each
|
64
|
+
# to_a.each{ |g| yield g }
|
65
|
+
#end
|
66
|
+
|
67
|
+
#
|
68
|
+
#def size
|
69
|
+
# to_a.size
|
70
|
+
#end
|
71
|
+
|
72
|
+
#
|
73
|
+
#def to_a
|
74
|
+
# @list
|
75
|
+
#end
|
76
|
+
|
77
|
+
#
|
78
|
+
def accept(*globs)
|
79
|
+
@accept.concat(globs.flatten)
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
def accept!(*globs)
|
84
|
+
@accept.replace(globs.flatten)
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
def ignore(*globs)
|
89
|
+
@ignore.concat(globs.flatten)
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
def ignore!(*globs)
|
94
|
+
@ignore.replace(globs.flatten)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Get a current digest.
|
98
|
+
#
|
99
|
+
# Returns digest. [Hash]
|
100
|
+
def digest(root=nil)
|
101
|
+
if root
|
102
|
+
Dir.chdir(root) do
|
103
|
+
read_digest
|
104
|
+
end
|
105
|
+
else
|
106
|
+
read_digest
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def read_digest
|
113
|
+
dig = {}
|
114
|
+
list = filter(Dir.glob('**/*', File::FNM_PATHNAME))
|
115
|
+
list.each do |path|
|
116
|
+
if File.directory?(path)
|
117
|
+
# TODO: how to handle directories as a whole?
|
118
|
+
elsif File.exist?(path)
|
119
|
+
dig[path] = checksum(path)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
dig
|
123
|
+
end
|
124
|
+
|
125
|
+
# Given a pattern and a file, does the file match the
|
126
|
+
# pattern? This code is based on the rules used by
|
127
|
+
# git's .gitignore file.
|
128
|
+
#
|
129
|
+
# TODO: The code is probably not quite right.
|
130
|
+
#
|
131
|
+
# TODO: Handle regular expressions.
|
132
|
+
#
|
133
|
+
# Returns [Boolean]
|
134
|
+
def match?(pattern, file)
|
135
|
+
if Regexp === pattern
|
136
|
+
return pattern.match(file) ? true : false
|
137
|
+
end
|
138
|
+
|
139
|
+
if pattern.start_with?('!')
|
140
|
+
return !match?(pattern.sub('!','').strip)
|
141
|
+
end
|
142
|
+
|
143
|
+
dir = pattern.end_with?('/')
|
144
|
+
pattern = pattern.chomp('/') if dir
|
145
|
+
|
146
|
+
if pattern.start_with?('/')
|
147
|
+
fnmatch?(pattern.sub('/',''), file)
|
148
|
+
else
|
149
|
+
if dir
|
150
|
+
fnmatch?(File.join(pattern, '**', '*'), file) ||
|
151
|
+
fnmatch?(pattern, file) && File.directory?(file)
|
152
|
+
elsif pattern.include?('/')
|
153
|
+
fnmatch?(pattern, file)
|
154
|
+
else
|
155
|
+
fnmatch?(File.join('**',pattern), file)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Shortcut to `File.fnmatch?` method.
|
161
|
+
#
|
162
|
+
# Returns [Boolean]
|
163
|
+
def fnmatch?(pattern, file, mode=File::FNM_PATHNAME)
|
164
|
+
File.fnmatch?(pattern, file, File::FNM_PATHNAME)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Compute the sha1 identifer for a file.
|
168
|
+
#
|
169
|
+
# file - path to a file
|
170
|
+
#
|
171
|
+
# Returns [String] SHA1 digest string.
|
172
|
+
def checksum(file)
|
173
|
+
sha = ::Digest::SHA1.new
|
174
|
+
File.open(file, 'r') do |fh|
|
175
|
+
fh.each_line do |l|
|
176
|
+
sha << l
|
177
|
+
end
|
178
|
+
end
|
179
|
+
sha.hexdigest
|
180
|
+
end
|
181
|
+
|
182
|
+
=begin
|
183
|
+
# Load ignore file. Removes blank lines and line starting with `#`.
|
184
|
+
#
|
185
|
+
# Returns [Array<String>]
|
186
|
+
def load_ignore
|
187
|
+
f = file
|
188
|
+
i = []
|
189
|
+
if f && File.exist?(f)
|
190
|
+
File.read(f).lines.each do |line|
|
191
|
+
glob = line.strip
|
192
|
+
next if glob.empty?
|
193
|
+
next if glob.start_with?('#')
|
194
|
+
i << glob
|
195
|
+
end
|
196
|
+
end
|
197
|
+
i
|
198
|
+
end
|
199
|
+
=end
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
data/man/.gitignore
ADDED
data/man/ergo.1.ronn
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
ergo(1) -- Ruby rules-based automated build tool
|
2
|
+
===============================================
|
3
|
+
|
4
|
+
## SYNOPSIS
|
5
|
+
|
6
|
+
`ergo`
|
7
|
+
`ergo` <bookmark> [<arg>...]<br>
|
8
|
+
`ergo` `-R`|`--rules`<br>
|
9
|
+
`ergo` `-h`|`--help`<br>
|
10
|
+
|
11
|
+
|
12
|
+
## DESCRIPTION
|
13
|
+
|
14
|
+
Ergo is an *autological* build tool, in that it is used to automate
|
15
|
+
build procedure via logical rules-based programming. The developer only
|
16
|
+
ever needs to run a single command, `ergo`, and the majority of maintenance
|
17
|
+
details of a project are handled. Ergo largely replaces the need of more
|
18
|
+
traditional task-based build tools, but it does not complete supplant them.
|
19
|
+
They reamin a useful complement for one-off manual build procedures.
|
20
|
+
|
21
|
+
|
22
|
+
## OPTIONS
|
23
|
+
|
24
|
+
These options act as subcommands:
|
25
|
+
|
26
|
+
* `-R`, `--rules`:
|
27
|
+
Display the described rules defined in the project's ergo script(s).
|
28
|
+
|
29
|
+
* `-h`, `--help`:
|
30
|
+
Display this help message.
|
31
|
+
|
32
|
+
|
33
|
+
## ISSUES
|
34
|
+
|
35
|
+
**Ergo** depends on **Ronn** to provide this man-page and command help message.
|
36
|
+
Ronn depends on hpricot and rdiscount, extension libraries that are non-trivial
|
37
|
+
to install on some systems. A more portable version of this program would be
|
38
|
+
welcome.
|
39
|
+
|
40
|
+
|
41
|
+
## COPYRIGHT
|
42
|
+
|
43
|
+
Ergo is Copyright (c) 2009 Thomas Sawyer, Rubyworks
|
44
|
+
|
45
|
+
Ergo is distributed in accordance to the **GPL-3** license.
|
46
|
+
|
47
|
+
|
48
|
+
## SEE ALSO
|
49
|
+
|
50
|
+
ronn(1), rake(1)
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rulebow
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- trans
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: notify
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: detroit
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: mast
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: qed
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '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'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: ae
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Rulebow is an automated build tool with a set-logic based rule system.
|
84
|
+
Rulebow is the perfect tool for performing continuous integration during development.
|
85
|
+
email:
|
86
|
+
- transfire@gmail.com
|
87
|
+
executables:
|
88
|
+
- bow
|
89
|
+
extensions: []
|
90
|
+
extra_rdoc_files:
|
91
|
+
- LICENSE.txt
|
92
|
+
- README.md
|
93
|
+
- HISTORY.md
|
94
|
+
files:
|
95
|
+
- .index
|
96
|
+
- .yardopts
|
97
|
+
- bin/bow
|
98
|
+
- demo/03_runner/01_applying_rules.md
|
99
|
+
- demo/applique/ae.rb
|
100
|
+
- demo/applique/rulebow.rb
|
101
|
+
- demo/overview.md
|
102
|
+
- lib/rulebow/cli.rb
|
103
|
+
- lib/rulebow/core_ext/boolean.rb
|
104
|
+
- lib/rulebow/core_ext/cli.rb
|
105
|
+
- lib/rulebow/core_ext/true_class.rb
|
106
|
+
- lib/rulebow/core_ext.rb
|
107
|
+
- lib/rulebow/digest.rb
|
108
|
+
- lib/rulebow/fact.rb
|
109
|
+
- lib/rulebow/ignore.rb
|
110
|
+
- lib/rulebow/match.rb
|
111
|
+
- lib/rulebow/rule.rb
|
112
|
+
- lib/rulebow/ruleset.rb
|
113
|
+
- lib/rulebow/runner.rb
|
114
|
+
- lib/rulebow/shellutils.rb
|
115
|
+
- lib/rulebow/system.rb
|
116
|
+
- lib/rulebow/watchlist.rb
|
117
|
+
- lib/rulebow.rb
|
118
|
+
- lib/rulebow.yml
|
119
|
+
- man/.gitignore
|
120
|
+
- man/ergo.1
|
121
|
+
- man/ergo.1.html
|
122
|
+
- man/ergo.1.ronn
|
123
|
+
- README.md
|
124
|
+
- HISTORY.md
|
125
|
+
- LICENSE.txt
|
126
|
+
homepage: http://rubyworks.github.com/rulebow
|
127
|
+
licenses:
|
128
|
+
- BSD-2-Clause
|
129
|
+
metadata: {}
|
130
|
+
post_install_message:
|
131
|
+
rdoc_options: []
|
132
|
+
require_paths:
|
133
|
+
- lib
|
134
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - '>='
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
requirements: []
|
145
|
+
rubyforge_project:
|
146
|
+
rubygems_version: 2.0.3
|
147
|
+
signing_key:
|
148
|
+
specification_version: 4
|
149
|
+
summary: An autological build tool.
|
150
|
+
test_files: []
|
151
|
+
has_rdoc:
|