cute_print 0.1.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.
- checksums.yaml +7 -0
- data/.config/cucumber.yml +1 -0
- data/.rspec +1 -0
- data/.yardopts +6 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +99 -0
- data/LICENSE +20 -0
- data/README.md +109 -0
- data/Rakefile +14 -0
- data/VERSION +1 -0
- data/basic101.gemspec +64 -0
- data/cute_print.gemspec +96 -0
- data/features/.nav +6 -0
- data/features/configuring/configure_output.feature +21 -0
- data/features/configuring/configure_position_format.feature +30 -0
- data/features/configuring/readme.md +1 -0
- data/features/configuring/reset_configuration.feature +27 -0
- data/features/inspect_call_chain.feature +39 -0
- data/features/inspect_objects/inspect.feature +29 -0
- data/features/inspect_objects/label_and_inspect.feature +16 -0
- data/features/inspect_objects/print_source_location.feature +41 -0
- data/features/inspect_objects/readme.md +1 -0
- data/features/readme.md +1 -0
- data/features/support/env.rb +9 -0
- data/features/support/helpers/example.rb +54 -0
- data/features/support/helpers/temp_dir.rb +15 -0
- data/features/support/step_definitions.rb +23 -0
- data/lib/cute_print/configure.rb +37 -0
- data/lib/cute_print/core_ext/object.rb +5 -0
- data/lib/cute_print/core_ext.rb +1 -0
- data/lib/cute_print/default_printer.rb +14 -0
- data/lib/cute_print/finds_foreign_caller.rb +19 -0
- data/lib/cute_print/mixin.rb +44 -0
- data/lib/cute_print/printer.rb +106 -0
- data/lib/cute_print/ruby_generator.rb +17 -0
- data/lib/cute_print/ruby_parser/block.rb +12 -0
- data/lib/cute_print/ruby_parser/method_call.rb +27 -0
- data/lib/cute_print/ruby_parser/parsed_code.rb +38 -0
- data/lib/cute_print/ruby_parser/wraps_sexp.rb +19 -0
- data/lib/cute_print/ruby_parser.rb +60 -0
- data/lib/cute_print/stderr_out.rb +13 -0
- data/lib/cute_print.rb +26 -0
- data/spec/cute_print_spec.rb +69 -0
- data/spec/printer_spec.rb +64 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/captures_stderr.rb +5 -0
- data/tasks/cucumber.rake +8 -0
- data/tasks/default.rake +1 -0
- data/tasks/jeweler.rake +29 -0
- data/tasks/spec.rake +5 -0
- data/tasks/test.rake +2 -0
- data/tasks/yard.rake +3 -0
- data/test_support/captures_stderr.rb +16 -0
- data/test_support/captures_stdout.rb +16 -0
- metadata +130 -0
@@ -0,0 +1,9 @@
|
|
1
|
+
require "rspec/expectations"
|
2
|
+
|
3
|
+
# Instead of letting cucumber load all of the .rb, we configure
|
4
|
+
# cucumber to require them (see .config/cucumber.yml).
|
5
|
+
glob = File.join(File.dirname(__FILE__), '**/*.rb')
|
6
|
+
ruby_paths = Dir[glob].sort
|
7
|
+
ruby_paths.each do |path|
|
8
|
+
require path
|
9
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative "../../../test_support/captures_stderr.rb"
|
2
|
+
require_relative "../../../test_support/captures_stdout.rb"
|
3
|
+
|
4
|
+
require_relative "temp_dir"
|
5
|
+
|
6
|
+
class Example
|
7
|
+
|
8
|
+
include CapturesStderr
|
9
|
+
include CapturesStdout
|
10
|
+
|
11
|
+
def initialize(contents, opts = {})
|
12
|
+
@contents = contents
|
13
|
+
@filename = opts.fetch(:filename, "example.rb")
|
14
|
+
@temp_dir = TempDir.new
|
15
|
+
create_file
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
@stdout = capture_stdout do
|
20
|
+
@stderr = capture_stderr do
|
21
|
+
load path
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def stderr
|
27
|
+
filter_output(@stderr)
|
28
|
+
end
|
29
|
+
|
30
|
+
def stdout
|
31
|
+
filter_output(@stdout)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def create_file
|
37
|
+
File.open(path, 'w') do |file|
|
38
|
+
file.write @contents
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def path
|
43
|
+
File.join(@temp_dir.path, @filename)
|
44
|
+
end
|
45
|
+
|
46
|
+
def filter_output(output)
|
47
|
+
redact_tmp_path(output)
|
48
|
+
end
|
49
|
+
|
50
|
+
def redact_tmp_path(output)
|
51
|
+
output.sub(/\/tmp\/.*\//, "/tmp/.../")
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative "../../lib/cute_print"
|
2
|
+
|
3
|
+
Before do
|
4
|
+
CutePrint.configure { |c| c.reset }
|
5
|
+
end
|
6
|
+
|
7
|
+
Given(/^a file with:$/) do |contents|
|
8
|
+
@example = Example.new(contents)
|
9
|
+
@example.run
|
10
|
+
end
|
11
|
+
|
12
|
+
Given(/^a file named "(.*?)" with:$/) do |filename, contents|
|
13
|
+
@example = Example.new(contents, :filename => filename)
|
14
|
+
@example.run
|
15
|
+
end
|
16
|
+
|
17
|
+
Then(/^stderr should be$/) do |expected|
|
18
|
+
expect(@example.stderr).to eq expected
|
19
|
+
end
|
20
|
+
|
21
|
+
Then(/^stdout should be$/) do |expected|
|
22
|
+
expect(@example.stdout).to eq expected
|
23
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
module CutePrint
|
4
|
+
|
5
|
+
class Configure
|
6
|
+
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
# Configure an instance of printer.
|
10
|
+
# @api private
|
11
|
+
# @param [Printer] printer
|
12
|
+
# @yield [Configure]
|
13
|
+
def initialize(printer)
|
14
|
+
@printer = printer
|
15
|
+
yield self
|
16
|
+
end
|
17
|
+
|
18
|
+
def_delegator :@printer, :set_defaults, :reset
|
19
|
+
|
20
|
+
def self.delegate_accessor(name)
|
21
|
+
def_delegator :@printer, name
|
22
|
+
def_delegator :@printer, "#{name}="
|
23
|
+
end
|
24
|
+
|
25
|
+
# @!attribute [rw] position_format
|
26
|
+
# @return [String] The position format
|
27
|
+
# @see Printer#position_format
|
28
|
+
delegate_accessor :position_format
|
29
|
+
|
30
|
+
# @!attribute [rw] out
|
31
|
+
# @return [#puts] The file to write to
|
32
|
+
# @see Printer#out
|
33
|
+
delegate_accessor :out
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "core_ext/object"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CutePrint
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
module FindsForeignCaller
|
5
|
+
|
6
|
+
def nearest_foreign_caller
|
7
|
+
caller.find do |s|
|
8
|
+
path = s.split(":").first
|
9
|
+
!File.expand_path(path).include?(File.expand_path(lib_path))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def lib_path
|
14
|
+
File.dirname(__FILE__)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative "default_printer"
|
2
|
+
require_relative "printer"
|
3
|
+
|
4
|
+
module CutePrint
|
5
|
+
|
6
|
+
# Methods mixed into Object, and so available globally.
|
7
|
+
#
|
8
|
+
# @note The methods in this module are part of the public API, but
|
9
|
+
# the module itself is not.
|
10
|
+
|
11
|
+
module Mixin
|
12
|
+
|
13
|
+
# @see Printer#q
|
14
|
+
def q(*args, &block)
|
15
|
+
CutePrint::DefaultPrinter.printer.q(*args, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
# @see Printer#ql
|
19
|
+
def ql(*args, &block)
|
20
|
+
CutePrint::DefaultPrinter.printer.ql(*args, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @see Printer#qq
|
24
|
+
def qq(*args, &block)
|
25
|
+
CutePrint::DefaultPrinter.printer.qq(*args, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Debug a call chain by printing self and then returning self.
|
29
|
+
# @return [Object] self
|
30
|
+
def tapq
|
31
|
+
q self
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
# Debug a call chain by printing self, with source position, and
|
36
|
+
# then returning self.
|
37
|
+
# @return [Object] self
|
38
|
+
def tapql
|
39
|
+
ql self
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require_relative "finds_foreign_caller"
|
2
|
+
require_relative "ruby_parser"
|
3
|
+
require_relative "stderr_out"
|
4
|
+
|
5
|
+
module CutePrint
|
6
|
+
class Printer
|
7
|
+
|
8
|
+
include FindsForeignCaller
|
9
|
+
|
10
|
+
DEFAULT_POSITION_FORMAT = "%<filename>s:%<line_number>d: "
|
11
|
+
|
12
|
+
# The object to write to. Defaults to $stderr.
|
13
|
+
# @return [#puts]
|
14
|
+
attr_accessor :out
|
15
|
+
|
16
|
+
# The position format. To format a position, String#% is called
|
17
|
+
# with a hash having these keys:
|
18
|
+
#
|
19
|
+
# * :path
|
20
|
+
# * :filename
|
21
|
+
# * :line_number
|
22
|
+
#
|
23
|
+
# The default format prints the filename and line number.
|
24
|
+
# @see Printer::DEFAULT_POSITION_FORMAT
|
25
|
+
# @return [String]
|
26
|
+
attr_accessor :position_format
|
27
|
+
|
28
|
+
# Create an instance. If attributes are supplied, they override
|
29
|
+
# the defaults. For example:
|
30
|
+
#
|
31
|
+
# CutePrint.new(:out => $stdout)
|
32
|
+
#
|
33
|
+
# @api private
|
34
|
+
def initialize(attrs = {})
|
35
|
+
set_defaults
|
36
|
+
attrs.each { |name, value| send "#{name}=", value }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Set all attributes to their defaults.
|
40
|
+
def set_defaults
|
41
|
+
@out = StderrOut.new
|
42
|
+
@position_format = DEFAULT_POSITION_FORMAT
|
43
|
+
end
|
44
|
+
|
45
|
+
# Inspect and write one or more objects.
|
46
|
+
#
|
47
|
+
# If called without a block, prints the inspected arguments, one
|
48
|
+
# on a line.
|
49
|
+
#
|
50
|
+
# If called with a block, prints the source code of the block and
|
51
|
+
# the inspected result of the block.
|
52
|
+
def q(*values, &block)
|
53
|
+
print(__method__, values, block) do |line|
|
54
|
+
@out.puts line
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Inspect and write one or more objects, with source position.
|
59
|
+
#
|
60
|
+
# If called without a block, prints the inspected arguments, one
|
61
|
+
# on a line.
|
62
|
+
#
|
63
|
+
# If called with a block, prints the source code of the block and
|
64
|
+
# the inspected result of the block.
|
65
|
+
def ql(*values, &block)
|
66
|
+
path, line_number = nearest_foreign_caller.split(":")
|
67
|
+
line_number = line_number.to_i
|
68
|
+
print(__method__, values, block) do |line|
|
69
|
+
position = format_position(path, line_number)
|
70
|
+
@out.puts "#{position}#{line}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def print(method, values, block)
|
77
|
+
if block && !values.empty?
|
78
|
+
raise ArgumentError, "arguments and block are mutually exclusive"
|
79
|
+
end
|
80
|
+
if block
|
81
|
+
ruby_parser = RubyParser.from_block(block)
|
82
|
+
parsed_code = ruby_parser.parse
|
83
|
+
method_call = parsed_code.first_call_to_method(method)
|
84
|
+
block_code = method_call.block.to_ruby
|
85
|
+
yield "%s is %s" % [
|
86
|
+
block_code,
|
87
|
+
block.call.inspect,
|
88
|
+
]
|
89
|
+
else
|
90
|
+
values.each do |value|
|
91
|
+
yield value.inspect
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def format_position(path, line_number)
|
97
|
+
position_values = {
|
98
|
+
path: path,
|
99
|
+
filename: File.basename(path),
|
100
|
+
line_number: line_number,
|
101
|
+
}
|
102
|
+
@position_format % position_values
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative "block"
|
2
|
+
require_relative "wraps_sexp"
|
3
|
+
|
4
|
+
module CutePrint
|
5
|
+
class RubyParser
|
6
|
+
|
7
|
+
# @api private
|
8
|
+
class MethodCall
|
9
|
+
|
10
|
+
include WrapsSexp
|
11
|
+
|
12
|
+
def self.call_to_method?(sexp, method_name)
|
13
|
+
call?(sexp) && sexp[1][2] == method_name
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.call?(sexp)
|
17
|
+
sexp[0] == :iter && sexp[1][0] == :call
|
18
|
+
end
|
19
|
+
|
20
|
+
def block
|
21
|
+
Block.new(@sexp[3])
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative "method_call"
|
2
|
+
require_relative "wraps_sexp"
|
3
|
+
|
4
|
+
module CutePrint
|
5
|
+
class RubyParser
|
6
|
+
|
7
|
+
# How this class works is cribbed this excellent code:
|
8
|
+
#
|
9
|
+
# https://github.com/sconover/wrong/blob/30475fc5ac9d0f73135d229b1b44c045156a7e7a/lib/wrong/d.rb
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
|
13
|
+
class ParsedCode
|
14
|
+
|
15
|
+
include WrapsSexp
|
16
|
+
|
17
|
+
def first_call_to_method(method_name)
|
18
|
+
MethodCall.new(method_call_node(method_name))
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def method_call_node(method_name)
|
24
|
+
if MethodCall.call_to_method?(@sexp, method_name)
|
25
|
+
return @sexp
|
26
|
+
end
|
27
|
+
@sexp.each_sexp do |node|
|
28
|
+
if MethodCall.call_to_method?(node, method_name)
|
29
|
+
return node
|
30
|
+
end
|
31
|
+
end
|
32
|
+
raise "Method call not found"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative "../ruby_generator"
|
2
|
+
|
3
|
+
module CutePrint
|
4
|
+
class RubyParser
|
5
|
+
|
6
|
+
# @api private
|
7
|
+
module WrapsSexp
|
8
|
+
|
9
|
+
def initialize(sexp)
|
10
|
+
@sexp = sexp
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_ruby
|
14
|
+
RubyGenerator.to_ruby(@sexp)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "ruby_parser"
|
2
|
+
|
3
|
+
require_relative "ruby_parser/parsed_code"
|
4
|
+
|
5
|
+
module CutePrint
|
6
|
+
|
7
|
+
# This class is very much cribbed from this excellent code:
|
8
|
+
#
|
9
|
+
# https://github.com/sconover/wrong/blob/30475fc5ac9d0f73135d229b1b44c045156a7e7a/lib/wrong/chunk.rb
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
|
13
|
+
class RubyParser
|
14
|
+
|
15
|
+
def self.from_block(block)
|
16
|
+
path, line_number = block.to_proc.source_location
|
17
|
+
new(path, line_number, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(path, line_number, &block)
|
21
|
+
@path = path
|
22
|
+
@line_number = line_number
|
23
|
+
@block = block
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse
|
27
|
+
@parsed ||= parse_source(read_source)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def read_source
|
33
|
+
File.read(@path)
|
34
|
+
end
|
35
|
+
|
36
|
+
def parser
|
37
|
+
@parser ||= ::RubyParser.new
|
38
|
+
end
|
39
|
+
|
40
|
+
# Try parsing just the starting line. While that doesn't work,
|
41
|
+
# add another line and try again.
|
42
|
+
def parse_source(source)
|
43
|
+
lines = source.lines.to_a
|
44
|
+
starting_line_index = @line_number - 1
|
45
|
+
ending_line_index = starting_line_index
|
46
|
+
sexp = nil
|
47
|
+
while !sexp && ending_line_index < lines.size
|
48
|
+
begin
|
49
|
+
snippet = lines.to_a[starting_line_index..ending_line_index].join
|
50
|
+
sexp = parser.parse(snippet)
|
51
|
+
return ParsedCode.new(sexp)
|
52
|
+
rescue Racc::ParseError
|
53
|
+
ending_line_index += 1
|
54
|
+
raise if ending_line_index >= lines.size
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
data/lib/cute_print.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative "cute_print/configure"
|
2
|
+
require_relative "cute_print/core_ext"
|
3
|
+
require_relative "cute_print/default_printer"
|
4
|
+
|
5
|
+
# Like Kernel#p, only fancier. For example, this code:
|
6
|
+
#
|
7
|
+
# require 'cute_print'
|
8
|
+
# q { 1 + 2 }
|
9
|
+
#
|
10
|
+
# prints this to $stderr:
|
11
|
+
#
|
12
|
+
# (1 + 2) is 3
|
13
|
+
module CutePrint
|
14
|
+
|
15
|
+
# Configure the library. For example:
|
16
|
+
#
|
17
|
+
# CutePrint.configure do |c|
|
18
|
+
# c.out = $stdout
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# @yieldparam config [Configure]
|
22
|
+
def self.configure(&block)
|
23
|
+
Configure.new(DefaultPrinter.printer, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
require "stringio"
|
4
|
+
|
5
|
+
require "cute_print"
|
6
|
+
|
7
|
+
# Test the library as the user uses it. The other specs test
|
8
|
+
# internals.
|
9
|
+
|
10
|
+
describe CutePrint do
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
CutePrint.configure { |c| c.reset }
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#q" do
|
17
|
+
When(:stderr) do
|
18
|
+
capture_stderr do
|
19
|
+
q 123
|
20
|
+
end
|
21
|
+
end
|
22
|
+
Then { stderr == "123\n" }
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#ql" do
|
26
|
+
When(:stderr) do
|
27
|
+
capture_stderr do
|
28
|
+
@location = [File.basename(__FILE__), __LINE__ + 1].join(":")
|
29
|
+
ql 123
|
30
|
+
end
|
31
|
+
end
|
32
|
+
Then { stderr == "#{@location}: 123\n" }
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#tapq" do
|
36
|
+
When do
|
37
|
+
@stderr = capture_stderr do
|
38
|
+
@result = ["1", "2"].map(&:to_i).tapq.inject(:+)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
Then { @result == 3}
|
42
|
+
Then { @stderr == "[1, 2]\n" }
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#tapql" do
|
46
|
+
When do
|
47
|
+
@stderr = capture_stderr do
|
48
|
+
@location = [File.basename(__FILE__), __LINE__ + 1].join(":")
|
49
|
+
@result = ["1", "2"].map(&:to_i).tapql.inject(:+)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
Then { @result == 3}
|
53
|
+
Then { @stderr == "#{@location}: [1, 2]\n" }
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'configure output' do
|
57
|
+
Given(:io) { StringIO.new }
|
58
|
+
Given do
|
59
|
+
CutePrint.configure do |c|
|
60
|
+
c.out = io
|
61
|
+
end
|
62
|
+
end
|
63
|
+
When do
|
64
|
+
q 123
|
65
|
+
end
|
66
|
+
Then { io.string == "123\n" }
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
require "cute_print/printer"
|
4
|
+
|
5
|
+
module CutePrint
|
6
|
+
|
7
|
+
describe Printer do
|
8
|
+
|
9
|
+
describe "#q" do
|
10
|
+
|
11
|
+
context "single value" do
|
12
|
+
Given(:out) { StringIO.new }
|
13
|
+
Given(:printer) { Printer.new(:out => out) }
|
14
|
+
When { printer.q [1, 2] }
|
15
|
+
Then { out.string == "[1, 2]\n" }
|
16
|
+
end
|
17
|
+
|
18
|
+
context "multiple values" do
|
19
|
+
Given(:out) { StringIO.new }
|
20
|
+
Given(:printer) { Printer.new(:out => out) }
|
21
|
+
When { printer.q 1, 2 }
|
22
|
+
Then { out.string == "1\n2\n" }
|
23
|
+
end
|
24
|
+
|
25
|
+
context "arguments and closure" do
|
26
|
+
Given(:out) { StringIO.new }
|
27
|
+
Given(:printer) { Printer.new(:out => out) }
|
28
|
+
When(:result) { printer.q("foo") {1 + 2} }
|
29
|
+
Then { result == Failure(ArgumentError) }
|
30
|
+
end
|
31
|
+
|
32
|
+
context "closure (one line)" do
|
33
|
+
Given(:out) { StringIO.new }
|
34
|
+
Given(:printer) { Printer.new(:out => out) }
|
35
|
+
When { printer.q {1 + 2} }
|
36
|
+
Then { out.string == "(1 + 2) is 3\n" }
|
37
|
+
end
|
38
|
+
|
39
|
+
context "closure (two lines)" do
|
40
|
+
Given(:out) { StringIO.new }
|
41
|
+
Given(:printer) { Printer.new(:out => out) }
|
42
|
+
When do
|
43
|
+
printer.q do
|
44
|
+
(1 + 2)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
Then { out.string == "(1 + 2) is 3\n" }
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#ql" do
|
53
|
+
Given(:out) { StringIO.new }
|
54
|
+
Given(:printer) { Printer.new(:out => out) }
|
55
|
+
When do
|
56
|
+
@location = [File.basename(__FILE__), __LINE__ + 1].join(":")
|
57
|
+
printer.ql [1, 2]
|
58
|
+
end
|
59
|
+
Then { out.string == "#{@location}: [1, 2]\n" }
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|