1

I'm trying to write a method that prints class variable names and their values. As an example:

class A
    def printvars
        ???
    end
end

class <<A
    def varlist(*args)
        ???
    end
end

class B < A
    varlist :c
    def initialize(c)
        @c = c
    end
b = B.new(10)
b.printvars()

And I would like the output to be c => 10. But I don't know what goes in the ???. I've tried using a self.class_eval in the body of varlist, but that won't let me store args. I've also tried keeping a hash in the class A and just printing it out in printvars, but the singleton class is a superclass of A and so has no access to this hash. So far everything I've tried doesn't work.

I think something similar must be possible, since Rails does something related with its validates_* methods. Ideally I could make this work exactly as expected, but even a pointer to how to print just the variable names (so just c as output) would be most appreciated.

Chuck Vose
  • 4,560
  • 24
  • 31
Steve D
  • 373
  • 2
  • 17

2 Answers2

1

You might like this answer: What is attr_accessor in Ruby?

Basically, as you surmised, varlist needs to be a class method which takes a variable list of arguments (*args). Once you have those arguments you could try any number of things using send, respond_to?, or maybe even instance_variable_get. Note, none of those are really recommended, but I wanted to answer your question a bit.

The other half is that you should probably look into method_missing in order to understand how things like validates_* are working. The * part necessitates that you do something like method_missing because you can't actually do module_eval until you know what you're looking for. In the case of the magic rails methods, you don't necessarily ever know what you're looking for! So we rely on the built in method_missing to let us know what got called.

For funzies, try this in IRB:

class A
  def method_missing(method, *args, &block)
    puts method, args.inspect
  end
end
A.new.banana(13, 'snakes')
A.new.validates_serenity_of('Scooters', :within => [:calm, :uncalm])

Does that help?

Community
  • 1
  • 1
Chuck Vose
  • 4,560
  • 24
  • 31
  • Thanks that is actually very helpful. My followup question is then how to replace the puts in method_missing with something more permanent? That is, how does validates :snakes, in: [1,2,3] store the name snakes, store the array to check it again during save, and also access the value of snakes when in save? – Steve D Feb 26 '15 at 04:12
  • Yeah, when I woke up this morning realizing that I hadn't actually answered your question. Probably you'd use `module_eval`. The problem I didn't talk about is that I used the instance version of `method_missing`, if you try it with the class version you'll note that `validates_*` gets called before anything. So that's where you'd use a singleton variable (which is also @variable_name, but when @ is used outside a method it's stored on the class instance (aka the singleton). Later, inside an instance, you'll use `self.class.variable_name` or something. – Chuck Vose Feb 26 '15 at 19:23
1

Just use Module#class_variables

As far as I can tell, you're vastly over-complicating this. All you need is the pre-defined Module#class_variables method. You can call this directly on the class, or invoke it through self if you want to bind it to an instance of the class. For example:

class Foo
  @@bar = "baz"

  def show_class_variables
    self.class.class_variables
  end
end

Foo.class_variables
#=> [:@@bar]

foo = Foo.new
foo.show_class_variables
#=> [:@@bar]
Todd A. Jacobs
  • 81,402
  • 15
  • 141
  • 199
  • I think that perhaps my example above didn't explain everything fully. But I want to be able to add select variables to a "watchlist" . – Steve D Feb 26 '15 at 11:23