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.
@@ -0,0 +1,181 @@
1
+ module Rulebow
2
+
3
+ ##
4
+ # Rulebow's command line interface.
5
+ #
6
+ class CLI
7
+
8
+ # Fire her up!
9
+ def self.fire!(argv=ARGV)
10
+ new(argv).fire!
11
+ end
12
+
13
+ # Initialize new instance of Rulebow::CLI.
14
+ # If `argv` is not provided than ARGV is used.
15
+ #
16
+ # argv - Command line argument. [Array<String>]
17
+ #
18
+ # Returns nothing.
19
+ def initialize(argv=ARGV)
20
+ #begin
21
+ # require 'dotopts'
22
+ #rescue LoadError
23
+ #end
24
+
25
+ @argv = Array(argv || ARGV)
26
+
27
+ @script = nil
28
+ @watch = nil
29
+ @fresh = false
30
+ end
31
+
32
+ # Returns runner instance. [Runner]
33
+ def runner
34
+ @runner ||= (
35
+ Runner.new(
36
+ :script => @script,
37
+ :fresh => @fresh,
38
+ :watch => @watch
39
+ )
40
+ )
41
+ end
42
+
43
+ # Execute command.
44
+ #
45
+ # command - Which command to execute.
46
+ #
47
+ # Returns nothing.
48
+ def fire!(argv=ARGV)
49
+ $DEBUG = argv.include?('--debug') || $DEBUG
50
+ return fire if $DEBUG
51
+ begin
52
+ fire
53
+ rescue => err
54
+ puts "#{$0}: error #{err}"
55
+ end
56
+ end
57
+
58
+ # Fire her up!
59
+ def fire
60
+ args = cli_parse
61
+
62
+ ensure_options(args)
63
+
64
+ #if args.first == 'init' && !runner.root?
65
+ # init_project(*args)
66
+ #end
67
+
68
+ case @command
69
+ when :list
70
+ print_rules(*args)
71
+ when :help
72
+ print_help(*args)
73
+ else
74
+ runner.run(args.first)
75
+ end
76
+ end
77
+
78
+ # Parse command line arguments with just the prettiest
79
+ # little CLI parser there ever was.
80
+ def cli_parse
81
+ @command = nil
82
+
83
+ cli @argv,
84
+ "-R --rules" => lambda{ @command = :list },
85
+ "-H --help" => lambda{ @command = :help },
86
+ "-a --auto" => method(:watch=),
87
+ "-f --fresh" => method(:fresh!),
88
+ "-s --script" => method(:script=),
89
+ "-D --debug" => method(:debug!)
90
+ end
91
+
92
+ #
93
+ def print_help(*names)
94
+ puts "-R --rules list ruleset descriptions"
95
+ puts "-H --help list these help options"
96
+ puts "-a --auto [TIME] autorun every so many seconds"
97
+ puts "-f --fresh clear digest for fresh run"
98
+ puts "-s --script [SCRIPT] use alternate script"
99
+ puts "-D --debug extra error information"
100
+ end
101
+
102
+ #
103
+ def ensure_options(args)
104
+ erropts = args.select{ |a| a.start_with?('-') }
105
+ unless erropts.empty?
106
+ raise "unsupported options #{erropts.join(' ')}"
107
+ end
108
+ end
109
+
110
+ # Shall we make a fresh start of it, and remove all digests?
111
+ #
112
+ # Returns [Boolean]
113
+ def fresh?
114
+ @fresh
115
+ end
116
+
117
+ # Set fresh flag to true.
118
+ #
119
+ # Returns [Boolean]
120
+ def fresh!
121
+ @fresh = true
122
+ end
123
+
124
+ # Is debug mode on?
125
+ #
126
+ # Returns [Boolean]
127
+ def debug?
128
+ $DEBUG
129
+ end
130
+
131
+ # Set debug flag to true.
132
+ #
133
+ # Returns [Boolean]
134
+ def debug!
135
+ $DEBUG = true
136
+ end
137
+
138
+ # Set the "watch" period --the rate at which
139
+ # autofiring of occurs.
140
+ #
141
+ # Returns [Fixnum[
142
+ def watch=(seconds)
143
+ @watch = seconds.to_i
144
+ end
145
+
146
+ # Use alternate rulebow script.
147
+ #
148
+ # Returns [Array]
149
+ def script=(script)
150
+ @script = script.to_s
151
+ end
152
+
153
+ # Initialize project for rulebow.
154
+ #
155
+ # Returns nothing.
156
+ def init_project(*args)
157
+ # anything to do?
158
+ end
159
+
160
+ # Print out a list of availabe manual triggers.
161
+ #
162
+ # Returns nothing.
163
+ def print_rules(*names)
164
+ names = nil if names.empty?
165
+ puts "(#{runner.root})"
166
+ runner.rulesets.each do |name, set|
167
+ next unless names.member?(name.to_s) if names
168
+ print "#{name}"
169
+ print " (#{set.chain.join(' ')})" unless set.chain.empty?
170
+ puts
171
+ set.docs.each_with_index do |d, i|
172
+ puts " * #{d}"
173
+ end
174
+ end
175
+
176
+ #exit
177
+ end
178
+
179
+ end
180
+
181
+ end
@@ -0,0 +1,3 @@
1
+ require 'rulebow/core_ext/cli'
2
+ require 'rulebow/core_ext/boolean'
3
+ require 'rulebow/core_ext/true_class'
@@ -0,0 +1,10 @@
1
+ module Boolean
2
+ end
3
+
4
+ class TrueClass
5
+ include Boolean
6
+ end
7
+
8
+ class FalseClass
9
+ include Boolean
10
+ end
@@ -0,0 +1,56 @@
1
+ module Kernel
2
+ private
3
+ #
4
+ # CLI is based on Clap library
5
+ # Copyright (c) 2010 Michel Martens
6
+ #
7
+ def cli(*args)
8
+ opts = (Hash === args.last ? args.pop : nil)
9
+ argv = args.first || ARGV.dup
10
+ args = []
11
+
12
+ #raise ArgumentError unless opts
13
+
14
+ # Split option aliases.
15
+ opts = opts.inject({}) do |h,(k,v)|
16
+ k.to_s.split(/\s+/).each{|o| h[o]=v}; h
17
+ end
18
+
19
+ # Convert single dash flags into multiple flags.
20
+ argv = argv.inject([]) do |a, v|
21
+ if v[0,1] == '-' && v[1,1] != '-'
22
+ a.concat(v[1..-1].chars.map{|c| "-#{c}"})
23
+ else
24
+ a << v
25
+ end
26
+ a
27
+ end
28
+
29
+ while argv.any?
30
+ item = argv.shift
31
+ flag = opts[item]
32
+
33
+ if flag
34
+ # Work around lambda semantics in 1.8.7.
35
+ arity = [flag.arity, 0].max
36
+
37
+ # Raise if there are not enough parameters
38
+ # available for the flag.
39
+ if argv.size < arity
40
+ raise ArgumentError
41
+ end
42
+
43
+ # Call the lambda with N items from argv,
44
+ # where N is the lambda's arity.
45
+ flag.call(*argv.shift(arity))
46
+ else
47
+
48
+ # Collect the items that don't correspond to
49
+ # flags.
50
+ args << item
51
+ end
52
+ end
53
+
54
+ args
55
+ end
56
+ end
@@ -0,0 +1,61 @@
1
+ # We are going to try doing it this way. If it proves a problem
2
+ # we'll make a special class.
3
+ #
4
+ class TrueClass
5
+ def empty?
6
+ false
7
+ end
8
+
9
+ def &(other)
10
+ other
11
+ end
12
+
13
+ def |(other)
14
+ other
15
+ end
16
+ end
17
+
18
+ #
19
+ class Array
20
+ alias and_without_t :&
21
+ alias or_without_t :|
22
+
23
+ def |(other)
24
+ TrueClass === other ? dup : or_without_t(other)
25
+ end
26
+
27
+ def &(other)
28
+ TrueClass === other ? dup : and_without_t(other)
29
+ end
30
+ end
31
+
32
+
33
+ =begin
34
+ class TrueArray < Array
35
+
36
+ def each; end
37
+
38
+ def size
39
+ 1 # ?
40
+ end
41
+
42
+ def empty?
43
+ false
44
+ end
45
+
46
+ def &(other)
47
+ other.dup
48
+ end
49
+
50
+ def |(other)
51
+ other.dup
52
+ end
53
+
54
+ ## If this would have worked we would not have had
55
+ ## to override Array.
56
+ #def coerce(other)
57
+ # return self, other
58
+ #end
59
+ end
60
+ =end
61
+
@@ -0,0 +1,240 @@
1
+ module Rulebow
2
+
3
+ ##
4
+ # Digest class is used to read and write lists of files with their
5
+ # associated checksums. This class uses SHA1.
6
+ #
7
+ class Digest
8
+
9
+ # The name of the master digest.
10
+ DEFAULT_NAME = 'default'
11
+
12
+ # System instance. [System]
13
+ attr :system
14
+
15
+ # Digest of files as they are presently on disk. [Hash]
16
+ attr :current
17
+
18
+ # Digest of files as saved in the digest file. [Hash]
19
+ attr :saved
20
+
21
+ # Initialize new instance of Digest.
22
+ #
23
+ # Options
24
+ # ignore - Instance of Ignore for filtering unwanted files. [Ignore]
25
+ # mark - Name of digest to load. [String]
26
+ #
27
+ def initialize(system)
28
+ @system = system
29
+ #@name = (options[:name] || MASTER_NAME).to_s
30
+ #@ignore = options[:ignore]
31
+
32
+ @filename = system.state_file
33
+
34
+ @current = Hash.new{ |h,k| h[k.to_s] = {} }
35
+ @saved = Hash.new{ |h,k| h[k.to_s] = {} }
36
+
37
+ read
38
+ refresh
39
+ end
40
+
41
+ # Get current digest for a given ruleset.
42
+ def [](ruleset)
43
+ for_ruleset(ruleset)
44
+ end
45
+
46
+ # The digest file's path.
47
+ #
48
+ # Returns [String]
49
+ def filename
50
+ @filename
51
+ end
52
+
53
+ # Remove all digests.
54
+ def clear_all
55
+ FileUtils.rm(filename)
56
+ end
57
+
58
+ # Load digest from file system.
59
+ #
60
+ # Returns nothing.
61
+ def read
62
+ return unless File.exist?(filename)
63
+
64
+ name = DEFAULT_NAME
65
+ save = Hash.new{ |h,k| h[k.to_s] = {} }
66
+
67
+ File.read(filename).lines.each do |line|
68
+ if md = /^\[(\w+)\]$/.match(line)
69
+ name = md[1]
70
+ end
71
+ if md = /^(\w+)\s+(.*?)$/.match(line)
72
+ save[name][md[2]] = md[1]
73
+ end
74
+ end
75
+
76
+ save.each do |name, digest|
77
+ @saved[name] = digest
78
+ end
79
+ end
80
+
81
+ # Refresh current digest for a given ruleset, or all rulesets if not given.
82
+ #
83
+ # Returns nothing.
84
+ def refresh(ruleset=nil)
85
+ if ruleset
86
+ ruleset = getruleset(ruleset)
87
+ current[ruleset.name.to_s] = ruleset.watchlist.digest
88
+ else
89
+ system.rulesets.each do |name, ruleset|
90
+ refresh(ruleset)
91
+ end
92
+ end
93
+ end
94
+
95
+ # Save current digest.
96
+ #
97
+ # Returns nothing.
98
+ def save(ruleset=nil)
99
+ if ruleset
100
+ ruleset = getruleset(ruleset)
101
+ refresh(ruleset)
102
+ saved[ruleset.name.to_s] = current[ruleset.name.to_s]
103
+ else
104
+ refresh
105
+ saved = current
106
+ end
107
+
108
+ dir = File.dirname(filename)
109
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
110
+ File.open(filename, 'w') do |f|
111
+ f << to_s
112
+ end
113
+ end
114
+
115
+ # Remove digest.
116
+ def remove(ruleset)
117
+ case ruleset
118
+ when Ruleset
119
+ current.remove(ruleset.name)
120
+ else
121
+ current.remove(ruleset.to_str)
122
+ end
123
+ save
124
+ end
125
+
126
+ # Produce the representation of the digest that is stored to disk.
127
+ #
128
+ # Returns digest file format. [String]
129
+ def to_s
130
+ s = ""
131
+ saved.each do |name, list|
132
+ s << "[#{name}]\n"
133
+ list.each do |path, id|
134
+ s << "#{id} #{path}\n"
135
+ end
136
+ s << "\n"
137
+ end
138
+ s
139
+ end
140
+
141
+ # Compute the sha1 identifer for a file.
142
+ #
143
+ # file - path to a file
144
+ #
145
+ # Returns [String] SHA1 digest string.
146
+ def checksum(file)
147
+ sha = ::Digest::SHA1.new
148
+ File.open(file, 'r') do |fh|
149
+ fh.each_line do |l|
150
+ sha << l
151
+ end
152
+ end
153
+ sha.hexdigest
154
+ end
155
+
156
+ # Filter files of those to be ignored.
157
+ #
158
+ # ruleset - instance of {Ruleset}
159
+ # files - files to be filtered
160
+ #
161
+ # Return [Array<String>]
162
+ def filter(ruleset, files)
163
+ #case ruleset.ignore
164
+ #when Watchlist
165
+ ruleset.digest.filter(files)
166
+ #when Array
167
+ # files.reject!{ |path| ignore.any?{ |ig| /^#{ig}/ =~ path } }
168
+ #else
169
+ # files
170
+ #end
171
+ end
172
+
173
+ #
174
+ def for_ruleset(ruleset)
175
+ For.instance(self, getruleset(ruleset))
176
+ end
177
+
178
+ private
179
+
180
+ #
181
+ def getruleset(ruleset)
182
+ case ruleset
183
+ when Ruleset
184
+ ruleset
185
+ else
186
+ system.rulesets[ruleset.to_sym]
187
+ end
188
+ end
189
+
190
+ ##
191
+ #
192
+ class For
193
+
194
+ #
195
+ def self.instance(digest, ruleset)
196
+ @instance ||= {}
197
+ @instance[[digest, ruleset]] ||= new(digest, ruleset)
198
+ end
199
+
200
+ def initialize(digest, ruleset)
201
+ @digest = digest
202
+ @ruleset = ruleset
203
+ end
204
+
205
+ attr :digest
206
+
207
+ attr :ruleset
208
+
209
+ def name
210
+ ruleset.name.to_s
211
+ end
212
+
213
+ def current
214
+ digest.current[name]
215
+ end
216
+
217
+ def saved
218
+ digest.saved[name]
219
+ end
220
+
221
+ # Filter files.
222
+ #
223
+ # Return [Array<String>]
224
+ def filter(files)
225
+ ruleset.watchlist.filter(files)
226
+ #case ruleset.ignore
227
+ #when Ignore
228
+ # ruleset.ignore.filter(list)
229
+ #when Array
230
+ # list.reject!{ |path| ignore.any?{ |ig| /^#{ig}/ =~ path } }
231
+ #else
232
+ # list
233
+ #end
234
+ end
235
+
236
+ end
237
+
238
+ end
239
+
240
+ end