gregb
gregb

Reputation: 772

ruby - Sharing a class across modules

I'm trying to mimic ActiveRecord with a simple set of ruby objects for running raw sql queries. Below is a spike I've been experimenting with:

module Runable
  def run
    return self::Results.new
  end
end

module Query
  class Results  
    def initialize
      @results = Object.find_by_sql()
    end

    def to_a
      #code
    end
  end
end

module Scored
  extend Runable
  include Query

  QUERY = 'a raw sql query string'
end

module Unseen
  extend Runable
  include Query

  QUERY = 'a different raw sql query string'
end

What I want to be able to do is create simple Modules for each type of raw sql query I'm going to run, put them into a file like Scored or Unseen above and call .run on them to get back a results object. So like this:

Scored.run #=> #<Scored::Results:0x0000000000>
Unseen.run #=> #<Unseen::Results:0x0000000000>

but instead I get this...

Scored.run #=> #<Query::Results:0x0000000000>
Unseen.run #=> #<Query::Results:0x0000000000>

I've been doing ruby and rails for over a year but I'm just beginning to get into more advanced ruby usage. This is my first big step into using modules and mixins.

The issue, as far as I can tell, is that module class methods have self scoped to the module they're defined in. So I get Query::Results because the initialize method for Results is defined in the Query module. That make sense?

Thank you for the help!

Update 5/30 16:45

Basically, I want to wrap a handful of raw SQL statements into modules like this:

module ScoredUsers
  include Queryable

  QUERY="SELECT * FROM users ..."
end

and interact with the queries like this:

r = ScoredUsers.run #=> ScoredUsers::Results
r.ids
r.load_objects
REDIS.zadd user:5:cache, r.to_a

I want to keep everything in modules and classes, the ruby way (I think?) so when I want to create a new query object I can simple use the boilerplate module like Scored above.

Upvotes: 0

Views: 248

Answers (1)

BroiSatse
BroiSatse

Reputation: 44675

The reason why you are getting such a results is that class Results is created just once. When the module is included new constant is created within including class (Scored::Results), but it is pointing to same memory space as constant Query::Results.

What you need is that you have to create a new class for each class this module is being included in. This is perfect opportunity to use included method:

module Query
  def self.included(mod)
    results = Class.new do  
      def initialize
        @results = Object.find_by_sql()
      end

      def to_a
        #code
      end
    end
    mod.const_set('Results', results)        
  end
end

Now of course we are left with the question - do we really need to do this? This depends on how you are planning to use those classes.

Upvotes: 1

Related Questions