Using ruby blocks with method calls - ruby

The following code works perfectly.
#doc = open(link) { |f| Hpricot(f) }
But I want to use the following code, which doesn't seem to play well with the Hpricot block (e.g. #doc is a TempFile object, not a Hpricot document object)
#doc = resolve_link(link) { |f| Hpricot(f) }
def resolve_link(link)
begin
return open(link)
rescue
logger.debug("#{$!} for link #{link}")
raise Exceptions::ErrorResolvingLink.new("Cannot resolve link #{link}.")
end
end
Any idea how I can get the second version of the code to work?

You're calling resolve_link with a block but you're not passing that block down to open. Try this instead:
def resolve_link(link, &block)
begin
return open(link, &block)
#...

You have to use yield to invoke the block.
See this answer for a very simple example:
Blocks and yields in Ruby
So something along the lines
def resolve_link(link)
...
yield ( some_value_to_pass_to_the_block )
...
end
Should work.

Related

how can get proc content using Opalrb parser

I need to get the content of a proc send to js by Opal ruby.
I am only using the static version of Opal, Opal-native, opal-parser.
how can I proceed ?
def parse_proc params
#### how can I get proc content?
end
def touch(&proc)
parse_proc proc
end
b=box()
c=circle()
b.touch do
b.color(:red)
c.x=200
end
I found a solution I think there must be an better way but it works :
def analysis_of_proc params
the_proc_conent_is=`the_proc_conent_is = #{params}.toString();`
puts the_proc_conent_is
end
def my_proc(&proc)
analysis_of_proc proc
proc.call
end
my_proc do
a="hello"
b="world"
def add_word fist, second
return fist+" "+second
end
add_word a,b
end

Can an I make this "block wrapping" optional in a idiomatic/neat way?

In ruby, you can call a remote api like this
def get_remote_date
Net::HTTP.get('example.com', '/index.json')
end
If you do gem install vcr, you can do this
def get_remote_date
VCR.use_cassette("cassette_001") do
Net::HTTP.get('example.com', '/index.json')
end
end
Vcr recording/playback helps during development, when
the remote api is expensive.
Whether to use vcr or not should be optional, indicated
by the first argument of the function:
def get_remote_date(should_use_vcr)
VCR.use_cassette("cassette_001") do
Net::HTTP.get('example.com', '/index.json')
end
end
My question is, how do I rewrite the method so that the "block wrapping" / "VCR.use_cassette("cassette_001") do" is conditional on the boolean value of the should_use_vcr local variable.
I could do
def get_remote_date(should_use_vcr)
if conditional here
VCR.use_cassette("cassette_001") do
Net::HTTP.get('example.com', '/index.json')
end
else
Net::HTTP.get('example.com', '/index.json')
end
end
but for a complex method that has "Net::HTTP.get( " plus a dozen more lines, there's too much duplication of code,
so looking for a neater way to do it.
You can try to use the following approach:
def get_remote_date
record_request { Net::HTTP.get('example.com', '/index.json') }
end
def record_request(&request)
ENV['RECORD_REQUEST'] ? VCR.use_cassette("cassette_001", &request) : request.call
end
This is a nice article, which explains what does &block (ampersand parameter) mean and how it relates to yield keyword.
Illustrating a yield based solution,
def maybe_cache_http_requests(cassette)
if ENV['CACHE_HTTP_REQUESTS'] == "1"
require 'vcr'
VCR.configure do |config|
config.cassette_library_dir = "vcr_cassettes"
config.hook_into :webmock
end
VCR.use_cassette(cassette) do
yield
end
else
yield
end
end
You could put the duplicate code into a method and call that method either wrapped in the VCR do block or without VCR.

rspec yield block, but call original

So I have the following:
foo.each do |f|
f.begin
do_stuff
do_more_stuff
end
end
And I mock the f object with an and_yield() call. I want to be able to test the begin method by passing it the original block { do_stuff do_more_stuff }, not a mock implementation.... I cant just let the begin method be called on the mock without at least stubbing it, so what do I do?
Again, an undocumented feature that i found:
allow(thing).to receive(:foo) do |_, &block|
block.call
end
le sigh....
The following worked for me:
original = thing.method(:foo)
expect(thing).to receive(:foo) do |_params|
# check params
expect(params).to include(something)
# then
original.call(params)
end

How to test a function which takes a block with rspec

I have a function, which accepts a block, opens a file, yields and returns:
def start &block
.....do some stuff
File.open("filename", "w") do |f|
f.write("something")
....do some more stuff
yield
end
end
I am trying to write a test for it using rspec. How do I stub File.open so that it passed an object f (supplied by me) to the block instead of trying to open an actual file? Something like:
it "should test something" do
myobject = double("File", {'write' => true})
File.should_receive(:open).with(&blk) do |myobject|
f.should_receive(:write)
blk.should_receive(:yield) (or somethig like that)
end
end
I think what you're looking for are yield matchers, i.e:
it "should test something" do
# just an example
expect { |b| my_object.start(&b) }.to yield_with_no_args
end
Your other choice is to stub :open with a new object of File, as such:
file = File.new
allow(File).to receive(:open) { file }
file.each { |section| expect(section).to receive(:write) }
# run your method

How to return a dynamic value from a Mocha mock in Ruby

The gist of my problem is as follows:-
I'm writing a Mocha mock in Ruby for the method represented as "post_to_embassy" below. It is not really our concern, for the purpose of describing the problem, what the actual method does. But I need the mock to return a dynamic value. The proc '&prc' below is executing rightly in place of the actual method. But the "with" method in Mocha only allows for boolean values to be returned. So the code below outputs nil. I need it to output the value being passed through orderInfoXml. Does anyone know of an alternate method I can use?
require 'rubygems'
require 'mocha'
include Mocha::API
class EmbassyInterface
def post_to_embassy(xml)
puts "This is from the original class:-"
puts xml
return xml
end
end
orderInfoXml = "I am THE XML"
mock = EmbassyInterface.new
prc = Proc.new do |orderXml|
puts "This is from the mocked proc:-"
puts orderXml
orderXml
end
mock.stubs(:post_to_embassy).with(&prc)
mock_result = mock.post_to_embassy(orderInfoXml)
p mock_result
#p prc.call("asd")
output:-
This is from the mocked proc:-
I am THE XML
nil
I'm not sure if there is a perfect method for this. But to make life easier, than to stubbing each possible response (as described in another answer), you could go with Mocha's yields method.
require "rubygems"
require "mocha"
include Mocha::API
class EmbassyInterface
def post_to_embassy(xml)
puts "This is form the original class:-"
puts xml
xml
end
end
order_info_xml = "I am the xml"
mock = EmbassyInterface.new
prc = Proc.new do |order_xml|
puts "This is from the mocked proc:-"
puts order_xml
order_xml
end
mock.stubs(:post_to_embassy).yields prc
prc_return = nil
mock.post_to_embassy { |value| prc_return = value.call("hello world") }
puts prc_return
mock.post_to_embassy { |value| prc_return = value.call("foo") }
puts prc_return
outputs:
This is from the mocked proc:-
hello world
hello world
This is from the mocked proc:-
foo
foo
This will require you to assign the return of your prc, and it's not exactly pretty (imo). But, you don't have to stub out each expectation, which will give you quite a bit of freedom.
In general, you are normally better off specifying explicit return values in tests. It tends to make tests hard to understand and hard to maintain if you introduce separate logic into determining what value to return.
I would suggest that you either use Expectation#with with suitable ParameterMatchers to explicitly define return values for different parameter values or use the StateMachine functionality.
Mocha doesn't appear to support this. Add this to your test_helper.rb:
# Replace klass's method_name with method_implementation
def stub_replace(klass, method_name, &method_implementation)
klass.singleton_class.send(:alias_method, "#{method_name}_mock_backup", method_name)
klass.define_singleton_method(method_name, method_implementation)
end
def undo_stub_replace(klass, method_name)
klass.singleton_class.send(:alias_method, method_name, "#{method_name}_mock_backup")
end
Then replace the last 4 lines of your test with:
stub_replace(EmbassyInterface, :post_to_embassy, &prc)
mock_result = mock.post_to_embassy(orderInfoXml)
p mock_result
# cleanup
undo_stub_replace(EmbassyInterface, :post_to_embassy)
I haven't found a way to make the output of a mocked method completely dynamic, but if you have a limited, known number of inputs you can get the output to work correctly.
require 'rubygems'
require 'mocha'
include Mocha::API
class EmbassyInterface
def post_to_embassy(xml)
"original: #{xml}"
end
end
to_mock = EmbassyInterface.new
orderInfoXml1 = "I am the first XML."
orderInfoXml2 = "I am the second XML."
p to_mock.post_to_embassy(orderInfoXml1)
prc = Proc.new do |xml|
"mocked: #{xml}"
end
to_mock.stubs(:post_to_embassy).with(orderInfoXml1).returns(prc.call(orderInfoXml1))
to_mock.stubs(:post_to_embassy).with(orderInfoXml2).returns(prc.call(orderInfoXml2))
p to_mock.post_to_embassy(orderInfoXml1)
p to_mock.post_to_embassy(orderInfoXml2)
p to_mock.post_to_embassy(orderInfoXml1)
output:
"original: I am the first XML."
"mocked: I am the first XML."
"mocked: I am the second XML."
"mocked: I am the first XML."

Resources