Mocha expect failing with Thor 0.19.1 - ruby

After upgrading Thor from 0.18.1 to 0.19.1, I'm seeing a weird failure of Mocha (using test-unit)
/gems/2.1.0/gems/test-unit-2.5.5/lib/test/unit/ui/console/testrunner.rb:395:
in `output': unexpected invocation: #<IO:0x7fd6c986ab58>.puts() (Mocha::ExpectationError)
unsatisfied expectations:
- expected exactly once, not yet invoked: #<IO:0x7fd6c986ab58>.puts('1.0.0')
from rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/test-unit-2.5.5/lib/test/unit/ui/console/testrunner.rb:389:in `nl'
Code:
def version
say VERSION
end
Test:
def test_should_print_version
$stdout.expects(:puts).with(VERSION)
App::CLI.start %W(version)
end
Interestingly, $stdout.expects(:print).with(VERSION + "\n") works without issues. I'm using ruby 2.1.1p76
It seems that the first exception is due to puts being called and the second is due to it not being called. Should I be using expect in a different way?

I worked around the issue by redirecting $stdout to an instance of StringIO as suggested here
require 'stringio'
module Kernel
def capture_stdout
out = StringIO.new
$stdout = out
yield
return out
ensure
$stdout = STDOUT
end
end
Updated test:
def test_should_print_version
#$stdout.expects(:puts).with(VERSION)
out = capture_stdout { App::CLI.start %W(version) }
assert_match VERSION, out.string
end

Related

Upgade to Ruby 3.1 breaks code when using CSV class from standard library

I'm upgrading a Project written for JRuby 1.7 (corresponding on the language level to Ruby 1.9) to JRuby 9.4 (corresponding to Ruby 3.1.0). In this code, we have
require 'csv'
....
CSV.parse(string, csv_options) { .... }
where string is of class String and csv_optionsis of class Hash. This statement produces, when run under the new Ruby version, the error
ArgumentError:
wrong number of arguments (given 2, expected 1)
I found in the Ruby docs the following difference in the definition of parse:
Old version:
def parse(str, options={}, &block)
New version
def parse(str, **options, &block)
I understand that in the new Ruby, I would have to invoke parse as
CSV.parse(string, **csv_options) {....}
However, I would like to keep the code compatible for both versions, at least for some transition period, but the old JRuby does not understand **variable (I would get a syntax error, unexpected tPOW).
Is there a way to write the invocation of CSV.parse in such a way, that it preserves the original semantics and can run under Ruby 1.9 and Ruby 3.1? Currently the best solution for this problem which I can think of, is to write something like turning the block argument into a proc and writing
if RUBY_VERSION < '2'
CSV.parse(string, csv_options, &myproc)
else
# Without the eval, the compiler would complain about
# the ** when compiled with Ruby 1.9
eval "CSV.parse(string, **csv_options, &myproc)"
end
which looks pretty awful.
Not sure exactly what you are passing as csv_options but all versions can handle this using an a combination of implicit Hash/kwargs. e.g.
CSV.parse(string, col_sep: '|', write_headers: false, quote_empty: true) { ... }
If this is not an option then you going to need to patch the CSV class to make it backwards compatible e.g.
csv_shim.rb
# Shim CSV::parse to mimic old method signature
# while supporting Ruby 3 kwargs argument passing
module CSVShim
def parse(string, options={}, &block)
super(string, **options, &block)
end
end
CSV.singleton_class.prepend(CSVShim)
Then you can modify as:
require 'csv'
require 'csv_shim.rb' if RUBY_VERSION > '2.6.0'
#...
CSV.parse(string, csv_options) { .... }

IO streams inconsistencies in different versions of ruby

I am trying to create a way to mirror all three standard (input,out and error) streams of a ruby program to different files.
I was able to do that for output stdout and error stderr streams.
However there is inconsistencies between different ruby version on how they handle IO streams.
Bottom line is
How do I handle stream duplication in older versions of ruby (point 2) ?
Issue Summary
Older ruby versions don't support variadic arguments while new version does, that can be handled.
On same older versions, even if I try to work around it by join ing arguments, stream contents are duplicated.
Variadic arguments issue
In ruby 2.1.x and ruby 2.2.x, $stdout accepts only single arguments.
So for those version of ruby
$stdout.write("Test") # works
while this doesn't work
$stdout.write("Hello","Test")
ArgumentError: wrong number of arguments (2 for 1)
from (pry):6:in `write'
However for ruby version 2.5.x and 2.6.x variable parameters works
$stdout.write("Hello")
$stdout.write("This","works", "as", "well")
Duplication of stream content, if I add version specific code to handle stream parameters, older ruby version end up duplicating the stream content.
Older ruby version
driver.rb:4 in `puts` --> This is it driver.rb:4 in `puts` -->
while on
New ruby versions
driver.rb:4 in `puts` --> This is it
Here is the code
reox.rb
require_relative 'reox_io.rb'
require_relative 'reox_file.rb'
class Reox
DEFAULT_RECORDING_PATH = "recordings"
def initialize(recording_path)
#recording_paths = recording_path || DEFAULT_RECORDING_PATH
end
def self.record
input_log,output_log,error_log = setup_files
$stdout = ReoxIO.new(STDOUT,output_log)
$stderr = ReoxIO.new(STDERR,error_log)
yield
end
private
def self.setup_files
["input.log","output.log","error.log"].map do |file_name|
self.setup_file(file_name)
end
end
def self.setup_file(file_name)
file = ReoxFile.open(File.join(__dir__,file_name),"a")
file.sync = true
file
end
end
reox_file.rb
class ReoxFile < File
end
reox_io.rb
# Insipired from https://stackoverflow.com/a/6407200
require_relative 'reox_file.rb'
class ReoxIO
$LOG_SEPARATOR = " --> ".freeze
def initialize(*streams)
#streams = streams
#verbose = true
end
def write(*content)
#streams.each do |stream|
if #verbose and stream.is_a? ReoxFile
stream.write([caller[2],$LOG_SEPARATOR,*content].join)
else
stream.write(*content)
end
end
end
def flush
#streams.each(&:flush)
end
def close
#streams.each(&:close)
end
end
driver.rb
require_relative 'reox.rb'
Reox.record do
puts "This is it"
end

Suppress warning messages in Thor CLI app

I've built a Thor CLI app that uses a number of external gems. When I run it I get warning messages from those gems cluttering my output- how can I suppress this?
Clarification: I want to suppress the warning messages only, but still receive the standard output for my app, including errors and puts results.
For example, when I use these same gems in my Sinatra app, I don't get all the warning messages emanating from the gem code like I do with Thor.
EXAMPLE (Derived from http://willschenk.com/making-a-command-line-utility-with-gems-and-thor/)
require 'thor'
require 'safe_yaml'
module Socialinvestigator
class HammerOfTheGods < Thor
desc "hello NAME", "This will greet you"
long_desc <<-HELLO_WORLD
`hello NAME` will print out a message to the person of your choosing.
HELLO_WORLD
option :upcase
def hello( name )
greeting = "Hello, #{name}"
greeting.upcase! if options[:upcase]
puts greeting
end
end
end
In this case, because we're requiring the safe_yaml gem, every time we run a command we'll get the following warnings in our output:
/usr/local/lib/ruby/gems/2.3.0/gems/safe_yaml-1.0.4/lib/safe_yaml.rb:28:
warning: method redefined; discarding old safe_load
/usr/local/Cellar/ruby/2.3.0/lib/ruby/2.3.0/psych.rb:290: warning:
previous definition of safe_load was here
/usr/local/lib/ruby/gems/2.3.0/gems/safe_yaml-1.0.4/lib/safe_yaml.rb:52:
warning: method redefined; discarding old load_file
/usr/local/Cellar/ruby/2.3.0/lib/ruby/2.3.0/psych.rb:470: warning:
previous definition of load_file was here
We're using a number of different gems and getting a whole array of warnings that are cluttering our output...
Firstly, you could potentially submit a Pull request and suppress the message in the dependency or raise an issue asking the gem developers to sort it out for you
Otherwise, this is something I have used before - It's probably from somewhere on SO (or the internet in general) but I can't remember where...
So you basically wrap the noisy method from the dependency with a silence method which simply pushes the STDOUT to a StringIO object and then back again to STDOUT...
require 'stringio'
def silence
$stdout = temp_out = StringIO.new
yield
temp_out.string
ensure
$stdout = STDOUT
end
out = silence { run_dependency_method }
puts out # if in dev mode etc...
Ruby has a global variable to define the verboseness of the output. nil means no warnings, false is the "normal" mode and true is extra verbose (adds some additional runtime information, equivalent to running ruby --verbose):
def without_warnings
verboseness_level = $VERBOSE
$VERBOSE = nil
yield
ensure
$VERBOSE = verboseness_level
end
# Doesn't show warnings about overwriting the constant
without_warnings do
Foo = 42
Foo = 'bar'
end
# Still shows normal output
without_warnings { puts 42 }
# Still shows and throws errors
without_warnings { raise 'wtf' }
If you have control over how the program is ran, you can instead use ruby's -W flag with respective values 0, 1 or 2 (in this case ruby -W 0).
#Yarin,
Following up on #Ismail's answer, you could extend this solution by overloading the new "temporary" StringIO inside the silence method to filter out messages that contain the word "warning".
Here's an example:
require 'stringio'
class FooIO < StringIO
def puts s
super unless s.start_with? "WARN"
end
end
def silence
$stdout = temp_out = FooIO.new
yield
temp_out.string
ensure
$stdout = STDOUT
end
def foo
puts "INFO : Hello World!"
puts "WARN : Oops.. This is a warning..."
puts "ALRT : EVERYONE IS GOING TO DIE!!!"
end
out = silence { foo }
puts out

Weird error when trying to test method with argument in Mocha. Is it a bug or is it me?

It's rather hard to find any documentation on Mocha, so I'm afraid I'm totally at sea here. I have found a problem with stubbing methods that pass arguments. So for instance if I set up a class like this:
class Red
def gets(*args)
#input.gets(*args)
end
def puts(*args)
#output.puts(*args)
end
def initialize
#input = $stdin
#output = $stdout
end
private
def first_method
input = gets.chomp
if input == "test"
second_method(input)
end
end
def second_method(value)
puts value
second_method(value)
end
end
Yes it's contrived, but it's a simplification of the idea that you may have a method that you don't want called in the test.
So I might write a test such as:
setup do
#project = Red.new
#project.instance_variable_set(:#input, StringIO.new("test\n"))
#project.stubs(:second_method)
end
should "pass input value to second_method" do
#project.expects(:second_method).with("test").once
#project.instance_eval {first_method}
end
Now I would expect this to pass. But instead I get this rather arcane error message:
Errno::ENOENT: No such file or directory - getcwd
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/backtrace_filter.rb:12:in `expand_path'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/backtrace_filter.rb:12:in `block in filtered'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/backtrace_filter.rb:12:in `reject'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/backtrace_filter.rb:12:in `filtered'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/expectation_error.rb:10:in `initialize'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/mockery.rb:53:in `new'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/mockery.rb:53:in `verify'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/api.rb:156:in `mocha_verify'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/integration/mini_test/version_131_and_above.rb:27:in `run'
This means absolutely nothing to me, other than something deep in Mochas bowels has just gone clang. If I write the same sort of test without an argument passing to the second method I get no problem. Am I missing something?
I think it must be something in shoulda causing the problem. I use test/unit, and everything appears to be OK.
require 'rubygems'
require "test/unit"
require 'mocha'
require File.dirname(__FILE__) + '/../src/red'
class RedTest < Test::Unit::TestCase
def setup
#project = Red.new
#project.instance_variable_set(:#input, StringIO.new("test\n"))
#project.stubs(:second_method)
end
def test_description_of_thing_being_tested
#project.expects(:second_method).with("test").once
#project.instance_eval {first_method}
end
end
gives the following output:
stephen#iolanta:~/tmp/red/test # ruby red_test.rb
Loaded suite red_test
Started
.
Finished in 0.000679 seconds.
1 tests, 1 assertions, 0 failures, 0 errors
stephen#iolanta:~/tmp/red/test #
Sorry - I've only just seen this. It's better to submit bug reports to us in Lighthouse. What documentation have you found? Have you seen the RDoc on Rubyforge? What sort of documentation were you looking for that you did not find?
I've been unable to reproduce your bug. What version of Ruby, Rubygems, Shoulda & Mocha were you using?
You can see the results of me running your test in this Gist.

Printing the source code of a Ruby block

I have a method that takes a block.
Obviously I don't know what is going to be passed in and for bizarre reasons that I won't go into here I want to print the contents of the block.
Is there a way to do this?
You can do this with Ruby2Ruby which implements a to_ruby method.
require 'rubygems'
require 'parse_tree'
require 'parse_tree_extensions'
require 'ruby2ruby'
def meth &block
puts block.to_ruby
end
meth { some code }
will output:
"proc { some(code) }"
I would also check out this awesome talk by Chris Wanstrath of Github http://goruco2008.confreaks.com/03_wanstrath.html He shows some interesting ruby2ruby and parsetree usage examples.
In Ruby 1.9+ (tested with 2.1.2), you can use https://github.com/banister/method_source
Print out the source via block#source:
#! /usr/bin/ruby
require 'rubygems'
require 'method_source'
def wait &block
puts "Running the following code: #{block.source}"
puts "Result: #{yield}"
puts "Done"
end
def run!
x = 6
wait { x == 5 }
wait { x == 6 }
end
run!
Note that in order for the source to be read you need to use a file and execute the file (testing it out from irb will result in the following error: MethodSource::SourceNotFoundError: Could not load source for : No such file or directory # rb_sysopen - (irb)
Building on Evangenieur's answer, here's Corban's answer if you had Ruby 1.9:
# Works with Ruby 1.9
require 'sourcify'
def meth &block
# Note it's to_source, not to_ruby
puts block.to_source
end
meth { some code }
My company uses this to display the Ruby code used to make carbon calculations... we used ParseTree with Ruby 1.8 and now sourcify with Ruby 1.9.
In Ruby 1.9, you can try this gem which extract the code from source file.
https://github.com/ngty/sourcify
In Ruby 2.5 the following works
puts block.source
In ruby 2.7, using the method_source gem (pry depends on it)
Set.instance_method(:merge).source.display
# =>
def merge(enum)
if enum.instance_of?(self.class)
#hash.update(enum.instance_variable_get(:#hash))
else
do_with_enum(enum) { |o| add(o) }
end
self
end
The repo says it works for procs, but I haven't tested it.

Resources