0

I'm trying to implement a funky version of method chaining. Returning the instance of the class after each function call is easy, you just do

def chainable_method
    some_code()
    self
end

My idea is that the methods you can call depend on the previous method call. I'm trying to achieve this by returning an object belonging to the containing object. The contained object will have a few special methods, and then implement method_missing to return the containing object's instance.

Edit: The child object has some state associated with it that should be in itself, and not the parent. It might not have been clear previously as to why I need a whole instance for just method calls.

super is irrelevant in this case because the contained object doesn't inherit from the containing object, and I wouldn't want to call the containing object's methods on the contained object anyway - I want to call the containing object's methods on the containing object itself. I want the containing object, not the containing object class.

Not sure if this is possible.

Edit: reworded everything to use "containing/contained object" instead of the completely incorrect parent/child object. Also, I'm using 1.9.3, if that matters. Version isn't important, I can change if needed.

My explanation was probably unclear. Here's the code:

class AliasableString
    def initialize(string)
        @string = string
    end

    def as(aka)
        @aka = aka
    end

    def has_aka?
        [email protected]?
    end

    # alias is a reserved word
    def aka
        @aka
    end

    def to_s
        @string + (self.has_aka? ? (" as " + @aka) : "")
    end
end

class Query
    def initialize
        @select_statements = Array.new
    end

    def select(statement)
        select_statement = AliasableString.new(statement)
        @select_statements.push(select_statement)
        select_statement
    end

    def print
        if @select_statements.size != 0
            puts "select"
            @select_statements.each_with_index {| select, i|
                puts select
            }
        end
    end
end

# Example usage

q0 = Query.new

q0.select("This is a select statement")
    .select("Here's another one")
        .as("But this one has an alias")
    .select("This should be passed on to the parent!")

q0.print

I haven't yet fully implemented print. AliasableString needs to have @string and @aka separate so I can pull them apart later.

7
  • Post the parent and child classes. Commented Jan 30, 2014 at 4:08
  • There is no “parent instance.” There is the instance only. Either you want to return Parent.new or you have an instance of parent already and may return it. Commented Jan 30, 2014 at 4:10
  • the child object doesn't inherit from the parent ... I want the parent instance, not the parent class ... wat. Commented Jan 30, 2014 at 4:16
  • I know. I'm butchering the word parent here. I wasn't sure how else to describe it. Commented Jan 30, 2014 at 4:17
  • The parent class can have many instances, and how exactly does the child class not inherit from its parent? I think there's some conceptual confusion that needs to get cleared up here (at least, I'm confused). Maybe what you want is a module or a singleton class? Commented Jan 30, 2014 at 4:18

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

1 Comment

This works, and it makes more sense/is simpler than any other option I could come up with. I'll go with this. Thanks! The only problem I can see is that this might break if multiple objects operate on one Query at the same time - for instance, with threads - but my end goal is just to automatically generate really repetitive HiveQL queries. I can't possibly think of any real life situation it would become a problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.