Разделение класса на несколько файлов в 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. Надеюсь, что это решает этот старый вопрос.

Ответ 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. Модули могут быть смешаны с классом, и их методы буквально включены в класс.