wvanbergen-request-log-analyzer 1.0.0 → 1.0.1
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/DESIGN +14 -0
- data/HACKING +7 -0
- data/README.textile +9 -98
- data/Rakefile +2 -2
- data/bin/request-log-analyzer +1 -1
- data/lib/cli/bashcolorizer.rb +60 -0
- data/lib/cli/command_line_arguments.rb +301 -0
- data/lib/cli/progressbar.rb +236 -0
- data/lib/request_log_analyzer/aggregator/base.rb +51 -0
- data/lib/request_log_analyzer/aggregator/database.rb +97 -0
- data/lib/request_log_analyzer/aggregator/echo.rb +25 -0
- data/lib/request_log_analyzer/aggregator/summarizer.rb +116 -0
- data/lib/request_log_analyzer/controller.rb +206 -0
- data/lib/request_log_analyzer/file_format/merb.rb +33 -0
- data/lib/request_log_analyzer/file_format/rails.rb +119 -0
- data/lib/request_log_analyzer/file_format.rb +77 -0
- data/lib/request_log_analyzer/filter/base.rb +29 -0
- data/lib/request_log_analyzer/filter/field.rb +36 -0
- data/lib/request_log_analyzer/filter/timespan.rb +32 -0
- data/lib/request_log_analyzer/line_definition.rb +159 -0
- data/lib/request_log_analyzer/log_parser.rb +183 -0
- data/lib/request_log_analyzer/log_processor.rb +121 -0
- data/lib/request_log_analyzer/request.rb +115 -0
- data/lib/request_log_analyzer/source/base.rb +42 -0
- data/lib/request_log_analyzer/source/log_file.rb +180 -0
- data/lib/request_log_analyzer/tracker/base.rb +54 -0
- data/lib/request_log_analyzer/tracker/category.rb +71 -0
- data/lib/request_log_analyzer/tracker/duration.rb +81 -0
- data/lib/request_log_analyzer/tracker/hourly_spread.rb +80 -0
- data/lib/request_log_analyzer/tracker/timespan.rb +54 -0
- data/spec/file_format_spec.rb +78 -0
- data/spec/file_formats/spec_format.rb +26 -0
- data/spec/filter_spec.rb +137 -0
- data/spec/log_processor_spec.rb +57 -0
- data/tasks/rspec.rake +6 -0
- metadata +53 -55
- data/TODO +0 -58
- data/bin/request-log-database +0 -81
- data/lib/base/log_parser.rb +0 -78
- data/lib/base/record_inserter.rb +0 -139
- data/lib/command_line/arguments.rb +0 -129
- data/lib/command_line/flag.rb +0 -51
- data/lib/merb_analyzer/log_parser.rb +0 -26
- data/lib/rails_analyzer/log_parser.rb +0 -35
- data/lib/rails_analyzer/record_inserter.rb +0 -39
- data/tasks/test.rake +0 -8
- data/test/log_fragments/fragment_1.log +0 -59
- data/test/log_fragments/fragment_2.log +0 -5
- data/test/log_fragments/fragment_3.log +0 -12
- data/test/log_fragments/fragment_4.log +0 -10
- data/test/log_fragments/fragment_5.log +0 -24
- data/test/log_fragments/merb_1.log +0 -84
- data/test/merb_log_parser_test.rb +0 -39
- data/test/rails_log_parser_test.rb +0 -94
- data/test/record_inserter_test.rb +0 -45
data/DESIGN
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Request-log-analyzer is set up like a simple pipe and filter system.
|
2
|
+
|
3
|
+
This allows you to easily add extra reports, filters and outputs.
|
4
|
+
|
5
|
+
1) Build pipeline.
|
6
|
+
-> Aggregator (database)
|
7
|
+
Source -> Filter -> Filter -> Aggregator (summary report)
|
8
|
+
-> Aggregator (...)
|
9
|
+
|
10
|
+
2) Start chunk producer and push chunks through pipeline.
|
11
|
+
Controller.start
|
12
|
+
|
13
|
+
3) Gather output from pipeline.
|
14
|
+
Controller.report
|
data/HACKING
ADDED
data/README.textile
CHANGED
@@ -3,13 +3,18 @@ h1. Request log analyzer
|
|
3
3
|
This is a simple command line tool to analyze request log files of both Rails and
|
4
4
|
Merb. Its purpose is to find what actions are best candidates for optimization.
|
5
5
|
|
6
|
-
* Analyzes Rails
|
6
|
+
* Analyzes Rails log files (all versions)
|
7
7
|
* Can combine multiple files (handy if you are using logrotate)
|
8
|
-
* Uses several metrics
|
8
|
+
* Uses several metrics, including cumulative request time, average request time, process blockers, database and rendering time, HTTP methods and states, Rails action cache statistics, etc.) ("Sample output":http://wiki.github.com/wvanbergen/request-log-analyzer/sample-output)
|
9
9
|
* Low memory footprint (server-safe)
|
10
10
|
* MIT licensed
|
11
11
|
* Fast
|
12
12
|
|
13
|
+
h2. Additional information
|
14
|
+
|
15
|
+
* "Project wiki at GitHub":http://wiki.github.com/wvanbergen/request-log-analyzer
|
16
|
+
* "wvanbergen's blog posts":http://techblog.floorplanner.com/tag/request-log-analyzer/
|
17
|
+
|
13
18
|
h2. Installation
|
14
19
|
|
15
20
|
@sudo gem install wvanbergen-request-log-analyzer --source http://gems.github.com@
|
@@ -30,10 +35,11 @@ h2. Usage
|
|
30
35
|
--boring, -b Output reports without ASCII colors.
|
31
36
|
--database <filename>, -d: Creates an SQLite3 database of all the parsed request information.
|
32
37
|
--debug Print debug information while parsing.
|
38
|
+
--file <filename> Output to file.
|
33
39
|
|
34
40
|
Examples:
|
35
41
|
request-log-analyzer development.log
|
36
|
-
request-log-analyzer
|
42
|
+
request-log-analyzer mongrel.0.log mongrel.1.log mongrel.2.log
|
37
43
|
request-log-analyzer --format merb -d requests.db production.log
|
38
44
|
|
39
45
|
To install rake tasks in your Rails application,
|
@@ -41,98 +47,3 @@ h2. Usage
|
|
41
47
|
|
42
48
|
request-log-analyzer install rails
|
43
49
|
</pre>
|
44
|
-
|
45
|
-
|
46
|
-
h2. Example result
|
47
|
-
|
48
|
-
Note that this example was shortened for your viewing pleasure.
|
49
|
-
@$ request-log-analyzer /var/log/my_app.log -o 5@
|
50
|
-
|
51
|
-
<pre>
|
52
|
-
Request log analyzer, by Willem van Bergen and Bart ten Brinke
|
53
|
-
|
54
|
-
Processing started, failed, completed log lines from /var/log/my_app.log...
|
55
|
-
|
56
|
-
Processing all log lines...
|
57
|
-
========================================================================
|
58
|
-
Timestamp first request: 2008-07-13T06:25:58+00:00
|
59
|
-
Timestamp last request: 2008-07-20T06:18:53+00:00
|
60
|
-
Total time analyzed: 7 days
|
61
|
-
|
62
|
-
Total requests analyzed 58908 requests from log file
|
63
|
-
Methods: GET (50.6%), POST (22.8%), PUT (25.4%), DELETE (1.1%), unknown (0.0%).
|
64
|
-
|
65
|
-
Top 5 most requested actions
|
66
|
-
========================================================================
|
67
|
-
/overview/:date/ : 19359 requests
|
68
|
-
/overview/day/:date/ : 6365 requests
|
69
|
-
/overview/:date/set/ : 5589 requests
|
70
|
-
/overview/ : 3985 requests
|
71
|
-
/clients/:id/ : 1976 requests
|
72
|
-
|
73
|
-
Top 5 actions by time - cumulative
|
74
|
-
========================================================================
|
75
|
-
/overview/:date/ : 9044.582s [19359 requests]
|
76
|
-
/overview/ : 8478.767s [3985 requests]
|
77
|
-
/overview/:date/set/ : 3309.041s [5589 requests]
|
78
|
-
/clients/:id/products/:id/ : 1479.911s [924 requests]
|
79
|
-
/clients/:id/ : 750.080s [1976 requests]
|
80
|
-
|
81
|
-
Top 5 actions by time - per request mean
|
82
|
-
========================================================================
|
83
|
-
/overview/ : 2.128s [3985 requests]
|
84
|
-
/clients/:id/products/:id/ : 1.602s [924 requests]
|
85
|
-
/overview/:date/set/ : 0.592s [5589 requests]
|
86
|
-
/overview/:date/ : 0.467s [19359 requests]
|
87
|
-
/clients/:id/ : 0.380s [1976 requests]
|
88
|
-
|
89
|
-
Top 5 worst DB offenders - cumulative time
|
90
|
-
========================================================================
|
91
|
-
/overview/:date/ : 8773.993s [19359 requests]
|
92
|
-
/overview/ : 8394.754s [3985 requests]
|
93
|
-
/overview/:date/set/ : 3307.928s [5589 requests]
|
94
|
-
/clients/:id/products/:id/ : 1425.220s [924 requests]
|
95
|
-
/clients/:id/ : 535.229s [1976 requests]
|
96
|
-
|
97
|
-
Top 5 worst DB offenders - mean time
|
98
|
-
========================================================================
|
99
|
-
/overview/:id/:id/:id/print/ : 6.994s [448 requests]
|
100
|
-
/overview/ : 2.128s [3985 requests]
|
101
|
-
/clients/:id/products/:id/ : 1.602s [924 requests]
|
102
|
-
/overview/:date/set/ : 0.592s [5589 requests]
|
103
|
-
/overview/:date/ : 0.467s [19359 requests]
|
104
|
-
|
105
|
-
Mongrel process blockers (> 1.0 seconds)
|
106
|
-
========================================================================
|
107
|
-
/overview/:date/ : 7494.233s [3144 requests]
|
108
|
-
/overview/ : 8320.293s [1549 requests]
|
109
|
-
/overview/:date/set/ : 1149.235s [803 requests]
|
110
|
-
/overview/:id/:id/:id/print/new/ : 613.693s [341 requests]
|
111
|
-
/clients/:id/products/:id/ : 1370.693s [313 requests]
|
112
|
-
|
113
|
-
Requests graph - per hour
|
114
|
-
========================================================================
|
115
|
-
........
|
116
|
-
7:00 - 2731 : XXXXXXX
|
117
|
-
8:00 - 6139 : XXXXXXXXXXXXXXXX
|
118
|
-
9:00 - 7465 : XXXXXXXXXXXXXXXXXXXX
|
119
|
-
10:00 - 7118 : XXXXXXXXXXXXXXXXXXX
|
120
|
-
11:00 - 7409 : XXXXXXXXXXXXXXXXXXX
|
121
|
-
12:00 - 6450 : XXXXXXXXXXXXXXXXX
|
122
|
-
13:00 - 5377 : XXXXXXXXXXXXXX
|
123
|
-
14:00 - 6058 : XXXXXXXXXXXXXXXX
|
124
|
-
15:00 - 4156 : XXXXXXXXXXX
|
125
|
-
16:00 - 2767 : XXXXXXX
|
126
|
-
17:00 - 1598 : XXXX
|
127
|
-
18:00 - 792 : XX
|
128
|
-
........
|
129
|
-
|
130
|
-
Errors
|
131
|
-
========================================================================
|
132
|
-
StaleObjectError: [28 requests]
|
133
|
-
-> Attempted to update a stale object
|
134
|
-
StatementError: [2 requests]
|
135
|
-
-> Mysql::Error: Deadlock found when trying to get lock; try restarting transaction
|
136
|
-
NoMethodError: [1 requests]
|
137
|
-
-> undefined method `code' for nil:NilClass
|
138
|
-
</pre>
|
data/Rakefile
CHANGED
data/bin/request-log-analyzer
CHANGED
@@ -114,7 +114,7 @@ when :anonymize
|
|
114
114
|
require File.dirname(__FILE__) + '/../lib/request_log_analyzer/log_processor'
|
115
115
|
RequestLogAnalyzer::LogProcessor.build(:anonymize, arguments).run!
|
116
116
|
else
|
117
|
-
puts "Request log analyzer, by Willem van Bergen and Bart ten Brinke - Version
|
117
|
+
puts "Request log analyzer, by Willem van Bergen and Bart ten Brinke - Version 1.0\n\n"
|
118
118
|
|
119
119
|
# Run the request_log_analyzer!
|
120
120
|
RequestLogAnalyzer::Controller.build(arguments, terminal_width).run!
|
@@ -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 false
|
5
|
+
def colorize(text, color_code, color = false)
|
6
|
+
color ? "#{color_code}#{text}\e[0m" : text
|
7
|
+
end
|
8
|
+
|
9
|
+
# Draw a red line of text
|
10
|
+
def red(text, color = false)
|
11
|
+
colorize(text, "\e[31m", color)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Draw a Green line of text
|
15
|
+
def green(text, color = false)
|
16
|
+
colorize(text, "\e[32m", color)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Draw a Yellow line of text
|
20
|
+
def yellow(text, color = false)
|
21
|
+
colorize(text, "\e[33m", color)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Draw a Yellow line of text
|
25
|
+
def blue(text, color = false)
|
26
|
+
colorize(text, "\e[34m", color)
|
27
|
+
end
|
28
|
+
|
29
|
+
def white(text, color = false)
|
30
|
+
colorize(text, "\e[37m", color)
|
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,301 @@
|
|
1
|
+
module CommandLine
|
2
|
+
|
3
|
+
class Option
|
4
|
+
|
5
|
+
attr_reader :name, :alias
|
6
|
+
attr_reader :parameter_count
|
7
|
+
attr_reader :default_value
|
8
|
+
|
9
|
+
# Rewrites a command line keyword by replacing the underscores with dashes
|
10
|
+
# <tt>sym</tt> The symbol to rewrite
|
11
|
+
def self.rewrite(sym)
|
12
|
+
sym.to_s.gsub(/_/, '-').to_sym
|
13
|
+
end
|
14
|
+
|
15
|
+
# Initialize new CommandLine::Option
|
16
|
+
# <tt>name</tt> The name of the flag
|
17
|
+
# <tt>definition</tt> The definition of the flag.
|
18
|
+
def initialize(name, definition = {})
|
19
|
+
@name = CommandLine::Option.rewrite(name)
|
20
|
+
@alias = definition[:alias].to_sym if definition[:alias]
|
21
|
+
@required = definition.has_key?(:required) && definition[:required] == true
|
22
|
+
@parameter_count = definition[:parameters] || 1
|
23
|
+
@multiple = definition[:multiple] || false
|
24
|
+
@default_value = definition[:default] || false
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse(arguments_parser)
|
28
|
+
if @parameter_count == 0
|
29
|
+
return true
|
30
|
+
elsif @parameter_count == 1
|
31
|
+
parameter = arguments_parser.next_parameter
|
32
|
+
raise CommandLine::ParameterExpected, self if parameter.nil?
|
33
|
+
return parameter
|
34
|
+
elsif @parameter_count == :any
|
35
|
+
parameters = []
|
36
|
+
while parameter = arguments_parser.next_parameter && parameter != '--'
|
37
|
+
parameters << parameter
|
38
|
+
end
|
39
|
+
return parameters
|
40
|
+
else
|
41
|
+
parameters = []
|
42
|
+
@parameter_count.times do |n|
|
43
|
+
parameter = arguments_parser.next_parameter
|
44
|
+
raise CommandLine::ParameterExpected, self if parameter.nil?
|
45
|
+
parameters << parameter
|
46
|
+
end
|
47
|
+
return parameters
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def =~(test)
|
52
|
+
[@name, @alias].include?(CommandLine::Option.rewrite(test))
|
53
|
+
end
|
54
|
+
|
55
|
+
# Argument representation of the flag (--fast)
|
56
|
+
def to_option
|
57
|
+
"--#{@name}"
|
58
|
+
end
|
59
|
+
|
60
|
+
# Argument alias representation of the flag (-f)
|
61
|
+
def to_alias
|
62
|
+
"-#{@alias}"
|
63
|
+
end
|
64
|
+
|
65
|
+
# Check if flag has an alias
|
66
|
+
def has_alias?
|
67
|
+
!@alias.nil?
|
68
|
+
end
|
69
|
+
|
70
|
+
# Check if flag is required
|
71
|
+
def required?
|
72
|
+
@required
|
73
|
+
end
|
74
|
+
|
75
|
+
# Check if flag is optional
|
76
|
+
def optional?
|
77
|
+
!@required
|
78
|
+
end
|
79
|
+
|
80
|
+
def multiple?
|
81
|
+
@multiple
|
82
|
+
end
|
83
|
+
|
84
|
+
def has_default?
|
85
|
+
!@default_value.nil?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class Arguments
|
90
|
+
|
91
|
+
class Definition
|
92
|
+
|
93
|
+
ENDLESS_PARAMETERS = 99999
|
94
|
+
|
95
|
+
attr_reader :commands, :options, :parameters
|
96
|
+
|
97
|
+
def initialize(parent)
|
98
|
+
@parent = parent
|
99
|
+
@options = {}
|
100
|
+
@commands = {}
|
101
|
+
@parameters = nil
|
102
|
+
end
|
103
|
+
|
104
|
+
def [](option_name)
|
105
|
+
option_symbol = CommandLine::Option.rewrite(option_name)
|
106
|
+
if the_option = @options.detect { |(name, odef)| odef =~ option_symbol }
|
107
|
+
the_option[1]
|
108
|
+
else
|
109
|
+
raise CommandLine::UnknownOption, option_name
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def minimum_parameters=(count_specifier)
|
114
|
+
@parameters = count_specifier..ENDLESS_PARAMETERS
|
115
|
+
end
|
116
|
+
|
117
|
+
def parameters=(count_specifier)
|
118
|
+
@parameters = count_specifier
|
119
|
+
end
|
120
|
+
|
121
|
+
alias :files= :parameters=
|
122
|
+
|
123
|
+
def option(name, options = {})
|
124
|
+
clo = CommandLine::Option.new(name, options)
|
125
|
+
@options[clo.name] = clo
|
126
|
+
end
|
127
|
+
|
128
|
+
def switch(name, switch_alias = nil)
|
129
|
+
option(name, :alias => switch_alias, :parameters => 0)
|
130
|
+
end
|
131
|
+
|
132
|
+
def command(name, &block)
|
133
|
+
command_definition = Definition.new(self)
|
134
|
+
yield(command_definition) if block_given?
|
135
|
+
@commands[CommandLine::Option.rewrite(name)] = command_definition
|
136
|
+
end
|
137
|
+
|
138
|
+
def has_command?(command)
|
139
|
+
@commands[CommandLine::Option.rewrite(command)]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
OPTION_REGEXP = /^\-\-([A-z0-9-]+)$/;
|
144
|
+
ALIASES_REGEXP = /^\-([A-z0-9]+)$/
|
145
|
+
|
146
|
+
attr_reader :definition
|
147
|
+
attr_reader :tokens
|
148
|
+
attr_reader :command, :options, :parameters
|
149
|
+
|
150
|
+
def self.parse(tokens = $*, &block)
|
151
|
+
cla = Arguments.new
|
152
|
+
cla.define(&block)
|
153
|
+
return cla.parse!(tokens)
|
154
|
+
end
|
155
|
+
|
156
|
+
def initialize
|
157
|
+
@tokens = []
|
158
|
+
@definition = Definition.new(self)
|
159
|
+
@current_definition = @definition
|
160
|
+
end
|
161
|
+
|
162
|
+
def define(&block)
|
163
|
+
yield(@definition)
|
164
|
+
end
|
165
|
+
|
166
|
+
def [](option)
|
167
|
+
if the_option = @options.detect { |(key, value)| key =~ option }
|
168
|
+
the_option[1]
|
169
|
+
else
|
170
|
+
@current_definition[option].default_value
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def next_token
|
175
|
+
@current_token = @tokens.shift
|
176
|
+
return @current_token
|
177
|
+
end
|
178
|
+
|
179
|
+
def next_parameter
|
180
|
+
parameter_candidate = @tokens.first
|
181
|
+
parameter = (parameter_candidate.nil? || OPTION_REGEXP =~ parameter_candidate || ALIASES_REGEXP =~ parameter_candidate) ? nil : @tokens.shift
|
182
|
+
return parameter
|
183
|
+
end
|
184
|
+
|
185
|
+
def parse!(tokens)
|
186
|
+
@current_definition = @definition
|
187
|
+
@first_token = true
|
188
|
+
@tokens = tokens.clone
|
189
|
+
|
190
|
+
@options = {}
|
191
|
+
@parameters = []
|
192
|
+
@command = nil
|
193
|
+
|
194
|
+
prepare_result!
|
195
|
+
|
196
|
+
while next_token
|
197
|
+
|
198
|
+
if @first_token && command_definition = @definition.has_command?(@current_token)
|
199
|
+
@current_definition = command_definition
|
200
|
+
@command = CommandLine::Option.rewrite(@current_token)
|
201
|
+
else
|
202
|
+
case @current_token
|
203
|
+
when ALIASES_REGEXP; handle_alias_expansion($1)
|
204
|
+
when OPTION_REGEXP; handle_option($1)
|
205
|
+
else; handle_other_parameter(@current_token)
|
206
|
+
end
|
207
|
+
@first_token = false
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
validate_arguments!
|
213
|
+
|
214
|
+
return self
|
215
|
+
end
|
216
|
+
|
217
|
+
protected
|
218
|
+
|
219
|
+
def prepare_result!
|
220
|
+
multiple_options = Hash[*@current_definition.options.select { |name, o| o.multiple? }.flatten]
|
221
|
+
multiple_options.each { |name, definition| @options[definition] = [] }
|
222
|
+
end
|
223
|
+
|
224
|
+
def validate_arguments!
|
225
|
+
if @current_definition.parameters && !(@current_definition.parameters === @parameters.length)
|
226
|
+
raise CommandLine::ParametersOutOfRange.new(@current_definition.parameters, @parameters.length)
|
227
|
+
end
|
228
|
+
|
229
|
+
required_options = Hash[*@current_definition.options.select { |name, o| o.required? }.flatten]
|
230
|
+
required_options.each do |name, definition|
|
231
|
+
raise CommandLine::RequiredOptionMissing, definition unless self[name]
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def handle_alias_expansion(aliases)
|
236
|
+
aliases.reverse.scan(/./) do |alias_char|
|
237
|
+
if option_definition = @current_definition[alias_char]
|
238
|
+
@tokens.unshift(option_definition.to_option)
|
239
|
+
else
|
240
|
+
raise CommandLine::UnknownOption, alias_char
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def handle_other_parameter(parameter)
|
246
|
+
@parameters << parameter
|
247
|
+
end
|
248
|
+
|
249
|
+
def handle_option(option_name)
|
250
|
+
option_definition = @current_definition[option_name]
|
251
|
+
raise CommandLine::UnknownOption, option_name if option_definition.nil?
|
252
|
+
|
253
|
+
if option_definition.multiple?
|
254
|
+
@options[option_definition] << option_definition.parse(self)
|
255
|
+
else
|
256
|
+
@options[option_definition] = option_definition.parse(self)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
# Commandline parsing errors and exceptions
|
263
|
+
class Error < Exception
|
264
|
+
end
|
265
|
+
|
266
|
+
# Missing a required flag
|
267
|
+
class RequiredOptionMissing < CommandLine::Error
|
268
|
+
def initialize(option)
|
269
|
+
super("You have to provide the #{option.name} option!")
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# Missing a required file
|
274
|
+
class ParametersOutOfRange < CommandLine::Error
|
275
|
+
def initialize(expected, actual)
|
276
|
+
if expected.kind_of?(Range)
|
277
|
+
if expected.end == CommandLine::Arguments::Definition::ENDLESS_PARAMETERS
|
278
|
+
super("The command expected at least #{expected.begin} parameters, but found #{actual}!")
|
279
|
+
else
|
280
|
+
super("The command expected between #{expected.begin} and #{expected.end} parameters, but found #{actual}!")
|
281
|
+
end
|
282
|
+
else
|
283
|
+
super("The command expected #{expected} parameters, but found #{actual}!")
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
# Missing a required flag argument
|
289
|
+
class ParameterExpected < CommandLine::Error
|
290
|
+
def initialize(option)
|
291
|
+
super("The option #{option.inspect} expects a parameter!")
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
# Encountered an unkown flag
|
296
|
+
class UnknownOption < CommandLine::Error
|
297
|
+
def initialize(option_identifier)
|
298
|
+
super("#{option_identifier.inspect} not recognized as a valid option!")
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|