2012-05-25 44 views
5

Tôi đang cố gắng mở rộng mô hình ActiveRecord (Vote) mà một viên đá quý (https://github.com/peteonrails/vote_fu) cung cấp cho ứng dụng của tôi. (Tức là, không có vote.rb trong app/models)Mở lại mô hình ActiveRecord do đá quý cung cấp

tiếp cận đầu tiên của tôi là tạo ra một tập tin gọi là lib/extend_vote.rb có chứa mã:

Vote.class_eval do 
    after_create :create_activity_stream_event 
    has_one :activity_stream_event 

    def create_activity_stream_event 
    # something.. 
    end 
end 

này hoạt động khi bỏ phiếu đầu tiên được tạo ra, nhưng khi tôi cố gắng tạo mỗi phiếu bầu tiếp theo tôi nhận được lỗi TypeError (can't dup NilClass).

Tôi nghĩ rằng lỗi này là do thực tế là lớp Vote được tải lại tự động sau mỗi yêu cầu, nhưng mã trong lib/extend_vote.rb chỉ được tải một lần khi máy chủ khởi động và điều này làm cho liên kết has_one :activity_stream_event hoạt động một cách kỳ lạ. (Ngoài ra, vấn đề đi xa nếu tôi đặt config.cache_classes = true trong development.rb)

Để giải quyết vấn đề này, tôi đã cố gắng để làm cho cuộc bỏ phiếu mở rộng reload trên mọi yêu cầu bằng cách thêm một khối to_prepare-development.rb tôi:

config.to_prepare do 
    load 'extend_vote.rb' 
end 

Điều này giải quyết vấn đề (can't dup NilClass), nhưng bây giờ bất cứ khi nào tôi tạo một phiếu bầu mới, cuộc gọi lại create_activity_stream_event được gọi là thời gian bổ sung. Tức là, cuộc bỏ phiếu đầu tiên gọi nó một lần, cuộc bỏ phiếu thứ hai gọi nó hai lần, v.v. Vì vậy, có vẻ như khối to_prepare đang tải lại phần mở rộng TOO tích cực và thêm các cuộc gọi lại trùng lặp.

Cách tốt nhất để thêm phương thức và gọi lại cho kiểu Vote này là gì?

+1

Tính năng này có hoạt động nếu bạn chỉ sử dụng 'hạng Vote' thay vì' Vote.class_eval'? Một điều bạn có thể làm là chỉnh sửa mã trong đá quý và chỉ sử dụng phiên bản đã sửa đổi của bạn. – agmcleod

+0

'lớp Vote' hoạt động giống như' Vote.class_eval' - không hoạt động. Tôi đoán tôi có thể sửa đổi đá quý, nhưng tôi thực sự thực sự không muốn lol. Thật là một mớ hỗn độn! –

+0

Tại sao bạn nghĩ rằng lớp Vote được tải lại? Trong sự tôn trọng, lớp học nằm trong thư mục lib nên nó giống với bạn ... – Dougui

Trả lời

1

Tôi muốn thử những gì agmcleod gợi ý trong các ý kiến ​​nhưng thay vì đặt nó trong lib, đặt nó trong config/initializers/vote.rb:

class Vote 
    after_create :create_activity_stream_event 
    has_one :activity_stream_event 

    def create_activity_stream_event 
    # something.. 
    end 
end 

Tất nhiên, bạn có thể ngã ba viên ngọc , thực hiện các sửa đổi của bạn và liên kết đến phiên bản chia đôi của bạn trong Gemfile của bạn (đó là sở thích của tôi).

+0

tại sao 'config/initializers' thay vì' lib'?Tôi cho rằng tôi cần giữ câu lệnh 'load' trong khối' to_prepare'? –

+0

không nên cần câu lệnh tải vì các mục trong tệp khởi tạo được nạp một lần khi khởi động cho tất cả các môi trường. – miked

+0

Điều này không có tác dụng - nếu tôi để 'tải' trong khối' to_prepare', tôi nhận được cùng lỗi mà tôi nhận được khi tệp nằm trong 'lib'. Nếu tôi loại bỏ 'tải', sau đó tôi nhận được lỗi' (không thể dup NilClass) ' –

0

bạn có thể thử một cái gì đó như thế này:

class Vote 
    after_create :create_activity_stream_event 
    has_one :activity_stream_event 

    def create_activity_stream_event 
     # something.. 
    end 
end 

Tôi nghĩ hơn nó sẽ thêm chức năng và cuộc gọi của bạn chức năng "after_create" và "has_hone".

+0

Xin lỗi, tôi không thấy nhận xét của agmcleod. Nó có lẽ là cùng một suy nghĩ ... Nó sẽ làm việc nhưng nó không. – Dougui

+0

heh, ít nhất bạn đã không nhận được một cuộc bỏ phiếu xuống như tôi đã làm ... và đề xuất của chúng tôi là, tốt, giống nhau! lol – miked

+0

Điều này có thể là do bạn nói để đặt nó trong bộ khởi tạo. Hoặc, có thể, tôi nói điều đó sau bạn. – Dougui

4

Một lời cảnh cáo: đây là một viên ngọc rất cũ (cam kết cuối cùng là 3 tuổi) và bởi vẻ ngoài của nó sẽ không hoạt động với đường ray 3.x như vậy. Trong công cụ Rails 3.x, công cụ này giúp việc này trở nên dễ dàng hơn.

Khi tôi hiểu vấn đề trong trường hợp đầu tiên không phải là mô hình bỏ phiếu được tải lại (không nên) nhưng mô hình activity_stream_event được tải lại. Vì mô hình bỏ phiếu không được tải lại nên liên kết này được treo trên phiên bản của lớp activity_stream_event trước khi tải lại. Kể từ khi rails ruột ra các lớp học trước khi chúng được tải lại, điều này gây ra vấn đề.

Với điều này trong tôi, cố gắng hack này:

#in config/initializers/abstract_vote.rb 
AbstractVote = Vote 
AbstractVote.abstract_class = true 
Object.send :remove_const, :Vote 

#in app/models/vote.rb 

class Vote < AbstractVote 
    after_create :create_activity_stream_event 
    has_one :activity_stream_event 

    def create_activity_stream_event 
    end 
end 

Điều này không là cho phép bạn có lớp Vote riêng bạn mà thừa hưởng từ một trong những viên ngọc quý.

Nhưng một lần nữa, tôi mong bạn tìm thấy một cái gì đó nhiều hơn đến nay hoặc cuộn của riêng bạn (viên ngọc chỉ ~ 250 dòng ruby)

6

[UPDATE: nên là giải pháp phù hợp để ngăn chặn các mô-đun phúc bao gồm nhiều lần trong cùng một lớp]

Tôi tin rằng bạn có thể sử dụng ActiveSupport::Concern để ngăn mô-đun được bao gồm nhiều lần do kết quả gọi lại nhiều lần. Xem ví dụ dưới đây:

module VotePatch 
    extend ActiveSupport::Concern 

    included do 
    after_create :create_activity_stream_event 
    has_one :activity_stream_event 
    end 

    module InstanceMethods 
    def create_activity_stream_event 
     #your code here 
    end 
    end 

end 

Vote.send(:include, VotePatch) 
+0

đã cập nhật câu trả lời của tôi, tôi tin rằng ActiveSupport :: Mối quan tâm sẽ khắc phục được sự cố của bạn –

0

vấn đề của bạn có thể là do thực tế rằng bạn là con khỉ vá lớp. Khi đường ray cố gắng tải lại các hằng số , nó sẽ không xem xét tệp của bạn.

Hãy thử sử dụng kỹ thuật mô-đun như được nêu bên dưới.

Thêm một tập tin gọi lib/vote_fu_extension.rb

module VoteFuExtension 
    def self.included(base) 
    base.has_one :activity_stream_event 
    base.after_create :create_activity_stream_event 
    end 
    def create_activity_stream_event 
    # something.. 
    end 
end 
Vote.send(:include, VoteFuExtension) 

Thêm một initializer gọi config/initializers/vote_fu.rb

require "vote_fu_extension" 

Note

Nếu bạn muốn thêm các phương pháp lớp để mô hình Vote tham khảo answer này .

Phích cắm không biết xấu hổ: My fork của đá quý vote_fu có một số tính năng và cải tiến mới.

1

Adrien Coquio có ý tưởng phù hợp với ActiveSupport::Concerns, là the Rails way để mở rộng mô hình. Mã của anh ta sẽ hoạt động, và bạn nên sử dụng nó.

Tuy nhiên điều này sẽ không hoạt động mọi lúc trong quá trình phát triển vì khi Rails tải lại các lớp học của bạn khi tệp thay đổi, nó sẽ không loại bỏ lại dòng #send. Giải pháp duy nhất tôi có thể tìm được gắn với một ActionDispatch callback trong sản xuất để đảm bảo các tập tin là lại đòi hỏi sau mỗi lần tải trang:

if Rails.env.development? 
    ActionDispatch::Callbacks.to_prepare do 
    require_dependency "../../lib/vote_fu_extensions" 
    end 
end 

Trong sản xuất, hoặc nếu bạn thiết lập cache_classes true trong cấu hình của bạn, bạn thắng 't cần phải làm điều này.

Các vấn đề liên quan