views:

85

answers:

4

Hi, sorry if this question has been asked before, I searched but wasn't sure on the proper name for what I'm looking for.

I'm pretty much a newcomer to rails and development in general, I've done some hacky programming but I wouldn't say I know what I'm doing. I've always been at a loss on how to define "types" in my Rails models and in databases in general.

For example, say I have a model "Car" and it has the property "Colour", where Colour is chosen from a known set rather than an RGB value or whatever. Something tells me that there should be another table (and associated model) for Colour and then have some kind of relationship between the two, but which relationship is appropriate? A car doesn't BELONG to its colour, or vice versa.

Or of course I could just store an integer and look it up in code, but this feels wrong to me.

What did I miss? :)

A: 

A car has only 1 color, so you would say a Car instance belongs_to its Color. Still, your second idea of just storing the color as a number and having those colors defined as constants somewhere is probably more sensible... as long as you dont need to add/remove/edit colors within the app (in other words, 'live').

thenduks
A: 

My experience of types in Ruby is that rather than thinking about what something is you have to think about what something does. It's very jQuery in that regard, don't ask what browser the user is using, as whether their browser can do certain things that we need it to do.

I know that wasn't the brunt of your question but it seemed an appropriate analogy considering the title and my own experiences learning Ruby and dealing with duck typing.

To you answer, the only time you really want to define a new object like color is if it's something you're going to let customers define. In the case of a car this could be totally valid, many cars only have certain colors and they have really particular colors. So indeed a color has_many cars and a car would belong_to the color.

The naming was always slightly odd to me too, the way I just learned was that belongs_to is the model that has the id number. It has its reasons but it's easier to just memorize that one fact than to try to untangle the logic each time. That's all you really have to keep straight and you'll do just fine. :)

Chuck Vose
A: 

For this example I don't think it's appropriate to store a car's colour in a separate model. Colour is a property of a car, so a column in the cars table is the solution.

If you wanted to limit the choice of colours to a finite list, you can use a validation

class Car < ActiveRecord::Base
  validates_inclusion_of :colour, :in => %w( red orange yellow green blue indigo violet )
end

If this is too simplistic for your use case, I recommend you look at ActiveRecord::Aggregations::ClassMethods.composed_of. I do not think an ActiveRecord model is the solution here.

Steve Graham
A: 

Color may be a complex object or may be a very simple one, depending on what you need.

  • Simple: Integer subsequently converted to any other format, or hex value like "a3ab34" stored in a string.
  • Complex: You want to map colors to certain human titles.

For simple you'll just have a column in cars named color, and override the reader method to return whichever color format you desire, like so.

class Car < ActiveRecord::Base
  def color(format = :integer)
     case format
     when :integer: super
     when :hex: # ... hex conversion code
     # ... more conditions ...
     end
  end
end

If we're talking about complex case, then it's different. You should then have a separate model called Color. Then Car would belongs_to :color like thenduks said above. It would be more semantically correct to say Car has_one :color but then you're forcing colors table to store car_id which would be kind of backwards. In our case, Car will be storing color_id. So make sure you make a migration that adds color_id to table cars.

Then you should create the migration for colors table, which would include id, title and code for example, and which you'd have to pre-populate yourself using rails seeding stuff. (see http://railscasts.com/episodes/179-seed-data)

hakunin
You need an equals appended to the method name, before the parentheses for a setter.
Steve Graham
This is a getter. Setter is a whole different business. : )
hakunin
Ahh. I see what you did. I assumed that because the method takes an argument, it was a setter. There is hardly any code in it. My mind filled in the gaps. I don't believe a getter should take an argument. I would prefer `Color#to_s`, `Color#to_hex`, etc. `Color#color` makes no sense to me.Also why are you calling super? That will make the interpreter look up the inheritance chain i.e. in AR::Base for the color method?! :s
Steve Graham
I believe he meant the class to be called Car.
Mopman
@Steve GrahamHow you specify format is a technicality. Regarding super: ActiveRecord::Base automatically creates methods for you which correspond to database columns. Assuming you have column color, you also get reader color. When you override the reader, super will refer to original reader which only returns column value - an integer.@MopmanYep, sorry about that.
hakunin