29

I have an object that I want created once and accessible in one of my models. Where do I put him? I'm afraid if I put him in the model class file he'll get created every time I make a new instance of that model. I only want this object created once at start up. Here's the object:

require 'pubnub'    
publish_key   = 'fdasfs'
subscribe_key = 'sdfsdsf'
secret_key    = 'fsdfsd'
ssl_on        = false

pubnub_obj = Pubnub.new(publish_key,
                    subscribe_key,
                    secret_key,
                    ssl_on)

I use him like this in the model:

class Message < ActiveRecord::Base

  def self.send_new_message_client(message)
    message = { 'some_data' => message }
    info = pubnub_obj.publish({
                            'channel' => 'testing',
                            'message' => message
                          })
    puts(info)
  end

end
Rimian
  • 36,864
  • 16
  • 117
  • 117
stackOverlord
  • 5,342
  • 6
  • 28
  • 29
  • I think you can also use a global variable, see this example here: https://github.com/pubnub/ruby/blob/master/examples/pubnub_livestream/config/initializers/pubnub.rb – Vincil Bishop Jan 21 '16 at 17:53
  • Does this answer your question? [How to implement a singleton model](https://stackoverflow.com/questions/399447/how-to-implement-a-singleton-model) – Nick Roz Apr 13 '22 at 20:35

2 Answers2

41

In Rails, objects are recreated on each request. If this is some kind of service, it should be a singleton in the scope of a request.

Singleton objects should be created with the ruby singleton mixin:

require 'singleton'

class Pubnub
  include Singleton

  def initialize(publish_key, subscribe_key, secret_key, ssl_on)
    # ...
  end

  def publish
    # ...
  end
end

Then you can call it with the instance method:

Pubnub.instance.publish

This way you make sure that this object will actually be a singleton (only one instance will exist).

You can place it safely in the models directory, though I often prefer the lib directory or maybe create a new directory for services. It depends on the situation.

Hope it helps!

alf
  • 18,372
  • 10
  • 61
  • 92
  • 2
    For the record, the first statement is only true if the config.cache_classes is set to false. Which is, by default, not true in Production. I don't know what the behavior is with the Singleton mixin. – Michael Jul 09 '13 at 08:20
  • 3
    There is a potential danger in this approach when using some multi-process server like Phusion Passenger. PP creates one thread per request and moreover - these threads will be likely to exist in separate processes, so each process will have it's own singleton object shared by multiple threads of this process. – Paul Aug 29 '14 at 06:56
  • to extend Paul's comment above, be aware there is also an issue if you don't want it shared, in that the desire is to have a singleton per request. http://stackoverflow.com/questions/24927928/singleton-in-scope-of-a-request-in-rails – futbolpal Sep 15 '15 at 16:03
5

If you want only one instance in your whole application, use a singleton, otherwise use a class variable.

To use a singleton, include the Singleton mixin.

require 'singleton'

class Pubnub
  include Singleton

  attr_writer :publish_key, :subscribe_key, :secret_key, :ssl_on

  def publish
    #...
  end
end

and then use it like this:

require 'pubnub'    
class Message < ActiveRecord::Base
  Pubnub.instance.publish_key = 'xyz'
  Pubnub.instance.subscribe_key = 'xyz'
  Pubnub.instance.secret_key = 'xyz'
  Pubnub.instance.ssl_on = 'xyz'

  def self.send_new_message_client(message)
    message = { 'some_data' => message }
    info = Pubnub.instance.publish({
                            'channel' => 'testing',
                            'message' => message
                          })
    puts(info)
  end
end

You could also make it a class variable, to link it more tightly to a specific model:

require 'pubnub'    
class Message < ActiveRecord::Base
    @@pubnub_obj = Pubnub.new('xyz', 'xyz', 'xyz', 'xyz')

  def self.send_new_message_client(message)
    message = { 'some_data' => message }
    info = @@pubnub_obj.publish({
                            'channel' => 'testing',
                            'message' => message
                          })
    puts(info)
  end

end
iHiD
  • 2,450
  • 20
  • 32