7

How do I limit the object of any class to one. My class looks like :

class Speaker
  include Mongoid::Document
  field :name, :type => String
end

I just want to let a single instance of speaker . One way would be to add a validation which would check the number of objects already present of the Speaker class. Is there a ruby way of doing thing ?

Dogbert
  • 212,659
  • 41
  • 396
  • 397
Prabesh Shrestha
  • 2,732
  • 4
  • 29
  • 44

6 Answers6

10

How about using the Singleton module?

Frank Schmitt
  • 30,195
  • 12
  • 73
  • 107
8

In this case I would write proper validation:

validate :only_one

def only_one
   errors.add(:base, "Only one Speaker can exist") if self.count > 0 
end
Sławosz
  • 11,187
  • 15
  • 73
  • 106
3

I recommend using a class/module that is tailored to storing configuration values rather rolling your own on top of a vanilla ActiveRecord model.

I use a old copy of the rails-settings plugin with some custom modification (it still works just fine in Rails 3). There are also a number of variant offerings listed on Github, so feel free to look and take your pick.

Jeremy Weathers
  • 2,556
  • 1
  • 16
  • 24
3

Why not provide a default Speaker object, and just not provide controller actions for create or delete?

Seems the simplest solution by far.

Steven Soroka
  • 19,404
  • 4
  • 52
  • 40
3

I see you're using Mongoid

The functionality you request is not available using mongoid validations.

Therefore, you will need to write your own. before_validation is a supported callback and chained Speaker.all.count methods are available to your model.

class Speaker
  include Mongoid::Document
  field :name, :type => String
  before_validation(:ensure_has_only_one_record, :on => :create)
  def ensure_has_only_one_record
    self.errors.add :base, "There can only be one Speaker." if Speaker.all.count > 0
  end
end

However, the best practice is to put all key/value settings in a single table.

Community
  • 1
  • 1
Alec Wenzowski
  • 3,878
  • 3
  • 25
  • 40
2

Using the Singleton module and a little overriding of its methods, I believe this works and it's thread safe (on ruby 1.8):

class Speaker 

  include Singleton
  include Mongoid::Document
  field :name, :type => String

  @@singleton__instance__ = nil
  @@singleton__mutex__ = Mutex.new

  def self.instance
    return @@singleton__instance__ if @@singleton__instance__
    @@singleton__mutex__.synchronize {
      return @@singleton__instance__ if @@singleton__instance__
      @@singleton__instance__ = self.first
      @@singleton__instance__ ||= new()
    }
    @@singleton__instance__
  end

  def destroy
    @@singleton__mutex__.synchronize {
      super
      @@singleton__instance__ = nil
    }
  end

end
andersonvom
  • 11,701
  • 4
  • 35
  • 40
  • 1
    Thanks for pointing that out. Back in Ruby 1.8 [that was not the case](http://www.ruby-forum.com/topic/4412037). – andersonvom Apr 08 '13 at 12:44