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,24 @@
1
+ # Defines HTTY::CLI::Commands::Quit.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+
5
+ module HTTY; end
6
+
7
+ class HTTY::CLI; end
8
+
9
+ module HTTY::CLI::Commands; end
10
+
11
+ # Encapsulates the _quit_ command.
12
+ class HTTY::CLI::Commands::Quit < HTTY::CLI::Command
13
+
14
+ # Returns the help text for the _quit_ command.
15
+ def self.help
16
+ 'Quits htty'
17
+ end
18
+
19
+ # Performs the _quit_ command.
20
+ def perform
21
+ throw :quit
22
+ end
23
+
24
+ end
@@ -0,0 +1,74 @@
1
+ # Defines HTTY::CLI::Commands::Reuse.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+ require File.expand_path("#{File.dirname __FILE__}/../display")
5
+ require File.expand_path("#{File.dirname __FILE__}/history")
6
+ require File.expand_path("#{File.dirname __FILE__}/history_verbose")
7
+
8
+ module HTTY; end
9
+
10
+ class HTTY::CLI; end
11
+
12
+ module HTTY::CLI::Commands; end
13
+
14
+ # Encapsulates the _reuse_ command.
15
+ class HTTY::CLI::Commands::Reuse < HTTY::CLI::Command
16
+
17
+ include HTTY::CLI::Display
18
+
19
+ # Returns the name of a category under which help for the _reuse_ command
20
+ # should appear.
21
+ def self.category
22
+ 'Navigation'
23
+ end
24
+
25
+ # Returns the arguments for the command-line usage of the _reuse_ command.
26
+ def self.command_line_arguments
27
+ 'index'
28
+ end
29
+
30
+ # Returns the help text for the _reuse_ command.
31
+ def self.help
32
+ 'Copies a previous request by the index number shown in history'
33
+ end
34
+
35
+ # Returns the extended help text for the _reuse_ command.
36
+ def self.help_extended
37
+ 'Copies the properties of a previous request to be used for the request, ' +
38
+ 'using the request index number shown in history. Does not communicate ' +
39
+ "with the endpoint.\n" +
40
+ "\n" +
41
+ 'The argument is an index number that appears when you type ' +
42
+ "#{strong HTTY::CLI::Commands::History.command_line}."
43
+ end
44
+
45
+ # Returns related command classes for the _reuse_ command.
46
+ def self.see_also_commands
47
+ [HTTY::CLI::Commands::History, HTTY::CLI::Commands::HistoryVerbose]
48
+ end
49
+
50
+ # Performs the _reuse_ command.
51
+ def perform
52
+ unless arguments.length == 1
53
+ raise ArgumentError,
54
+ "wrong number of arguments (#{arguments.length} for 1)"
55
+ end
56
+
57
+ requests = session.requests
58
+ requests_with_responses = requests.select do |r|
59
+ r.response
60
+ end
61
+ raise 'no requests in history' if requests_with_responses.empty?
62
+
63
+ index = arguments.first.to_i
64
+ unless (1..requests_with_responses.length).include?(index)
65
+ raise ArgumentError,
66
+ "index must be between 1 and #{requests_with_responses.length}"
67
+ end
68
+
69
+ add_request_if_has_response do
70
+ requests[index - 1].send :dup_without_response
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,69 @@
1
+ # Defines HTTY::CLI::Commands::SchemeSet.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+ require File.expand_path("#{File.dirname __FILE__}/address")
5
+ require File.expand_path("#{File.dirname __FILE__}/port_set")
6
+
7
+ module HTTY; end
8
+
9
+ class HTTY::CLI; end
10
+
11
+ module HTTY::CLI::Commands; end
12
+
13
+ # Encapsulates the _scheme-set_ command.
14
+ class HTTY::CLI::Commands::SchemeSet < HTTY::CLI::Command
15
+
16
+ # Returns the name of a category under which help for the _scheme-set_ command
17
+ # should appear.
18
+ def self.category
19
+ 'Navigation'
20
+ end
21
+
22
+ # Returns the arguments for the command-line usage of the _scheme-set_ command.
23
+ def self.command_line_arguments
24
+ 'scheme'
25
+ end
26
+
27
+ # Returns the help text for the _scheme-set_ command.
28
+ def self.help
29
+ "Changes the scheme (protocol identifier) of the request's address"
30
+ end
31
+
32
+ # Returns the extended help text for the _scheme-set_ command.
33
+ def self.help_extended
34
+ 'Changes the scheme, or protocol identifier, used for the request. Does ' +
35
+ "not communicate with the endpoint.\n" +
36
+ "\n" +
37
+ "The scheme you supply must be either 'http' or 'https'. Changing the " +
38
+ "scheme has no effect on the port, and vice versa.\n" +
39
+ "\n" +
40
+ 'The console prompt shows the address for the current request.'
41
+ end
42
+
43
+ # Returns related command classes for the _scheme-set_ command.
44
+ def self.see_also_commands
45
+ [HTTY::CLI::Commands::Address, HTTY::CLI::Commands::PortSet]
46
+ end
47
+
48
+ # Initializes a new HTTY::CLI::SchemeSet with attribute values specified in
49
+ # the _attributes_ hash.
50
+ #
51
+ # Valid _attributes_ keys include:
52
+ #
53
+ # * <tt>:arguments</tt>
54
+ # * <tt>:session</tt>
55
+ def initialize(attributes={})
56
+ super attributes
57
+ @arguments.collect! do |a|
58
+ a.gsub(/:\/\/$/, '')
59
+ end
60
+ end
61
+
62
+ # Performs the _scheme-set_ command.
63
+ def perform
64
+ add_request_if_has_response do |request|
65
+ request.scheme_set(*arguments)
66
+ end
67
+ end
68
+
69
+ end
@@ -0,0 +1,52 @@
1
+ # Defines HTTY::CLI::Commands::Status.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+ require File.expand_path("#{File.dirname __FILE__}/../display")
5
+ require File.expand_path("#{File.dirname __FILE__}/body_response")
6
+ require File.expand_path("#{File.dirname __FILE__}/headers_response")
7
+
8
+ module HTTY; end
9
+
10
+ class HTTY::CLI; end
11
+
12
+ module HTTY::CLI::Commands; end
13
+
14
+ # Encapsulates the _status_ command.
15
+ class HTTY::CLI::Commands::Status < HTTY::CLI::Command
16
+
17
+ include HTTY::CLI::Display
18
+
19
+ # Returns the name of a category under which help for the _status_ command
20
+ # should appear.
21
+ def self.category
22
+ 'Inspecting Responses'
23
+ end
24
+
25
+ # Returns the help text for the _status_ command.
26
+ def self.help
27
+ 'Displays the status of the response'
28
+ end
29
+
30
+ # Returns the extended help text for the _status_ command.
31
+ def self.help_extended
32
+ 'Displays the status signal received in the response. Does not ' +
33
+ "communicate with the endpoint.\n" +
34
+ "\n" +
35
+ 'Status is displayed automatically when a response is received.'
36
+ end
37
+
38
+ # Returns related command classes for the _status_ command.
39
+ def self.see_also_commands
40
+ [HTTY::CLI::Commands::BodyResponse, HTTY::CLI::Commands::HeadersResponse]
41
+ end
42
+
43
+ # Performs the _status_ command.
44
+ def perform
45
+ unless (response = session.last_response)
46
+ raise HTTY::NoResponseError
47
+ end
48
+ show_response response
49
+ self
50
+ end
51
+
52
+ end
@@ -0,0 +1,13 @@
1
+ # Defines HTTY::CLI::Commands::Undo.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+
5
+ module HTTY; end
6
+
7
+ class HTTY::CLI; end
8
+
9
+ module HTTY::CLI::Commands; end
10
+
11
+ # Encapsulates the _undo_ command.
12
+ class HTTY::CLI::Commands::Undo < HTTY::CLI::Command
13
+ end
@@ -0,0 +1,20 @@
1
+ # Defines HTTY::CLI::Commands::UserinfoClear.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+ require File.expand_path("#{File.dirname __FILE__}/userinfo_unset")
5
+
6
+ module HTTY; end
7
+
8
+ class HTTY::CLI; end
9
+
10
+ module HTTY::CLI::Commands; end
11
+
12
+ # Encapsulates the _userinfo-clear_ command.
13
+ class HTTY::CLI::Commands::UserinfoClear < HTTY::CLI::Command
14
+
15
+ # Returns the command that the _userinfo-clear_ command is an alias for.
16
+ def self.alias_for
17
+ HTTY::CLI::Commands::UserinfoUnset
18
+ end
19
+
20
+ end
@@ -0,0 +1,56 @@
1
+ # Defines HTTY::CLI::Commands::UserinfoSet.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+ require File.expand_path("#{File.dirname __FILE__}/../url_escaping")
5
+ require File.expand_path("#{File.dirname __FILE__}/address")
6
+ require File.expand_path("#{File.dirname __FILE__}/userinfo_unset")
7
+
8
+ module HTTY; end
9
+
10
+ class HTTY::CLI; end
11
+
12
+ module HTTY::CLI::Commands; end
13
+
14
+ # Encapsulates the _userinfo-set_ command.
15
+ class HTTY::CLI::Commands::UserinfoSet < HTTY::CLI::Command
16
+
17
+ include HTTY::CLI::UrlEscaping
18
+
19
+ # Returns the name of a category under which help for the _userinfo-set_
20
+ # command should appear.
21
+ def self.category
22
+ 'Navigation'
23
+ end
24
+
25
+ # Returns the arguments for the command-line usage of the _userinfo-set_
26
+ # command.
27
+ def self.command_line_arguments
28
+ 'userinfo'
29
+ end
30
+
31
+ # Returns the help text for the _userinfo-set_ command.
32
+ def self.help
33
+ "Sets the userinfo of the request's address"
34
+ end
35
+
36
+ # Returns the extended help text for the _userinfo-set_ command.
37
+ def self.help_extended
38
+ 'Sets the userinfo used for the request. Does not communicate with the ' +
39
+ "endpoint.\n" +
40
+ "\n" +
41
+ 'The console prompt shows the address for the current request.'
42
+ end
43
+
44
+ # Returns related command classes for the _userinfo-set_ command.
45
+ def self.see_also_commands
46
+ [HTTY::CLI::Commands::UserinfoUnset, HTTY::CLI::Commands::Address]
47
+ end
48
+
49
+ # Performs the _userinfo-set_ command.
50
+ def perform
51
+ add_request_if_has_response do |request|
52
+ request.userinfo_set(*escape_or_warn_of_escape_sequences(arguments))
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,47 @@
1
+ # Defines HTTY::CLI::Commands::UserinfoUnset.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+ require File.expand_path("#{File.dirname __FILE__}/address")
5
+ require File.expand_path("#{File.dirname __FILE__}/userinfo_set")
6
+
7
+ module HTTY; end
8
+
9
+ class HTTY::CLI; end
10
+
11
+ module HTTY::CLI::Commands; end
12
+
13
+ # Encapsulates the _userinfo-unset_ command.
14
+ class HTTY::CLI::Commands::UserinfoUnset < HTTY::CLI::Command
15
+
16
+ # Returns the name of a category under which help for the _userinfo-unset_
17
+ # command should appear.
18
+ def self.category
19
+ 'Navigation'
20
+ end
21
+
22
+ # Returns the help text for the _userinfo-unset_ command.
23
+ def self.help
24
+ "Removes the userinfo from the request's address"
25
+ end
26
+
27
+ # Returns the extended help text for the _userinfo-unset_ command.
28
+ def self.help_extended
29
+ 'Removes the userinfo used for the request. Does not communicate with ' +
30
+ "the endpoint.\n" +
31
+ "\n" +
32
+ 'The console prompt shows the address for the current request.'
33
+ end
34
+
35
+ # Returns related command classes for the _userinfo-unset_ command.
36
+ def self.see_also_commands
37
+ [HTTY::CLI::Commands::UserinfoSet, HTTY::CLI::Commands::Address]
38
+ end
39
+
40
+ # Performs the _userinfo-unset_ command.
41
+ def perform
42
+ add_request_if_has_response do |request|
43
+ request.userinfo_unset(*arguments)
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,26 @@
1
+ # Defines HTTY::CLI::CookieClearingCommand.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/display")
4
+
5
+ module HTTY; end
6
+
7
+ class HTTY::CLI; end
8
+
9
+ # Encapsulates behavior common to all HTTY::CLI::Command subclasses that can
10
+ # result in cookies being cleared automatically.
11
+ module HTTY::CLI::CookieClearingCommand
12
+
13
+ include HTTY::CLI::Display
14
+
15
+ protected
16
+
17
+ def notify_if_cookies_cleared(request)
18
+ had_cookies = !request.cookies.empty?
19
+ new_request = yield
20
+ if (had_cookies && new_request.cookies.empty?)
21
+ puts notice('Cookies cleared')
22
+ end
23
+ new_request
24
+ end
25
+
26
+ end
@@ -0,0 +1,182 @@
1
+ # Defines HTTY::CLI::Display.
2
+
3
+ module HTTY; end
4
+
5
+ class HTTY::CLI; end
6
+
7
+ # Encapsulates the display logic of _htty_'s command-line interface.
8
+ module HTTY::CLI::Display
9
+
10
+ FORMATS = {:bold => '1',
11
+ :underlined => '4',
12
+ :blinking => '5',
13
+ :inverse => '7',
14
+ :foreground_black => '30',
15
+ :foreground_dark_red => '31',
16
+ :foreground_dark_green => '32',
17
+ :foreground_dark_yellow => '33',
18
+ :foreground_dark_blue => '34',
19
+ :foreground_dark_magenta => '35',
20
+ :foreground_dark_cyan => '36',
21
+ :foreground_light_gray => '37',
22
+ :foreground_dark_default => '39',
23
+ :foreground_dark_gray => '1;30',
24
+ :foreground_red => '1;31',
25
+ :foreground_green => '1;32',
26
+ :foreground_yellow => '1;33',
27
+ :foreground_blue => '1;34',
28
+ :foreground_magenta => '1;35',
29
+ :foreground_cyan => '1;36',
30
+ :foreground_white => '1;37',
31
+ :foreground_default => '1;39',
32
+ :background_black => '40',
33
+ :background_dark_red => '41',
34
+ :background_dark_green => '42',
35
+ :background_dark_yellow => '43',
36
+ :background_dark_blue => '44',
37
+ :background_dark_magenta => '45',
38
+ :background_dark_cyan => '46',
39
+ :background_light_gray => '47',
40
+ :background_default => '49'}
41
+
42
+ def format(string, *attributes)
43
+ segments = attributes.collect do |a|
44
+ "\x1b[#{FORMATS[a]}m"
45
+ end
46
+ segments << string
47
+ segments << "\x1b[0m"
48
+ segments.join ''
49
+ end
50
+
51
+ def rescuing_from(*exception_classes)
52
+ yield
53
+ rescue Interrupt
54
+ nil
55
+ rescue *exception_classes => e
56
+ $stderr.puts notice(sentence_case(e.message))
57
+ nil
58
+ end
59
+
60
+ def indent(string, column=2)
61
+ "#{' ' * column}#{string}"
62
+ end
63
+
64
+ def logotype
65
+ format ' htty ', :bold, :background_dark_red, :foreground_yellow
66
+ end
67
+
68
+ def notice(string)
69
+ "*** #{string}"
70
+ end
71
+
72
+ def normal(string)
73
+ return string
74
+ # format string, :foreground_dark_default
75
+ end
76
+
77
+ def say(message, style=:normal)
78
+ puts send(style, notice(message))
79
+ end
80
+
81
+ def say_goodbye
82
+ say 'Happy Trails To You!'
83
+ end
84
+
85
+ def say_header(message, style=:normal)
86
+ puts send(style, notice('')) + highlight(messag)
87
+ end
88
+
89
+ def say_hello
90
+ puts normal(notice('Welcome to ')) + logotype + normal(', the ') +
91
+ strong('HTTP TTY') + normal('. Heck To The Yeah!')
92
+ end
93
+
94
+ def show_headers(headers, show_asterisk_next_to=nil)
95
+ asterisk_symbol = nil
96
+ margin = headers.inject 0 do |result, header|
97
+ header_name = header.first
98
+ asterisk_symbol ||= (header_name == show_asterisk_next_to) ? '*' : nil
99
+ asterisk = (header_name == show_asterisk_next_to) ? asterisk_symbol : ''
100
+ [(header_name.length + asterisk.length), result].max
101
+ end
102
+ headers.each do |name, value|
103
+ asterisk = (name == show_asterisk_next_to) ? asterisk_symbol : nil
104
+ puts "#{name.rjust margin - asterisk.to_s.length}:#{strong asterisk} " +
105
+ value
106
+ end
107
+ end
108
+
109
+ def show_request(request)
110
+ method = format(" #{request.request_method.to_s.upcase} ", :inverse)
111
+ print "#{method} "
112
+ cookies_asterisk = request.cookies.empty? ? '' : strong('*')
113
+ body_length = request.body.to_s.length
114
+ body_size = body_length.zero? ? 'empty' : "#{body_length}-character"
115
+ puts [request.uri,
116
+ pluralize('header', request.headers.length) + cookies_asterisk,
117
+ "#{body_size} body"].join(' -- ')
118
+ end
119
+
120
+ def show_response(response)
121
+ code, description = response.status
122
+ case code.to_i
123
+ when 100...200, 300...400
124
+ print format(" #{code} ", :background_dark_blue, :foreground_white)
125
+ when 200...300
126
+ print format(" #{code} ", :background_dark_green, :foreground_black)
127
+ when 500...600
128
+ print format(" #{code} ", :inverse, :blinking,
129
+ :background_black, :foreground_yellow)
130
+ else
131
+ print format(" #{code} ", :background_dark_red, :foreground_white)
132
+ end
133
+ print ' '
134
+ cookies_asterisk = response.cookies.empty? ? '' : strong('*')
135
+ body_length = response.body.to_s.length
136
+ body_size = body_length.zero? ? 'empty' : "#{body_length}-character"
137
+ puts([description,
138
+ pluralize('header', response.headers.length) + cookies_asterisk,
139
+ "#{body_size} body"].join(' -- '))
140
+ end
141
+
142
+ def strong(string)
143
+ format string, :bold
144
+ end
145
+
146
+ def word_wrap(text, column=nil)
147
+ word_wrap_indented(text, (0..(column || 80)))
148
+ end
149
+
150
+ # Adapted from
151
+ # http://api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html#M002281
152
+ def word_wrap_indented(text, columns=2..80)
153
+ indent_by, wrap_at = columns.min, columns.max - columns.min
154
+ text.split("\n").collect do |line|
155
+ (wrap_at < line.length) ?
156
+ line.gsub(/(.{1,#{wrap_at}})(\s+|$)/, "\\1\n").strip :
157
+ line
158
+ end.join("\n").split("\n").collect do |line|
159
+ indent line, indent_by
160
+ end.join "\n"
161
+ end
162
+
163
+ private
164
+
165
+ def pluralize(word, number)
166
+ case number
167
+ when 0
168
+ "no #{word}s"
169
+ when 1
170
+ "1 #{word}"
171
+ else
172
+ "#{number} #{word}s"
173
+ end
174
+ end
175
+
176
+ def sentence_case(text)
177
+ text.gsub(/^./) do |letter|
178
+ letter.upcase
179
+ end
180
+ end
181
+
182
+ end