2017-02-16 24 views
6

Я использую Rails 5. У меня есть следующие моделиКак мне заставить мою модель использовать мой кеш при сохранении?

class MyObject < ActiveRecord::Base 
    ... 
    belongs_to :distance_unit 

    ... 
    def save_with_location 
    transaction do 
     address = LocationHelper.get_address(location) 
     if !self.address.nil? && !address.nil? 
     self.address.update_attributes(address.attributes.except("id", "created_at", "updated_at")) 
     elsif !address.nil? 
     address.race = self 
     address.save 
     end 

     # Save the object 
     save 
    end 
    end 

Через некоторое хитрого отладки, я понял, что «сохранить» метод вызывает этот запрос будет выполнен ...

DistanceUnit Load (0.3ms) SELECT "distance_units".* FROM "distance_units" WHERE "distance_units"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]] 
    ↳ app/models/my_object.rb:54:in `block in save_with_location' 

Это heppens каждый раз, когда вызывается вышеупомянутый метод. это не оптимально, потому что я установил мою модель DistanceUnit для хранения кеша. Ниже приведен код. Как я могу получить метод «сохранить», чтобы автоматически использовать кеш, а не выполнять этот запрос каждый раз?

class DistanceUnit < ActiveRecord::Base 

    def self.cached_find_by_id(id) 
    Rails.cache.fetch("distanceunit-#{id}") do 
     puts "looking for id: #{id}" 
     find_by_id(id) 
    end 
    end 

    def self.cached_find_by_abbrev(abbrev) 
    Rails.cache.fetch("distanceunit-#{abbrev}") do 
     find_by_abbrev(abbrev) 
    end 
    end 

    def self.cached_all() 
    Rails.cache.fetch("distanceunit-all") do 
     all 
    end 
    end 

end 
+0

Вы используете это в разработке или производстве? – Iceman

+0

Я провел тест выше в разработке, но это происходит и в производстве. – Dave

+0

Вы можете включить отношения для distance_unit и my_object? –

ответ

3

Rails 5 делает belongs_to ассоциацию по умолчанию требуется после this change. Это означает, что связанная запись должна присутствовать в базе данных при сохранении или проверке. Есть несколько возможных способов решения проблемы

1) Установите distance_unit вручную из кэша перед сохранением MyObject экземпляра, чтобы предотвратить извлечение из базы данных:

def save_with_location 
     # ... 

     # Save the object 
     self.distance_unit = DistanceUnit.cached_find_by_id(self.distance_unit_id) 
     save 
    end 
    end 

2) Или отказаться от такого поведения:

Вы можете передать optional: true в belongs_to ассоциации, которая будет удалить эту проверку правильности:

class MyObject < ApplicationRecord 
    # ... 
    belongs_to :distance_unit, optional: true 
    # ... 
end 
2

Честно говоря, я думаю, что был плохой дизайн

Для расчета расстояния а предложить другой подход

реквизита:

  • нет кэш нужен
  • SOLID
  • нет собственного письменного в моделях

Код для вычисления расстояния логика следующая:

require 'active_record' 

ActiveRecord::Base.logger = Logger.new STDOUT 

class Address < ActiveRecord::Base 
    establish_connection adapter: 'sqlite3', database: 'foobar.db' 
    connection.create_table table_name, force: true do |t| 
    t.string :name 
    t.decimal :latitude, precision: 15, scale: 13 
    t.decimal :longitude, precision: 15, scale: 13 
    t.references :addressable, polymorphic: true, index: true 
    end 

    belongs_to :addressable, polymorphic: true 
end 

class Shop < ActiveRecord::Base 
    establish_connection adapter: 'sqlite3', database: 'foobar.db' 
    connection.create_table table_name, force: true do |t| 
    t.string :name 
    end 

    has_one :address, as: :addressable 
end 

class House < ActiveRecord::Base 
    establish_connection adapter: 'sqlite3', database: 'foobar.db' 
    connection.create_table table_name, force: true do |t| 
    t.string :name 
    end 

    has_one :address, as: :addressable 
end 

class Measure 
    def initialize address_from, address_to 
    @address_from = address_from 
    @address_to = address_to 
    end 

    def distance 
    # some calculation logic 
    lat_delta = @address_from.latitude - @address_to.latitude 
    lon_delta = @address_from.longitude - @address_to.longitude 
    lat_delta.abs + lon_delta.abs 
    end 

    def units 
    'meters' 
    end 
end 

shop_address = Address.new name: 'Middleberge FL 32068', latitude: 30.11, longitude: 32.11 
house_address = Address.new name: 'Tucson AZ 85705-7598', latitude: 40.12, longitude: 42.12 
shop = Shop.create! name: 'Food cort' 
house = House.create! name: 'My home' 
shop.update! address: shop_address 
house.update! address: house_address 
p 'Shop:', shop 
p 'House:', house 
measure = Measure.new shop_address, house_address 
p "Distance between #{shop.name} (#{shop.address.name}) and #{house.name} (#{house.address.name}): #{measure.distance.to_s} #{measure.units}" 

Вы можете запустить его с: $ ruby path_to_file.rb

И результат должен быть следующим:

"Shop:" 
#<Shop id: 1, name: "Food cort"> 
"House:" 
#<House id: 1, name: "My home"> 
"Distance between Food cort (Middleberge FL 32068) and My home (Tucson AZ 85705-7598): 20.02 meters"