htty 1.0.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.
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