[Nitro] Concerns over Og mandating the form of initialize
James Britt
james_b at neurogami.com
Mon Apr 11 12:42:11 EDT 2005
Hi all,
While trying to get the Og FileSystem adapter working to my liking, I
noticed that the other database adapters were making an interesting
assumption about client code.
There is a method common to all adapters, read_all, that is given a
result set 'res' and a class 'klass'.
The code (at least in the mysql and sqlite case) does this:
obj = klass.new
obj.og_read(res)
So, if you have a blog application and have marked the BlogEntry class
for persistence by Og, the BlogEntry constructor cannot have any
mandatory arguments, else read_all fails.
I can understand the need to for this; general-purpose code cannot know
the arity and details of each and every constructor. But I am unhappy
with this solution for two reasons.
The first is that part of the appeal og Nitro/Og is that you can build
up an application without having to make design decisions based on
unknown future needs, or based on the demands and quirks of the
framework. I like the idea that I can start with a set of vanilla HTML
pages and migrate them to use templates, server code, and a database. I
don't have to plan for all that well before I've determined actual
business needs. I would much prefer to be able to take a business
object class, switch the attr_* calls to prop_* calls, and magically get
Og goodness. Being required to define a particular form of 'initialize'
method, even when it does not fit in with a preferred design scheme,
feels cumbersome.
The second objection is that having a constructor that allows for no
arguments may mean one can create an object with invalid state. For
example, a BlogEntry instance is meaningless and perhaps disruptive if
it does not contain, say some content and a date.
Ironically, on my first pass at adding code to the filesys adapter I
simply used YAML::load to convert the serialized objects back to Ruby.
There was no need to invoke a constructor. It was only after looking at
how the other adapters did this that I tried the kalss.new approach, and
my code broke. The (arguably) weakest adapter has the best tool for
object revival.
One thought then was that load_all (and similar methods) could take the
database results and use them to create a YAML representation of the
object, then call YAML::load.
I believe that YAML uses binary code under the hood to actually do this,
so mimicking the technique may be a problem. But I hope someone can
prove me wrong. (Might be worth asking this in the ruby-musing list,
where extreme metaprogramming seems to be the norm.)
Thoughts? Is it a reasonable concession to ask of developers that
Og-persisted objects have to have a certain style of constructor?
On a side note, it is of course possible to write a "have it both ways"
constructor, which is not a bad idea in itself, but maybe not something
people should be forced to do in order to have both Og persistence and
argument checking.
#---------------------------------------------
class BlogEntry
attr_accessor :content, :time , :title
def initialize( content = nil, time = nil , title = nil )
@content, @time , @title = content, time , title
yield self unless (content || time || title )
raise "All args are required" unless (@content || @time || @title )
end
end
be = BlogEntry.new( 'This is my entry', Time.now, "Test" )
be2 = BlogEntry.new { |entry|
entry.content = "Content"
entry.time = Time.now
entry.title = "Testing"
}
def make_it( res, klass )
obj = klass.new { |k|
res.each { |prop, val| k.send( "#{prop}=", val) }
}
end
res = { 'content' => "Content",
'time' => Time.now,
'title' => "Testing"
}
be3 = make_it( res, BlogEntry )
#---------------------------------------------
If there is a requirement that the constructor have no mandatory
arguments, perhaps there can also be a requirement that the constructor
yield self so that og_read can dynamically set object properties while
the initialize method still has an opportunity to do internal validity
checks.
James
More information about the Nitro-general
mailing list