Code
First I'll show you the class that stores a property. It is a just a simple key/value pair.
class Property attr_accessor :name, :value def initialize(name, value) @name = name @value = value end end
Next, is my code for the Object class (which I call Thing):
class Thing attr_accessor :prototype, :type, :properties def initialize @properties = {} end def self_keys @properties.keys end def keys self_keys | (@prototype ? @prototype.keys : []) end def [](key) return @properties[key].value if @properties.has_key? key @prototype ? @prototype[key] : nil end def []=(key, value) if @properties.has_key? key @properties[key].value = value else @properties[key] = Property.new(key, value) end end def to_hash hash = @prototype ? @prototype.to_hash : {} @properties.each_value {|p| hash[p.name] = p.value} hash end def method_missing(method, *args) name = method.to_s if (name.end_with?('=')) self[name[0..-2]] = args[0] else return self[name] end end endAs you can see a "Thing" consists of a type, a prototype object, and a map of key/property pairs. Property lookups (which can be done using either array type syntax or the dot syntax of methods and fields) first look at a thing's properties, and then at its prototype's properties. All in all, it is a fairly simple pair of classes.
Thoughts
There are a couple of decisions here that seem odd, so I'll explain my thinking.
Thing
Why did I call the Object type Thing? Well there is already an Object class in Ruby and I didn't want to worry about naming collisions. Like object, "thing" is a generic enough word that if I want to create a class with that name, I should probably rethink my design. So it satisfies the qualifications of describing "something" without causing any likely naming collisions.
Property
Why do I have a Property class rather than doing the more straightforward thing of having Thing just containing a Hash of key/values? Well, this is an example of me violating the YAGNI principle. I am going to want to persist this information to a database so I am envisioning a Thing table and a Property table. My assumption is that when I do that, I'll want the key/value pairs broken out into their own Property class. So I am (perhaps foolishly) breaking that out ahead of time.
method_missing
Like JavaScript, I want to be able access properties via either array syntax (thing['property']) or method syntax (thing.property). Ruby provides the functionality to handle unknown method names, via the method_missing method. As you can see, I wrote a bare bones implementation that does no error checking. If it gets passed a method that ends with '=', it assumes that it is a property assignment, otherwise it assumes it is a property read. I suppose in the future I could/should add error checking like that there are the right number of arguments to the method. I should probably also implement respond_to so it will return true for any property that exists.
Lack of Functionality
Do these classes actually buy me anything? I'm not sure. I am sure that I am going to need a bunch of additional functionality (like database persistance) before they are really useful to me. But I have to start somewhere. So rather than blog about a fully baked solution, I've decided to show you some of the raw ingredients as I go. I am hoping that this gives me a good starting point.
No comments:
Post a Comment