Limiting table to just one row?

I'm new at RoR, so please excuse me if this is a very basic question. I've tried some googling, but haven't found an answer yet.

Is there any established, or preferred way to limit a table to have one and only one row? I have some default values that I will use in multiple places in my app, and I want to be able to modify them during production, so I don't want to hard code them. So I made a "Defaults" model. I can simply limit my code to only accessing the first row, but it seems cleaner to me if there's someway to prevent more than one row from existing.

Is the best/easiest way just to add a validate method in the model that checks on creating if there's already an entry in the database, and returns an error? Or is there some simpler way I'm overlooking?

Thank you in advance!

Are these constant variables that won't change during execution? If so, you can just put them in your config/environment.rb, defined in all caps. It sounds like a bad idea to continually hit the database just to grab a few constants.

If you rely on them to be mutable, you're effectively creating global variables which is a sign that you should probably refactor your code.

If you really, really want to go down this route, you can trap the before_create event on your Default model so that no new records can get made. It doesn't others from inserting rows from outside rails.

They could change during execution.

They're some default values that we will use when creating some models (they are percentages for allocating a budget into several categories). Changing the values won't affect already created budgets, but will affect new budgets.

I don't want to hard code them in environment.rb, because I'd like "less technical" people to change them while the app is live.

How would you refactor this then?

Thanks for the advice!

I would not jump to the conclusion that singleton tables are bad or imply bad design. For things like current conversion rates for currency, desired margin for budgets, etc it is quite reasonable to want modifiable values that are global. You will add overhead to read that row over and over. You should think about what type of caching you can do to reduce the database I//O. I am assuming that you are going to have a model class to access these values and it will be able to perform caching. At a minimum it could keep it locally in a class variable and only read it if there is no value in the variable already.

Michael

Thanks for the advice.

These values are really only going to be accessed when a new user signs up, so I'm not too concerned with I/O usage.

Is there any mechanism to do a singleton table, or is using before_create and validate the best way?

Hey,

another option is to split out your one row into multiple rows:

create_table :defaults do |t|    t.column :name, :string    t.column :value, :string end

class Default < ActiveRecord::Base    validates_uniqueness_of :name    def self.(default_name)      find_by_name(default_name) or raise
ActiveRecord::RecordNotFound, "cannot find a default named #{name}"    end

   def to_i      self.value.to_i    end    # etc for different data types end

Default[:some_limit].to_i

This serves 2 purposes:

1) it ensures that if a requirement for a new default value turns up
you're not redefining your schema

2) it avoids the 'singleton' issue because each default value can
have at most one row

The main drawbacks are hitting the database even more often than
getting a "single-row collection of defaults" and that you have to
write accessors that convert the string representation into an
intrinsic value (like the to_i I included above).

Just something you might want to consider.

Trevor

No, this kind of restriction should be built in at the model level. Omitting the controller actions is a nice additional guard, but it doesn't prevent you from inadvertently messing it up in another controller, or in a piece of code called from script/runner, or by messing around in the console, or whatever.

before_save and/or validate is how to achieve this.

Chris

another option is to split out your one row into multiple rows:

snip

This serves 2 purposes:

1) it ensures that if a requirement for a new default value turns up
you're not redefining your schema

2) it avoids the 'singleton' issue because each default value can
have at most one row

This is a good approach. Every single commercial app I've written has ended up with a Settings model in it, looking very similar to this one.

The main drawbacks are hitting the database even more often than
getting a "single-row collection of defaults" and that you have to
write accessors that convert the string representation into an
intrinsic value (like the to_i I included above).

One way to avoid this would be to marshal your native Ruby object (like an integer, float, whatever) before you save it in the DB, and then unmarshal it on the way out.

Or, I've done things where the table contains a separate column for each type (integer, float, string, boolean), and the model checks the behaviour of the object to be saved, and saves it in the most appropriate native format in the correct DB column. Then it reverses the translation on the way out. Bit hacky, but if you like your database tables to contain real native data, then it's an alright approach.

Chris

I like that! Thanks!

I am really only asking for information here; it is not a suggestion. What would be the impact of just marshaling the settings into a YAML or JSON or XML string and storing that in a single cell of the single row? I really like the idea about separate columns for each type of value, but I was just wondering about this single-cell approach.

Ron