* John W Higgins (wishdev-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org) [050526
18:10]:> 4) What exactly do you wish to see for "conditions"??? Is it
going to be
> some series of nested objects handling AND''s OR''s
NOT''s IN''s and such?
> How exactly does a flat file parser handle that? You would either have
> to inject "SQL style" logic here or you really completely blow
things up
> - making it much more difficult to debug and completely more error
> prone. Again - something could work here that would be simply ruby-magic
> - but man I don''t want to see how hard it will make life for
people to
> try and pick it up.
Recalling historical posts to this mailing lists (and vaguely remembered
IRC discussions) and other sources, I''m of the understanding that the
Rails Way is not to try to reimplement SQL as one of those Gawd-Awful
object hierarchies that basically represent an SQL parse tree, and that
means that non-SQL AR-style abstractions will have to go a different
route.
At one point I was writing "ActiveYAML", but time overtook me and then
Rails got ahead of me a few . releases and I''ve never had the time to
catch back up. I did, instead, implement an AR-style YAML class for a
recent project, but which doesn''t take advantage of things like
Validations from AR.
What I believe would be a Ruby Way style of implementing something like
a YAML or flat-file or Madeleine AR wrapper is to do the following
(which is what I was doing for "ActiveYAML"):
1 - Create an ActiveFoo base class which does what the ActiveRecord
base class does, i.e., pulls in the various ActiveRecord::* modules to
get validations, foreign key relationships, etc. Essentially what I
did was to create active_yaml/ and copy over active_record.rb to
active_yaml.rb and tweak the module includes a little bit.
2 - Inherit from (or copy in) the ActiveRecord::Base methods which
implement basic find(), find_all(), save(), etc. functionality (in a
SQL style). I.e., use active_yaml/base.rb to pull in the inheritance
with AR.
3 - Update the primitive methods which actually dispatch SQL to
operate instead on the particular non-SQL store in use -- most likely
using a block/iterator instead of passing SQL for conditions.
For example, (from my abortive ActiveYAML attempt):
class << self # Class methods
# Returns objects for the records responding to either a specific id (1),
a list of ids (1, 5, 6) or an array of ids.
# If only one ID is specified, that object is returned directly. If more
than one ID is specified, an array is returned.
# Examples:
# Person.find(1) # returns the object for ID = 1
# Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2,
6)
# Person.find([7, 17]) # returns an array for objects with IDs in (7,
17)
# Person.find([1]) # returns an array for objects the object with ID
= 1
#
# A block may be supplied to allow for conditional finds. This will be
treated as a block to Enumerable#find_all.
# Person.find(1) { |rec| rec.salary > 30_000 }
# Person.find(1, 2, 6) { |rec| rec.status = ''active'' }
# Person.find([7, 17]) { |rec| rec.safe_field =
"bare''quote" }
#
# +RecordNotFound+ is raised if no record can be found.
def find(*args, &block)
# identity function is default for block
block = lambda { |i| i } unless block_given?
# Return an Array if ids are passed in an Array.
expects_array = args.first.kind_of?(Array)
ids = args.flatten.compact.uniq
case ids.size
# Raise if no ids passed.
when 0
raise RecordNotFound, "Couldn''t find #{name} without
an ID"
# Find a single id.
when 1
#TODO: this needs to be translated into block version of selecting an id (look
at "primary_key" bit)
unless result = find_first(ids.first) { |i| ids.first ==
i.send(primary_key) and block.call(i) }
raise RecordNotFound, "Couldn''t find #{name} with
ID=#{ids.first}"
end
# Box result if expecting array.
expects_array ? [result] : result
# Find multiple ids.
else
result = find_all() do |i|
#TODO: this needs to be translated into block version of selecting an id
ids.include?(i.send(primary_key)) and block.call(i)
end
if result.size == ids.size
result
else
raise RecordNotFound, "Couldn''t find #{name} with ID
in (#{ids_list})"
end
end
end
You can see where I was headed probably, there''s more code in the
dependent functions but it''s the same block-passing (and defaulting)
idiom to do the conditions since SQL is not an option. Anyway, at the
bottom of this (once all the TODO''s are traced down to the lowest
level)
you hit various primitive operations, essentially atomic reads and
writes. For the project where I did simpler YAML processing I found Ara
T. Howard''s file locking library to be quite helpful in getting the
atomic reads and writes done. FWIW.
This methodology, however, means that you have a split between the
ActiveRecord functionality and the non-ActiveRecord but AR-style
functionality. I don''t think that''s easily avoidable, or
probably even
desirable, frankly -- SQL behaves differently than, e.g., flat files,
Madeleine, etc., and the calling code really can''t readily be oblivious
-- or, if it can the cost seems way too high.
At a few points I tried to pick back up on the AY project, but never had
the time. If you look back through the archives on this list you''ll
see
a few places where I was posting and asking about validations being used
in non-AR classes, etc. (and never getting any response ;-) That was me
trying to get started again on AY from a new AR, but things just never
got going well enough to finish.
Anyway, if anyone wants the bits that I did for AY (starting on AR
version 1.5.1) I can make them available.
Rick
--
http://www.rickbradley.com MUPRN: 168
| on his US boycott
random email haiku | and submit his resume,
| ISDN.net would hire him.