wvanbergen-request-log-analyzer 0.1.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.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Willem van Bergen / Bart ten Brinke
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,119 @@
1
+ Request log analyzer
2
+ --------------------------------
3
+
4
+ This is a simple command line tool to analyze request log files. At this moment,
5
+ it only supports Rails log files, but Merb log files are planned to be supported
6
+ as well. Its purpose is to find what actions are best candidates for optimization.
7
+
8
+ This tool will parse all requests in the logfile and aggregate the
9
+ information. Once it is finished parsing the log file, it will show the
10
+ requests that take op most server time. Different metrics are used (cumulative
11
+ time, average time, blockers, DB time, etc)
12
+
13
+
14
+ Installation
15
+ --------------------------------
16
+ gem sources -a http://gems.github.com
17
+ sudo gem install wvanbergen-request-log-analyzer
18
+
19
+ Usage
20
+ --------------------------------
21
+
22
+ Usage: request-log-analyzer [FILE] [OPTION]
23
+ Analyze the given log FILE with the given OPTION
24
+ Example: request-log-analyzer mongrel.log
25
+
26
+ --fast, -t: Only use completed requests
27
+ --guess-database-time, -g: Guesses the database duration of requests if they are not in the log
28
+ --output, -o: Comma-separated list of reports to show
29
+ --amount, -c: Displays the top <amount> elements in the reports
30
+ --colorize, -z: Fancy bash coloring
31
+
32
+
33
+
34
+ Example
35
+ --------------------------------
36
+
37
+ Note that this example was shortened for your viewing pleasure.
38
+ $ request-log-analyzer /var/log/my_app.log
39
+
40
+ Request log analyzer, by Willem van Bergen and Bart ten Brinke
41
+
42
+ Processing all log lines...
43
+ ========================================================================
44
+ Successfully analyzed 58908 requests from log file
45
+
46
+ Timestamp first request: 2008-07-13T06:25:58+00:00
47
+ Timestamp last request: 2008-07-20T06:18:53+00:00
48
+ Total time analyzed: 7 days
49
+
50
+ Top 10 most requested actions
51
+ ========================================================================
52
+ /overview/:date/ : 19359 requests
53
+ /overview/day/:date/ : 6365 requests
54
+ /overview/:date/set/ : 5589 requests
55
+ /overview/ : 3985 requests
56
+ /clients/:id/ : 1976 requests
57
+ ........
58
+
59
+ Top 10 actions by time - cumulative
60
+ ========================================================================
61
+ /overview/:date/ : 9044.582s [19359 requests]
62
+ /overview/ : 8478.767s [3985 requests]
63
+ /overview/:date/set/ : 3309.041s [5589 requests]
64
+ /clients/:id/products/:id/ : 1479.911s [924 requests]
65
+ /clients/:id/ : 750.080s [1976 requests]
66
+ ........
67
+
68
+ Top 10 actions by time - per request mean
69
+ ========================================================================
70
+ /overview/ : 2.128s [3985 requests]
71
+ /clients/:id/products/:id/ : 1.602s [924 requests]
72
+ /overview/:date/set/ : 0.592s [5589 requests]
73
+ /overview/:date/ : 0.467s [19359 requests]
74
+ /clients/:id/ : 0.380s [1976 requests]
75
+ ........
76
+
77
+ Top 10 worst DB offenders - cumulative time
78
+ ========================================================================
79
+ /overview/:date/ : 8773.993s [19359 requests]
80
+ /overview/ : 8394.754s [3985 requests]
81
+ /overview/:date/set/ : 3307.928s [5589 requests]
82
+ /clients/:id/products/:id/ : 1425.220s [924 requests]
83
+ /clients/:id/ : 535.229s [1976 requests]
84
+ ........
85
+
86
+ Top 10 worst DB offenders - mean time
87
+ ========================================================================
88
+ /overview/:id/:id/:id/print/ : 6.994s [448 requests]
89
+ /overview/ : 2.128s [3985 requests]
90
+ /clients/:id/products/:id/ : 1.602s [924 requests]
91
+ /overview/:date/set/ : 0.592s [5589 requests]
92
+ /overview/:date/ : 0.467s [19359 requests]
93
+ ........
94
+
95
+ Mongrel process blockers (> 1.0 seconds) - frequency
96
+ ========================================================================
97
+ /overview/:date/ : 7494.233s [3144 requests]
98
+ /overview/ : 8320.293s [1549 requests]
99
+ /overview/:date/set/ : 1149.235s [803 requests]
100
+ /overview/:id/:id/:id/print/new/ : 613.693s [341 requests]
101
+ /clients/:id/products/:id/ : 1370.693s [313 requests]
102
+ ........
103
+
104
+ Requests graph - per hour
105
+ ========================================================================
106
+ ........
107
+ 7:00 - 2731 : XXXXXXX
108
+ 8:00 - 6139 : XXXXXXXXXXXXXXXX
109
+ 9:00 - 7465 : XXXXXXXXXXXXXXXXXXXX
110
+ 10:00 - 7118 : XXXXXXXXXXXXXXXXXXX
111
+ 11:00 - 7409 : XXXXXXXXXXXXXXXXXXX
112
+ 12:00 - 6450 : XXXXXXXXXXXXXXXXX
113
+ 13:00 - 5377 : XXXXXXXXXXXXXX
114
+ 14:00 - 6058 : XXXXXXXXXXXXXXXX
115
+ 15:00 - 4156 : XXXXXXXXXXX
116
+ 16:00 - 2767 : XXXXXXX
117
+ 17:00 - 1598 : XXXX
118
+ 18:00 - 792 : XX
119
+ ........
data/Rakefile ADDED
@@ -0,0 +1,43 @@
1
+ require 'rubygems'
2
+
3
+ load 'test/tasks.rake'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+
9
+ namespace :gem do
10
+
11
+ desc "Builds a ruby gem for request-log-analyzer"
12
+ task :build => [:manifest] do
13
+ system %[gem build request-log-analyzer.gemspec]
14
+ end
15
+
16
+ desc %{Update ".manifest" with the latest list of project filenames. Respect\
17
+ .gitignore by excluding everything that git ignores. Update `files` and\
18
+ `test_files` arrays in "*.gemspec" file if it's present.}
19
+ task :manifest do
20
+ list = Dir['**/*'].sort
21
+ spec_file = Dir['*.gemspec'].first
22
+ list -= [spec_file] if spec_file
23
+
24
+ File.read('.gitignore').each_line do |glob|
25
+ glob = glob.chomp.sub(/^\//, '')
26
+ list -= Dir[glob]
27
+ list -= Dir["#{glob}/**/*"] if File.directory?(glob) and !File.symlink?(glob)
28
+ puts "excluding #{glob}"
29
+ end
30
+
31
+ if spec_file
32
+ spec = File.read spec_file
33
+ spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do
34
+ assignment = $1
35
+ bunch = $2 ? list.grep(/^test.*_test\.rb$/) : list
36
+ '%s%%w(%s)' % [assignment, bunch.join(' ')]
37
+ end
38
+
39
+ File.open(spec_file, 'w') {|f| f << spec }
40
+ end
41
+ File.open('.manifest', 'w') {|f| f << list.join("\n") }
42
+ end
43
+ end
data/TODO ADDED
@@ -0,0 +1,20 @@
1
+ TODO items for Rails-log-analyzer
2
+ =================================
3
+ Contact willem AT vanbergen DOT org if you want to help out with the development.
4
+
5
+ Summarizer:
6
+ - Look at request types (GET / POST)
7
+
8
+ Database:
9
+ - Add query functionality for the resulting database file (interactive reports?)
10
+ - Link request processing line to request completed line
11
+
12
+ Rails integration:
13
+ - Create script that calls request-log-analyzer
14
+ - Optionally use local or specific routes.rb file to parse URLs
15
+ - Add rake tasks to Rails application when included
16
+
17
+ General:
18
+ - Add useful rake tasks
19
+ - Add more tests
20
+ - World domination
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/ruby
2
+ require File.dirname(__FILE__) + '/../lib/command_line/arguments'
3
+ require File.dirname(__FILE__) + '/../lib/rails_analyzer/log_parser'
4
+ require File.dirname(__FILE__) + '/../lib/rails_analyzer/summarizer'
5
+ require File.dirname(__FILE__) + '/../lib/bashcolorizer'
6
+ require File.dirname(__FILE__) + '/../lib/ruby-progressbar/progressbar.rb'
7
+
8
+ puts "Request log analyzer, by Willem van Bergen and Bart ten Brinke\n\n"
9
+
10
+ # Substitutes variable elements in a url (like the id field) with a fixed string (like ":id")
11
+ # This is used to aggregate simular requests.
12
+ # <tt>request</tt> The request to evaluate.
13
+ # Returns uniformed url string.
14
+ # Raises on mailformed request.
15
+ def request_hasher(request)
16
+ if request[:url]
17
+ url = request[:url].downcase.split(/^http[s]?:\/\/[A-z0-9\.-]+/).last.split('?').first # only the relevant URL part
18
+ url << '/' if url[-1] != '/'[0] && url.length > 1 # pad a trailing slash for consistency
19
+
20
+ url.gsub!(/\/\d+-\d+-\d+/, '/:date') # Combine all (year-month-day) queries
21
+ url.gsub!(/\/\d+-\d+/, '/:month') # Combine all date (year-month) queries
22
+ url.gsub!(/\/\d+/, '/:id') # replace identifiers in URLs
23
+
24
+ return url
25
+ elsif request[:controller] && request[:action]
26
+ return "#{request[:controller]}##{request[:action]}"
27
+ else
28
+ raise 'Cannot hash this request! ' + request.inspect
29
+ end
30
+ end
31
+
32
+ # Print results using a ASCII table.
33
+ # <tt>summarizer</tt> The summarizer containg information to draw the table.
34
+ # <tt>field</tt> The field containing the data to be printed
35
+ # <tt>amount</tt> The length of the table (defaults to 20)
36
+ def print_table(summarizer, field, amount = 20)
37
+ summarizer.sort_actions_by(field).reverse[0, amount.to_i].each do |a|
38
+ # As we show count by default, show totaltime if we sort by count
39
+ field = :total_time if field == :count
40
+
41
+ puts "#{a[0].ljust(50)}: %10.03fs [#{green("%d requests")}]" % [a[1][field], a[1][:count]]
42
+ end
43
+ end
44
+
45
+ # Parse the arguments given via commandline
46
+ begin
47
+ $arguments = CommandLine::Arguments.parse do |command_line|
48
+ command_line.switch(:guess_database_time, :g)
49
+ command_line.switch(:fast, :f)
50
+ command_line.switch(:colorize, :z)
51
+ command_line.flag(:output, :alias => :o)
52
+ command_line.flag(:amount, :alias => :c)
53
+ command_line.required_files = 1
54
+ end
55
+
56
+ rescue CommandLine::Error => e
57
+ puts "ARGUMENT ERROR: " + e.message
58
+ puts
59
+ load File.dirname(__FILE__) + "/../output/usage.rb"
60
+ exit(0)
61
+ end
62
+
63
+ $summarizer = RailsAnalyzer::Summarizer.new(:calculate_database => $arguments[:guess_database_time])
64
+ $summarizer.blocker_duration = 1.0
65
+
66
+ line_types = $arguments[:fast] ? [:completed] : [:started, :completed, :failed]
67
+
68
+ # Walk through al the files given via the arguments.
69
+ $arguments.files.each do |log_file|
70
+ puts "Processing #{line_types.join(', ')} log lines from #{log_file}..."
71
+ parser = RailsAnalyzer::LogParser.new(log_file)
72
+
73
+ # add progress bar
74
+ unless $arguments[:fast]
75
+ pbar = ProgressBar.new(green(log_file), File.size(log_file))
76
+ parser.progress { |pos, total| (pos == :finished) ? pbar.finish : pbar.set(pos) }
77
+ end
78
+
79
+ parser.each(*line_types) do |request|
80
+ $summarizer.group(request) { |r| request_hasher(r) }
81
+ end
82
+ end
83
+
84
+ # Select the reports to output and generate them.
85
+ output_reports = $arguments[:output].split(',') rescue [:timespan, :most_requested, :total_time, :mean_time, :total_db_time, :mean_db_time, :mean_rendering_time, :blockers, :hourly_spread, :errors]
86
+
87
+ output_reports.each do |report|
88
+ report_location = "#{File.dirname(__FILE__)}/../output/#{report}.rb"
89
+
90
+ if File.exist?(report_location)
91
+ load report_location
92
+ else
93
+ puts "\nERROR: Output report #{report} not found!"
94
+ end
95
+ end
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/command_line/arguments'
4
+ require File.dirname(__FILE__) + '/../lib/rails_analyzer/log_parser'
5
+ require File.dirname(__FILE__) + '/../lib/rails_analyzer/record_inserter'
6
+ require File.dirname(__FILE__) + '/../lib/bashcolorizer'
7
+ require File.dirname(__FILE__) + '/../lib/ruby-progressbar/progressbar.rb'
8
+
9
+
10
+ puts "Rails log parser, by Willem van Bergen and Bart ten Brinke\n\n"
11
+
12
+ begin
13
+
14
+ $arguments = CommandLine::Arguments.parse do |command_line|
15
+ command_line.switch(:guess_database_time, :g)
16
+ command_line.switch(:reset_database, :r)
17
+ command_line.flag(:database, :alias => :d, :required => false)
18
+ command_line.required_files = 1
19
+ end
20
+
21
+ rescue CommandLine::Error => e
22
+ puts "ARGUMENT ERROR: " + e.message
23
+ puts
24
+ puts "Usage: ruby parsetodb.rb [LOGFILES*] <OPTIONS>"
25
+ puts
26
+ puts "Options:"
27
+ puts " --database, -t: The database file to use"
28
+ puts " --reset-database, -r: Resets the database before inserting new records"
29
+ puts " --guess-database-time, -g: Guesses the database duration of requests"
30
+ puts
31
+ puts "Examples:"
32
+ puts " ./parsetodb.rb development.log"
33
+ puts " ./parsetodb.rb mongrel.0.log mongrel.1.log mongrel.2.log -g -d mongrel.db"
34
+ puts
35
+
36
+ exit(0)
37
+ end
38
+
39
+ log_files = $arguments.files
40
+ db_file = $arguments[:database] || log_files.first + '.db'
41
+
42
+ if $arguments[:reset_database] && File.exist?(db_file)
43
+ File.delete(db_file)
44
+ puts "Database file cleared."
45
+ end
46
+
47
+ records_inserted = 0
48
+ inserter = RailsAnalyzer::RecordInserter.insert_batch_into(db_file) do |db|
49
+ log_files.each do |log_file|
50
+
51
+ puts "Processing all log lines from #{log_file}..."
52
+ parser = RailsAnalyzer::LogParser.new(log_file)
53
+
54
+ pbar = ProgressBar.new(green(log_file), File.size(log_file))
55
+ parser.progress { |pos, total| (pos == :finished) ? pbar.finish : pbar.set(pos) }
56
+
57
+ parser.each do |request|
58
+ db.insert(request)
59
+ records_inserted += 1
60
+ end
61
+ end
62
+
63
+ if $arguments[:guess_database_time]
64
+ puts "Calculating database times..."
65
+ db.calculate_db_durations!
66
+ end
67
+ end
68
+
69
+ started = inserter.count(:started)
70
+ completed = inserter.count(:completed)
71
+ failed = inserter.count(:failed)
72
+
73
+ puts
74
+ puts "Inserted #{records_inserted} records from #{log_files.length} files."
75
+ puts "Parse warnings: #{inserter.warning_count}. Check the parse_warnings table in the database for details."
76
+ puts
77
+ puts "Requests started: #{started}"
78
+ puts "Requests completed: #{completed}"
79
+ puts "Requests failed: #{failed}"
@@ -0,0 +1,60 @@
1
+ # Colorize a text output with the given color if.
2
+ # <tt>text</tt> The text to colorize.
3
+ # <tt>color_code</tt> The color code string to set
4
+ # <tt>color</tt> Does not color if false. Defaults to ($arguments && $arguments[:colorize])
5
+ def colorize(text, color_code, color = $arguments && $arguments[:colorize])
6
+ color ? "#{color_code}#{text}\e[0m" : text
7
+ end
8
+
9
+ # Draw a red line of text
10
+ def red(text)
11
+ colorize(text, "\e[31m")
12
+ end
13
+
14
+ # Draw a Green line of text
15
+ def green(text)
16
+ colorize(text, "\e[32m")
17
+ end
18
+
19
+ # Draw a Yellow line of text
20
+ def yellow(text)
21
+ colorize(text, "\e[33m")
22
+ end
23
+
24
+ # Draw a Yellow line of text
25
+ def blue(text)
26
+ colorize(text, "\e[34m")
27
+ end
28
+
29
+ def white(text)
30
+ colorize(text, "\e[37m")
31
+ end
32
+
33
+
34
+ #STYLE = {
35
+ # :default => “33[0m”,
36
+ # # styles
37
+ # :bold => “33[1m”,
38
+ # :underline => “33[4m”,
39
+ # :blink => “33[5m”,
40
+ # :reverse => “33[7m”,
41
+ # :concealed => “33[8m”,
42
+ # # font colors
43
+ # :black => “33[30m”,
44
+ # :red => “33[31m”,
45
+ # :green => “33[32m”,
46
+ # :yellow => “33[33m”,
47
+ # :blue => “33[34m”,
48
+ # :magenta => “33[35m”,
49
+ # :cyan => “33[36m”,
50
+ # :white => “33[37m”,
51
+ # # background colors
52
+ # :on_black => “33[40m”,
53
+ # :on_red => “33[41m”,
54
+ # :on_green => “33[42m”,
55
+ # :on_yellow => “33[43m”,
56
+ # :on_blue => “33[44m”,
57
+ # :on_magenta => “33[45m”,
58
+ # :on_cyan => “33[46m”,
59
+ # :on_white => “33[47m” }
60
+ #
@@ -0,0 +1,129 @@
1
+ require "#{File.dirname(__FILE__)}/flag"
2
+ require "#{File.dirname(__FILE__)}/exceptions"
3
+
4
+ # Module used to parse commandline arguments
5
+ module CommandLine
6
+
7
+ # Parse argument lists and return an argument object containing all set flags and switches.
8
+ class Arguments
9
+
10
+ FLAG_REGEXP = /^--?[A-z0-9]/
11
+
12
+ attr_reader :flag_definitions
13
+
14
+ attr_reader :flags
15
+ attr_reader :files
16
+ attr_reader :command
17
+
18
+ attr_accessor :required_files
19
+
20
+ # Initializer.
21
+ # <tt>arguments</tt> The arguments which are going to be parsed (defaults to $*).
22
+ def initialize(arguments = $*, &block)
23
+ @arguments = arguments
24
+ @flag_definitions = {}
25
+ @begins_with_command = false
26
+ end
27
+
28
+ # Parse a list of arguments. Intatiates a Argument object with the given arguments and yeilds
29
+ # it so that flags and switches can be set by the application.
30
+ # <tt>arguments</tt> The arguments which are going to be parsed (defaults to $*).
31
+ # Returns the arguments object.parse!
32
+ def self.parse(arguments = $*, &block)
33
+ cla = Arguments.new(arguments)
34
+ yield(cla)
35
+ return cla.parse!
36
+ end
37
+
38
+ # Handle argument switches for the application
39
+ # <tt>switch</tt> A switch symbol like :fast
40
+ # <tt>switch_alias</tt> An short alias for the same switch (:f).
41
+ def switch(switch, switch_alias = nil)
42
+ return self.flag(switch, :alias => switch_alias, :expects => nil)
43
+ end
44
+
45
+ # Handle argument flags for the application
46
+ # <tt>flag</tt> A flag symbol like :fast
47
+ # Options
48
+ # * <tt>:expects</tt> Expects a value after the flag
49
+ def flag(flag, options)
50
+ options[:expects] = String unless options.has_key?(:expects)
51
+ argument = Flag.new(flag, options)
52
+ @flag_definitions[argument.to_argument] = argument
53
+ @flag_definitions[argument.to_alias] = argument if argument.has_alias?
54
+ return argument
55
+ end
56
+
57
+ # If called argument list must begin with a command.
58
+ # <tt>begins_w_command</tt> Defaults to true.
59
+ def begins_with_command!(begins_w_command=true)
60
+ @begins_with_command = begins_w_command
61
+ end
62
+
63
+ # Unknown flags will be silently ignored.
64
+ # <tt>ignore</tt> Defaults to true.
65
+ def ignore_unknown_flags!(ignore = true)
66
+ @ignore_unknown_flags = ignore
67
+ end
68
+
69
+ def [](name)
70
+ return flags[name.to_s.gsub(/_/, '-').to_sym]
71
+ end
72
+
73
+ # Parse the flags and switches set by the application.
74
+ # Returns an arguments object containing the flags and switches found in the commandline.
75
+ def parse!
76
+ @flags = {}
77
+ @files = []
78
+
79
+ i = 0
80
+ while @arguments.length > i do
81
+ arg = @arguments[i]
82
+ if FLAG_REGEXP =~ arg
83
+ if @flag_definitions.has_key?(arg)
84
+ flag = @flag_definitions[arg]
85
+ if flag.expects_argument?
86
+
87
+ if @arguments.length > (i + 1) && @arguments[i + 1]
88
+ @flags[flag.name] = @arguments[i + 1]
89
+ i += 1
90
+ else
91
+ raise CommandLine::FlagExpectsArgument.new(arg)
92
+ end
93
+
94
+ else
95
+ @flags[flag.name] = true
96
+ end
97
+ else
98
+ raise CommandLine::UnknownFlag.new(arg) unless @ignore_unknown_flags
99
+ end
100
+ else
101
+ if @begins_with_command && @command.nil?
102
+ @command = arg
103
+ else
104
+ @files << arg
105
+ end
106
+ end
107
+ i += 1
108
+ end
109
+
110
+ check_parsed_arguments!
111
+
112
+ return self
113
+ end
114
+
115
+ # Check if the parsed arguments meet their requirements.
116
+ # Raises CommandLineexception on error.
117
+ def check_parsed_arguments!
118
+ if @begins_with_command && @command.nil?
119
+ raise CommandLine::CommandMissing.new
120
+ end
121
+
122
+ if @required_files && @files.length < @required_files
123
+ raise CommandLine::FileMissing.new("You need at least #{@required_files} files")
124
+ end
125
+
126
+ end
127
+ end
128
+
129
+ end
@@ -0,0 +1,37 @@
1
+ module CommandLine
2
+
3
+ # Commandline parsing errors and exceptions
4
+ class Error < Exception
5
+ end
6
+
7
+ # Missing a required flag
8
+ class FlagMissing < CommandLine::Error
9
+ end
10
+
11
+ # Missing a required file
12
+ class FileMissing < CommandLine::Error
13
+ end
14
+
15
+ # Missing a required flag argument
16
+ class FlagExpectsArgument < CommandLine::Error
17
+ def initialize(flag)
18
+ super("#{flag} expects an argument!")
19
+ end
20
+ end
21
+
22
+ # Missing a required command
23
+ class CommandMissing < CommandLine::Error
24
+ def initialize(msg = "A command is missing")
25
+ super(msg)
26
+ end
27
+
28
+ end
29
+
30
+ # Encountered an unkown flag
31
+ class UnknownFlag < CommandLine::Error
32
+ def initialize(flag)
33
+ super("#{flag} not recognized as a valid flag!")
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,51 @@
1
+ module CommandLine
2
+
3
+ # Argument flag handling.
4
+ class Flag
5
+
6
+ attr_reader :name
7
+ attr_reader :alias
8
+ attr_reader :argument
9
+
10
+ # Initialize new Flag
11
+ # <tt>name</tt> The name of the flag
12
+ # <tt>definition</tt> The definition of the flag.
13
+ def initialize(name, definition)
14
+ @name = name.to_s.gsub(/_/, '-').to_sym
15
+ @alias = definition[:alias].to_sym if definition[:alias]
16
+ @required = definition.has_key?(:required) && definition[:required] == true
17
+ @argument = definition[:expects] if definition[:expects]
18
+ end
19
+
20
+ # Argument representation of the flag (--fast)
21
+ def to_argument
22
+ "--#{@name}"
23
+ end
24
+
25
+ # Argument alias representation of the flag (-f)
26
+ def to_alias
27
+ "-#{@alias}"
28
+ end
29
+
30
+ # Check if flag has an alias
31
+ def has_alias?
32
+ !@alias.nil?
33
+ end
34
+
35
+ # Check if flag is optional
36
+ def optional?
37
+ !@required
38
+ end
39
+
40
+ # Check if flag is required
41
+ def required?
42
+ @required
43
+ end
44
+
45
+ # Check if flag expects an argument (Are you talking to me?)
46
+ def expects_argument?
47
+ !@argument.nil?
48
+ end
49
+ end
50
+
51
+ end