Error no block given when used yield inside a block - ruby

I'm getting this error
LocalJumpError: no block given (yield)
from fops.rb:52:in `block (2 levels) in gen_list'
from /home/phanindra/.gem/ruby/1.9.1/gems/mp3info-0.6.18/lib/mp3info.rb:306:in `open'
from fops.rb:51:in `block in gen_list'
from fops.rb:46:in `each'
from fops.rb:46:in `gen_list'
from fops.rb:48:in `block in gen_list'
from fops.rb:46:in `each'
from fops.rb:46:in `gen_list'
from fops.rb:48:in `block in gen_list'
from fops.rb:46:in `each'
from fops.rb:46:in `gen_list'
from fops.rb:48:in `block in gen_list'
from fops.rb:46:in `each'
from fops.rb:46:in `gen_list'
from (irb):2
from /usr/bin/irb:12:in `<main>
when used yield inside another block which is inside a begin statement which is inside a if statement,
for a simple understanding here is the prototype
def test
if 1 then
begin
test2(5) do |x|
yield x
end
rescue
end
end
end
def test2(n)
n.times do |k|
yield k
end
end
test() do |y|
puts y
end
The problem is there is no error with the prototype, it worked fine so I dont understand why I'm getting this error, here is my actual code
require "mp3info"
module MusicTab
module FOps
def self.gen_list(dir)
prev_pwd=Dir.pwd
begin
Dir.chdir(dir)
rescue Errno::EACCES
end
Dir[Dir.pwd+'/*'].each{|x|
if File.directory?(x) then
self.gen_list(x)
else
begin
Mp3Info.open(x) do |y|
yield "#{y.tag.title},#{y.tag.album},#{y.tag.artist},#{x}"
end
rescue Mp3InfoError
end
end
}
Dir.chdir(prev_pwd)
end
end
end
I was testing this code using irb
[phanindra#pahnin musictab]$ irb
irb(main):001:0> load 'fops.rb'
/usr/share/rubygems/rubygems/custom_require.rb:36:in `require': iconv will be deprecated in the future, use String#encode instead.
=> true
irb(main):002:0> MusicTab::FOps.gen_list('/fun/Music') do |l|
irb(main):003:1* puts l
irb(main):004:1> end
Any help?
regards

The problem is that you are calling gen_list recursively and at the call site for the recursive descent there really is no block.
What you can do is either:
capture the block as a proc with a & parameter and then forward it, or
add a block that does another yield to the recursive call
So...
def f1 x, &block
block.call(x)
if x > 0
f1 x - 1, &block
end
end
# ...or...
def f2 x
yield x
if x > 0
f2 x - 1 do |y|
yield y
end
end
end
f1 2 do |q|
p ['b1', q]
end
f2 2 do |q|
p ['b2', q]
end

Related

Prevent object from being extended

I have a module that provides some lazy lookup via dot syntax for hashes:
module DotHash
def method_missing(method_name, *args, &block)
# look for keys...
end
def respond_to_missing?(method_name, _include_all = nil)
# some logic
end
end
I ran into the problem of accidentally extending nil:
# #hash == nil
#hash.extend(DotHash)
and this caused HUGE problems, because now nil has this method_missing logic which messes things up.
I though adding a hook would solve the problem:
module DotHash
def self.extended(base)
return if base.is_a?(Hash)
raise "must be a hash"
end
def method_missing(method_name, *args, &block)
# look for keys...
end
def respond_to_missing?(method_name, _include_all = nil)
# some logic
end
end
And indeed, it throws an error:
[1] pry(main)> nil.extend(DotHash)
RuntimeError: must be a hash
But the logic got added anyway:
[2] pry(main)> nil.foobar
Traceback (most recent call last):
9707: from bin/rails:6:in `<main>'
9706: from /usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/dependencies.rb:291:in `require'
9705: from /usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/dependencies.rb:257:in `load_dependency'
9704: from /usr/local/bundle/gems/activesupport-5.2.4.3/lib/active_support/dependencies.rb:291:in `block in require'
9703: from /usr/local/bundle/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
9702: from /usr/local/bundle/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `require_with_bootsnap_lfi'
9701: from /usr/local/bundle/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
9700: from /usr/local/bundle/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `block in require_with_bootsnap_lfi'
... 9695 levels...
4: from /usr/src/app/app/lib/dot_hash.rb:26:in `respond_to_missing?'
3: from /usr/src/app/app/lib/dot_hash.rb:14:in `method_missing'
2: from /usr/src/app/app/lib/dot_hash.rb:26:in `respond_to_missing?'
1: from /usr/src/app/app/lib/dot_hash.rb:14:in `method_missing'
/usr/src/app/app/lib/mapper/dot_hash.rb:26:in `respond_to_missing?': stack level too deep (SystemStackError)
Is there a hook that get's triggered BEFORE the object is extended, and not after?
You can override extend_object: (the docs contain a similar example)
Extends the specified object by adding this module's constants and methods (which are added as singleton methods). This is the callback method used by Object#extend.
module DotHash
def self.extend_object(obj)
raise TypeError, 'must be a hash' unless obj.is_a?(Hash)
super
end
def foo
123
end
end
h = {}
h.extend(DotHash)
h.foo #=> 123
n = nil
n.extend(DotHash) # TypeError: must be a hash
n.foo # NoMethodError: undefined method `foo' for nil:NilClass

How to wrap a method that yields in Ruby 1.9

I have a method that prints out a numbered list, yielding to a code block to print a prefix.
arr = %w(a b c)
def print_lines(array)
array.each_with_index do |item, index|
prefix = yield index
puts "#{prefix} #{item}"
end
end
print_lines(arr) do |index|
"(#{index})"
end
This produces the following output:
(0) a
(1) b
(2) c
Now I want to wrap print_lines in another method and call it.
def print_lines_wrapped(array)
puts 'print_lines_wrapped'
print_lines(array)
end
print_lines_wrapped(arr) do |index|
"(#{index})"
end
However, this gives me a LocalJumpError
test_yield.rb:5:in `block in print_lines': no block given (yield) (LocalJumpError)
from test_yield.rb:4:in `each'
from test_yield.rb:4:in `each_with_index'
from test_yield.rb:4:in `print_lines'
from test_yield.rb:16:in `print_lines_wrapped'
from test_yield.rb:19:in `<main>'
Why do I get a LocalJumpError?
How can I implement print_lines_wrapped such that I can call it like this:
print_lines_wrapped(arr) do |index|
"(#{index})"
end
and get the following output:
print_lines_wrapped
(0) a
(1) b
(2) c
?
Your wrapper method also has to accept a block and pass it to the wrapped method. There is no implicit passing of the block:
def print_lines_wrapped(array, &block)
puts 'print_lines_wrapped'
print_lines(array, &block)
end
Example:
def asdf(&block) puts yield(2) end
def qwer(&block)
puts "I am going to call asdf"
asdf &block
end
asdf { |x| x * 3 }
6
=> nil
qwer { |x| x * 5 }
I am going to call asdf
10
=> nil
The & operator converts its operand into a block if possible
qwer &Proc.new { |x| x * 2 }
I am going to call asdf
4

Why is rspec not seeing these class methods?

If I have ruby file maze.rb with
class Maze
def self.x
end
def self.y
end
end
and a rspec file with
require 'maze'
describe "A Maze" do
it "exists" do
expect(Maze).to be
end
it " has x-y dimension" do
expect(Maze.x).to be
expect(Maze.y).to be
end
end
Why does the test for Maze.x fail ?
Failures:
1) A Maze has x-y dimension
Failure/Error: expect(Maze.x).to be
expected nil to evaluate to true
# ./spec/maze_spec.rb:8:in `block (2 levels) in <top (required)>'
It is working.
What's happening is that the class level method isn't doing anything and thus returns nil - as opposed to method not found. Simply adding true as the return value resolves this, i.e.
def x
true
end

Ruby - How to populate a 2d array?

I have this rspec test:
it 'has a populated chessboard' do
expect(ChessBoard.new.populate_new_board).to eq [
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn'],
['pawn','pawn','pawn','pawn','pawn','pawn','pawn','pawn']]
end
For this code:
class ChessBoard
def initialize
#board=Array.new(7){Array.new(7)}
end
def populate_new_board
(0..7).each do |row|
(0..7).each do |cell|
#board[row][cell]='pawn'
end
end
#board
end
end
but I'm getting:
1) least number of moves from x to y has a populated chessboard
Failure/Error: expect(ChessBoard.new.populate_new_board).to eq [
NoMethodError:
undefined method `[]=' for nil:NilClass
# ./code.rb:10:in `block (2 levels) in populate_new_board'
# ./code.rb:9:in `each'
# ./code.rb:9:in `block in populate_new_board'
# ./code.rb:8:in `each'
# ./code.rb:8:in `populate_new_board'
# ./code_spec.rb:12:in `block (2 levels) in <top (required)>'
how can I fix this?
btw pawns in every space is not the final result but it's what I want for this test right now (then I can modify it further).
class ChessBoard
def populate_new_board
#board = [['pawn'] * 7] * 7
end
end
Change it to:
def populate_new_board
(0...7).each do |row|
(0...7).each do |cell|
#board[row][cell]='pawn'
end
end
#board
end
As it was denoted that you did a mistake in range, but a strongly advice you to reduce usage of index ranges. You can use :each, and :map methods instead:
class ChessBoard
def initialize
#board = Array.new( 7 ){ Array.new( 7 ) }
end
def populate_new_board
#board.each {| row | row.map! {| _ | 'pawn' } }
end
end
But I'd use more the simple code:
class ChessBoard
def populate_new_board
#board = Array.new( 7 ){Array.new( 7 ) { 'pawn' } }
end
end

Recursive lambdas in Ruby

I have the following code which correctly generates all possible trees of size num:
class Tree
attr_accessor :left, :right
def initialize left = nil, right = nil
#left = left
#right = right
end
# Don't ever specify any arguments, it will make me very angry.
# Tilt your head 90 degrees to the side to see the tree when viewing.
def print level = 0
#right.pretty_print(level + 1) if #right
puts (' ' * level) + to_s
#left.pretty_print(level + 1) if #left
end
def self.generate num
trees = []
generate_subtrees(num) { |tree| trees << tree } if num > 0
trees
end
private
def self.generate_subtrees num, &block
if num == 0
yield nil
else
(1..num).each do |root_position|
generate_subtrees(root_position - 1) do |left|
generate_subtrees(num - root_position) do |right|
yield Tree.new nil, left, right
end
end
end
end
end
end
I’m trying to (for the sake of it) “condense” this into one method, utilizing lambda recursion. My current attempt (of several iterations) is below:
def self.generate num
trees = []
gen = ->(num, &block) do
if num == 0
yield nil # L61
else
(1..num).each do |root_position| # L63
gen.call(root_position - 1) do |left| # L64
gen.call(num - root_position) do |right|
block.call { Tree.new nil, left, right }
end
end
end
end
end
gen.call(num) { |tree| trees << tree } # L73
trees
end
This results in the error (referenced lines noted above):
LocalJumpError: no block given (yield)
from tree.rb:61:in `block in generate'
from tree.rb:64:in `call'
from tree.rb:64:in `block (2 levels) in generate'
from tree.rb:63:in `each'
from tree.rb:63:in `block in generate'
from tree.rb:73:in `call'
from tree.rb:73:in `generate'
from (irb):4
from /Users/amarshall/.rbenv/versions/1.9.2-p290/bin/irb:12:in `<main>'
What am I doing wrong? Alternative solutions to this mostly academic problem are also welcome.
The yield keyword does not work from inside a lambda. The alternative is to use &block, in the same way that you are already doing on line 64 and 65:
gen = ->(num, &block) do
if num == 0
block.call(nil)
else
# ...
end
gen.call(num) { |tree| trees << tree }

Resources