rulebow 0.4.0

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