yay 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,6 +3,8 @@ require 'yay/colourizer'
3
3
  require 'yay/version'
4
4
 
5
5
  class Yay
6
+ # this class acts as controller for yay. it interprets user input and
7
+ # coordinates with the parser
6
8
  class Application
7
9
 
8
10
  # arg that can be used if you don't want the application to do anything
@@ -14,11 +16,12 @@ class Yay
14
16
  # arg that can be used to dump the version and exit
15
17
  SHOW_VERSION_ARG = '--version'
16
18
 
19
+ # create an application with all the necessary user context
17
20
  def initialize(input, output, error, args)
18
- raise ArgumentError, "input" unless input.kind_of? IO
21
+ raise ArgumentError, "input" unless input.kind_of? IO
19
22
  raise ArgumentError, "output" unless output.kind_of? IO
20
- raise ArgumentError, "error" unless error.kind_of? IO
21
- raise ArgumentError, "args" unless args.kind_of? Array
23
+ raise ArgumentError, "error" unless error.kind_of? IO
24
+ raise ArgumentError, "args" unless args.kind_of? Array
22
25
 
23
26
  @input = input
24
27
  @output = output
@@ -27,6 +30,7 @@ class Yay
27
30
  @running = false
28
31
  end
29
32
 
33
+ # run the application. when this ends, termination is expected
30
34
  def run
31
35
  raise "already running" if @running
32
36
  @running = true
@@ -44,26 +48,40 @@ class Yay
44
48
  return if preArg == DO_NOTHING_ARG
45
49
 
46
50
  begin
47
-
51
+
48
52
  @parser = Yay::Parser.new
49
53
  @parser.allow_all = true
50
54
  @parser.parse_array(@args)
51
55
  @rules = @parser.get_rules
52
56
 
57
+ # the parser may instruct us to shut down
58
+ if @parser.shutdown
59
+ return
60
+ end
61
+
53
62
  @colourizer = Yay::Colourizer.new @rules, @input, @output
54
-
63
+
55
64
  if preArg == DUMP_RULES_ARG
56
65
  dump_colours @colourizer.line_rules, @colourizer.part_rules
57
66
  return
58
67
  end
59
68
 
60
69
  @colourizer.colourize_pipe
70
+
71
+ # provide readable text for internal errors
61
72
  rescue Yay::Error => error
62
- @error.puts error.printable_message
73
+ @error.puts "#{ColourWheel::fail}#{error.printable_message}#{ColourWheel::end_colour}"
74
+
75
+ # catch ctrl+c and similar events
63
76
  rescue Interrupt
64
77
  end
78
+
79
+ # emit the end colour just in case we were interrupted
80
+ print ColourWheel.end_colour
65
81
  end
66
82
 
83
+ # with the --dump command we can see the resulting rules created by the
84
+ # rule definitions (from all files included too)
67
85
  def dump_colours line_rules, word_rules
68
86
 
69
87
  puts "line rules:" if line_rules
@@ -1,58 +1,72 @@
1
- # To change this template, choose Tools | Templates
2
- # and open the template in the editor.
3
-
4
1
  class Yay
5
- class ColourWheel
6
-
7
- # not colours as such. commandline support varies
8
- MISC = {
9
- :reset => 0,
10
- :bright => 1,
11
- :dim => 2,
12
- :underscore => 4,
13
- :blink => 5,
14
- :reverse => 7,
15
- :hidden => 8,
16
-
17
- :normal => 0, #alias
18
- :invert => 7, #alias
19
- :inverted => 7, #alias
20
- :underscored => 4, #alias
21
- }
22
-
23
- FG = {
24
- :black => 30,
25
- :red => 31,
26
- :green => 32,
27
- :yellow => 33,
28
- :blue => 34,
29
- :magenta => 35,
30
- :cyan => 36,
31
- :white => 37,
32
- };
33
-
34
- BG = {
35
- :black => 40,
36
- :red => 41,
37
- :green => 42,
38
- :yellow => 43,
39
- :blue => 44,
40
- :magenta => 45,
41
- :cyan => 46,
42
- :white => 47
43
- };
44
-
45
- def self.all_names
46
- # assume BG and FG have the same keys
47
- MISC.keys | FG.keys
48
- end
49
-
50
- def self.begin_colours(colours)
51
- "\033[#{colours.join(';')}m"
52
- end
53
-
54
- def self.end_colour()
55
- "\033[0m"
56
- end
57
- end
58
- end
2
+ # the colour wheel contains all the constants needed to create coloured text
3
+ # there are also a few static helper methods to make rendering text easier
4
+ class ColourWheel
5
+
6
+ # commands. support varies
7
+ MISC = {
8
+ :reset => 0,
9
+ :bright => 1,
10
+ :dim => 2,
11
+ :underscore => 4,
12
+ :blink => 5,
13
+ :reverse => 7,
14
+ :hidden => 8,
15
+
16
+ :normal => 0, #alias
17
+ :invert => 7, #alias
18
+ :inverted => 7, #alias
19
+ :underscored => 4, #alias
20
+ }
21
+
22
+ # foreground colours
23
+ FG = {
24
+ :black => 30,
25
+ :red => 31,
26
+ :green => 32,
27
+ :yellow => 33,
28
+ :blue => 34,
29
+ :magenta => 35,
30
+ :cyan => 36,
31
+ :white => 37,
32
+ };
33
+
34
+ # background colours
35
+ BG = {
36
+ :black => 40,
37
+ :red => 41,
38
+ :green => 42,
39
+ :yellow => 43,
40
+ :blue => 44,
41
+ :magenta => 45,
42
+ :cyan => 46,
43
+ :white => 47
44
+ };
45
+
46
+ # return all the possible colour names
47
+ # the keys are used as string representations by the parser
48
+ def self.all_names
49
+ # assume BG and FG have the same keys
50
+ MISC.keys | FG.keys
51
+ end
52
+
53
+ # ge the string that begins the current colour code
54
+ def self.begin_colours(colour_numbers)
55
+ "\033[#{colour_numbers.join(';')}m"
56
+ end
57
+
58
+ # the command necessary to stop printing with colour
59
+ def self.end_colour
60
+ "\033[0m"
61
+ end
62
+
63
+ # ge the string that begins the current colour code
64
+ def self.success
65
+ self.begin_colours([FG[:green]])
66
+ end
67
+
68
+ def self.fail()
69
+ self.begin_colours([FG[:red]])
70
+ end
71
+ end
72
+ end
@@ -1,21 +1,32 @@
1
1
  require 'yay/colour_wheel'
2
2
 
3
3
  class Yay
4
+ # this class adds colour to a stream based on rules that have been generated
5
+ # by a parser
4
6
  class Colourizer
7
+ # create a colourizer using specified parser rules and input and output
8
+ # streams (usually stdin/stdout)
5
9
  def initialize rules, input, output
6
10
  colourize_rules rules
7
11
  @input = input
8
12
  @output = output
9
13
  end
10
14
 
15
+ # get the rules that are applied to a whole line if it contains matching text
16
+ # of the form [[regex, colour_string],..]
11
17
  def line_rules
12
18
  @line_rules
13
19
  end
14
20
 
21
+ # get the rules that are applied to matching text
22
+ # of the form [[regex, colour_string],..]
15
23
  def part_rules
16
24
  @part_rules
17
25
  end
18
26
 
27
+ # process the rules created by a parser and determine the actual strings we
28
+ # need to emit when we use this colour. this breaks parser rules up in to
29
+ # two categories - line rules and part rules
19
30
  def colourize_rules rules
20
31
  @line_rules = []
21
32
  @part_rules = []
@@ -36,25 +47,32 @@ class Yay
36
47
  }
37
48
  end
38
49
 
50
+ # create a pipe between the input and output streams, applying colour rules
51
+ # to every line that appears. only an interrupt, end of file or exception
52
+ # will end this process
39
53
  def colourize_pipe
54
+ # this is the colour we'll use when we haven't already applied a colour
55
+ # we remember the previous colour as we can colourize words within a line
56
+ # that's already been coloured
40
57
  default_end_colour = ColourWheel::end_colour
41
58
 
42
59
  @input.each_line { |line|
43
60
 
44
- # track the line_rules end colour so we can return to this after each match
61
+ # track the line_rules end colour so we can return to this after each
62
+ # match
45
63
  end_colour = default_end_colour
46
64
 
47
- #
65
+ # apply all line rules
48
66
  @line_rules.each { |rule|
49
67
  if line.match(rule[0])
50
68
  line = "#{rule[1]}#{line.rstrip}#{default_end_colour}"
51
69
  end_colour = rule[1]
52
- # only allow one line
70
+ # leave loop; only allow one line match per line
53
71
  break
54
72
  end
55
73
  }
56
74
 
57
- #
75
+ # apply all partial rules
58
76
  @part_rules.each { |rule|
59
77
  line.gsub!(rule[0], "#{rule[1]}\\0#{end_colour}")
60
78
  }
data/lib/yay/errors.rb CHANGED
@@ -1,16 +1,19 @@
1
1
 
2
2
  class Yay
3
+ # the base class for errors in yay. this provides error reporting in a
4
+ # friendly way (who likes stack traces?)
3
5
  class Error < StandardError
4
- attr :position
6
+ attr_reader :position
5
7
 
8
+ # override this to provide user feedback
6
9
  def printable_message
7
10
  raise "Unimplemented printable error"
8
11
  end
9
12
 
13
+ # a generic representation of the error's location on the line, file, etc
10
14
  def printable_position
11
15
  array = @position
12
16
  return "" unless @position
13
- length = array.length
14
17
  return " in file #{array[2]}, line #{array[1]}, word #{array[0]}" if array[2]
15
18
  return " on line #{array[1]}, word #{array[0]}" if array[1]
16
19
  return " at word #{array[0]}" if array[0]
@@ -18,8 +21,47 @@ class Yay
18
21
  end
19
22
  end
20
23
 
24
+ # thrown when an invalid filename was entered
25
+ class BadFilenameError < Error
26
+ attr_reader :value
27
+ attr_reader :message
28
+
29
+ def initialize value
30
+ @value = value
31
+ end
32
+
33
+ def printable_message
34
+ return "Invalid filename #{value} entered"
35
+ end
36
+ end
37
+
38
+ # this error wraps an underlying error
39
+ class InstallFailedError < Error
40
+ MAX_LENGTH = 1028
41
+
42
+ attr_reader :error
43
+ attr_reader :url
44
+ attr_reader :content
45
+
46
+ def initialize url, error, content=nil
47
+ @url = url
48
+ @error = error
49
+ @content = content
50
+ @content = 'empty' if (content==nil||content.strip=="")
51
+ if @content.length >= InstallFailedError::MAX_LENGTH
52
+ @content = "#{@content.slice(0,InstallFailedError::MAX_LENGTH)}\n.."
53
+ end
54
+ end
55
+
56
+ def printable_message
57
+ return "Failed to download and install file from #{url}\nReason: #{error}\nResponse was:\n#{content}\nCheck your url!"
58
+ end
59
+ end
60
+
61
+ # this error is raised when a variable has already been assigned a value
62
+ # for example @x is red and @x is blue
21
63
  class AlreadyAssignedError < Error
22
- attr :variable
64
+ attr_reader :variable
23
65
 
24
66
  def initialize variable
25
67
  @variable = variable
@@ -30,9 +72,12 @@ class Yay
30
72
  end
31
73
  end
32
74
 
75
+ # this is a generic access control error that's raised when someoen tries to
76
+ # do something disallowed in their current context. for example, you can
77
+ # use the install command from the command line but not a yayfile
33
78
  class NotAllowedError < Error
34
- attr :action
35
- attr :path
79
+ attr_reader :action
80
+ attr_reader :path
36
81
 
37
82
  def initialize action, path
38
83
  @action = action
@@ -44,22 +89,26 @@ class Yay
44
89
  end
45
90
  end
46
91
 
92
+ # raised when there's a circular reference between rules. this happens when
93
+ # variables point back to themselves in some way. e.g. @x is @y and @y is @x
47
94
  class CircularReferenceError < Error
48
- attr :current
49
- attr :path
95
+ attr_reader :current
96
+ attr_reader :path
50
97
 
51
98
  def initialize current, path
52
99
  @current = current
53
100
  @path = path
54
101
  end
55
-
102
+
56
103
  def printable_message
57
104
  return "There is a circular reference between variables: #{path.join(' => ')} => #{current}#{printable_position}"
58
105
  end
59
106
  end
60
107
 
108
+ # raised when a variable has been referenced but not given a value. for example
109
+ # cheese is @x without ever defining what @x is
61
110
  class UnresolvedSubstitutionError < Error
62
- attr :variable
111
+ attr_reader :variable
63
112
 
64
113
  def initialize variable
65
114
  @variable = variable
@@ -70,9 +119,10 @@ class Yay
70
119
  end
71
120
  end
72
121
 
122
+ # raised when include file resolution has failed
73
123
  class CouldntFindFileError < Error
74
- attr :filename
75
- attr :tried
124
+ attr_reader :filename
125
+ attr_reader :tried
76
126
 
77
127
  def initialize filename, tried
78
128
  @filename = filename
@@ -84,10 +134,13 @@ class Yay
84
134
  end
85
135
  end
86
136
 
137
+ # raised when a colour sequence was malformed. in practice you can use
138
+ # infinite colour commands but zero to two actual colours, the first one will
139
+ # be the foreground and the second one will be the background
87
140
  class TooManyColoursError < Error
88
- attr :fg
89
- attr :bg
90
- attr :colour
141
+ attr_reader :fg
142
+ attr_reader :bg
143
+ attr_reader :colour
91
144
 
92
145
  def initialize fg, bg, colour, position
93
146
  @fg = fg
@@ -101,9 +154,12 @@ class Yay
101
154
  end
102
155
  end
103
156
 
157
+ # raised when an unexpected token was found by the parser. in otherwords,
158
+ # the rules of the syntax have been broken somehow and the user hasn't written
159
+ # a valid command
104
160
  class UnexpectedTokenError < Error
105
- attr :type
106
- attr :value
161
+ attr_reader :type
162
+ attr_reader :value
107
163
 
108
164
  def initialize type, value, position
109
165
  @type = type
@@ -111,12 +167,16 @@ class Yay
111
167
  @position = position
112
168
  end
113
169
 
170
+ # add extra feedback for some tokens. help the user out!
114
171
  def extra_message
115
172
  return "Since #{value} has a special meaning, try enclosing it in quotes or a regex when searching for it" if type == "colour"
173
+ return "Have you finished the line off properly?" if type == "$end"
116
174
  return ""
117
175
  end
118
176
 
119
177
  def printable_message
178
+ return "Unexpected text \"#{value}\"#{printable_position}\n#{extra_message}" if type == "error"
179
+ return "Unexpected end of line#{printable_position}\n#{extra_message}" if type == "$end"
120
180
  return "Unexpected #{type} \"#{value}\"#{printable_position}\n#{extra_message}"
121
181
  end
122
182
  end
data/lib/yay/installer.rb CHANGED
@@ -1,12 +1,135 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'uri'
4
+ require 'yay/paths'
5
+ require 'fileutils'
1
6
 
2
7
  class Yay
8
+ # installs .yay files from a remote url in to either ~/.yay or /etc/yay
9
+ # depending on the users permissions
3
10
  class Installer
4
- def initialize(url)
5
- @url = url
11
+
12
+ # initialize an installer for the specified url. the .install method will
13
+ # attempt the actual installation process
14
+ def initialize file_name, url
15
+ # directories to iterate. begin with /etc/yay, which should only be
16
+ # accessible to sudo. then try a local install.
17
+ paths = Yay::Paths.new
18
+ validate_filename file_name
19
+ @file_name = append_filetype file_name
20
+ @dirs = [
21
+ paths.global_yay_path,
22
+ paths.local_yay_path
23
+ ]
24
+ @url = url
25
+ end
26
+
27
+ # some filenames just aren't a good idea
28
+ def validate_filename string
29
+ raise BadFilenameError.new string if /[\?\*\\\/]/.match(string)
30
+ end
31
+
32
+ # add the .yay part to the filename if it's missing
33
+ def append_filetype string
34
+ return "#{string}.yay" unless /\.yay$/.match(string)
35
+ return string
36
+ end
37
+
38
+ # see if we can write to a directory. will try to create the folder (and
39
+ # its parents) unless asked otherwise
40
+ def can_write_to_dir dir, create
41
+
42
+ begin
43
+ stat = File.stat(dir)
44
+ return true if stat && stat.writable?
45
+ rescue Errno::ENOENT
46
+ end
47
+
48
+ # create and try again if it doesn't exist
49
+ if create
50
+ begin
51
+ FileUtils.mkdir_p dir
52
+ #FileUtils.chmod 0644, dir
53
+ result = can_write_to_dir dir, false
54
+ puts "Created #{dir}" if result
55
+ return result
56
+ rescue Errno::EACCES
57
+ end
58
+ end
59
+
60
+ return false
61
+ end
62
+
63
+ # try to find the install directory
64
+ def get_install_directory
65
+ # search globally and then locally to see if we can write
66
+ @dirs.each { |dir|
67
+ return dir if can_write_to_dir dir, true
68
+ }
69
+ return nil
70
+ end
71
+
72
+ # make a curl-ish request and get the contents
73
+ def get_remote_string url, limit=5
74
+ raise InstallFailedError.new url, 'HTTP redirect too deep' if limit == 0
75
+
76
+ begin
77
+ puts "Requesting #{url}"
78
+ url = URI.parse(url)
79
+
80
+ request = Net::HTTP::Get.new(url.path || '/')
81
+
82
+ http = Net::HTTP.new url.host, url.port
83
+ #http.set_debug_output($stderr)
84
+ http.use_ssl = url.scheme == 'https'
85
+
86
+ response = http.start { |http2|
87
+ http2.request(request)
88
+ }
89
+
90
+ rescue StandardError => error
91
+ raise InstallFailedError.new url, error.to_s
92
+ end
93
+
94
+ case response
95
+ when Net::HTTPSuccess then
96
+ return response.body
97
+ when Net::HTTPRedirection then
98
+ puts "Redirecting to #{response['location']}";
99
+ return get_remote_string(response['location'], limit - 1)
100
+ end
101
+
102
+ raise InstallFailedError.new url, "#{response.code} \"#{response.message}\"", response.body
103
+ end
104
+
105
+ # ensure the rules we've downloaded parse correctly
106
+ def verify_rules url, string
107
+ begin
108
+ parser = Yay::Parser.new
109
+ parser.parse(string)
110
+ rescue Yay::Error => error
111
+ raise InstallFailedError.new url, error.printable_message, string
112
+ end
113
+ raise InstallFailedError.new url, "No rules in downloaded file", string if parser.get_rules == []
114
+ return parser
115
+ end
116
+
117
+ # store a string
118
+ def install_string string, dest_folder
119
+ dest = "#{dest_folder}/#{@file_name}"
120
+ File.open(dest, 'w') {|file|
121
+ file.write(string)
122
+ }
123
+ puts "#{ColourWheel::success}Installed to #{dest}#{ColourWheel::end_colour}"
6
124
  end
7
125
 
126
+ # attempt the installation process
8
127
  def install
9
- raise "installing files is not yet implemented! :-("
128
+ dest_folder = get_install_directory
129
+ raise "Couldn't write to or create directories #{@dirs.join(' or ')} something is up with your system!" unless dest_folder
130
+ string = get_remote_string @url
131
+ verify_rules @url, string
132
+ install_string string, dest_folder
10
133
  end
11
134
  end
12
135
  end
data/lib/yay/lexer.rb CHANGED
@@ -2,11 +2,12 @@ require 'strscan'
2
2
  require 'yay/lexer_regex'
3
3
 
4
4
  class Yay
5
+ # tokenises yay rules
5
6
  class Lexer
6
7
  attr :context_name
7
8
 
8
9
  def initialize(string="", context_name=nil)
9
- @position = 0
10
+ @position = 1
10
11
  @line = 1
11
12
  @context_name = context_name
12
13
 
@@ -15,19 +16,23 @@ class Yay
15
16
  use_string(string)
16
17
  end
17
18
 
18
- def context_name= value
19
- @context_name = value
20
- end
19
+ # the context name describes the source yay rules. this is free-form
20
+ # text but is best set to the filename
21
+ def context_name= value
22
+ @context_name = value
23
+ end
21
24
 
22
25
  # take a string and begin scanning it
23
26
  def use_string(string)
24
27
  @scanner = StringScanner.new string
25
28
  end
26
29
 
30
+ # return the current word
27
31
  def position
28
32
  @position
29
33
  end
30
34
 
35
+ # return the current line
31
36
  def line
32
37
  @line
33
38
  end
@@ -27,6 +27,9 @@ class Yay
27
27
  [:literal , /\b\S+\b/],
28
28
  ]
29
29
 
30
+ # get the regular expressions we need. always use this instead of
31
+ # referencing the base patterns directly as we augment that array with
32
+ # the colour names that are available
30
33
  def get_patterns
31
34
  patterns = BASE_PATTERNS
32
35
  # add the colour keywords. generate these from the colour wheel's constants
data/lib/yay/lister.rb ADDED
@@ -0,0 +1,59 @@
1
+ require 'yay/paths'
2
+
3
+ class Yay
4
+ # Lists installed .yay files
5
+ # See also: Rimmer, Cat
6
+ class Lister
7
+
8
+ # returns [[name, path],..]
9
+ def get_files_in_dir dir
10
+ results = []
11
+ Dir.glob("#{dir}/*.yay").each { |path|
12
+ matches = /\/(\w*)?\.yay$/.match(path)
13
+ if matches[1]
14
+ results.unshift [matches[1], path]
15
+ else
16
+ puts "Eh.. #{path} isn't quite right"
17
+ end
18
+ }
19
+ return results
20
+ end
21
+
22
+ # returns {dir=>[file,..]
23
+ def get_all_files
24
+ paths = Yay::Paths.new
25
+ result = {}
26
+ paths.yay_paths.each { |path|
27
+ result[path] = get_files_in_dir path
28
+ }
29
+ return result
30
+ end
31
+
32
+ # print the search paths we'll use when looking for yay files
33
+ def print_paths
34
+ puts "Search paths: (in order of precedence)"
35
+ paths = Yay::Paths.new
36
+ paths.yay_paths.each { |dir|
37
+ puts dir
38
+ }
39
+ end
40
+
41
+ # print the files we can use and the command that can be typed to use it
42
+ def print_files
43
+ puts "Installed styles:"
44
+ get_all_files.each_pair { |dir, files|
45
+ #puts "#{dir}:"
46
+ files.each { |file|
47
+ puts "#{file[0]}#{' '*(20-file[0].length)}#{file[1]}"
48
+ }
49
+ }
50
+ end
51
+
52
+ # print all paths and files we find in them
53
+ def print
54
+ print_paths
55
+ puts
56
+ print_files
57
+ end
58
+ end
59
+ end
data/lib/yay/loader.rb CHANGED
@@ -1,11 +1,13 @@
1
1
  require 'yay/paths'
2
2
 
3
3
  class Yay
4
+ # resolves the location of an include file
4
5
  class Loader
5
6
  def initialize filename
6
7
  @filename = filename
7
8
  end
8
9
 
10
+ # get the default loader. this, um, loads the default.yay file!
9
11
  def self.default_file_loader
10
12
  return Yay::Loader.new Yay::Paths::DEFAULT_YAYFILE
11
13
  end
data/lib/yay/parser.rb CHANGED
@@ -6,150 +6,175 @@ require 'yay/loader'
6
6
  require 'yay/installer'
7
7
  require 'yay/errors'
8
8
  require 'yay/paths'
9
+ require 'yay/lister'
9
10
 
10
11
  class Yay
11
- class Parser < Yay::ParserGen
12
- attr :allow_default
13
- attr :allow_install
14
- attr :allow_include
15
- attr :allow_print
12
+ # extends the parser that was automatically generated by racc and adds the
13
+ # behaviour necessary. this separation is done only because it's difficult to
14
+ # use .y files for anything other than grammar rules, even if they do allow
15
+ # ruby code
16
+ class Parser < Yay::ParserGen
17
+
18
+ # allow this parser to load the default rule file if the rule body is empty
19
+ attr_accessor :allow_default
20
+
21
+ # allow this parser to install new rule files from remote sources. beware
22
+ # the consequences. this should be allowed from the command line only!
23
+ attr_accessor :allow_install
24
+
25
+ # allow this parser to include rule files that are installed. including
26
+ # ones in the gem folders, globally and locally
27
+ attr_accessor :allow_include
28
+
29
+ # allow this parser to search for and print out all the rules that are
30
+ # installed
31
+ attr_accessor :allow_list
32
+
33
+ # set to true to signal to the application or parent parser that it's time
34
+ # to shut down
35
+ attr_reader :shutdown
16
36
 
17
37
  def initialize context_name=nil
18
- @lexer = Yay::Lexer.new
38
+ @lexer = Yay::Lexer.new
19
39
  @lexer.context_name = context_name
20
40
  end
21
41
 
22
- def allow_include= value
23
- @allow_include = value
24
- end
25
-
26
42
  # load a file from a url
27
- def include_file filename
43
+ def include_file filename
28
44
  raise NotAllowedError.new "include #{filename}", current_position unless @allow_include
29
- loader = Yay::Loader.new filename
30
- loader.load
31
- @ruleset.merge loader.get_rules
32
- end
45
+ loader = Yay::Loader.new filename
46
+ loader.load
47
+ @ruleset.merge loader.get_rules
48
+ end
33
49
 
34
50
  # install a file from a url
35
- def install_file url
51
+ def install_file file_name, url
36
52
  raise NotAllowedError.new "install #{url}", current_position unless @allow_install
37
- installer = Yay::Installer.new url
38
- installer.install
39
- end
40
-
53
+ installer = Yay::Installer.new file_name, url
54
+ installer.install
55
+ @shutdown = true
56
+ end
57
+
41
58
  # print the full list of yay files
42
- def list_installed
43
- raise NotAllowedError.new "list installed yay files", current_position unless @allow_print
44
- # TODO
45
- end
46
-
59
+ def list_installed
60
+ raise NotAllowedError.new "list installed yay files", current_position unless @allow_list
61
+ lister = Yay::Lister.new
62
+ lister.print
63
+ @shutdown = true
64
+ end
65
+
47
66
  # allow all parser actions
48
67
  def allow_all= value
49
- @allow_default = @allow_install = @allow_include = @allow_print = value
68
+ @allow_default = @allow_install = @allow_include = @allow_list = value
50
69
  end
51
70
 
52
71
  # load the default file. used when the commandline is empty
53
72
  def use_default_file
54
73
  # don't throw an error in this case. it's legitimate for a file to be empty
55
74
  return unless @allow_default
56
- loader = Yay::Loader.default_file_loader
57
- loader.load
58
- @ruleset.merge loader.get_rules
59
- end
60
-
61
- def handle_string string
62
- string = Regexp::escape(string)
63
- return Regexp.new(string, Regexp::IGNORECASE)
64
- end
65
-
66
- def handle_regex string
67
- return string_to_regex string, false
68
- end
69
-
70
- # for lack of a better function, this will take the ending sequence from
71
- # a regular expression and convert it in to a bytewise options variable
72
- def extract_regexp_options args
73
- return 0 if args.nil?
74
- raise ArgumentError unless args.kind_of? String
75
- options_map = {
76
- 'i' => Regexp::IGNORECASE,
77
- 'm' => Regexp::MULTILINE,
78
- 'x' => Regexp::EXTENDED,
79
- }
80
- options = 0
81
- args.each { |char|
82
- options |= options_map[char] || 0
83
- }
84
- return options
85
- end
86
-
87
- # for lack of a better function, this will take a string like "/abc/" and
88
- # transform it in to a regex object
89
- def string_to_regex string, escape=true
90
- matches = /\/([^\/\\\r\n]*(?:\\.[^\/\\\r\n]*)*)\/([a-z]\b)*/.match string
91
- return nil if matches[1].nil?
92
- content = matches[1]
93
- content = Regexp::escape(content) if escape
94
- options = extract_regexp_options matches[2]
95
- return Regexp.new(content, options)
96
- end
97
-
98
- # given an array of colour strings, create an array with the VT100 colour
99
- # sequences inside
100
- def handle_colours colours
101
- fg = bg = nil
102
- result = []
103
- # iterate the colour list and try to find up to two colours (foreground,
104
- # background) and unlimited miscellaneous colours (reset, invert, etc)
105
- colours.each { |colour|
106
- misc_val = ColourWheel::MISC[colour]
107
- if !misc_val.nil?
108
- result.push misc_val
109
- elsif !fg
110
- fg = ColourWheel::FG[colour]
111
- result.push fg
112
- elsif !bg
113
- bg = ColourWheel::BG[colour]
114
- result.push bg
115
- else
116
- raise Yay::TooManyColoursError.new fg, bg, colour, current_position
117
- end
118
- }
119
- result
120
- end
121
-
122
- def get_rules
123
- @ruleset.get_rules
124
- end
125
-
126
- # process commandline arguments as if they were from a yay file
127
- def parse_array args
128
- raise ArgumentError, "args" unless args.kind_of? Array
129
- parse args.join(' ')
130
- end
131
-
132
- # parse a string
133
- def parse(str)
134
- @lexer.use_string(str)
135
- @ruleset = Yay::RuleSet.new
136
-
137
- do_parse
138
- get_rules
139
- end
140
-
141
- # get the next token
142
- def next_token
143
- @lexer.next_token
144
- end
75
+ loader = Yay::Loader.default_file_loader
76
+ loader.load
77
+ @ruleset.merge loader.get_rules
78
+ end
79
+
80
+ # process a string token in to something we can use
81
+ def handle_string string
82
+ string = Regexp::escape(string)
83
+ return Regexp.new(string, Regexp::IGNORECASE)
84
+ end
85
+
86
+ # process a regex token in to something we can use
87
+ def handle_regex string
88
+ return string_to_regex string, false
89
+ end
145
90
 
91
+ # for lack of a better function, this will take the ending sequence from
92
+ # a regular expression and convert it in to a bytewise options variable
93
+ def extract_regexp_options args
94
+ return 0 if args.nil?
95
+ raise ArgumentError unless args.kind_of? String
96
+ options_map = {
97
+ 'i' => Regexp::IGNORECASE,
98
+ 'm' => Regexp::MULTILINE,
99
+ 'x' => Regexp::EXTENDED,
100
+ }
101
+ options = 0
102
+ args.each { |char|
103
+ options |= options_map[char] || 0
104
+ }
105
+ return options
106
+ end
107
+
108
+ # for lack of a better function, this will take a string like "/abc/" and
109
+ # transform it in to a regex object
110
+ def string_to_regex string, escape=true
111
+ matches = /\/([^\/\\\r\n]*(?:\\.[^\/\\\r\n]*)*)\/([a-z]\b)*/.match string
112
+ return nil if matches[1].nil?
113
+ content = matches[1]
114
+ content = Regexp::escape(content) if escape
115
+ options = extract_regexp_options matches[2]
116
+ return Regexp.new(content, options)
117
+ end
118
+
119
+ # given an array of colour strings, create an array with the VT100 colour
120
+ # sequences inside
121
+ def handle_colours colours
122
+ fg = bg = nil
123
+ result = []
124
+ # iterate the colour list and try to find up to two colours (foreground,
125
+ # background) and unlimited miscellaneous colours (reset, invert, etc)
126
+ colours.each { |colour|
127
+ misc_val = ColourWheel::MISC[colour]
128
+ if !misc_val.nil?
129
+ result.push misc_val
130
+ elsif !fg
131
+ fg = ColourWheel::FG[colour]
132
+ result.push fg
133
+ elsif !bg
134
+ bg = ColourWheel::BG[colour]
135
+ result.push bg
136
+ else
137
+ raise Yay::TooManyColoursError.new fg, bg, colour, current_position
138
+ end
139
+ }
140
+ result
141
+ end
142
+
143
+ # get the end result of the parse
144
+ def get_rules
145
+ @ruleset.get_rules
146
+ end
147
+
148
+ # process commandline arguments as if they were from a yay file
149
+ def parse_array args
150
+ raise ArgumentError, "args" unless args.kind_of? Array
151
+ parse args.join(' ')
152
+ end
153
+
154
+ # parse a string. returns the results
155
+ def parse(str)
156
+ @lexer.use_string(str)
157
+ @ruleset = Yay::RuleSet.new
158
+
159
+ do_parse
160
+ return get_rules
161
+ end
162
+
163
+ # get the next token
164
+ def next_token
165
+ @lexer.next_token
166
+ end
167
+
168
+ # get location information from the lexer in a nice little array we can
169
+ # pass to an exception constructor
146
170
  def current_position
147
171
  return [@lexer.position, @lexer.line, @lexer.context_name]
148
172
  end
149
-
150
- def on_error error_token_id, error_value, cant_touch_this
151
- type = token_to_str error_token_id
152
- raise Yay::UnexpectedTokenError.new type, error_value, current_position
153
- end
154
- end
173
+
174
+ # the racc on_error function
175
+ def on_error error_token_id, error_value, cant_touch_this
176
+ type = token_to_str error_token_id
177
+ raise Yay::UnexpectedTokenError.new type, error_value, current_position
178
+ end
179
+ end
155
180
  end
@@ -15,37 +15,39 @@ module_eval(<<'...end grammar.y/module_eval...', 'grammar.y', 54)
15
15
  ##### State transition tables begin ###
16
16
 
17
17
  racc_action_table = [
18
- -7, -25, 32, 17, 25, 17, -25, 12, 22, 22,
19
- 32, 20, 30, 9, 10, 12, 2, 4, 8, 9,
20
- 10, 12, 28, 35, 19, 29, 29, -2, 17, 37,
21
- 29, 37 ]
18
+ -7, -25, 33, 17, 25, 17, -25, 12, 22, 22,
19
+ 33, 20, 30, 9, 10, 12, 2, 4, 8, 9,
20
+ 10, 12, 28, 36, 31, 29, 29, 19, -2, 17,
21
+ 38, 29, 38 ]
22
22
 
23
23
  racc_action_check = [
24
24
  7, 6, 21, 17, 10, 9, 6, 21, 6, 7,
25
25
  23, 4, 19, 23, 23, 23, 0, 0, 0, 0,
26
- 0, 0, 18, 24, 3, 18, 24, 2, 1, 27,
27
- 29, 34 ]
26
+ 0, 0, 18, 24, 20, 18, 24, 3, 2, 1,
27
+ 27, 29, 35 ]
28
28
 
29
29
  racc_action_pointer = [
30
- 14, 17, 27, 24, 9, nil, -1, 0, nil, -6,
30
+ 14, 18, 28, 27, 9, nil, -1, 0, nil, -6,
31
31
  2, nil, nil, nil, nil, nil, nil, -8, 17, 12,
32
- nil, 0, nil, 8, 18, nil, nil, 19, nil, 22,
33
- nil, nil, nil, nil, 21, nil, nil, nil, nil, nil ]
32
+ 22, 0, nil, 8, 18, nil, nil, 20, nil, 23,
33
+ nil, nil, nil, nil, nil, 22, nil, nil, nil, nil,
34
+ nil ]
34
35
 
35
36
  racc_action_default = [
36
37
  -5, -29, -20, -30, -30, -1, -19, -25, -4, -29,
37
38
  -30, -8, -21, -9, -10, -11, -12, -29, -30, -30,
38
- -3, -30, -24, -30, -30, -17, -28, -27, -14, -23,
39
- 40, -18, -20, -6, -27, -16, -13, -26, -22, -15 ]
39
+ -30, -30, -24, -30, -30, -17, -28, -27, -14, -23,
40
+ 41, -3, -18, -20, -6, -27, -16, -13, -26, -22,
41
+ -15 ]
40
42
 
41
43
  racc_goto_table = [
42
- 5, 18, 27, 36, 21, 23, 31, 3, 34, 24,
43
- 39, nil, nil, 38, nil, nil, nil, 26, nil, nil,
44
- nil, nil, nil, 33 ]
44
+ 5, 18, 27, 37, 21, 23, 32, 3, 35, 24,
45
+ nil, 40, nil, 39, nil, nil, nil, 26, nil, nil,
46
+ nil, nil, nil, 34 ]
45
47
 
46
48
  racc_goto_check = [
47
49
  2, 11, 12, 13, 4, 4, 10, 1, 12, 11,
48
- 13, nil, nil, 12, nil, nil, nil, 11, nil, nil,
50
+ nil, 13, nil, 12, nil, nil, nil, 11, nil, nil,
49
51
  nil, nil, nil, 2 ]
50
52
 
51
53
  racc_goto_pointer = [
@@ -60,7 +62,7 @@ racc_reduce_table = [
60
62
  0, 0, :racc_error,
61
63
  1, 13, :_reduce_1,
62
64
  1, 13, :_reduce_2,
63
- 2, 13, :_reduce_3,
65
+ 3, 13, :_reduce_3,
64
66
  1, 13, :_reduce_4,
65
67
  0, 13, :_reduce_5,
66
68
  3, 14, :_reduce_6,
@@ -90,7 +92,7 @@ racc_reduce_table = [
90
92
 
91
93
  racc_reduce_n = 30
92
94
 
93
- racc_shift_n = 40
95
+ racc_shift_n = 41
94
96
 
95
97
  racc_token_table = {
96
98
  false => 0,
@@ -177,7 +179,7 @@ module_eval(<<'.,.,', 'grammar.y', 6)
177
179
 
178
180
  module_eval(<<'.,.,', 'grammar.y', 7)
179
181
  def _reduce_3(val, _values, result)
180
- install_file val[1]
182
+ install_file val[1], val[2]
181
183
  result
182
184
  end
183
185
  .,.,
data/lib/yay/paths.rb CHANGED
@@ -2,10 +2,10 @@ require 'rubygems'
2
2
  require 'yay/version'
3
3
 
4
4
  class Yay
5
-
5
+
6
6
  # some utility functions for finding the current install and .yay paths
7
7
  class Paths
8
-
8
+
9
9
  DEFAULT_YAYFILE = 'default'
10
10
 
11
11
  # get the paths to installed gems
@@ -20,13 +20,17 @@ class Yay
20
20
 
21
21
  # get all the paths where we might be able to find .yay files
22
22
  def yay_paths
23
- result = [local_yay_path]
23
+ result = [local_yay_path,global_yay_path]
24
24
  gempaths.each { |v|
25
25
  result.push gempath_to_yaypath(v)
26
26
  }
27
27
  return result
28
28
  end
29
29
 
30
+ def global_yay_path
31
+ return '/etc/yay'
32
+ end
33
+
30
34
  # get the path we store local .yay files
31
35
  def local_yay_path
32
36
  raise "ENV[HOME] not found!" unless ENV['HOME']
@@ -34,5 +38,5 @@ class Yay
34
38
  end
35
39
 
36
40
  end
37
-
38
- end
41
+
42
+ end
data/lib/yay/rule_set.rb CHANGED
@@ -21,7 +21,8 @@ class Yay
21
21
  def merge rules
22
22
  @rules = @rules | rules
23
23
  end
24
-
24
+
25
+ # get the rules, complete with variable substitutions made
25
26
  def get_rules
26
27
  # ensure we substitute all variables
27
28
  substitute_variables if @string_to_var
@@ -79,6 +80,7 @@ class Yay
79
80
  return result
80
81
  end
81
82
 
83
+ # replace all references to variables with their actual colours
82
84
  def substitute_variables
83
85
  @string_to_var.each { |ref|
84
86
  string = ref[0]
data/lib/yay/version.rb CHANGED
@@ -2,18 +2,21 @@ require 'open3'
2
2
 
3
3
  class Yay
4
4
 
5
- VERSION = "0.0.5"
5
+ # the gem version. increment to make a new release!
6
+ VERSION = "0.0.6"
6
7
 
7
- def self.version
8
- @rev ||= begin
9
- begin
10
- rev = Open3.popen3("git rev-parse HEAD") {|stdin, stdout, stderr| stdout.read }.strip
11
- rescue Errno::ENOENT
12
- rev = ""
13
- end
14
- rev.empty? ? nil : " (#{rev})"
15
- end
16
- "#{VERSION}#@rev"
17
- end
8
+ # yoinked from chef. this gives us the git revision number of the current
9
+ # build if possible. used for --version
10
+ def self.version
11
+ @rev ||= begin
12
+ begin
13
+ rev = Open3.popen3("git rev-parse HEAD") {|stdin, stdout, stderr| stdout.read }.strip
14
+ rescue Errno::ENOENT
15
+ rev = ""
16
+ end
17
+ rev.empty? ? nil : " (#{rev})"
18
+ end
19
+ "#{VERSION}#@rev"
20
+ end
18
21
 
19
22
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yay
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 5
10
- version: 0.0.5
9
+ - 6
10
+ version: 0.0.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - jon davey
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-11-18 00:00:00 +00:00
18
+ date: 2011-11-20 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -30,21 +30,22 @@ extra_rdoc_files: []
30
30
  files:
31
31
  - LICENSE
32
32
  - data/yay/default.yay
33
- - data/yay/syslog.yay
34
33
  - data/yay/log4x.yay
35
- - lib/yay/lexer.rb
36
- - lib/yay/parser.rb
34
+ - data/yay/syslog.yay
35
+ - lib/yay/colourizer.rb
36
+ - lib/yay/application.rb
37
+ - lib/yay/version.rb
37
38
  - lib/yay/parser_gen.rb
38
- - lib/yay/errors.rb
39
- - lib/yay/colour_wheel.rb
39
+ - lib/yay/lister.rb
40
40
  - lib/yay/lexer_regex.rb
41
- - lib/yay/paths.rb
42
- - lib/yay/version.rb
43
- - lib/yay/application.rb
41
+ - lib/yay/errors.rb
42
+ - lib/yay/parser.rb
44
43
  - lib/yay/loader.rb
45
- - lib/yay/rule_set.rb
46
- - lib/yay/colourizer.rb
44
+ - lib/yay/paths.rb
45
+ - lib/yay/colour_wheel.rb
47
46
  - lib/yay/installer.rb
47
+ - lib/yay/rule_set.rb
48
+ - lib/yay/lexer.rb
48
49
  - bin/yay
49
50
  has_rdoc: true
50
51
  homepage: http://github.com/jond3k/yay