Forum: Ruby included module and scope confusion

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
6a49593b413f4ad2fae5ad0eb455b787?d=identicon&s=25 hmmm (Guest)
on 2006-01-10 20:12
(Received via mailing list)
I am confused about modules and scope.

Here is a module:

module Crumbs
  MAX_CRUMBS = 10

  def self.included(mod)
    def cookies
      return @cookies == nil ? Hash.new : @cookies
    end
  end

  def crumb_new( controller, action, params )
    cstr = controller + "^" + action
    if params != nil && params.keys != nil
      params.keys.each {|p|
        if params[p] != nil
          cstr << "^" << p << "^" << params[p]
        end
      }
    end
    return cstr
  end

  def crumb_add( bite )
    cbs = @cookies
    puts cbs.class
    cbs = cbs[:rdf_crumbs]
    if cbs == nil
      puts "crumb_add::cbs is nil!"
      cbs =[bite]
    else
      puts "crumb_add::cbs is OK!"
      cbs = cbs.split("|")
      #don't add a redundant
      if cbs.last == bite
        return
      end
      # add to
      cbs << bite
      # cap the cookie queue at 5 using fifo
      if cbs.length > @@MAX_CRUMBS
        cbs.delete_at(0)
      end
    end
    crumbs_set( cbs )
  end

  def crumbs_set( mouthful )
    cr = ""
    mouthful.each { |m|
      if cr == ""
        cr = m
      else
        cr << "|" << m
      end
    }
    puts cookies.class
    cookies[:rdf_crumbs] = cr
  end

  def get_crumbs()
    crs = @cookies["rdf_crumbs"]
    if crs == nil
      return []
    else
      return crs.split("|")
    end
  end

end
==================================

Here is a unit test:
==================================
class Bread
  include Crumbs
  def initialize
    @cookies = Hash.new
  end
end

class CrumbTest < Test::Unit::TestCase

  attr :bread

  def setup
    @bread = Bread.new
  end

  def test_crumbs_simple
    @params = Hash.new
    bite =bread.crumb_new( "test1", "test_action", @params )
    bread.crumb_add(bite)
    puts bite
    bite =bread.crumb_new( "test2", "test_action_2", @params )
    bread.crumb_add(bite)
    puts bite
    bite =bread.crumb_new( "test3", "test_action_3", @params )
    bread.crumb_add(bite)
    puts bite
    assert bite == "test3^test_action_3"
    bites =bread.get_crumbs()
    puts bites.class
    assert_equal bites.length, 3
  end
end

Here is the output:
------------------------------------------
test cookies! on Bread
Hash
crumb_add::cbs is nil!
sets Hash with test1^test_action
has 1
test1^test_action
Hash
crumb_add::cbs is nil!
sets Hash with test2^test_action_2
has 1
test2^test_action_2
Hash
crumb_add::cbs is nil!
sets Hash with test3^test_action_3
has 1
test3^test_action_3
------------------------------------------

And:
------------------------------------------
Test::Unit::AssertionFailedError: <1> expected but was <3>.
test/unit/crumbs_test.rb:62:in `test_crumbs_simple'
------------------------------------------

I have tried different ways, but can't get the Correct Way to have a
module
insert a variable into the Class that includes it.  In this case it
should
inject a @cookies hash, but in tests @cookies always gets reset to
Hash.new...

Excuse the inconsistencies in the code (towards @cookies) b/c I can't
seem
to figure out the proper way.

So how do you add instance vars to a class from a module and how to
reference that attribute?  Or is this not the Right Ruby Way?

I have a lot of confusion about modules... suppose its my java funk :/

Please point me to any more docs on modules besides the ruby-lang
snippet.

-netcam
6a49593b413f4ad2fae5ad0eb455b787?d=identicon&s=25 hmmm (Guest)
on 2006-01-10 20:28
(Received via mailing list)
Before I was mixing (include) Crumbs on the unit test class and I think
there was a name collision with the rails test_helper.rb file and
@cookies
hence it got me all confused.

Ok here is what I did, and it seems simple.  But a better way it seems
would
to do it transparently to the subclass on the module, so if anyone knows
how, divulge.  I don't want to have to declare the attr_reader :cookies
on
the Class.

class Bread
  include Crumbs

  attr_reader :cookies

  def initialize
    @cookies = Hash.new
  end
end

module Crumbs

#  def self.included(mod)
#    puts "test cookies! on #{mod}"
#    def mod.cookies
#      if @cookies == nil
#        @cookies = Hash.new
#      end
#      return @cookies
#    end
#  end

  #----------------------------------------
  #--------------begin/ cookie crumbs mixin
  #----------------------------------------
  MAX_CRUMBS = 10

  def crumbs_clear
    if @cookies != nil and @cookies["rdf_crumbs"] != nil
      @cookies.delete "rdf_crumbs"
    end
  end

  def crumb_new( controller, action, params )
    cstr = controller + "^" + action
    if params != nil && params.keys != nil
      params.keys.each {|p|
        if params[p] != nil
          cstr << "^" << p << "^" << params[p]
        end
      }
    end
    return cstr
  end

  def crumb_add( bite )
    cbs = @cookies
    puts cbs.class
    cbs = cbs["rdf_crumbs"]
    if cbs == nil
      puts "crumb_add::cbs is nil!"
      cbs =[bite]
    else
      puts "crumb_add::cbs is OK!"
      cbs = cbs.split("|")
      #don't add a redundant
      if cbs.last == bite
        return
      end
      # add to
      cbs << bite
      # cap the cookie queue at 5 using fifo
      if cbs.length > MAX_CRUMBS
        cbs.delete_at(0)
      end
    end

    crumbs_set( cbs )
  end

  def crumbs_set( mouthful )
    cr = ""
    mouthful.each { |m|
      if cr == ""
        cr = m
      else
        cr << "|" << m
      end
    }
    puts "sets #{@cookies.class} with #{cr}"
    if @cookies == nil
      @cookies = Hash.new
    end
    @cookies["rdf_crumbs"]=cr
    puts "has #{@cookies.keys.length}"
  end

  def crumb_del( bite )
    crs = cookies[:rdf_crumbs]
    if crs != nil
      crs = crumbs.split("|")
      c2 =""
      crs.each { |c|
        if crs[c] != bite
          if c2 == ""
            c2 = c
          else
            c2 << "|" << c
          end
        end
      }
      cookies[:rdf_crumbs] =c2
    end
  end

  def get_crumbs()
    crs = @cookies["rdf_crumbs"]
    if crs == nil
      return []
    else
      return crs.split("|")
    end
  end

  #----------------------------------------
  #--------------end/ cookie crumbs mixin
  #----------------------------------------
end
6a49593b413f4ad2fae5ad0eb455b787?d=identicon&s=25 hmmm (Guest)
on 2006-01-10 22:47
(Received via mailing list)
Ok here is the final version...

Unit Test:
===============================
class Bread
  include Crumbs
  def cookies
    @cookies ||= Hash.new
  end
  def initialize
    @cookies = Hash.new
  end
end

class CrumbTest < Test::Unit::TestCase

  attr :bread

  def setup
    @bread = Bread.new
  end

  # Simple add/delete/clear tests
  def test_crumbs_simples
    @params = Hash.new
    bite =bread.crumb_new( "test1", "test_action", @params )
    bread.crumb_add(bite)
    bite =bread.crumb_new( "test2", "test_action_2", @params )
    bread.crumb_add(bite)
    bite =bread.crumb_new( "test3", "test_action_3", @params )
    bread.crumb_add(bite)
    assert bite == "test3^test_action_3"
    bites =bread.get_crumbs()
    assert_equal bites.length, 3
    bread.crumbs_clear()
    bites =bread.get_crumbs()
    assert_equal bites.length, 0
    bite =bread.crumb_new( "test1", "test_action", @params )
    bread.crumb_add(bite)
    bread.crumb_del(bite)
    bites =bread.get_crumbs()
    assert_equal bites.length, 0
  end

  def test_crumbs_params
    bite =bread.crumb_new( "test1", "test_action",
{"filter"=>"s`divx:divx"})
    bread.crumb_add(bite)
    bites =bread.get_crumbs()
    assert_equal bites[0], bite
  end
end


And the crumbs module:
===============================

module Crumbs
  #----------------------------------------
  #--------------begin/ cookie crumbs mixin
  #----------------------------------------
  MAX_CRUMBS = 10

  public
  def crumbs_clear
    if cookies != nil and cookies[:rdf_crumbs] != nil
      cookies.delete :rdf_crumbs
    end
  end

  def crumb_new( controller, action, params )
    cstr = controller + "^" + action
    if params != nil && params.keys != nil
      params.keys.each {|p|
        if params[p] != nil
          cstr << "^" << p << "^" << params[p]
        end
      }
    end
    return cstr
  end

  def crumb_add( bite )
    cbs = cookies[:rdf_crumbs]
    if (cbs == nil or cbs == "") or (cbs.instance_of? Array and
cbs.length==0)
      cbs =[bite]
    else
      cbs = cbs.split("|")
      #don't add a redundant
      if cbs.last == bite
        return
      end
      # add to
      cbs << bite
      # cap the cookie queue at 5 using fifo
      if cbs.length > MAX_CRUMBS
        cbs.delete_at(0)
      end
    end

    crumbs_set( cbs )
  end

  def crumbs_set( mouthful )
    cr = ""
    mouthful.each { |m|
      if cr == ""
        cr = m
      else
        cr << "|" << m
      end
    }
    cookies[:rdf_crumbs]=cr
  end

  def crumb_del( bite )
    crs = cookies[:rdf_crumbs]
    if crs != nil
      crs = crs.split("|")
      c2 =""
      crs.each { |c|
        if c != bite
          if c2 == ""
            c2 = c
          else
            c2 << "|" << c
          end
        end
      }
      cookies[:rdf_crumbs] =c2
    end
  end

  def get_crumbs()
    crs = cookies[:rdf_crumbs]
    if crs == nil
      return []
    else
      return crs.split("|")
    end
  end

  #----------------------------------------
  #--------------end/ cookie crumbs mixin
  #----------------------------------------
end
5befe95e6648daec3dd5728cd36602d0?d=identicon&s=25 Robert Klemme (Guest)
on 2006-01-11 10:55
(Received via mailing list)
hmmm wrote:

</snip>

> I have tried different ways, but can't get the Correct Way to have a
> module insert a variable into the Class that includes it.  In this
> case it should inject a @cookies hash, but in tests @cookies always
> gets reset to Hash.new...

Of course because you assign it in initialize.

Two options:

1 Lazy init: You need to define cookies differently and access it always
through the getter method (attachment ex1.rb)

2 Init during constructor: you need to define #initialize in the module
and either leave initialize out of Bread or use super (attachment
ex2.rb)

Module#included is completely wrong here as you do not want to do
anything
to the class that uses the mod.

> Excuse the inconsistencies in the code (towards @cookies) b/c I can't
> seem to figure out the proper way.
>
> So how do you add instance vars to a class from a module and how to
> reference that attribute?  Or is this not the Right Ruby Way?
>
> I have a lot of confusion about modules... suppose its my java funk :/

Apparently. :-)

> Please point me to any more docs on modules besides the ruby-lang
> snippet.



HTH

Kind regards

    robert
This topic is locked and can not be replied to.