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.
On Wed, Feb 3, 2010 at 9:38 PM, Ralph S. [email protected] wrote:
puts 'found it!'
env[‘X’] = ‘Y’
env[‘PATH_INFO’] = ‘/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 []= method.
What you want is something like this:
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
env = {}
env.extend RalphMod
env[‘X’] = ‘Y’
env[‘PATH_INFO’] = ‘/undefined’
This effectively inserts your module between the instance of Hash
referenced by the variable env and it’s class.
–
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
RD> On Wed, Feb 3, 2010 at 9:38 PM, Ralph S. [email protected]
wrote:
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?
Ralph S. 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
[email protected]
Hello,
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
what’s the “super” here? is it coming from which object?
 end
end
env = RalphMod.new()
env[‘X’] = ‘Y’
env[‘PATH_INFO’] = ‘/undefined’
Is env[‘X’] = ‘Y’ the same as env[]=(‘X’,‘Y’) ?
Since you defined a method with def []= so I think env[]=(‘X’,‘Y’) is
easier for understanding.
Thanks.
On Thu, Feb 4, 2010 at 8:41 AM, Ralph S. [email protected] wrote:
if index == ‘PATH_INFO’ and value == ‘/undefined’
env = {}
env[‘X’] = ‘Y’
env[‘PATH_INFO’] = ‘/undefined’
Or why can’t you use?
class RalphMod < Hash
def []=(index, value)
p index
p value
if index == ‘PATH_INFO’ and value == ‘/undefined’
puts ‘found it!’
end
super index, value
end
end
env = RalphMod.new()
env[‘X’] = ‘Y’
env[‘PATH_INFO’] = ‘/undefined’
On Feb 3, 10:50 pm, Marnen Laibow-Koser [email protected] wrote:
–
Marnen Laibow-Koserhttp://www.marnen.org
[email protected]
Posted viahttp://www.ruby-forum.com/.
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.
On 2/3/2010 8:11 PM, Ralph S. wrote:
Is there any way to do it for all hash subscript operations?
irb(main):012:0> class Hash
irb(main):013:1> alias_method :old, :[]=
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!
On 04.02.2010 04:11, Ralph S. wrote:
if index == ‘PATH_INFO’ and value == ‘/undefined’
env = {}
RD> class Hash
RD> end
RD> p value
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
@h[k]
end
end
You can also use delegate for this.
http://ruby-doc.org/stdlib/libdoc/delegate/rdoc/index.html
Kind regards
robert
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
Wednesday, February 3, 2010, 8:53:49 PM, you wrote:
WH> On 2/3/2010 8:11 PM, Ralph S. wrote:
Is there any way to do it for all hash subscript operations?
irb(main):012:0>> class Hash
irb(main):013:1>> alias_method :old, :[]=
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?!?!?
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’
Ralph S. wrote:
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
[email protected]
On Thu, Feb 4, 2010 at 6:21 AM, Ralph S. [email protected] wrote:
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
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’
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
::Hash
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.
–
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
On Thu, Feb 4, 2010 at 9:54 PM, Marnen Laibow-Koser [email protected]
wrote:
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.
Thanks. I lost the “.”, yes it should be env.[]=(…)
OK I got it that env[‘X’]=‘Y’ is better, thanks again.
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.
On Thu, Feb 4, 2010 at 8:54 AM, Marnen Laibow-Koser [email protected]
wrote:
But you are wrong. It’s just like any other method call:
Create
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.
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 []=(key, value)
“Hey Look, No Hands!”
end
end
mh = MyHash.new
a = mh[1] = 2
a # => 2
b = mh.[]=(1, 2)
b # => “Hey Look, No Hands!”
Ruby sees mh[index]=value as an assignment, and compiles it to the
equivalent of
mh,[]=(index, value)
a = value
–
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