3

I have two different classes that both represent objects that need to be persisted to my database and now I want to share the database client object between the two classes. I want to avoid instantiating the client object more than once.

Currently I do this by using a global variable

$client = Mysql2::Client.new(:database => "myDb", :user => "user", :password => "password", :host => "localhost")

class Person
  def save
    $client.query("INSERT INTO persons")
  end
end

class Car
  def save
    $client.query("INSERT INTO cars")
  end
end

This works, but I am wondering if there are more correct ways to do this and why they are more correct?

Simon Thordal
  • 893
  • 10
  • 28

2 Answers2

4

You can inherit from a parent class. This allows you to share common functionality across objects and follows DRY (do not repeat yourself) programming principles. It will also allow you to protect your DB connection with locks, resuces, queues, pools, and whatever else you may want to do without having to worry about it in your children classes

class Record
  @table_name = nil
  @@client = Mysql2::Client.new(:database => "myDb", :user => "user", :password => "password", :host => "localhost")

  def save
    @@client.query("INSERT INTO #{@table_name}") if @table_name
  end
end

class Person < Record
  @table_name = "persons"
end

class Car < Record
  @table_name = "cars"
end

While we are on the subject, you should look at using ActiveRecord for handling your database models and connections. It already does pretty much anything you'll need and will be more compatible with other gems already out there. It can be used without rails.

lightswitch05
  • 9,058
  • 7
  • 52
  • 75
  • One of the reasons for my question was that I did not want to instantiate the database client object more than once. When using your solution does the fact that `@@client` is a class variable mean that every subclass shares the same object? – Simon Thordal May 21 '14 at 19:46
  • 1
    @SimonThordal `A class variable is shared by all instances of a class` [source](https://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Variables_and_Constants#Class_Variables). – lightswitch05 May 21 '14 at 19:49
  • 1
    This answer has the right idea, but it could use some refactoring. `@@client` can be set outside of `initialize` (to avoid the if). The use of `attr_accessor` seems unnecessary since 1) it's only being called from inside the class and 2) `table_name` should always be the same for instances of the same class. – Max May 21 '14 at 19:54
1

As an alternative on using inheritance, why not consider a simple Singleton pattern? This could make your models cleaner, by separating the responsibility outside your classes. And eliminating the need for inheritance.

The example below illustrates this. Only one, single instance of the DataManager class can exist. So, you'll only instantiate it once - but can use it everywhere:

require 'singleton'

class DataManager
  include Singleton

  attr_accessor :last_run_query

  def initialize()
    if @client.nil?
      p "Initialize the Mysql client here - note that this'll only be called once..."
    end
  end

  def query(args)
    # do your magic here
    @last_run_query = args
  end

end

Next, calling it using the .instance accessor is a breeze - and will always point to one single instance, like so:

# Fetch, or create a new singleton instance
first = DataManager.instance
first.query('drop table mother')
p first.last_run_query

# Again, fetch or create a new instance
# this'll actually just fetch the first instance from above
second = DataManager.instance
p second.last_run_query

# last line prints: "drop table mother"

For the record, the Singleton pattern can have some downsides and using it frequently results in a never-ending debate on whether you should use it or not. But in my opinion it's a decent alternative to your specific question.

Community
  • 1
  • 1
Juliën
  • 9,047
  • 7
  • 49
  • 80