hooking subscript operations in a hash

R

Ralph Shnelvar

In order to help debug something, I'd like to hook the hash subscript operation.

I've tried this (and several variants) in irb ... but it doesn't do what I want it to.


module RalphMod
def []=(index, value)
p index
p value
if index == 'PATH_INFO' and value == '/undefined'
puts 'found it!'
end
super index, value
end
end

class Hash
extend RalphMod
end

env = {}
env['X'] = 'Y'
env['PATH_INFO'] = '/undefined'


The values are not displayed and 'found it!' is also not displayed.
 
R

Rick DeNatale

In order to help debug something, I'd like to hook the hash subscript ope= ration.

I've tried this (and several variants) in irb ... but it doesn't do what = I want it to.


module RalphMod
=A0def []=3D(index, value)
=A0 =A0p index
=A0 =A0p value
=A0 =A0if index =3D=3D 'PATH_INFO' and value =3D=3D '/undefined'
=A0 =A0 =A0puts 'found it!'
=A0 =A0end
=A0 =A0super index, value
=A0end
end

class Hash
=A0extend RalphMod
end

env =3D {}
env['X'] =3D 'Y'
env['PATH_INFO'] =3D '/undefined'


The values are not displayed and 'found it!' is also not displayed.

A few problems with this code.

First

class Hash
extend RalphMod
end

makes the method in RalphMod a method of the Hash class object not
hash instances.

The way to do that would be

class Hash
include RalphMod
end

but this wouldn't work either because methods in included modules come
after methods defined in the class, so you'll still invoke the
original []=3D method.

What you want is something like this:

module RalphMod
def []=3D(index, value)
p index
p value
if index =3D=3D 'PATH_INFO' and value =3D=3D '/undefined'
puts 'found it!'
end
super index, value
end
end
env =3D {}
env.extend RalphMod
env['X'] =3D 'Y'
env['PATH_INFO'] =3D '/undefined'

This effectively inserts your module between the instance of Hash
referenced by the variable env and it's class.





--=20
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
 
R

Ralph Shnelvar

In order to help debug something, I'd like to hook the hash subscript operation.
I've tried this (and several variants) in irb ... but it doesn't do what I want it to.
module RalphMod
def []=(index, value)
p index
p value
if index == 'PATH_INFO' and value == '/undefined'
puts 'found it!'
end
super index, value
end
end
class Hash
extend RalphMod
end
env = {}
env['X'] = 'Y'
env['PATH_INFO'] = '/undefined'
The values are not displayed and 'found it!' is also not displayed.

RD> A few problems with this code.

RD> First

RD> class Hash
RD> extend RalphMod
RD> end

RD> makes the method in RalphMod a method of the Hash class object not
RD> hash instances.

RD> The way to do that would be

RD> class Hash
RD> include RalphMod
RD> end

RD> but this wouldn't work either because methods in included modules come
RD> after methods defined in the class, so you'll still invoke the
RD> original []= method.

RD> What you want is something like this:

RD> module RalphMod
RD> def []=(index, value)
RD> p index
RD> p value
RD> if index == 'PATH_INFO' and value == '/undefined'
RD> puts 'found it!'
RD> end
RD> super index, value
RD> end
RD> end
RD> env = {}
RD> env.extend RalphMod
RD> env['X'] = 'Y'
RD> env['PATH_INFO'] = '/undefined'

RD> This effectively inserts your module between the instance of Hash
RD> referenced by the variable env and it's class.

Yeah ... tried that ... but apparently env is going in and out of scope.

Is there any way to do it for all hash subscript operations?
 
H

hemant

RD> On Wed, Feb 3, 2010 at 9:38 PM, Ralph Shnelvar <[email protected]> wro= te: peration. t I want it to.

module RalphMod
=A0def []=3D(index, value)
=A0 =A0p index
=A0 =A0p value
=A0 =A0if index =3D=3D 'PATH_INFO' and value =3D=3D '/undefined'
=A0 =A0 =A0puts 'found it!'
=A0 =A0end
=A0 =A0super index, value
=A0end
end
class Hash
=A0extend RalphMod
end
env =3D {}
env['X'] =3D 'Y'
env['PATH_INFO'] =3D '/undefined'


Or why can't you use?

class RalphMod < Hash
def []=3D(index, value)
p index
p value
if index =3D=3D 'PATH_INFO' and value =3D=3D '/undefined'
puts 'found it!'
end
super index, value
end
end

env =3D RalphMod.new()
env['X'] =3D 'Y'
env['PATH_INFO'] =3D '/undefined'
 
R

Ruby Newbee

Hello,

I have two questions.
class RalphMod < Hash
=C2=A0def []=3D(index, value)
=C2=A0 =C2=A0p index
=C2=A0 =C2=A0p value
=C2=A0 =C2=A0if index =3D=3D 'PATH_INFO' and value =3D=3D '/undefined'
=C2=A0 =C2=A0 =C2=A0puts 'found it!'
=C2=A0 =C2=A0end
=C2=A0 =C2=A0super index, value

what's the "super" here? is it coming from which object?


=C2=A0end
end

env =3D RalphMod.new()
env['X'] =3D 'Y'
env['PATH_INFO'] =3D '/undefined'

Is env['X'] =3D 'Y' the same as env[]=3D('X','Y') ?
Since you defined a method with def []=3D so I think env[]=3D('X','Y') is
easier for understanding.

Thanks.
 
M

Marnen Laibow-Koser

Ralph Shnelvar wrote:
[...]
Is there any way to do it for all hash subscript operations?

Using include instead of extend should do the trick. You may also need
to alias the old Hash#[]= and then call it from your new one.

Best,
-- 
Marnen Laibow-Koser
http://www.marnen.org
(e-mail address removed)
 
W

Walton Hoops

Is there any way to do it for all hash subscript operations?
irb(main):012:0> class Hash
irb(main):013:1> alias_method :eek:ld, :[]=
irb(main):014:1> def []=(index, value)
irb(main):015:2> p index
irb(main):016:2> p value
irb(main):017:2> old index, value
irb(main):018:2> end
irb(main):019:1> end
=> nil
irb(main):020:0> x={}
=> {}
irb(main):021:0> x[:foo]=:bar
:foo
:bar
=> :bar
irb(main):022:0>

That should get you where your trying to go.

Best of luck!
 
P

pharrington

Ralph Shnelvar wrote:

[...]
Is there any way to do it for all hash subscript operations?

Using include instead of extend should do the trick.  You may also need
to alias the old Hash#[]= and then call it from your new one.

Best,
-- 
Marnen Laibow-Koserhttp://www.marnen.org
(e-mail address removed)

eh as Rick already pointed out includ'ing a module will insert the
module above the including class, so the original class's method still
gets called.

You'll need to "monkey patch" Hash's []=, and alias the original []=
prior-to.
 
R

Robert Klemme

In order to help debug something, I'd like to hook the hash subscript operation.
I've tried this (and several variants) in irb ... but it doesn't do what I want it to.
module RalphMod
def []=(index, value)
p index
p value
if index == 'PATH_INFO' and value == '/undefined'
puts 'found it!'
end
super index, value
end
end
class Hash
extend RalphMod
end
env = {}
env['X'] = 'Y'
env['PATH_INFO'] = '/undefined'
The values are not displayed and 'found it!' is also not displayed.

RD> A few problems with this code.

RD> First

RD> class Hash
RD> extend RalphMod
RD> end

RD> makes the method in RalphMod a method of the Hash class object not
RD> hash instances.

RD> The way to do that would be

RD> class Hash
RD> include RalphMod
RD> end

RD> but this wouldn't work either because methods in included modules come
RD> after methods defined in the class, so you'll still invoke the
RD> original []= method.

RD> What you want is something like this:

RD> module RalphMod
RD> def []=(index, value)
RD> p index
RD> p value
RD> if index == 'PATH_INFO' and value == '/undefined'
RD> puts 'found it!'
RD> end
RD> super index, value
RD> end
RD> end
RD> env = {}
RD> env.extend RalphMod
RD> env['X'] = 'Y'
RD> env['PATH_INFO'] = '/undefined'

RD> This effectively inserts your module between the instance of Hash
RD> referenced by the variable env and it's class.

Yeah ... tried that ... but apparently env is going in and out of scope.

Well, that's not a problem. As long as you retain a reference to the
object somewhere it will stay there.
Is there any way to do it for all hash subscript operations?

You have been shown solutions but frankly I would not do it. This might
have totally unwanted side effects on other code which uses Hash as
well. Better not modify core classes and instead either inherit them or
- even better - encapsulate them in another class. This has the added
advantage that nobody can modify the Hash in unwanted ways and you are
exposing only that part of Hash's interface that you intend to.

class RalphHash
def initialize
@h = {}
end

def []=(k,v)
if k == 'PATH_INFO' and v == '/undefined'
puts 'found it!'
end

@h[k]=v
end

def [](k)
@h[k]
end
end

You can also use delegate for this.

http://ruby-doc.org/stdlib/libdoc/delegate/rdoc/index.html

Kind regards

robert
 
R

Ralph Shnelvar

RN> Hello,

RN> I have two questions.

class RalphMod < Hash
def []=(index, value)
p index
p value
if index == 'PATH_INFO' and value == '/undefined'
puts 'found it!'
end
super index, value

RN> what's the "super" here? is it coming from which object?

What it means is "pretend you are calling the enclosing parent class's method of the same name with these arguments." Well, that's my informal way of putting it.

The Pickaxe book puts it more formally:
Within the body of a method, a call to super acts just like a call to that original method, except that the search for a method body starts in the superclass of the object that was found to contain the original method.

- - - -

Not in direct answer to your question but as an aside ...

I think I could replace
super index, value
with
super
since the function takes the same arguments
 
R

Ralph Shnelvar

RN> Is env['X'] = 'Y' the same as env[]=('X','Y') ?
RN> Since you defined a method with def []= so I think env[]=('X','Y') is
RN> easier for understanding.

First ... I think you meant
env[]={'X', 'Y'}
(braces instead of parenthesis)

The first form
env[]={'X', 'Y'}
means
Create and/or replace a hash table called "env" and stuff it with an index of 'X' and a value of 'Y'

The second form
env['X'] = 'Y'
means
Take an existing hash table called "env" and add/replace the index 'X' in that table with the value 'Y'
 
R

Ralph Shnelvar

Wednesday, February 3, 2010, 8:53:49 PM, you wrote:


irb(main):012:0>> class Hash
irb(main):013:1>> alias_method :eek:ld, :[]=
irb(main):014:1>> def []=(index, value)
irb(main):015:2>> p index
irb(main):016:2>> p value
irb(main):017:2>> old index, value
irb(main):018:2>> end
irb(main):019:1>> end
=>> nil
irb(main):020:0>> x={}
=>> {}
irb(main):021:0>> x[:foo]=:bar
WH> :foo
WH> :bar
=>> :bar


WH> That should get you where your trying to go.

WH> Best of luck!

So I tested this in irb and it seems to work fine ...

Then I stuck this into init.rb as part of a Rails app and ... boom ...


f:\Ralph-Rails-Apps\WebOfTrust>ruby script/server webrick --debugger
=> Booting WEBrick
=> Rails 2.3.5 application starting on http://0.0.0.0:3000
F:/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/validates_captcha-0.9.6/rail
s/init.rb:11:in `alias_method': undefined method `[]=' for class `Rails::plugin:
:Hash' (NameError)
from F:/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/validates_captc
ha-0.9.6/rails/init.rb:11:in `evaluate_init_rb'
from F:/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/rails-2.3.5/lib
/rails/plugin.rb:158:in `evaluate_init_rb'
from F:/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activesupport-2
3.5/lib/active_support/core_ext/kernel/reporting.rb:11:in `silence_warnings'
from F:/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/rails-2.3.5/lib
/rails/plugin.rb:154:in `evaluate_init_rb'
from F:/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/rails-2.3.5/lib
/rails/plugin.rb:48:in `load'
from F:/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/rails-2.3.5/lib
/rails/plugin/loader.rb:38:in `load_plugins'
from F:/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/rails-2.3.5/lib
/rails/plugin/loader.rb:37:in `each'
from F:/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/rails-2.3.5/lib
/rails/plugin/loader.rb:37:in `load_plugins'
... 10 levels...
from F:/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/rails-2.3.5/lib
/commands/server.rb:84
from F:/InstantRails-2.0-win/ruby/lib/ruby/site_ruby/1.8/rubygems/custom
_require.rb:31:in `gem_original_require'
from F:/InstantRails-2.0-win/ruby/lib/ruby/site_ruby/1.8/rubygems/custom
_require.rb:31:in `require'
from script/server:3


I am so lost.

Rails has a plugin for Hash?!?!?!? And []= is undefined?!?!?
 
M

Marnen Laibow-Koser

Ralph said:
RN> Is env['X'] = 'Y' the same as env[]=('X','Y') ?
RN> Since you defined a method with def []= so I think env[]=('X','Y')
is
RN> easier for understanding.

First ... I think you meant
env[]={'X', 'Y'}
(braces instead of parenthesis)

But you are wrong. It's just like any other method call:
object.method(args)
so
env.[]=('X', 'Y')

But never do it this way. env['X'] = 'Y' is far clearer.
The first form
env[]={'X', 'Y'}
means
Create

No. It won't create env.
and/or replace a hash table

Hash, not hash table.
called "env" and stuff it with an
index of 'X' and a value of 'Y'

The second form
env['X'] = 'Y'
means
Take an existing hash table called "env" and add/replace the index 'X'
in that table with the value 'Y'

No. The two forms are 100% equivalent.

Best,
-- 
Marnen Laibow-Koser
http://www.marnen.org
(e-mail address removed)
 
R

Rick DeNatale

Wednesday, February 3, 2010, 8:53:49 PM, you wrote:


irb(main):012:0>> class Hash
irb(main):013:1>> alias_method :eek:ld, :[]=3D
irb(main):014:1>> def []=3D(index, value)
irb(main):015:2>> p index
irb(main):016:2>> p value
irb(main):017:2>> old index, value
irb(main):018:2>> end
irb(main):019:1>> end
=3D>> nil
irb(main):020:0>> x=3D{}
=3D>> {}
irb(main):021:0>> x[:foo]=3D:bar
WH> :foo
WH> :bar
=3D>> :bar
So I tested this in irb and it seems to work fine ...

Then I stuck this into init.rb as part of a Rails app and ... boom ...


f:\Ralph-Rails-Apps\WebOfTrust>ruby script/server webrick --debugger
=3D> Booting WEBrick
=3D> Rails 2.3.5 application starting on http://0.0.0.0:3000
F:/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/validates_captcha-0.9= 6/rail
s/init.rb:11:in `alias_method': undefined method `[]=3D' for class `Rails= ::plugin:
:Hash' (NameError)
=A0 =A0 =A0 =A0from F:/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/v= alidates_captc
ha-0.9.6/rails/init.rb:11:in `evaluate_init_rb'
=A0 =A0 =A0 =A0from F:/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/r= ails-2.3.5/lib
/rails/plugin.rb:158:in `evaluate_init_rb'

I'm guessing a bit here, but I suspect that you inserted this code
into the init.rb of the validates_captcha plugin.

This is being evaluated as source from the rails plugin initialization
code, and the name space is Rails:plugin

So the unscoped name Hash is not the normal and already created Hash class

and

class Hash
....
end

creates a new class called Rails::plugin::Hash with no relationship to ::Ha=
sh

Now changing that code to

class ::Hash ....

would get you around this immediate problem, but I'm not sure you
really want to do this. Your Rails app is going to spew that
debugging information for EVERY instance of Hash or one of it's
subclasses.


--=20
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
 
R

Rick DeNatale

Ralph said:
RN> Is env['X'] =3D 'Y' the same as env[]=3D('X','Y') ?
RN> Since you defined a method with def []=3D so I think env[]=3D('X','Y= ')
is
RN> easier for understanding.

First ... I think you meant
=A0 env[]=3D{'X', 'Y'}
(braces instead of parenthesis)

But you are wrong. =A0It's just like any other method call:
object.method(args)
so
env.[]=3D('X', 'Y')

But never do it this way. =A0env['X'] =3D 'Y' is far clearer.
The first form
=A0 env[]=3D{'X', 'Y'}
means
=A0 Create

No. =A0It won't create env.
and/or replace a hash table

Hash, not hash table.
called "env" and stuff it with an
index of 'X' and a value of 'Y'

The second form
=A0 env['X'] =3D 'Y'
means
=A0 Take an existing hash table called "env" and add/replace the index '= X'
in that table with the value 'Y'

No. =A0The two forms are 100% equivalent.

Actually more like only 99 44/100% equivalent.

In cases where a 'setter' method returns a value other than the
argument there's a subtle diference:

class MyHash
def []=3D(key, value)
"Hey Look, No Hands!"
end
end

mh =3D MyHash.new

a =3D mh[1] =3D 2
a # =3D> 2

b =3D mh.[]=3D(1, 2)
b # =3D> "Hey Look, No Hands!"

Ruby sees mh[index]=3Dvalue as an assignment, and compiles it to the equiva=
lent of

mh,[]=3D(index, value)
a =3D value


--=20
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
 
R

Ruby Newbee

Ralph said:
RN> Is env['X'] =3D 'Y' the same as env[]=3D('X','Y') ?
RN> Since you defined a method with def []=3D so I think env[]=3D('X','Y= ')
is
RN> easier for understanding.

First ... I think you meant
=C2=A0 env[]=3D{'X', 'Y'}
(braces instead of parenthesis)

But you are wrong. =C2=A0It's just like any other method call:
object.method(args)
so
env.[]=3D('X', 'Y')

But never do it this way. =C2=A0env['X'] =3D 'Y' is far clearer.

Thanks. I lost the ".", yes it should be env.[]=3D(...)
OK I got it that env['X']=3D'Y' is better, thanks again.
 
R

Ralph Shnelvar

RD> I'm guessing a bit here, but I suspect that you inserted this code
RD> into the init.rb of the validates_captcha plugin.

RD> This is being evaluated as source from the rails plugin initialization
RD> code, and the name space is Rails:plugin

RD> So the unscoped name Hash is not the normal and already created Hash class

RD> and

RD> class Hash
RD> ....
RD> end

RD> creates a new class called Rails::plugin::Hash with no relationship to ::Hash

RD> Now changing that code to

RD> class ::Hash ....


Thank you, thank you, thank you.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,981
Messages
2,570,187
Members
46,730
Latest member
AudryNolan

Latest Threads

Top