2010-09-28 32 views
11

Tôi có một ứng dụng mà người dùng của tôi có thể có một tập hợp các tùy chọn. Cả hai đều được lưu trữ như ActiveRecord-mô hình như sau:Đường ray: Tạo liên kết nếu không tìm thấy lỗi nào để tránh lỗi nil

class User < AR::Base 
    has_one :preference_set 
end 

class PreferenceSet < AR::Base 
    belongs_to :user 
end 

bây giờ tôi có thể truy cập vào sở thích của người dùng:

@u = User.first 
@u.preference_set => #<PreferenceSet...> 
@u.preference_set.play_sounds => true 

Nhưng điều này không nếu một bộ sở thích chưa được tạo ra, kể từ khi @u. preferences_set sẽ trả về 0 và tôi sẽ gọi số play_sounds trên nil.

Điều tôi muốn lưu trữ là User.preference_set luôn trả về một thể hiện PreferenceSet. Tôi đã thử xác định nó như thế này:

class User < .. 
    has_one :preference_set 

    def preference_set 
    preference_set || build_preference_set 
    end 
end 

Điều này gây ra một 'Stack level too deep', vì nó đang tự gọi mình đệ quy.

Câu hỏi của tôi là thế này:

Làm thế nào tôi có thể đảm bảo rằng @user.preference_set lợi nhuận hoặc là preference_set kỷ lục tương ứng hoặc, nếu không tồn tại, xây dựng một cái mới?

Tôi biết tôi có thể đổi tên liên kết của mình (ví dụ: preference_set_real) và tránh các cuộc gọi đệ quy theo cách này, nhưng vì mục đích đơn giản trong ứng dụng của tôi, tôi muốn giữ tên.

Cảm ơn!

Trả lời

27

Vâng cách tốt nhất để làm điều này là để tạo ra các kỷ lục liên quan đến khi bạn tạo cho chính một:

class User < ActiveRecord::Base 
    has_one  :preference_set, :autosave => true 
    before_create :build_preference_set 
end 

Điều đó sẽ thiết lập nó để bất cứ khi nào một User được tạo ra, do đó, là một PreferenceSet. Nếu bạn cần khởi tạo bản ghi được liên kết với đối số, sau đó gọi một phương thức khác trong before_create gọi build_preference_set(:my_options => "here") và sau đó trả về true.

Sau đó, bạn có thể bình thường hóa tất cả các bản ghi hiện có bằng cách lặp qua bất kỳ bản ghi nào không có PreferenceSet và tạo một bản ghi bằng cách gọi #create_preference_set.

Nếu bạn chỉ muốn tạo ra PreferenceSet khi nó hoàn toàn là cần thiết, sau đó bạn có thể làm một cái gì đó như:

class User < ActiveRecord::Base 
    has_one :preference_set 

    def preference_set_with_initialize 
    preference_set_without_initialize || build_preference_set 
    end 

    alias_method_chain :preference_set, :initialize 
end 
+3

Wow, đã không đi qua alias_method_chain. Thật tuyệt. – Chowlett

+0

Hãy thông báo rằng 'alias_method_chain' đã không được hỗ trợ cho' Module # prepend'. Thông tin thêm tại đây: https://github.com/rails/rails/pull/19434 –

38

hoặc đơn giản là

class User < ApplicationRecord 
    has_one :preference_set 

    def preference_set 
    super || build_preference_set 
    end 
end 
Các vấn đề liên quan