From: Hugh Sasse Date: 2005-10-25T22:45:34+09:00 Subject: Re: Reflection, observ(er|able) instance vars? On Tue, 25 Oct 2005, Daniel Sheppard wrote: > I generally use define_method only because it works better with the > syntax highlighting of SciTE, and also make the code look a little > unnatural... you also can't pass in real objects to be used by the [...] > > works fine. If I was trying to make it run fast, I'd probably be using > class_eval, not define_method. I couldn't get the class_eval form to work. > > Evals can be slow, but you're only doing the eval once when it's a > class_eval, and then it's just like any other method, whereas with > define_method you're calling off to the proc object every time, which is > slower than a method call for each and every call. I don't know by how much. Anyway, this will be much faster than doing a full compare each time, and faster than doing a hash each time, so I don't really need to milk the last drop. Here is what I ended up with in the end. It does what I want. If anyone feels like improving on it, you are welcome to do so. Thank you Hugh #!/usr/local/bin/ruby -w # # Ruby-Talk:161789 from: "Daniel Sheppard" # Provides hashing for an object based on instance vars, and handles # changes to those vars gracefully. # The thread begins with Ruby-Talk:161631. # # Modified by Hugh Sasse to move it into class Class so attr_hashing # works like the other attr methods. Also added code so that # freezing works correctly: Hash works faster with frozen objects. # # require 'fasthashequals' # class Example # attr_hashing :this, :that, :other # # end # # For getting an array back as Ruby code. class Array def display return "[ "+ map{|x| x.inspect }.join(', ') + " ]" end end class Class # attr_hashing needs to be a method of class to be useable as # the other attr methods are. This method sets up the # informtion and methods needed to create the hash from those # instance variables. def attr_hashing(*arr) stararr = arr.flatten # puts "stararr is #{stararr.inspect}" (stararr).each do |sym| attr_reader sym end hashers = class_eval "@@__hashers ||= []" hashers.concat(stararr) hashers.uniq! # puts "@@__hashers = " + hashers.display class_eval "@@__hashers = " + hashers.display stararr.each do |var| define_method(var) { instance_variable_get("@#{var}") } define_method("#{var}=") {|obj| instance_variable_set("@#{var}", obj) instance_variable_set(:@__equals_hash, nil) } define_method('eql?') { |other| hash === other.hash && hashers.all? { |x| send(x) == other.send(x) } } define_method('hash') { # puts "in hash, self is #{self.inspect}" @__equals_hash ||= hashers.inject(0) do |s,x| s + instance_variable_get("@#{x}").hash end } # Once we freeze an object we can't update its hash again define_method('freeze') { hash super } end end end