Aim
My program uses an algorithm to make a board, line-by-line in an array of arrays.
The final array looks like this (a standard 3x3 board with a border):
[['-----'],['|...|'],['|...|'],['|...|'],['-----']]
Problem
The failing test is as follows:
Note: view_board puts #board
expect { board.view_board }.to output(['-----','|...|','|...|','|...|','-----']).to_stdout
Outputting:
`Failure/Error: expect { board.view_board }.to output(['-----','|...|','|...|','|...|','-----']).to_stdout
expected block to output ["-----", "|...|", "|...|", "|...|", "-----"] to stdout, but output "-----\n|...|\n|...|\n|...|\n-----\n"
Diff:`
What'd different?
I'm not sure if there's a space somewhere I've missed, if I change the final array to "-----\n", then I get:
Diff:
## -2,5 +2,5 ##
|...|
|...|
|...|
------\n
+-----
Edit:
Board-Generating Code
class Board
BOARD_ROW = '-'
BOARD_COLUMM = '|'
BEGINNING_AND_END_LENGTH = 2
def initialize(board_size = 3)
#board = []
#board_top_and_bottom = []
#board_middle = []
#board_size = board_size
end
def go
set_board
end
def set_board
set_board_top_and_bottom
set_board_middle
assemble_board
view_board
end
def set_board_top_and_bottom
(#board_size + BEGINNING_AND_END_LENGTH).times do
#board_top_and_bottom.push(BOARD_ROW)
end
#board_top_and_bottom = [#board_top_and_bottom.join]
end
def set_board_middle
add_board_edge
add_board_spaces
add_board_edge
#board_middle = [#board_middle.join]
end
def add_board_spaces
#board_size.times do
#board_middle.push('.')
end
end
def add_board_edge
#board_middle << BOARD_COLUMM
end
def assemble_board
#board << #board_top_and_bottom
#board_size.times do
#board << #board_middle
end
#board << #board_top_and_bottom
end
def view_board
puts #board
end
end
From the docs of puts:
puts(obj, ...) → nil
Writes the given object(s) to ios. Writes a newline after any that do not already end with a newline sequence. [...]
That means when your call puts ['foo', 'bar'] then Ruby will actually output "foo\nbar"
To make your spec pass change the expectation to:
expect { board.view_board }.to output("-----\n|...|\n|...|\n|...|\n-----\n").to_stdout
Related
I have a class that does calculations. Most of the times it is used in the code to output a single value:
Use: value = Calculator.new(1234).output
This is an example of the class definition:
class Calculator
def initialize(parameter_1)
#parameter_1 = parameter_1
end
def output
op_1_result = operation_1(#parameter_1)
op_2_result = operation_2(op_1_result)
operation_3(op_2_result)
end
def operation_1(param)
...
end
But sometimes the user has to print a report of the calculation, showing many of the variables from inside the calculations.
The first solution I implemented was to pass a parameter at initialization telling the class that it should save some internal variables for the report, like this:
class Calculator
attr_reader :report
def initialize(parameter_1, reporting=false)
#parameter_1 = parameter_1
#reporting = reporting
#report = {}
end
def output
op_1_result = operation_1(#parameter_1)
#report[:op_1_result] = op_1_result if #reporting
op_2_result = operation_2(op_1_result)
#report[:op_2_result] = op_2_result if #reporting
operation_3(op_2_result)
end
def operation_1(param)
...
end
Then, when I want to get those intermediate variables, I would:
calculator = Calculator.new(1234, true) # reporting activated
report = calculator.report
report[:op_1_result] # one of the intermediate variables of the calculation
Does this break the single responsibility principle, as the class is now calculating the value and reporting at the same time?
Is there a better way to do this, a design pattern where I could have a fast calculation of the output result where it is needed and show the report where needed without all those ifs?
Any light on this and comments will be really appreciated!
Obs (another discussion): I've read that a more functional approach to just outputting a value would be a great thing. But that kept me wondering about how to show those internal intermediate values when needed. How do more functional programmers would do it...?
I guess "builder pattern" is suitable and "report pad" should be injected from outside.
class Calculator
def initialize(*parameters)
#parameters = parameters
end
def report_to(report_pad)
#report_pad = report_pad
self
end
def output()
ret = #parameters[0].to_i + #parameters[1].to_i
report('Did p0 + p1')
ret
end
private
def report(message)
#report_pad << "\n".prepend(message) if #report_pad.respond_to? '<<'
end
end
####
reports = ""
result = Calculator
.new(1, 2)
.report_to(reports)
.output()
puts result, reports
Why don't you just make all intermediate results public methods and chain the results in the final output?
Perhaps something like this:
class Calculator
def initialize(parameter)
#parameter = parameter
end
def output
op_3_result
end
def op_1_result
#op_1_result ||= operation_1(parameter)
end
def op_2_result
#op_2_result ||= operation2(op_1_result)
end
def op_3_result
#op_3_result ||= operation3(op_2_result)
end
private
def operation1(arg)
# ...
end
def operation2(arg)
# ...
end
def operation3(arg)
# ...
end
attr_reader :parameter
end
That would allow you to call whatever you need on the same instance:
calculator = Calculator.new(1234)
calculator.output #=> the final result
calculator.op_2_result #=> the intermediate result of step 2
You could use a different pattern with Report being its own class and allow it to just pass through when reporting is turned off. Here is a simple example:
class Calculator
attr_reader :report
def initialize(parameter_1, reporting=false)
#parameter_1 = parameter_1
#report = Report.new(reporting)
end
def output
op1 = operation_1(report.capture(:param1,#parameter_1))
report.capture(:op1,op1)
op2 = report.capture(:op2) { operation_2(op1) }
operation_3(op2)
end
def operation_1(param);
param + 7
end
def operation_2(param);
param - 3
end
def operation_3(param);
param * 2
end
end
class Report
attr_reader :reporting, :reportables
def initialize(reporting)
#reporting = reporting
#reportables = {}
end
def capture(key, val=nil,&block)
warn "Block supercedes value" if val && block_given?
result = block_given? ? block.call : val
#reportables[key] = result if reporting
result
end
def [](key)
return 'No Reporting' unless reporting
#reportables[key]
end
end
Then you can use like so
c = Calculator.new(12)
c.output
#=> 32
c.report[:op1]
#=> "No Reporting"
c = Calculator.new(12, true)
c.output
#=> 32
c.report[:op1]
#=> 19
c.report[:param1]
#=> 12
This way each step can use a block for more complicated items where the result should be captured or just pass a value if you choose and intermediate steps like operation_3 (in this case) that do not need to be "captured" can just flow through.
If reporting is off then everything just flows through and the captures are ignored.
Your #output method could also look like this (no intermediate locals at all although it does hurt the readability a bit)
def output
operation_3 (
report.capture(:op2,
operation_2(
report.capture(:op1) do
operation_1(report.capture(:param1,#parameter_1))
end
)
)
)
end
You can use dependency injection like this:
class Calculator
def initialize(parameter_1, reporter = nil)
#parameter_1 = parameter_1
#reporter = reporter
end
def output
op_1_result = call_with_reporting :operation_1, #parameter_1
op_2_result = call_with_reporting :operation_2, op_1_result
operation_3(op_2_result)
end
def operation_1(param)
...
end
def call_with_reporting(operation, *args)
result = self.send(operation, *args)
#reporter.report(operation, result) if #reporter
result
end
end
class ConsoleReporter
def initialize
#results = {}
end
def report(operation, result)
#results[operation] = result
end
def run_report
puts #operations
end
end
Now you can use Calculator like this:
reporter = ConsoleReporter.new
Calculator.new(12, reporter).output
reporter.run_report
Later you can use Calculator with other format reporter (like ToFileReporter)
How do I write a rspec test for the following.
the value that gets passed in is this...
90A14F 1.4
def classname
def init
#input_colors = Array.new
end
def speak
puts "enter your line of color values"
result = STDIN.gets.chomp
new_result = result.gsub!(/([\s])+/,':')
#input_colors << new_result
end
end
How do I write an rspec 3.1 test for this speak method that test if gets.chomp is...90A14F 1.4
They will get an instance var #input_colors == ["90A14F:1.4"]
There are some issues in your example. I would change and fix it to something like this:
class ColorReader # with `class` and an upcase class name
def initialize # not `init`
#colors = [] # the use of `Array.new` is uncommon
end
def read
puts "enter your line of color values"
result = STDIN.gets.chomp
new_result = result.gsub!(/([\s])+/,':')
#colors << new_result
end
end
Then a test could look like this:
describe ColorReader do
describe '#read' do
let(:input) { "90A14F 1.4\n" }
subject(:color_reader) { ColorReader.new }
before do
allow(color_reader).to receive(:puts).and_return(nil)
allow(STDIN).to receive(:gets).and_return(input)
end
it 'writes to the console' do
color_reader.read
expect(color_reader).to have_received(:puts).
with("enter your line of color values").once
end
it 'reads from STDIN' do
color_reader.read
expect(STDIN).to have_received(:gets).once
end
it 'returns the sanitized input' do
expect(color_reader.read).to eq(['90A14F:1.4'])
end
end
end
Btw. I would prefer to explicitly test the new value of the #colors array (perhaps with attr_reader :colors) and not the implizit return value of the read method. If you have a reader for the #colors then the last spec can be changed to:
it 'adds the sanitized input to `colors`' do
expect {
color_reader.read
}.to change { color_reader.colors }.from([]).to(['90A14F:1.4'])
end
I would like to create a Pipe class to emulate Unix commands in Ruby in a two step fashion. First step is to compile a pipeline by adding a number of commands, and the second step is to run that pipeline. Here is a mockup:
#!/usr/bin/env ruby
p = Pipe.new
p.add(:cat, input: "table.txt")
p.add(:cut, field: 2)
p.add(:grep, pattern: "foo")
p.add(:puts, output: "result.txt")
p.run
The question is how to code this using lazy evaluation, so that the pipe is processed record by record when run() is called without loading all of the data into memory at any one time?
Take a look at the http://ruby-doc.org/core-2.0.0/Enumerator.html class. The Pipe class will stitch together an Enumerator, e.g. add(:cat, input: 'foo.txt') will create an enumerator which yields lines of foo.txt. add(:grep) will filter it according to regexp etc.
Here's the lazy file reader
require 'benchmark'
def lazy_cat(filename)
e = Enumerator.new do |yielder|
f = File.open filename
s = f.gets
while s
yielder.yield s
s = f.gets
end
end
e.lazy
end
def cat(filename)
Enumerator.new do |yielder|
f = File.open filename
s = f.gets
while s
yielder.yield s
s = f.gets
end
end
end
lazy = Benchmark.realtime { puts lazy_cat("log.txt").map{|s| s.upcase}.take(1).to_a }
puts "Lazy: #{lazy}"
eager = Benchmark.realtime { puts cat("log.txt").map{|s| s.upcase}.take(1).to_a }
puts "Eager: #{eager}"
Eager version takes 7 seconds for 10 million line file, lazy version takes pretty much no time.
For what I understood you can simply read one line at a time and move this single line thought the pipeline, then write it to the output. Some code:
output = File.new("output.txt")
File.new("input.txt").each do |line|
record = read_record(line)
newrecord = run_pipeline_on_one_record(record)
output.write(dump_record(newrecord))
end
Another much heavier option would be create actual IO blocking pipes and use one thread for each task in the pipeline. This somewhat reassembles what Unix does.
Sample usage with OP's syntax:
class Pipe
def initialize
#actions = []
end
def add(&block)
#actions << block
end
def run(infile, outfile)
output = File.open(outfile, "w")
File.open(infile).each do |line|
line.chomp!
#actions.each {|act| line = act[line] }
output.write(line+"\n")
end
end
end
p = Pipe.new
p.add {|line| line.size.to_s }
p.add {|line| "number of chars: #{line}" }
p.run("in.txt", "out.txt")
Sample in.txt:
aaa
12345
h
Generated out.txt:
number of chars: 3
number of chars: 5
number of chars: 1
This seems to work:
#!/usr/bin/env ruby
require 'pp'
class Pipe
def initialize
#commands = []
end
def add(command, options = {})
#commands << [command, options]
self
end
def run
enum = nil
#commands.each do |command, options|
enum = method(command).call enum, options
end
enum.each {}
enum
end
def to_s
cmd_string = "Pipe.new"
#commands.each do |command, options|
opt_list = []
options.each do |key, value|
if value.is_a? String
opt_list << "#{key}: \"#{value}\""
else
opt_list << "#{key}: #{value}"
end
end
cmd_string << ".add(:#{command}, #{opt_list.join(", ")})"
end
cmd_string << ".run"
end
private
def cat(enum, options)
Enumerator.new do |yielder|
enum.map { |line| yielder << line } if enum
File.open(options[:input]) do |ios|
ios.each { |line| yielder << line }
end
end.lazy
end
def cut(enum, options)
Enumerator.new do |yielder|
enum.each do |line|
fields = line.chomp.split(%r{#{options[:delimiter]}})
yielder << fields[options[:field]]
end
end.lazy
end
def grep(enum, options)
Enumerator.new do |yielder|
enum.each do |line|
yielder << line if line.match(options[:pattern])
end
end.lazy
end
def save(enum, options)
Enumerator.new do |yielder|
File.open(options[:output], 'w') do |ios|
enum.each do |line|
ios.puts line
yielder << line
end
end
end.lazy
end
end
p = Pipe.new
p.add(:cat, input: "table.txt")
p.add(:cut, field: 2, delimiter: ',\s*')
p.add(:grep, pattern: "4")
p.add(:save, output: "result.txt")
p.run
puts p
https://stackoverflow.com/a/20049201/3183101
require 'benchmark'
def lazy_cat(filename)
e = Enumerator.new do |yielder|
f = File.open filename
s = f.gets
while s
yielder.yield s
s = f.gets
end
end
e.lazy
end
def cat(filename)
Enumerator.new do |yielder|
f = File.open filename
s = f.gets
while s
yielder.yield s
s = f.gets
end
end
end
lazy = Benchmark.realtime { puts lazy_cat("log.txt").map{|s| s.upcase}.take(1).to_a }
puts "Lazy: #{lazy}"
eager = Benchmark.realtime { puts cat("log.txt").map{|s| s.upcase}.take(1).to_a }
puts "Eager: #{eager}"
This could have been simplified to the following, which I think makes the diff between the two methods easier to see.
require 'benchmark'
def cat(filename, evaluation_strategy: :eager)
e = Enumerator.new do |yielder|
f = File.open filename
s = f.gets
while s
yielder.yield s
s = f.gets
end
end
e.lazy if evaluation_strategy == :lazy
end
lazy = Benchmark.realtime { puts cat("log.txt", evaluation_strategy: :lazy).map{ |s|
s.upcase}.take(1).to_a
}
puts "Lazy: #{lazy}"
eager = Benchmark.realtime { puts cat("log.txt", evaluation_strategy: :eager).map{ |s|
s.upcase}.take(1).to_a
}
puts "Eager: #{eager}"
I would have just put this in a comment, but I'm too 'green' here to be permitted to do so. Anyway, the ability to post all of the code I think makes it clearer.
This builds on previous answers, and serves as a warning about a gotcha regarding enumerators. An enumerator that hasn't been exhausted (i.e. raised StopIteration) will not run ensure blocks. That means a construct like File.open { } won't clean up after itself.
Example:
def lazy_cat(filename)
f = nil # visible to the define_singleton_method block
e = Enumerator.new do |yielder|
# Also stored in #f for demonstration purposes only, so we examine it later
#f = f = File.open filename
s = f.gets
while s
yielder.yield s
s = f.gets
end
end
e.lazy.tap do |enum|
# Provide a finish method to close the File
# We can't use def enum.finish because it can't see 'f'
enum.define_singleton_method(:finish) do
f.close
end
end
end
def get_first_line(path)
enum = lazy_cat(path)
enum.take(1).to_a
end
def get_first_line_with_finish(path)
enum = lazy_cat(path)
enum.take(1).to_a
ensure
enum.finish
end
# foo.txt contains:
# abc
# def
# ghi
puts "Without finish"
p get_first_line('foo.txt')
if #f.closed?
puts "OK: handle was closed"
else
puts "FAIL: handle not closed!"
#f.close
end
puts
puts "With finish"
p get_first_line_with_finish('foo.txt')
if #f.closed?
puts "OK: handle was closed"
else
puts "FAIL: handle not closed!"
#f.close
end
Running this produces:
Without finish
["abc\n"]
FAIL: handle not closed!
With finish
["abc\n"]
OK: handle was closed
Note that if you don't provide the finish method, the stream won't be closed, and you'll leak file descriptors. It's possible that GC will close it, but you shouldn't depend on that.
I'm trying to format tags using "method_missing", the result I want is shown below.
<foo>\n
<bar>\n
<ab/>\n
</bar>\n
</foo>\n
I believe a running index is required but I'm unclear where best to put it or whether that's the best approach. How would I add indentation?
def method_missing(meth, *args, &block)
if args.length > 0
my_other_method(args)
else
my_method(meth.to_s, &block)
end
end
def my_other_method(args)
"<#{args}/>"
end
def my_method(meth)
s = "<#{meth}>\n"
s << "#{_indentation}"
s << yield.to_s << "\n"
s << "#{_indentation}"
s << "</#{meth}>\n"
end
def _indentation
("--" * _level.to_i) # dashes added to more easily infer spacing
end
def _level
caller.rindex {|val| val.scan('my_method')} / 3
end
p foo{bar{baz(:a => "b"){}}}
I get this wrong output (not certain whats causing the extra \n)
<foo>\n
--<bar>\n
----<ab>\n
----</bar>\n
\n
--</foo>\n
You can use call stack to get the level of indentation. See caller in my code.
I also suggest to isolate your code in some class, so that you can use more tags, because there could be a lot of methods defined in main
So, I modified your code a bit:
class TagFormatter
def method_missing(method_name, *args, &block)
_tagify method_name, args, &block
end
def _tagify(tag_name, args)
if block_given?
s = "#{_indentation}<#{tag_name}#{_attrs args}>\n"
s << (yield || "")
s << "#{_indentation}</#{tag_name}>\n"
else
"#{_indentation}<#{tag_name}#{_attrs args}/>\n"
end
end
def _indentation
" " * _level
end
def _level
caller.index { |val| val =~ /instance_eval/ } / 3 - 1
end
def _attrs(args)
args.map { |arg| _to_attr arg }.join if args
end
def _to_attr(arg)
if arg.kind_of? Hash
arg.map { |k, v| %Q{ #{k}="#{v}"}}
else
%Q{ #{arg.to_s}}
end
end
end
Example:
> tf = TagFormatter.new
> puts tf.instance_eval 'foo{bar{baz(:a => "b"){}}}'
<foo>
<bar>
<baz a="b">
</baz>
</bar>
</foo>
> puts tf.instance_eval 'foo{bar{baz(:a => "b")}}' # no block given in baz
<foo>
<bar>
<baz a="b"/>
</bar>
</foo>
> puts tf.instance_eval 'foo{bar{baz("disabled", :readonly, :a=>"b")}}'
<foo>
<bar>
<baz disabled readonly a="b"/>
</bar>
</foo>
Just for fun, again, but is it possible to take a block that contains method definitions and add those to an object, somehow? The following doesn't work (I never expected it to), but just so you get the idea of what I'm playing around with.
I do know that I can reopen a class with class << existing_object and add methods that way, but is there a way for code to pass that information in a block?
I guess I'm trying to borrow a little Java thinking here.
def new(cls)
obj = cls.new
class << obj
yield
end
obj
end
class Cat
def meow
puts "Meow"
end
end
cat = new(Cat) {
def purr
puts "Prrrr..."
end
}
cat.meow
# => Meow
# Not working
cat.purr
# => Prrrr...
EDIT | Here's the working version of the above, based on edgerunner's answer:
def new(cls, &block)
obj = cls.new
obj.instance_eval(&block)
obj
end
class Cat
def meow
puts "Meow"
end
end
cat = new(Cat) {
def purr
puts "Prrrr..."
end
}
cat.meow
# => Meow
cat.purr
# => Prrrr...
You can use class_eval(also aliased as module_eval) or instance_eval to evaluate a block in the context of a class/module or an object instance respectively.
class Cat
def meow
puts "Meow"
end
end
Cat.module_eval do
def purr
puts "Purr"
end
end
kitty = Cat.new
kitty.meow #=> Meow
kitty.purr #=> Purr
kitty.instance_eval do
def purr
puts "Purrrrrrrrrr!"
end
end
kitty.purr #=> Purrrrrrrrrr!
Yes
I suspect you thought of this and were looking for some other way, but just in case...
class A
def initialize
yield self
end
end
o = A.new do |o|
class << o
def purr
puts 'purr...'
end
end
end
o.purr
=> purr...
For the record, this isn't the usual way to dynamically add a method. Typically, a dynamic method starts life as a block itself, see, for example, *Module#define_method*.