views:

102

answers:

3

What's the cleanest way to convert data (field values) when storing/loading into/from a database.

I usually convert the values the user has entered in the model class:

  def date_str
    format_date(self.date)
  end

  def date_str=(str)
    self.date = DateParser.parse(str)
  end

(Note: The internal date parser is not sufficient, it doesn't cover many of our locale date notation styles, anyway).

This works already pretty fine, but I doesn't let me check for validity the usual RoR way because we don't store the string the user has entered. I usually let the parser function return nil if anything was wrong with the date, but this isnt very user-friendly – I cant really tell if the user hasn't entered anything or just a invalid value.

I'm looking for some way to integrate the conversion better into the A::R world where I can produce some useful error message if the conversion fails. Best would be if I could use the same facilities as the A::R::Adapter uses internally.

Has anybody some good ideas? What are other approaches?

+1  A: 

The way you've suggested is fine, another alternative would be to do something like

attr_accessor :when

before_validation :update_date

def update_date
  self.date = parse_date(self.when) if self.when
end

def parse_date(date_as_string)
  # do something with the date
end

However, you're going to have write the validation yourself. It's not ideal, but it shouldn't be that much code.

If you're planning on using this all over your app, you should maybe look at writing a custom validator too, you can get more info about that here:

http://marklunds.com/articles/one/312

jonnii
A: 

The attribute_normalizer provides a nice dsl for doing this kind of thing like this:

class Klass < ActiveRecord::Base

  normalize_attributes :date do |value|
    value.is_a?(String) ? DateParser.parse(value) : nil
  end

end
Andrew Nesbitt
+1  A: 

You can do this in a before validation callback that way you can use the normal AR validations on date.

before_validation :convert_date

def convert_date
   self.date = DateParser.parse(self[:date])
end
MatthewFord
Hmm, but whats the difference to the setter method? During the validation only the parsed value will be accessible, right?
reto