Tôi đã đọc một số cách về cách mở rộng ActiveRecord: Lớp cơ sở để các mô hình của tôi có một số phương thức đặc biệt. Cách dễ dàng để mở rộng nó (hướng dẫn từng bước) là gì?
Tôi đã đọc một số cách về cách mở rộng ActiveRecord: Lớp cơ sở để các mô hình của tôi có một số phương thức đặc biệt. Cách dễ dàng để mở rộng nó (hướng dẫn từng bước) là gì?
Câu trả lời:
Có một số cách tiếp cận:
Đọc tài liệu ActiveSupport :: Mối quan tâm để biết thêm chi tiết.
Tạo một tập tin được gọi active_record_extension.rb
trong lib
thư mục.
require 'active_support/concern'
module ActiveRecordExtension
extend ActiveSupport::Concern
# add your instance methods here
def foo
"foo"
end
# add your static(class) methods here
class_methods do
#E.g: Order.top_ten
def top_ten
limit(10)
end
end
end
# include the extension
ActiveRecord::Base.send(:include, ActiveRecordExtension)
Tạo một tệp trong config/initializers
thư mục được gọi extensions.rb
và thêm dòng sau vào tệp:
require "active_record_extension"
Tham khảo câu trả lời của Toby .
Tạo một tập tin trong config/initializers
thư mục được gọi active_record_monkey_patch.rb
.
class ActiveRecord::Base
#instance method, E.g: Order.new.foo
def foo
"foo"
end
#class method, E.g: Order.top_ten
def self.top_ten
limit(10)
end
end
Câu nói nổi tiếng về các biểu thức chính quy của Jamie Zawinski có thể được tái sử dụng để minh họa các vấn đề liên quan đến vá khỉ.
Một số người, khi gặp vấn đề, nghĩ rằng tôi biết, tôi sẽ sử dụng phương pháp vá khỉ. Bây giờ họ có hai vấn đề.
Khỉ vá rất dễ dàng và nhanh chóng. Nhưng, thời gian và công sức tiết kiệm luôn được trích xuất trở lại vào lúc nào đó trong tương lai; với lãi kép. Những ngày này, tôi hạn chế vá khỉ để nhanh chóng tạo ra một giải pháp trong bảng điều khiển đường ray.
require
tập tin vào cuối environment.rb
. Tôi đã thêm bước bổ sung này vào câu trả lời của tôi.
ImprovedActiveRecord
và kế thừa từ đó, khi bạn đang sử dụng module
, bạn đang cập nhật định nghĩa của lớp đang đề cập. Tôi đã từng sử dụng tính kế thừa (nguyên nhân của nhiều năm kinh nghiệm Java / C ++). Ngày nay tôi chủ yếu sử dụng các mô-đun.
Refinements
giải quyết hầu hết các vấn đề với việc vá khỉ ( yehudakatz.com/2010/11/30/ruby-2-0-refinements-in-practice ). Đôi khi một tính năng là có để buộc bạn phải cám dỗ số phận. Và đôi khi bạn làm.
Bạn chỉ có thể mở rộng lớp và chỉ cần sử dụng kế thừa.
class AbstractModel < ActiveRecord::Base
self.abstract_class = true
end
class Foo < AbstractModel
end
class Bar < AbstractModel
end
abstract_models
. Tôi nên đặt nó ở đâu?
self.abstract_class = true
vào của bạn AbstractModel
. Bây giờ Rails sẽ nhận ra mô hình là một mô hình trừu tượng.
AbstractModel
trong cơ sở dữ liệu. Ai biết một setter đơn giản sẽ giúp tôi DRY mọi thứ lên! (Tôi đã bắt đầu co rúm ... thật tệ). Cảm ơn Toby và Harish!
Bạn cũng có thể sử dụng ActiveSupport::Concern
và có nhiều thành ngữ cốt lõi Rails như:
module MyExtension
extend ActiveSupport::Concern
def foo
end
module ClassMethods
def bar
end
end
end
ActiveRecord::Base.send(:include, MyExtension)
[Chỉnh sửa] sau nhận xét từ @daniel
Sau đó, tất cả các mô hình của bạn sẽ có phương thức được foo
bao gồm như một phương thức cá thể và các phương thức được ClassMethods
bao gồm dưới dạng các phương thức lớp. Ví dụ: FooBar < ActiveRecord::Base
bạn sẽ có: FooBar.bar
vàFooBar#foo
http://api.rubyonrails.org/groupes/ActiveSupport/Concern.html
InstanceMethods
không được chấp nhận kể từ Rails 3.2, chỉ cần đặt các phương thức của bạn vào thân mô-đun.
ActiveRecord::Base.send(:include, MyExtension)
trong một bộ khởi tạo và sau đó điều này làm việc cho tôi. Đường ray 4.1.9
Với Rails 4, khái niệm sử dụng các mối quan tâm để mô đun hóa và DRY lên các mô hình của bạn đã được nêu bật.
Về cơ bản, mối quan tâm cho phép bạn nhóm mã tương tự của một mô hình hoặc trên nhiều mô hình trong một mô-đun và sau đó sử dụng mô-đun này trong các mô hình. Đây là một ví dụ:
Xem xét mô hình Bài viết, mô hình Sự kiện và Mô hình Nhận xét. Một bài viết hoặc một sự kiện có nhiều ý kiến. Một bình luận thuộc về một trong hai bài viết hoặc sự kiện.
Theo truyền thống, các mô hình có thể trông như thế này:
Mô hình bình luận:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Mô hình bài viết:
class Article < ActiveRecord::Base
has_many :comments, as: :commentable
def find_first_comment
comments.first(created_at DESC)
end
def self.least_commented
#return the article with least number of comments
end
end
Mô hình sự kiện
class Event < ActiveRecord::Base
has_many :comments, as: :commentable
def find_first_comment
comments.first(created_at DESC)
end
def self.least_commented
#returns the event with least number of comments
end
end
Như chúng ta có thể nhận thấy, có một đoạn mã quan trọng phổ biến cho cả Mô hình sự kiện và Mô hình bài viết. Sử dụng mối quan tâm, chúng tôi có thể trích xuất mã phổ biến này trong một mô-đun riêng biệt Có thể nhận xét.
Đối với điều này, hãy tạo một tệp commentable.rb trong ứng dụng / mô hình / mối quan tâm.
module Commentable
extend ActiveSupport::Concern
included do
has_many :comments, as: :commentable
end
# for the given article/event returns the first comment
def find_first_comment
comments.first(created_at DESC)
end
module ClassMethods
def least_commented
#returns the article/event which has the least number of comments
end
end
end
Và bây giờ mô hình của bạn trông như thế này:
Mô hình bình luận:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Mô hình bài viết:
class Article < ActiveRecord::Base
include Commentable
end
Mô hình sự kiện
class Event < ActiveRecord::Base
include Commentable
end
Một điểm tôi muốn nêu bật trong khi sử dụng Mối quan tâm là Mối quan tâm nên được sử dụng cho nhóm 'dựa trên miền' thay vì nhóm 'kỹ thuật'. Ví dụ: một nhóm tên miền giống như 'Có thể nhận xét', 'Có thể gắn thẻ', v.v ... Một nhóm dựa trên kỹ thuật sẽ giống như 'FinderMethods', 'ValidationMethods'.
Đây là một liên kết đến một bài đăng mà tôi thấy rất hữu ích để hiểu mối quan tâm trong Mô hình.
Hy vọng các bài viết giúp :)
Bước 1
module FooExtension
def foo
puts "bar :)"
end
end
ActiveRecord::Base.send :include, FooExtension
Bước 2
# Require the above file in an initializer (in config/initializers)
require 'lib/foo_extension.rb'
Bước 3
There is no step 3 :)
Rails 5 cung cấp một cơ chế tích hợp để mở rộng ActiveRecord::Base
.
Điều này đạt được bằng cách cung cấp lớp bổ sung:
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
# put your extensions here
end
và tất cả các mô hình kế thừa từ đó:
class Post < ApplicationRecord
end
Xem ví dụ blogpost này .
Chỉ để thêm vào chủ đề này, tôi đã dành một lúc để tìm cách kiểm tra các tiện ích mở rộng như vậy (tôi đã đi theo ActiveSupport::Concern
tuyến đường.)
Đây là cách tôi thiết lập một mô hình để kiểm tra các tiện ích mở rộng của mình.
describe ModelExtensions do
describe :some_method do
it 'should return the value of foo' do
ActiveRecord::Migration.create_table :test_models do |t|
t.string :foo
end
test_model_class = Class.new(ActiveRecord::Base) do
def self.name
'TestModel'
end
attr_accessible :foo
end
model = test_model_class.new(:foo => 'bar')
model.some_method.should == 'bar'
end
end
end
Với Rails 5, tất cả các mô hình được kế thừa từ ApplicationRecord và nó mang lại cách hay để bao gồm hoặc mở rộng các thư viện mở rộng khác.
# app/models/concerns/special_methods.rb
module SpecialMethods
extend ActiveSupport::Concern
scope :this_month, -> {
where("date_trunc('month',created_at) = date_trunc('month',now())")
}
def foo
# Code
end
end
Giả sử mô-đun phương thức đặc biệt cần có sẵn trên tất cả các mô hình, bao gồm nó trong tệp application_record.rb. Nếu chúng ta muốn áp dụng điều này cho một tập các mô hình cụ thể, thì hãy đưa nó vào các lớp mô hình tương ứng.
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
include SpecialMethods
end
# app/models/user.rb
class User < ApplicationRecord
include SpecialMethods
# Code
end
Nếu bạn muốn có các phương thức được định nghĩa trong mô-đun như các phương thức lớp, hãy mở rộng mô-đun sang ApplicationRecord.
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
extend SpecialMethods
end
Hy vọng nó sẽ giúp người khác!