htty 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/MIT-LICENSE.rdoc +9 -0
  2. data/README.rdoc +199 -0
  3. data/VERSION +1 -0
  4. data/app/htty.rb +14 -0
  5. data/app/htty/cli.rb +77 -0
  6. data/app/htty/cli/command.rb +185 -0
  7. data/app/htty/cli/commands.rb +43 -0
  8. data/app/htty/cli/commands/address.rb +84 -0
  9. data/app/htty/cli/commands/body_clear.rb +20 -0
  10. data/app/htty/cli/commands/body_request.rb +51 -0
  11. data/app/htty/cli/commands/body_response.rb +59 -0
  12. data/app/htty/cli/commands/body_set.rb +58 -0
  13. data/app/htty/cli/commands/body_unset.rb +45 -0
  14. data/app/htty/cli/commands/cd.rb +20 -0
  15. data/app/htty/cli/commands/cookie_add.rb +20 -0
  16. data/app/htty/cli/commands/cookie_remove.rb +20 -0
  17. data/app/htty/cli/commands/cookies.rb +68 -0
  18. data/app/htty/cli/commands/cookies_add.rb +62 -0
  19. data/app/htty/cli/commands/cookies_clear.rb +20 -0
  20. data/app/htty/cli/commands/cookies_remove.rb +60 -0
  21. data/app/htty/cli/commands/cookies_remove_all.rb +50 -0
  22. data/app/htty/cli/commands/cookies_use.rb +57 -0
  23. data/app/htty/cli/commands/delete.rb +20 -0
  24. data/app/htty/cli/commands/exit.rb +20 -0
  25. data/app/htty/cli/commands/follow.rb +56 -0
  26. data/app/htty/cli/commands/form.rb +20 -0
  27. data/app/htty/cli/commands/form_add.rb +20 -0
  28. data/app/htty/cli/commands/form_clear.rb +26 -0
  29. data/app/htty/cli/commands/form_remove.rb +20 -0
  30. data/app/htty/cli/commands/form_remove_all.rb +20 -0
  31. data/app/htty/cli/commands/fragment_clear.rb +20 -0
  32. data/app/htty/cli/commands/fragment_set.rb +59 -0
  33. data/app/htty/cli/commands/fragment_unset.rb +48 -0
  34. data/app/htty/cli/commands/get.rb +20 -0
  35. data/app/htty/cli/commands/header_set.rb +20 -0
  36. data/app/htty/cli/commands/header_unset.rb +20 -0
  37. data/app/htty/cli/commands/headers_clear.rb +20 -0
  38. data/app/htty/cli/commands/headers_request.rb +70 -0
  39. data/app/htty/cli/commands/headers_response.rb +65 -0
  40. data/app/htty/cli/commands/headers_set.rb +57 -0
  41. data/app/htty/cli/commands/headers_unset.rb +54 -0
  42. data/app/htty/cli/commands/headers_unset_all.rb +48 -0
  43. data/app/htty/cli/commands/help.rb +100 -0
  44. data/app/htty/cli/commands/history.rb +60 -0
  45. data/app/htty/cli/commands/history_verbose.rb +81 -0
  46. data/app/htty/cli/commands/host_set.rb +59 -0
  47. data/app/htty/cli/commands/http_delete.rb +40 -0
  48. data/app/htty/cli/commands/http_get.rb +42 -0
  49. data/app/htty/cli/commands/http_head.rb +36 -0
  50. data/app/htty/cli/commands/http_options.rb +36 -0
  51. data/app/htty/cli/commands/http_post.rb +34 -0
  52. data/app/htty/cli/commands/http_put.rb +29 -0
  53. data/app/htty/cli/commands/http_trace.rb +36 -0
  54. data/app/htty/cli/commands/path_set.rb +54 -0
  55. data/app/htty/cli/commands/port_set.rb +55 -0
  56. data/app/htty/cli/commands/post.rb +20 -0
  57. data/app/htty/cli/commands/put.rb +20 -0
  58. data/app/htty/cli/commands/query_clear.rb +20 -0
  59. data/app/htty/cli/commands/query_set.rb +59 -0
  60. data/app/htty/cli/commands/query_unset.rb +56 -0
  61. data/app/htty/cli/commands/query_unset_all.rb +50 -0
  62. data/app/htty/cli/commands/quit.rb +24 -0
  63. data/app/htty/cli/commands/reuse.rb +74 -0
  64. data/app/htty/cli/commands/scheme_set.rb +69 -0
  65. data/app/htty/cli/commands/status.rb +52 -0
  66. data/app/htty/cli/commands/undo.rb +13 -0
  67. data/app/htty/cli/commands/userinfo_clear.rb +20 -0
  68. data/app/htty/cli/commands/userinfo_set.rb +56 -0
  69. data/app/htty/cli/commands/userinfo_unset.rb +47 -0
  70. data/app/htty/cli/cookie_clearing_command.rb +26 -0
  71. data/app/htty/cli/display.rb +182 -0
  72. data/app/htty/cli/http_method_command.rb +75 -0
  73. data/app/htty/cli/url_escaping.rb +27 -0
  74. data/app/htty/cookies_util.rb +36 -0
  75. data/app/htty/no_location_header_error.rb +12 -0
  76. data/app/htty/no_response_error.rb +12 -0
  77. data/app/htty/no_set_cookie_header_error.rb +13 -0
  78. data/app/htty/ordered_hash.rb +69 -0
  79. data/app/htty/payload.rb +48 -0
  80. data/app/htty/request.rb +471 -0
  81. data/app/htty/requests_util.rb +92 -0
  82. data/app/htty/response.rb +34 -0
  83. data/app/htty/session.rb +29 -0
  84. data/bin/htty +5 -0
  85. data/spec/unit/htty/cli_spec.rb +27 -0
  86. data/spec/unit/htty/ordered_hash_spec.rb +54 -0
  87. data/spec/unit/htty/request_spec.rb +1236 -0
  88. data/spec/unit/htty/response_spec.rb +0 -0
  89. data/spec/unit/htty/session_spec.rb +13 -0
  90. metadata +158 -0
@@ -0,0 +1,9 @@
1
+ The MIT License
2
+
3
+ Source code for _htty_ is copyright (c) 2010 Nils Jonsson[mailto:htty@nilsjonsson.com].
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,199 @@
1
+ __ .__
2
+ s s _/ |_| |__ ____
3
+ .uef^" :8 :8 .. \ __\ | \_/ __ \
4
+ :d88E .88 .88 @L | | | Y \ ___/
5
+ `888E :888ooo :888ooo 9888i .dL |__| |___| /\___ >
6
+ 888E .z8k -*8888888 -*8888888 `Y888k:*888. __________\/_____\/____________________
7
+ 888E~?888L 8888 8888 888E 888I / | \__ ___/\__ ___/\______ \
8
+ 888E 888E 8888 8888 888E 888I / ~ \| | | | | ___/
9
+ 888E 888E 8888 8888 888E 888I \ Y /| | | | | |
10
+ 888E 888E .8888Lu= .8888Lu= 888E 888I \___|_ / |____| |____| |____|
11
+ 888E 888E ^%888* ^%888* x888N><888' ______\/___________________.___.
12
+ m888N= 888> 'Y" 'Y" "88" 888 \__ ___/\__ ___/\__ | |
13
+ `Y" 888 88F | | | | / | |
14
+ J88" 98" | | | | \____ |
15
+ @% ./" |____| |____| / ______|
16
+ :" ~` \/
17
+
18
+
19
+ htty[http://htty.github.com] is a console application for interacting with HTTP servers. It's something of a cross between _curl_ and the Lynx browser.
20
+
21
+ = Installation
22
+
23
+ It couldn't be much easier.
24
+
25
+ $ gem install htty
26
+
27
+ You'll need Ruby and RubyGems. It's known to work well under OS X against Ruby v1.8.7 and v1.9.2 under OS X.
28
+
29
+ = Features
30
+
31
+ * Intuitive commands and command aliases
32
+ * Support for familiar HTTP methods _GET_, _POST_, _PUT_, and _DELETE_, as well as _HEAD_, _OPTIONS_ and _TRACE_
33
+ * Automatic URL-encoding of query-string parameters and URL fragments
34
+ * Transcripts, both verbose and summary
35
+ * Dead-simple cookie handling and redirect following
36
+ * Built-in help
37
+
38
+ The things you can do with _htty_ are:
39
+
40
+ * <b>Build a request</b> -- you can tweak the address, headers, cookies, and body at will
41
+ * <b>Send the request to the server</b> -- after the request is sent, it remains unchanged in your session history
42
+ * <b>Inspect the server's response</b> -- you can look at the status, headers, cookies, and body in various ways
43
+ * <b>Review history</b> -- a normal and a verbose transcript of your session are available at all times (destroyed when you quit _htty_)
44
+ * <b>Reuse previous requests</b> -- you can refer to prior requests and copy them
45
+
46
+ = Examples
47
+
48
+ Here are a few annotated _htty_ session transcripts to get you started.
49
+
50
+ == Querying a web service
51
+
52
+ This simple example shows how to explore read-only web services with _htty_.
53
+
54
+ link:../doc_bin/esvapi1.png
55
+
56
+ You can point _htty_ at a complete or partial web URL. If you don't supply a URL, \http://0.0.0.0/ (port 80) will be used. You can vary the protocol scheme, userinfo, host, port, path, query string, and fragment as you wish.
57
+
58
+ The _htty_ shell prompt shows the address of the current request.
59
+
60
+ The +get+ command is one of seven HTTP request methods supported. A concise summary of the response is shown when you issue a request.
61
+
62
+ You can follow redirects using the +follow+ command.
63
+
64
+ link:../doc_bin/esvapi2.png
65
+
66
+ You can tweak segments of the address at will. Here we are navigating the site's path hierarchy, which you can do with relative as well as absolute pathspecs.
67
+
68
+ link:../doc_bin/esvapi3.png
69
+
70
+ Here we add query-string parameters. Notice that characters that require URL encoding are automatically URL-encoded (unless they are part of a URL-encoded expression).
71
+
72
+ The +headers-response+ and +body-response+ commands reveal the details of a response.
73
+
74
+ link:../doc_bin/esvapi4.png
75
+
76
+ There was some cruft in the web service's response (a horizontal line, a passage reference, verse numbers, a copyright stamp, and line breaks). We eliminate it by using API options provided by the web service we're talking to.
77
+
78
+ We do a Julia Child maneuver and use the +address+ command to change the entire URL, rather than add individual query-string parameters one by one.
79
+
80
+ Exit your session at any time by typing +quit+.
81
+
82
+ == Working with cookies
83
+
84
+ The next example demonstrates _htty_'s cookies features, as well as how to review and revisit past requests.
85
+
86
+ link:../doc_bin/google1.png
87
+
88
+ Notice that when cookies are offered in a response, a bold asterisk (it looks like a cookie) appears in the response summary. The same cookie symbol appears next to the _Set-Cookie_ header when you display response headers.
89
+
90
+ link:../doc_bin/google2.png
91
+
92
+ The +cookies-use+ command copies cookies out of the response into the next request. The cookie symbol appears next to the _Cookie_ header when you display request headers.
93
+
94
+ link:../doc_bin/google3.png
95
+
96
+ An abbreviated history is available through the +history+ command. Information about requests in the history includes request method, URL, number of headers (and a cookie symbol, if cookies were sent), and the size of the body. Information about responses in the history includes response code, number of headers (and a cookie symbol, if cookies were received), and the size of the body.
97
+
98
+ Note that history contains only numbered HTTP request and response pairs, not a record of all the commands you enter.
99
+
100
+ The +reuse+ command makes a copy of the headers and body of an earlier request for you to build on.
101
+
102
+ == Understanding complex HTTP conversations at a glance using history
103
+
104
+ Assume that we have the following Sinatra application listening on Sinatra's default port, 4567.
105
+
106
+ require 'sinatra'
107
+
108
+ get '/all-good' do
109
+ [200, [['Set-Cookie', 'foo=bar; baz']], 'Hello World!']
110
+ end
111
+
112
+ get '/huh' do
113
+ [404, 'What?']
114
+ end
115
+
116
+ get '/hurl' do
117
+ [500, 'Barf!']
118
+ end
119
+
120
+ post '/give-it-to-me' do
121
+ redirect '/all-good'
122
+ end
123
+
124
+ This application expects _GET_ and _POST_ requests and responds in various contrived ways.
125
+
126
+ link:../doc_bin/sinatra1.png
127
+
128
+ Here you can see a request body being specified. Type +body-set+ to enter body data, and terminate it by typing Return three times consecutively.
129
+
130
+ Also note how different response codes are rendered:
131
+
132
+ * Response codes between 200 and 299 appear black on green to indicate success
133
+ * Response codes between 300 and 399 appear white on blue to indicate redirection
134
+ * Response codes between 400 and 499 appear white on red to indicate failure
135
+ * Response codes between 500 and 599 appear flashing black on yellow to indicate a server error
136
+
137
+ link:../doc_bin/sinatra2.png
138
+
139
+ As with the abbreviated history demonstrated earlier, verbose history shows a numbered list of requests and the responses they elicited. All information exchanged between client and server is shown.
140
+
141
+ == Getting help
142
+
143
+ You can learn how to use _htty_ commands from within _htty_.
144
+
145
+ link:../doc_bin/help.png
146
+
147
+ The +help+ command takes an optional argument of the abbreviated or full name of a command.
148
+
149
+ = Coming soon
150
+
151
+ Here are some features that are coming down the pike.
152
+
153
+ == HTTP Secure URLs
154
+
155
+ The HTTPS code is broken right now. Pardon the dust.
156
+
157
+ == Commands for streamlining web form submission
158
+
159
+ These features will make _htty_ better at screen-scraping.
160
+
161
+ Using any of the forthcoming +form+ commands will clear any non-form content in the body of the request. Adding at least one URL-encoded form parameter to the request will set the _Content-Type_ header to _application/x-www-form-urlencoded_. Removing all URL-encoded form parameters will remove this header.
162
+
163
+ * +form+ -- display all form parameters offered in the response
164
+ * +form-fill+ -- prompt in turn for a value for each of the form inputs in the response
165
+ * <tt>form-add _name_ _value_</tt> -- add a URL-encoded form parameter for the request, using the specified name and value
166
+ * <tt>form-remove _name_</tt> -- remove a URL-encoded form parameter from the request, using the specified name
167
+ * +form-remove-all+ -- remove all URL-encoded form parameters from the request
168
+
169
+ You will also be able to pop open a browser window containing request and response bodies.
170
+
171
+ == Shiny _curses_ goodness
172
+
173
+ We'll have command history using the arrow keys, command autocompletion, and Tab key navigation of forms.
174
+
175
+ == Custom command aliases and shell emulation of _http-console_
176
+
177
+ You should be able to make your own command aliases.
178
+
179
+ _http-console_ has a nice command-line. We should have an _http-console_ skin for _htty_.
180
+
181
+ = Contributing
182
+
183
+ Your patches are welcome, and you will receive attribution here for good stuff.
184
+
185
+ Fork the official _htty_ repository located at http://github.com/htty/htty and send a pull request to htty[http://github.com/htty].
186
+
187
+ = News and information
188
+
189
+ Stay in touch with the _htty_ project by following the Twitter account: get_htty[http://twitter.com/get_htty].
190
+
191
+ You can also get help in the \#htty channel on Freenode[http://webchat.freenode.net/?channels=htty].
192
+
193
+ = Credits
194
+
195
+ The author, Nils&nbsp;Jonsson[mailto:htty@nilsjonsson.com], owes a debt of inspiration to the http-console[http://github.com/cloudhead/http-console] project.
196
+
197
+ = License
198
+
199
+ Released under the MIT&nbsp;License[link:MIT-LICENSE_rdoc.html].
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,14 @@
1
+ # Loads constants defined within HTTY.
2
+
3
+ # Contains the implementation of _htty_.
4
+ module HTTY
5
+
6
+ # The version of this release of _htty_.
7
+ VERSION = File.read("#{File.dirname __FILE__}/../VERSION").chomp
8
+
9
+ end
10
+
11
+ Dir.glob "#{File.dirname __FILE__}/htty/*.rb" do |f|
12
+ require File.expand_path("#{File.dirname __FILE__}/htty/" +
13
+ File.basename(f, '.rb'))
14
+ end
@@ -0,0 +1,77 @@
1
+ # Defines HTTY::CLI and loads constants defined within HTTY::CLI.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/cli/commands")
4
+ require File.expand_path("#{File.dirname __FILE__}/cli/commands/help")
5
+ require File.expand_path("#{File.dirname __FILE__}/cli/commands/quit")
6
+ require File.expand_path("#{File.dirname __FILE__}/cli/display")
7
+ require File.expand_path("#{File.dirname __FILE__}/session")
8
+
9
+ module HTTY; end
10
+
11
+ # Encapsulates the command-line interface to _htty_.
12
+ class HTTY::CLI
13
+
14
+ include HTTY::CLI::Display
15
+
16
+ # Returns the HTTY::Session created from command-line arguments.
17
+ attr_reader :session
18
+
19
+ # Instantiates a new HTTY::CLI with the specified _command_line_arguments_.
20
+ def initialize(command_line_arguments)
21
+ exit unless @session = rescuing_from(ArgumentError) do
22
+ everything_but_options = command_line_arguments.reject do |a|
23
+ a[0..0] == '-'
24
+ end
25
+ HTTY::Session.new(everything_but_options.first)
26
+ end
27
+ end
28
+
29
+ # Takes over stdin, stdout, and stderr to expose #session to command-line
30
+ # interaction.
31
+ def run!
32
+ say_hello
33
+ catch :quit do
34
+ loop do
35
+ begin
36
+ unless (command = prompt_for_command)
37
+ $stderr.puts notice('Unrecognized command')
38
+ puts notice('Try typing ' +
39
+ strong(HTTY::CLI::Commands::Help.command_line))
40
+ next
41
+ end
42
+ if ARGV.include?('--debug')
43
+ command.perform
44
+ else
45
+ rescuing_from Exception do
46
+ command.perform
47
+ end
48
+ end
49
+ rescue Interrupt
50
+ puts
51
+ puts notice('Type ' +
52
+ strong(HTTY::CLI::Commands::Quit.command_line) +
53
+ ' to quit')
54
+ next
55
+ end
56
+ end
57
+ end
58
+ say_goodbye
59
+ end
60
+
61
+ private
62
+
63
+ def prompt_for_command
64
+ command_line = ''
65
+ while command_line.empty? do
66
+ print strong(session.requests.last.uri) + normal('> ')
67
+ command_line = $stdin.gets.chomp.strip
68
+ end
69
+ HTTY::CLI::Commands.build_for command_line, :session => session
70
+ end
71
+
72
+ end
73
+
74
+ Dir.glob "#{File.dirname __FILE__}/cli/*.rb" do |f|
75
+ require File.expand_path("#{File.dirname __FILE__}/cli/" +
76
+ File.basename(f, '.rb'))
77
+ end
@@ -0,0 +1,185 @@
1
+ # Defines HTTY::CLI::Command.
2
+
3
+ require 'abbrev'
4
+ require 'shellwords'
5
+ require File.expand_path("#{File.dirname __FILE__}/display")
6
+
7
+ module HTTY; end
8
+
9
+ class HTTY::CLI; end
10
+
11
+ # A base class for all HTTY::CLI::Commands.
12
+ class HTTY::CLI::Command
13
+
14
+ extend HTTY::CLI::Display
15
+
16
+ # Returns a command that the command is an alias for, otherwise +nil+.
17
+ def self.alias_for; end
18
+
19
+ # Returns command classes that are aliases for the command.
20
+ def self.aliases
21
+ namespace_siblings.select do |s|
22
+ s.alias_for == self
23
+ end
24
+ end
25
+
26
+ # Returns a new HTTY::CLI::Commands::Command if the specified _command_line_
27
+ # references it, otherwise +nil+. If an _attributes_ hash is specified, it is
28
+ # used to initialize the command.
29
+ def self.build_for(command_line, attributes={})
30
+ command_line_regexp = make_command_line_regexp
31
+ if (match = (command_line_regexp.match(command_line)))
32
+ all_attributes = attributes
33
+ if (arguments = match.captures[0])
34
+ return new(attributes.merge(:arguments => arguments.strip.shellsplit))
35
+ end
36
+ return new(attributes)
37
+ end
38
+ nil
39
+ end
40
+
41
+ # Returns the name of a category under which help for the command should
42
+ # appear.
43
+ def self.category
44
+ return nil unless alias_for
45
+ alias_for.category
46
+ end
47
+
48
+ # Returns the string used to invoke the command from the command line. Its
49
+ # abbreviation is calculated to avoid collision with other commands in the
50
+ # same module.
51
+ def self.command_line
52
+ my_command_line = command_line_for_class_name(name)
53
+ other_command_lines = namespace_siblings.collect do |s|
54
+ if s.method_defined?(:command_line)
55
+ s.command_line
56
+ else
57
+ command_line_for_class_name s.name
58
+ end
59
+ end
60
+ all_command_lines = [my_command_line] + other_command_lines
61
+ all_abbrevs = Abbrev.abbrev(all_command_lines).group_by do |abbrev,
62
+ command_line|
63
+ command_line
64
+ end.collect do |command_line, abbrevs|
65
+ abbrevs.sort_by do |command_line, abbrev|
66
+ command_line
67
+ end.first
68
+ end
69
+ my_abbrev = all_abbrevs.detect do |abbrev, command_line|
70
+ command_line == my_command_line
71
+ end.first
72
+ my_abbrev_regexp = Regexp.new("^(#{Regexp.escape my_abbrev})(.*)$")
73
+ my_command_line.gsub my_abbrev_regexp do
74
+ $2.empty? ? $1 : "#{$1}[#{$2}]"
75
+ end
76
+ end
77
+
78
+ # Returns the arguments for the command-line usage of the command.
79
+ def self.command_line_arguments
80
+ return alias_for.command_line_arguments if alias_for
81
+ nil
82
+ end
83
+
84
+ # Returns the help text for the command.
85
+ def self.help
86
+ return "Alias for #{strong alias_for.command_line}" if alias_for
87
+ "(Help for #{strong command_line} is not available)"
88
+ end
89
+
90
+ # Returns the extended help text for the _get_ command.
91
+ def self.help_extended
92
+ return "(Extended help for #{command_line} is not available.)" unless help
93
+ "#{help}."
94
+ end
95
+
96
+ # Returns an array of the classes or modules that contain this command.
97
+ def self.namespaces
98
+ container = nil
99
+ name.split('::')[0...-1].collect do |element|
100
+ if container.nil?
101
+ container = instance_eval("::#{element}", __FILE__, __LINE__)
102
+ else
103
+ container = container.module_eval(element, __FILE__, __LINE__)
104
+ end
105
+ end
106
+ end
107
+
108
+ # Returns an array of the constants that share this command's namespace.
109
+ def self.namespace_siblings
110
+ namespace = namespaces.last
111
+ other_commands = namespace.constants.collect do |constant|
112
+ type = namespace.module_eval(constant.to_s, __FILE__, __LINE__)
113
+ (type == self) ? nil : type
114
+ end.compact
115
+ end
116
+
117
+ # Returns related command classes for the command.
118
+ def self.see_also_commands
119
+ Array(alias_for)
120
+ end
121
+
122
+ # Returns the arguments provided to the command.
123
+ attr_reader :arguments
124
+
125
+ # Returns the session within which the command operates.
126
+ attr_reader :session
127
+
128
+ # Initializes a new HTTY::CLI::Command with attribute values specified in the
129
+ # _attributes_ hash.
130
+ #
131
+ # Valid _attributes_ keys include:
132
+ #
133
+ # * <tt>:arguments</tt>
134
+ # * <tt>:session</tt>
135
+ def initialize(attributes={})
136
+ @arguments = Array(attributes[:arguments])
137
+ @session = attributes[:session]
138
+ end
139
+
140
+ # Performs the command, or raises NotImplementedError.
141
+ def perform
142
+ unless (alias_for = self.class.alias_for)
143
+ raise NotImplementedError, 'not implemented yet'
144
+ end
145
+ alias_for.new(:arguments => arguments, :session => session).perform
146
+ end
147
+
148
+ protected
149
+
150
+ # Yields the last request in #session. If that request already has a response,
151
+ # then it adds the result of the +yield+ to the requests of #session.
152
+ def add_request_if_has_response
153
+ requests = session.requests
154
+ last_request = requests.last
155
+ if last_request.response
156
+ requests << yield(last_request)
157
+ else
158
+ requests[requests.length - 1] = yield(last_request)
159
+ end
160
+ self
161
+ end
162
+
163
+ private
164
+
165
+ def self.command_line_for_class_name(class_name)
166
+ class_name.split('::').last.gsub(/(.)([A-Z])/, '\1-\2').downcase
167
+ end
168
+
169
+ def self.completion_optional(text)
170
+ return nil if text.empty?
171
+ char_length = (text[0..0] == '\\') ? 2 : 1
172
+ rest = (text.length > char_length) ?
173
+ completion_optional(text[char_length..-1]) :
174
+ nil
175
+ "(?:#{text[0...char_length]}#{rest})?"
176
+ end
177
+
178
+ def self.make_command_line_regexp
179
+ pattern = Regexp.escape(command_line).gsub(/\\\[(.+)\\\]/) do |optional|
180
+ completion_optional($1)
181
+ end
182
+ Regexp.new "^#{pattern}(\\s.+)?$", Regexp::IGNORECASE
183
+ end
184
+
185
+ end