views:

1493

answers:

7

I have a site in rails and want to have site-wide settings. One part of my app can notify the admin by SMS if a specific event happens. This is an example of a feature that I want configurable via the site-wide settings.

So I was thinking I should have a Setting model or something. It needs to be a model because I want to be able to has_many :contacts for the SMS notification.

The problem is that there can only be one post in the database for the settings model. So I was thinking of using a Singleton model but that only prevents new object to be created right?

Would I still need to create getter and setter methods for each attribute like so:

def self.attribute=(param)
  Model.first.attribute = param
end

def self.attribute
  Model.first.attribute
end

Is it perhaps not best-practice to use Model.attribute directly but always create an instance of it and use that?

What should I do here?

+3  A: 

I wouldn't recommend a Singleton.

Google has a Singleton detector that you can use to help eliminate Singletons from Java applications. That would suggest that they don't advise using the pattern. There's a companion link that discusses why. Perhaps you should reconsider the design if they explanation makes sense to you.

duffymo
I don't really agree with this logic. You are basically saying that regardless of the many realistic reasons that the pattern was designed originally, Google has deemed it a useless pattern that should not be used.Having said that, maybe its not always a good idea for the web (stateless world).
Derek P.
@Derek P, if you watch the google video they acknowledge a few legitimate use cases for the singleton. But they are few and far between and most people use them as glorified global state with tight coupling instead of DI.
Simucal
A: 

Here's another SO thread that's pertinent (It discusses the problems with singletons).

duffymo
+1  A: 

Odds are good you don't need a singleton. It's unfortunate that one of the worst design habits to come out of the patterns craze is also one of the most commonly adopted. I blame the unfortunate appearance of simplicity, but I digress. If they had called it the "Static Global" pattern I'm sure people would have been more sheepish about using it.

I suggest using a wrapper class with a private static instance of the class you want to use for the singleton. You won't introduce a tight couple throughout your code like you will with the singleton.

Some people call this a monostate pattern. I tend to think of it as just another twist on the strategy/agent concept since you can allow for more flexibility by implementing different interfaces to expose/hide functionality.

JGeiser
+4  A: 

I am not sure I'd waste the database/ActiveRecord/Model overhead for such a basic need. This data is relatively static (I am assuming) and on the fly calculations aren't necessary (including database lookups).

Having said that, I'd recommend you define a YAML file with your site-wide settings and define an initializer file that loads the settings into a constant. You won't have nearly as many of the unnecessary moving parts.

There is no reason that data couldn't just sit in memory and save you a ton of complexity. Constants are available everywhere, and they don't need to be initialized or instantiated. If its absolutely critical that you utilize a class as a singleton, I'd recommend doing these two things:

  1. undef the initialize/new method
  2. define only self.* methods that way it is not possible for you to maintain a state
Derek P.
+1  A: 

Using has_many :contacts doesn't mean you need a model. has_many does some magic, but in the end it's just adds some method with a specified contract. There's no reason why you can't implement those methods (or some subset that you need) to make your model behave like it has_many :contacts yet not actually use an ActiveRecord model (or model at all) for Contact.

Otto
A: 

You might also check out Configatron:

http://configatron.mackframework.com/

Configatron makes configuring your applications and scripts incredibly easy. No longer is a there a need to use constants or global variables. Now you can use a simple and painless system to configure your life. And, because it‘s all Ruby, you can do any crazy thing you would like to!

+1  A: 

I disagree with common opinion - there is nothing wrong with reading a property out of the database. You can read the database value and freeze if you'd like, however there could be more flexible alternatives to simple freezing.

How is YAML different from database? .. same drill - external to application code persistent setting.

Nice thing about the database approach is that it can be changed on the fly in more or less secure way (not opening and overwriting files directly). Another nice thing is it can be shared across the network between cluster nodes (if properly implemented).

The question however remains what would be the proper way to implement such a setting using ActiveRecord.