Luca Anceschi
Luca Anceschi

Reputation: 2277

Active Record 4.x and MySQL table column type without using migrations

I'm doing some test with Sinatra v1.4.4 and Active Record v4.0.2. I've created a DBase and a table named Company with Mysql Workbench. In table Company there are two fields lat & long of DECIMAL(10,8) and DECIMAL(11,8) type respectively. Without using migrations I defined the Company model as follow:

class Company < ActiveRecord::Base
end

Everything works except the fact that lat and lng are served as string and not as float/decimal. Is there any way to define the type in the above Class Company definition. Here you can find the Sinatra route serving the JSON response:

get '/companies/:companyId' do |companyId|
  begin
    gotCompany = Company.find(companyId)

    [200, {'Content-Type' => 'application/json'}, [{code:200, company: gotCompany.attributes, message: t.company.found}.to_json]]          
  rescue
    [404, {'Content-Type' => 'application/json'}, [{code:404, message:t.company.not_found}.to_json]]
  end
end

Active Record correctly recognize them as decimal. For example, executing this code:

Company.columns.each {|c| puts c.type}

Maybe its the Active Record object attributes method typecast?

Thanks, Luca

Upvotes: 0

Views: 90

Answers (2)

DiegoSalazar
DiegoSalazar

Reputation: 13531

You can wrap the getter methods for those attributes and cast them:

class Company < ActiveRecord::Base
  def lat
    read_attribute(:lat).to_f
  end

  def lng
    read_attribute(:lng).to_f
  end
end

That will convert them to floats, e.g:

"1.61803399".to_f
=> 1.61803399

Edit: Want a more declarative way? Just extend ActiveRecord::Base:

# config/initializers/ar_type_casting.rb
class ActiveRecord::Base
  def self.cast_attribute(attribute, type_cast)
    define_method attribute do
      val = read_attribute(attribute)
      val.respond_to?(type_cast) ? val.send(type_cast) : val
    end
  end
end

Then use it like this:

class Company < ActiveRecord::Base
  cast_attribute :lat, :to_f
  cast_attribute :lng, :to_f
end

Now when you call those methods on an instance they will be type casted to_f.

Upvotes: 2

Luca Anceschi
Luca Anceschi

Reputation: 2277

Following diego.greyrobot reply I modified my Company class with an additional method. It overrides the attributes method and afterwards typecast the needed fields. Yet something more declarative would be desirable imho.

class Company < ActiveRecord::Base

  def attributes
    retHash = super
    retHash['lat'] = self.lat.to_f
    retHash['lng'] = self.lng.to_f
    retHash
  end
end

Upvotes: 0

Related Questions