First of all, it doesn't matter what class of object is contained within a Query
instance. All of the syntax shown on your 'example usage' section is appropriately defined in Query
. The only requirement of the objects contained within a query instance is that they respond to as
(or some similar method). What you have here is something like a state machine, but the only state that really matters is that some object occupies the last position in the select_statements
array. Here's how I would build this (again, based mostly on your example at the end, I'm afraid I can't quite follow your initial explanation):
class Query
# ... initialize, etc.
def select(statement, statement_class = AliasableString)
select_statements << statement_class.new(statement)
self
end
def as(aka)
# this will only ever be used on the most recent statement added
statement_to_alias = select_statements.last
# throw an error if select_statements is empty (i.e., :last returns nil)
raise 'You must add a statement first' unless statement_to_alias
# forward the message on to the statement
statement_to_alias.as(aka)
# return the query object again to permit further chaining
self
end
end
AliasableString
doesn't need to know a thing about Query
; all it needs to do is respond appropriately to as
.