Разделение класса на несколько файлов в Ruby on Rails
Я пытаюсь разбить большую модель на несколько файлов для логической организации. Итак, у меня есть два файла:
model1.rb
class Model1 < ActiveRecord::Base
before_destroy :destroying
has_many :things, :dependent=>:destroy
def method1
...
end
def method2
...
end
end
require 'model1_section1'
model1_section1.rb
class Model1
def method3
...
end
def self.class_method4
...
end
end
но когда приложение загружается, и есть вызов Model1.class_method4, я получаю:
undefined method `class_method4' for #<Class:0x92534d0>
Я также попробовал это для запроса:
require File.join(File.dirname(__FILE__), 'model1_section1')
Что я здесь делаю неправильно?
Ответы
Ответ 1
Я знаю, что я немного отвечаю на это немного, но я только что сделал это в одном из своих приложений, поэтому подумал, что отправлю решение, которое я использовал.
Пусть это была моя модель:
class Model1 < ActiveRecord::Base
# Stuff you'd like to keep in here
before_destroy :destroying
has_many :things, :dependent => :destroy
def method1
end
def method2
end
# Stuff you'd like to extract
before_create :to_creation_stuff
scope :really_great_ones, #...
def method3
end
def method4
end
end
Вы можете реорганизовать его на:
# app/models/model1.rb
require 'app/models/model1_mixins/extra_stuff'
class Model1 < ActiveRecord::Base
include Model1Mixins::ExtraStuff
# Stuff you'd like to keep in here
before_destroy :destroying
has_many :things, :dependent => :destroy
def method1
end
def method2
end
end
и:
# app/models/model1_mixins/extra_stuff.rb
module Model1Mixins::ExtraStuff
extend ActiveSupport::Concern
included do
before_create :to_creation_stuff
scope :really_great_ones, #...
end
def method3
end
def method4
end
end
Он отлично работает благодаря дополнительной чистоте, которую дает ActiveSupport::Concern
. Надеюсь, что это решает этот старый вопрос.
Ответ 2
Вот статья, в которой есть хорошая идея предложить решения этой проблемы:
http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
Ответ 3
Я знаю, что я опаздываю на это, но, пытаясь разобраться, как это сделать, я наткнулся на этот вопрос.
Я думаю, что ответ на вопрос, почему повторное открытие класса не работает должным образом в примере кода, состоит в том, что класс изначально определяется как:
(в model1.rb)
class Model1 < ActiveRecord::Base
а затем снова открывается как:
(в model1_section1.rb)
class Model1
то есть. во втором определении отсутствует унаследованный класс.
Я использовал отдельные файлы .rb, чтобы разделить мои огромные модели, и они отлично работали для меня. Хотя я признаю, что использовал include и что-то еще подобное:
(в файле workcase.rb)
class Workcase < ActiveRecord::Base
include AuthorizationsWorkcase
include WorkcaseMakePublic
include WorkcasePostActions
after_create :set_post_create_attributes
# associations, etc
# rest of my core model definition
end
(in workcase_make_public.rb)
module WorkcaseMakePublic
def alt_url_subject
self.subject.gsub(/[^a-zA-Z0-9]/, '_').downcase
end
# more object definitions
end
class Workcase < ActiveRecord::Base
def self.get_my_stuff
do_something_non_instance_related
end
# more class definitions
end
Это позволило мне включить методы класса и объекта в каждый включенный .rb файл. Единственное предостережение (поскольку я не использовал расширение проблем) заключалось в том, что доступ к константам класса из методов объекта модуля требовал, чтобы константа была квалифицирована с именем класса (например, Workcase:: SOME_CONST), а не напрямую, как это было бы возможно если вызывается в основном файле.
В целом, этот подход, по-видимому, требует наименьшего количества перезаписи моего кода, чтобы сделать вещи управляемыми блоками кода.
Возможно, это не настоящий "Rails-путь", но он, похоже, хорошо работает в моем конкретном сценарии.
Ответ 4
Там аккуратный драгоценный камень, называемый modularity, который будет делать именно то, что вы хотите.
Хорошее руководство о том, как правильно их разбить, находится на gem-session.
Ответ 5
Если вы буквально пытаетесь разбить класс на два файла (аналогичные частичным классам на С#), я не знаю, как это сделать с рубином.
Однако, один из распространенных способов завершить работу с классами, имеющими значительную функциональность (включая большое количество методов), - это Mixins. Модули могут быть смешаны с классом, и их методы буквально включены в класс.