small medium large xlarge

Scottgardner 200x200_pragsmall
08 May 2008, 23:58
Scott Gardner (7 posts)

The following section of code (excerpted from page 110 depot_g/app/models/cart.rb line 2 in the book, but I’ve included relevant setup code) has_thrown_me_for :a => :loop…

class Cart
	attr_reader :items
	
	def initialize
		@items = []
	end
	
	def add_product(product)
		current_item = @items.find {|item| item.product == product}  # say what?!?

Here’s how I understand it: 1. @items is initialized as an empty array 2. add_product receives a product object 3. The @items array is passed to the block 4. Each @items array item is checked to see if its product.id equals the product.id of the product passed to add_product, and if so that matched item is returned to find, which then returns that item to be assigned to current_item

Can someone please confirm that I have this correct? Presuming yes, I still do not understand how the block, which resolves true or false, knows to return the actual item object when a true condition occurs.

Thanks!

Samr_small_pragsmall
09 May 2008, 01:32
Sam Ruby (634 posts)
  1. @items is initialized as an empty array

Note that this only occurs when the Cart itself is initialized. After that point, @items can change.

  1. add_product receives a product object

Yes.

  1. The @items array is passed to the block

More precisely, each item in the @items array is passed to the block until an item is found for which the block evaluates to true.

  1. Each @items array item is checked …

This is correct.

I still do not understand how the block … knows to return the actual item object

It doesn’t.

The block is a procedure. It is passed as a parameter to the find method. It is an object with a call method. This means that the caller (in this case, the find method) can call it as many times at it likes, with whatever parameters it likes. And that is exactly what the find method does. It calls the block repeatedly with each successive item in the array until it finds one which causes the block to evaluate true. If this occurs, the find method knows which item it passed to the block, and it simply returns that item. If, instead, it exhausts the list, it simply returns nil instead.

Scottgardner 200x200_pragsmall
09 May 2008, 12:50
Scott Gardner (7 posts)

Thanks for the detailed explanation. I am really just trying to put a handle on this because I realize it’s a key and common usage in Ruby/Rails. I think, for now, I will just have to rely on rote memory of how this works.

Also, can you confirm/correct your statement that this block is in fact an object? According to “this explanation on rubylearning.com”:http://rubylearning.com/satishtalim/ruby_blocks_and_procs.html, a block is not an object unless it is converted to a Proc (e.g., using lambda).

Samr_small_pragsmall
09 May 2008, 13:51
Sam Ruby (634 posts)

The block is automatically converted to a Proc, which is an object.

It may help to play with this yourself by adding your own find method to the Array class, thus:

class Array
  def my_find &block
    puts "block class = #{block.class}"
    for item in self
      puts "testing item: #{item}"
      if block.call(item) == true
        return item
      end
    end
    return false
  end
end

data = [1, 2, 3, 4, 5]

puts "scanning for a three"
result = data.my_find {|i| i == 3}
puts "result: #{result}"
Scottgardner 200x200_pragsmall
09 May 2008, 14:53
Scott Gardner (7 posts)

That really helped, thanks!

You must be logged in to comment