[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