yay 0.0.5 → 0.0.6

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.
@@ -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