wrong 0.6.3 → 0.7.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/README.markdown +10 -7
- data/lib/wrong.rb +1 -0
- data/lib/wrong/adapters/rspec.rb +4 -0
- data/lib/wrong/assert.rb +1 -2
- data/lib/wrong/chunk.rb +5 -170
- data/lib/wrong/close_to.rb +7 -1
- data/lib/wrong/config.rb +3 -3
- data/lib/wrong/d.rb +2 -1
- data/lib/wrong/failure_message.rb +109 -7
- data/lib/wrong/message/string_comparison.rb +1 -1
- data/lib/wrong/sexp_ext.rb +2 -1
- data/lib/wrong/terminal.rb +43 -0
- data/lib/wrong/version.rb +1 -1
- data/test/adapters/railsapp/app/controllers/application_controller.rb +3 -0
- data/test/adapters/railsapp/app/helpers/application_helper.rb +2 -0
- data/test/adapters/railsapp/config/application.rb +54 -0
- data/test/adapters/railsapp/config/boot.rb +6 -0
- data/test/adapters/railsapp/config/environment.rb +5 -0
- data/test/adapters/railsapp/config/environments/development.rb +30 -0
- data/test/adapters/railsapp/config/environments/production.rb +60 -0
- data/test/adapters/railsapp/config/environments/test.rb +42 -0
- data/test/adapters/railsapp/config/initializers/backtrace_silencers.rb +7 -0
- data/test/adapters/railsapp/config/initializers/inflections.rb +10 -0
- data/test/adapters/railsapp/config/initializers/mime_types.rb +5 -0
- data/test/adapters/railsapp/config/initializers/secret_token.rb +7 -0
- data/test/adapters/railsapp/config/initializers/session_store.rb +8 -0
- data/test/adapters/railsapp/config/initializers/wrap_parameters.rb +14 -0
- data/test/adapters/railsapp/config/routes.rb +58 -0
- data/test/adapters/railsapp/db/seeds.rb +7 -0
- data/test/adapters/railsapp/spec/spec_helper.rb +33 -0
- data/test/adapters/railsapp/spec/wrong_spec.rb +8 -0
- data/test/adapters/rspec_rails_test.rb +4 -4
- data/test/adapters/rspec_test.rb +2 -2
- data/test/assert_advanced_test.rb +9 -1
- data/test/chunk_test.rb +0 -200
- data/test/close_to_test.rb +34 -2
- data/test/d_test.rb +2 -2
- data/test/eventually_test.rb +27 -34
- data/test/failure_message_test.rb +212 -13
- data/test/test_helper.rb +2 -2
- metadata +47 -56
- data/lib/wrong/ruby2ruby_patch.rb +0 -37
data/README.markdown
CHANGED
@@ -100,6 +100,8 @@ If you want to compare floats, try this:
|
|
100
100
|
|
101
101
|
If you don't want `close_to?` cluttering up `Float` in your test runs then use `include Wrong::Assert` instead of `include Wrong`.
|
102
102
|
|
103
|
+
`close_to?` also works on `Time`s, `Date`s, and `DateTime`s. (The default tolerance of 1 msec may be too small for you.)
|
104
|
+
|
103
105
|
### d
|
104
106
|
|
105
107
|
We also implement the most amazing debugging method ever, `d`, which gives you a sort of mini-wrong wherever you want it
|
@@ -251,29 +253,30 @@ Wrong is compatible with RSpec and MiniTest::Spec, and probably Cucumber too, so
|
|
251
253
|
Here's an RSpec example:
|
252
254
|
|
253
255
|
require "wrong/adapters/rspec"
|
254
|
-
Wrong.config.alias_assert :
|
256
|
+
Wrong.config.alias_assert :expect, override: true
|
255
257
|
|
256
258
|
describe BleuCheese do
|
257
259
|
it "stinks" do
|
258
|
-
|
260
|
+
cheese = BleuCheese.new
|
261
|
+
expect { cheese.smell > 9000 }
|
259
262
|
end
|
260
263
|
end
|
261
264
|
|
262
265
|
This makes your code read like a BDD-style DSL, without RSpec's "should" syntax (which is, let's face it, pretty weird the first few hundred times you have to use it). Compare
|
263
266
|
|
264
|
-
|
267
|
+
expect { cheese.smell > 9000 }
|
265
268
|
|
266
269
|
to
|
267
270
|
|
268
|
-
|
271
|
+
cheese.smell.should be > 9000
|
269
272
|
|
270
|
-
and consider which one more clearly describes the desired behavior. The object under test doesn't really have a `should` method, so why should it magically get one during a test?
|
273
|
+
and consider which one more clearly describes the desired behavior. The object under test doesn't really have a `should` method, so why should it magically get one during a test?
|
271
274
|
|
272
275
|
Warning: currently the use of `alias_assert :expect` is **not** compatible with RSpec, since RSpec also defines `expect` as a synonym for `lambda`. If you really want to use `expect` as an alias form `assert`, then use `Wrong.config.alias_assert :expect, :override => true`. See [issue #6](https://github.com/sconover/wrong/issues/6) for more details.
|
273
276
|
|
274
277
|
## Algorithm ##
|
275
278
|
|
276
|
-
So wait a second. How do we do it? Doesn't Ruby have [poor support for AST introspection](http://blog.zenspider.com/2009/04/parsetree-eol.html)? Well, yes, it does, so we cheat: we figure out what file and line the assert block is defined in, then open the file, read the code, and parse it directly using Ryan Davis' amazing [RubyParser](http://parsetree.rubyforge.org/ruby_parser/) and [Ruby2Ruby](http://seattlerb.rubyforge.org/ruby2ruby/). You can bask in the kludge by examining `chunk.rb` and `assert.rb`. If you find some code it can't parse, please send it our way.
|
279
|
+
So wait a second. How do we do it? Doesn't Ruby have [poor support for AST introspection](http://blog.zenspider.com/2009/04/parsetree-eol.html)? Well, yes, it does, so we cheat: we figure out what file and line the assert block is defined in, then open the file, read the code, and parse it directly using Ryan Davis' amazing [RubyParser](http://parsetree.rubyforge.org/ruby_parser/) and [Ruby2Ruby](http://seattlerb.rubyforge.org/ruby2ruby/). You can bask in the kludge by examining `chunk.rb` and `assert.rb`. If you find some code it can't parse, please send it our way.
|
277
280
|
|
278
281
|
Before you get your knickers in a twist about how this is totally unacceptable because it doesn't support this or that use case, here are our caveats and excuses:
|
279
282
|
|
@@ -462,7 +465,7 @@ If you're in Ruby 1.8, you **really** shouldn't do it! But if you do, you can us
|
|
462
465
|
|
463
466
|
## Bugs ##
|
464
467
|
|
465
|
-
*
|
468
|
+
* see Github Issues <http://github.com/sconover/wrong/issues>
|
466
469
|
|
467
470
|
## todo ##
|
468
471
|
|
data/lib/wrong.rb
CHANGED
data/lib/wrong/adapters/rspec.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "wrong"
|
2
|
+
require "wrong/terminal"
|
2
3
|
|
3
4
|
if Object.const_defined? :RSpec
|
4
5
|
# RSpec 2
|
@@ -58,6 +59,9 @@ if Object.const_defined? :RSpec
|
|
58
59
|
end
|
59
60
|
end
|
60
61
|
|
62
|
+
# tweak terminal width so exceptions wrap properly inside RSpec output
|
63
|
+
Wrong::Terminal.width -= 7
|
64
|
+
|
61
65
|
elsif Object.const_defined? :Spec
|
62
66
|
# RSpec 1
|
63
67
|
Spec::Runner.configure do |config|
|
data/lib/wrong/assert.rb
CHANGED
@@ -5,7 +5,6 @@ require "predicated/to/sentence"
|
|
5
5
|
require "wrong/chunk"
|
6
6
|
require "wrong/config"
|
7
7
|
require "wrong/failure_message"
|
8
|
-
require "wrong/ruby2ruby_patch" # need to patch it after some other stuff loads
|
9
8
|
require "wrong/rainbow"
|
10
9
|
|
11
10
|
module Wrong
|
@@ -68,7 +67,7 @@ module Wrong
|
|
68
67
|
value = !value if valence == :deny
|
69
68
|
if value
|
70
69
|
if Wrong.config[:verbose]
|
71
|
-
code = Wrong::Chunk.from_block(block, depth + 2).code
|
70
|
+
code = Wrong::Chunk.from_block(block, depth + 2).code
|
72
71
|
if Wrong.config[:color]
|
73
72
|
explanation = explanation.color(:blue) if explanation
|
74
73
|
code = code.color(:green)
|
data/lib/wrong/chunk.rb
CHANGED
@@ -2,18 +2,6 @@ require 'ruby_parser'
|
|
2
2
|
require 'ruby2ruby'
|
3
3
|
require 'pp'
|
4
4
|
|
5
|
-
def require_optionally(library)
|
6
|
-
begin
|
7
|
-
require library
|
8
|
-
rescue LoadError => e
|
9
|
-
raise e unless e.message == "no such file to load -- #{library}" or
|
10
|
-
e.message == "cannot load such file -- #{library}" # 1.9.3 changed the error message
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
require_optionally "ParseTree"
|
15
|
-
require_optionally "sourcify"
|
16
|
-
|
17
5
|
require "wrong/config"
|
18
6
|
require "wrong/sexp_ext"
|
19
7
|
require "wrong/capturing"
|
@@ -60,21 +48,11 @@ module Wrong
|
|
60
48
|
end
|
61
49
|
|
62
50
|
def build_sexp
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
rescue Exception => e
|
69
|
-
# sourcify failed, so fall through
|
70
|
-
end
|
71
|
-
|
72
|
-
# next try glomming
|
73
|
-
sexp ||= glom(if @file == "(irb)"
|
74
|
-
IRB.CurrentContext.all_lines
|
75
|
-
else
|
76
|
-
read_source_file(@file)
|
77
|
-
end)
|
51
|
+
glom(if @file == "(irb)"
|
52
|
+
IRB.CurrentContext.all_lines
|
53
|
+
else
|
54
|
+
read_source_file(@file)
|
55
|
+
end)
|
78
56
|
end
|
79
57
|
|
80
58
|
def read_source_file(file)
|
@@ -95,7 +73,6 @@ module Wrong
|
|
95
73
|
while sexp.nil? && line_index + c < lines.size
|
96
74
|
begin
|
97
75
|
@chunk = lines[line_index..line_index+c].join("\n")
|
98
|
-
|
99
76
|
capturing(:stderr) do # new RubyParser is too loud
|
100
77
|
sexp = @parser.parse(@chunk)
|
101
78
|
end
|
@@ -164,148 +141,6 @@ module Wrong
|
|
164
141
|
end
|
165
142
|
end
|
166
143
|
|
167
|
-
def details
|
168
|
-
@details ||= build_details
|
169
|
-
end
|
170
|
-
|
171
|
-
def pretty_value(value, starting_col = 0, indent_wrapped_lines = 6, width = Chunk.terminal_width)
|
172
|
-
# inspected = value.inspect
|
173
|
-
|
174
|
-
# note that if the first line overflows due to the starting column then pp won't wrap it right
|
175
|
-
inspected = PP.pp(value, "", width - starting_col).chomp
|
176
|
-
|
177
|
-
# this bit might be redundant with the pp call now
|
178
|
-
indented = indent_all(6, inspected)
|
179
|
-
if width
|
180
|
-
wrap_and_indent(indented, starting_col, indent_wrapped_lines, width)
|
181
|
-
else
|
182
|
-
indented
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
private
|
187
|
-
|
188
|
-
# todo: move to FailureMessage?
|
189
|
-
def build_details
|
190
|
-
require "wrong/rainbow" if Wrong.config[:color]
|
191
|
-
s = ""
|
192
|
-
parts = self.parts
|
193
|
-
parts.shift # remove the first part, since it's the same as the code
|
194
|
-
|
195
|
-
details = []
|
196
|
-
|
197
|
-
if parts.size > 0
|
198
|
-
parts.each do |part|
|
199
|
-
begin
|
200
|
-
value = eval(part, block.binding)
|
201
|
-
unless part == value.inspect # this skips literals or tautologies
|
202
|
-
if part =~ /\n/m
|
203
|
-
part.gsub!(/\n/, newline(2))
|
204
|
-
part += newline(3)
|
205
|
-
end
|
206
|
-
value = pretty_value(value, (4 + part.length + 4))
|
207
|
-
if Wrong.config[:color]
|
208
|
-
part = part.color(:blue)
|
209
|
-
value = value.color(:magenta)
|
210
|
-
end
|
211
|
-
details << indent(4, part, " is ", value)
|
212
|
-
end
|
213
|
-
rescue Exception => e
|
214
|
-
raises = "raises #{e.class}"
|
215
|
-
if Wrong.config[:color]
|
216
|
-
part = part.color(:blue)
|
217
|
-
raises = raises.bold.color(:red)
|
218
|
-
end
|
219
|
-
formatted_exeption = if e.message and e.message != e.class.to_s
|
220
|
-
indent(4, part, " ", raises, ": ", indent_all(6, e.message))
|
221
|
-
else
|
222
|
-
indent(4, part, " ", raises)
|
223
|
-
end
|
224
|
-
details << formatted_exeption
|
225
|
-
end
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
details.uniq!
|
230
|
-
if details.empty?
|
231
|
-
""
|
232
|
-
else
|
233
|
-
"\n" + details.join("\n") + "\n"
|
234
|
-
end
|
235
|
-
|
236
|
-
end
|
237
|
-
|
238
|
-
public # don't know exactly why this needs to be public but eval'ed code can't find it otherwise
|
239
|
-
def indent(indent, *s)
|
240
|
-
"#{" " * indent}#{s.join('')}"
|
241
|
-
end
|
242
|
-
|
243
|
-
def newline(indent)
|
244
|
-
"\n" + self.indent(indent)
|
245
|
-
end
|
246
|
-
|
247
|
-
def indent_all(amount, s)
|
248
|
-
s.gsub("\n", "\n#{indent(amount)}")
|
249
|
-
end
|
250
|
-
|
251
|
-
def wrap_and_indent(indented, starting_col, indent_wrapped_lines, full_width)
|
252
|
-
first_line = true
|
253
|
-
width = full_width - starting_col # the first line is essentially shorter
|
254
|
-
indented.split("\n").map do |line|
|
255
|
-
s = ""
|
256
|
-
while line.length > width
|
257
|
-
s << line[0...width]
|
258
|
-
s << newline(indent_wrapped_lines)
|
259
|
-
line = line[width..-1]
|
260
|
-
if first_line
|
261
|
-
width += starting_col - indent_wrapped_lines
|
262
|
-
first_line = false
|
263
|
-
end
|
264
|
-
end
|
265
|
-
s << line
|
266
|
-
s
|
267
|
-
end.join("\n")
|
268
|
-
end
|
269
|
-
|
270
|
-
# Returns [width, height] of terminal when detected, nil if not detected.
|
271
|
-
# Think of this as a simpler version of Highline's Highline::SystemExtensions.terminal_size()
|
272
|
-
# Lifted from https://github.com/cldwalker/hirb/blob/master/lib/hirb/util.rb#L59
|
273
|
-
#
|
274
|
-
# See also http://stackoverflow.com/questions/2068859/how-to-get-the-width-of-terminal-window-in-ruby
|
275
|
-
# https://github.com/genki/ruby-terminfo/blob/master/lib/terminfo.rb
|
276
|
-
# http://www.mkssoftware.com/docs/man1/stty.1.asp
|
277
|
-
|
278
|
-
|
279
|
-
def self.terminal_size
|
280
|
-
@@terminal_size ||= begin
|
281
|
-
if (ENV['COLUMNS'] =~ /^\d+$/) && (ENV['LINES'] =~ /^\d+$/)
|
282
|
-
[ENV['COLUMNS'].to_i, ENV['LINES'].to_i]
|
283
|
-
elsif (RUBY_PLATFORM =~ /java/ || (!STDIN.tty? && ENV['TERM'])) && command_exists?('tput')
|
284
|
-
[`tput cols`.to_i, `tput lines`.to_i]
|
285
|
-
elsif STDIN.tty? && command_exists?('stty')
|
286
|
-
`stty size`.scan(/\d+/).map { |s| s.to_i }.reverse
|
287
|
-
else
|
288
|
-
nil
|
289
|
-
end
|
290
|
-
rescue
|
291
|
-
nil
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
|
-
def self.terminal_width
|
296
|
-
(@terminal_width ||= nil) || (terminal_size && terminal_size.first) || 80
|
297
|
-
end
|
298
|
-
|
299
|
-
def self.terminal_width= forced_with
|
300
|
-
@terminal_width = forced_with
|
301
|
-
end
|
302
|
-
|
303
|
-
# Determines if a shell command exists by searching for it in ENV['PATH'].
|
304
|
-
def self.command_exists?(command)
|
305
|
-
ENV['PATH'].split(File::PATH_SEPARATOR).any? {|d| File.exists? File.join(d, command) }
|
306
|
-
end
|
307
|
-
|
308
|
-
|
309
144
|
end
|
310
145
|
|
311
146
|
end
|
data/lib/wrong/close_to.rb
CHANGED
@@ -1,8 +1,14 @@
|
|
1
1
|
module Wrong
|
2
2
|
module CloseTo
|
3
3
|
def close_to?(other, tolerance = 0.001)
|
4
|
-
|
4
|
+
if respond_to? :to_f
|
5
|
+
(self.to_f - other.to_f).abs < tolerance
|
6
|
+
elsif respond_to? :to_time
|
7
|
+
self.to_time.close_to?( other.to_time, tolerance)
|
8
|
+
end
|
5
9
|
end
|
6
10
|
end
|
7
11
|
Numeric.send :include, CloseTo
|
12
|
+
Date.send :include, CloseTo
|
13
|
+
Time.send :include, CloseTo
|
8
14
|
end
|
data/lib/wrong/config.rb
CHANGED
@@ -10,7 +10,7 @@ module Wrong
|
|
10
10
|
end
|
11
11
|
Config.new settings
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def self.config
|
15
15
|
@config ||= load_config
|
16
16
|
end
|
@@ -72,8 +72,8 @@ module Wrong
|
|
72
72
|
self[:aliases][:deny]
|
73
73
|
end
|
74
74
|
|
75
|
-
def
|
76
|
-
assert_method_names + deny_method_names
|
75
|
+
def hidden_methods
|
76
|
+
assert_method_names + deny_method_names + [:eventually]
|
77
77
|
end
|
78
78
|
end
|
79
79
|
end
|
data/lib/wrong/d.rb
CHANGED
@@ -21,6 +21,7 @@ module Wrong
|
|
21
21
|
|
22
22
|
# look for a "d" inside the block
|
23
23
|
sexp.each_subexp do |subexp|
|
24
|
+
#sexp.deep_each do |subexp| # todo: try to use deep_each
|
24
25
|
if subexp.d?
|
25
26
|
sexp = subexp[3] # swap in the block part of the nested d call
|
26
27
|
end
|
@@ -28,7 +29,7 @@ module Wrong
|
|
28
29
|
|
29
30
|
code = sexp.to_ruby
|
30
31
|
value = eval(code, block.binding, called_from[0], called_from[1].to_i)
|
31
|
-
width =
|
32
|
+
width = Terminal.width
|
32
33
|
value = PP.pp(value, "", width - (code.size + 3)).chomp
|
33
34
|
|
34
35
|
if Wrong.config[:color]
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require "wrong/chunk"
|
2
|
+
require "wrong/terminal"
|
3
|
+
|
1
4
|
module Wrong
|
2
5
|
class FailureMessage
|
3
6
|
@@formatters = []
|
@@ -40,7 +43,6 @@ module Wrong
|
|
40
43
|
end
|
41
44
|
end
|
42
45
|
|
43
|
-
|
44
46
|
attr_accessor :chunk, :valence, :explanation
|
45
47
|
|
46
48
|
def initialize(chunk, valence, explanation)
|
@@ -57,20 +59,89 @@ module Wrong
|
|
57
59
|
message << basic
|
58
60
|
|
59
61
|
formatted_message = if predicate && !(predicate.is_a? Predicated::Conjunction)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
62
|
+
if formatter = FailureMessage.formatter_for(predicate)
|
63
|
+
colored(:bold, formatter.describe)
|
64
|
+
end
|
65
|
+
end
|
64
66
|
|
65
|
-
unless
|
67
|
+
unless details.empty? and formatted_message.nil?
|
66
68
|
message << ", but"
|
67
69
|
end
|
68
70
|
|
69
71
|
message << formatted_message if formatted_message
|
70
|
-
message <<
|
72
|
+
message << details unless details.empty?
|
71
73
|
message
|
72
74
|
end
|
73
75
|
|
76
|
+
def details
|
77
|
+
@details ||= begin
|
78
|
+
require "wrong/rainbow" if Wrong.config[:color]
|
79
|
+
s = ""
|
80
|
+
parts = chunk.parts
|
81
|
+
|
82
|
+
parts.shift while parts.first == "()" # the parser adds this sometimes
|
83
|
+
parts.shift # remove the first part, since it's the same as the code
|
84
|
+
|
85
|
+
details = []
|
86
|
+
|
87
|
+
if parts.size > 0
|
88
|
+
parts.each do |part|
|
89
|
+
begin
|
90
|
+
value = eval(part, chunk.block.binding)
|
91
|
+
unless part == value.inspect # this skips literals or tautologies
|
92
|
+
if part =~ /\n/m
|
93
|
+
part.gsub!(/\n/, newline(2))
|
94
|
+
part += newline(3)
|
95
|
+
end
|
96
|
+
value = pretty_value(value, (4 + part.length + 4))
|
97
|
+
if Wrong.config[:color]
|
98
|
+
part = part.color(:blue)
|
99
|
+
value = value.color(:magenta)
|
100
|
+
end
|
101
|
+
details << indent(4, part, " is ", value)
|
102
|
+
end
|
103
|
+
rescue Exception => e
|
104
|
+
raises = "raises #{e.class}"
|
105
|
+
if Wrong.config[:color]
|
106
|
+
part = part.color(:blue)
|
107
|
+
raises = raises.bold.color(:red)
|
108
|
+
end
|
109
|
+
formatted_exeption = if e.message and e.message != e.class.to_s
|
110
|
+
indent(4, part, " ", raises, ": ", indent_all(6, e.message))
|
111
|
+
else
|
112
|
+
indent(4, part, " ", raises)
|
113
|
+
end
|
114
|
+
details << formatted_exeption
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
details.uniq!
|
120
|
+
if details.empty?
|
121
|
+
""
|
122
|
+
else
|
123
|
+
"\n" + details.join("\n") + "\n"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
# todo: use awesome_print
|
130
|
+
def pretty_value(value, starting_col = 0, indent_wrapped_lines = 6, width = Terminal.width)
|
131
|
+
# inspected = value.inspect
|
132
|
+
|
133
|
+
# note that if the first line overflows due to the starting column then pp won't wrap it right
|
134
|
+
inspected = PP.pp(value, "", width - starting_col).chomp
|
135
|
+
|
136
|
+
# this bit might be redundant with the pp call now
|
137
|
+
indented = indent_all(6, inspected)
|
138
|
+
if width
|
139
|
+
wrap_and_indent(indented, starting_col, indent_wrapped_lines, width)
|
140
|
+
else
|
141
|
+
indented
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
74
145
|
protected
|
75
146
|
def code
|
76
147
|
@code ||= chunk.code
|
@@ -98,5 +169,36 @@ module Wrong
|
|
98
169
|
end
|
99
170
|
end
|
100
171
|
|
172
|
+
def indent(indent, *s)
|
173
|
+
"#{" " * indent}#{s.join('')}"
|
174
|
+
end
|
175
|
+
|
176
|
+
def newline(indent)
|
177
|
+
"\n" + self.indent(indent)
|
178
|
+
end
|
179
|
+
|
180
|
+
def indent_all(amount, s)
|
181
|
+
s.gsub("\n", "\n#{indent(amount)}")
|
182
|
+
end
|
183
|
+
|
184
|
+
def wrap_and_indent(indented, starting_col, indent_wrapped_lines, full_width)
|
185
|
+
first_line = true
|
186
|
+
width = full_width - starting_col # the first line is essentially shorter
|
187
|
+
indented.split("\n").map do |line|
|
188
|
+
s = ""
|
189
|
+
while line.length > width
|
190
|
+
s << line[0...width]
|
191
|
+
s << newline(indent_wrapped_lines)
|
192
|
+
line = line[width..-1]
|
193
|
+
if first_line
|
194
|
+
width += starting_col - indent_wrapped_lines
|
195
|
+
first_line = false
|
196
|
+
end
|
197
|
+
end
|
198
|
+
s << line
|
199
|
+
s
|
200
|
+
end.join("\n")
|
201
|
+
end
|
202
|
+
|
101
203
|
end
|
102
204
|
end
|