Reputation: 724
I'm trying to split a large model into multiple files for logical organization. So i have two files:
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
but when the app loads, and there is a call to Model1.class_method4, i get:
undefined method `class_method4' for #<Class:0x92534d0>
i've also tried this for the require:
require File.join(File.dirname(__FILE__), 'model1_section1')
What am i doing wrong here?
Upvotes: 14
Views: 11761
Reputation: 13404
Here's an article that does a good job of proposing solutions to this problem:
http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
Upvotes: 4
Reputation: 2807
I know that I'm late on this, but while trying to work out how to do this myself I stumbled on this question.
I think the answer to why the class re-opening is not working as expected in the example code is that the class is initially defined as:
(in model1.rb)
class Model1 < ActiveRecord::Base
and then a re-opened as: (in model1_section1.rb)
class Model1
i.e. the second definition is lacking the inherited class.
I've used separate .rb files to split up my enormous models, and they have worked nicely for me. Though I'll admit I used include and something more like this:
(in 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
This has allowed me to incorporate class and object methods into each included .rb file. The only caveat (since I didn't use the concerns extension) was that access to class constants from within module object methods required the constant to be qualified with the class name (such as Workcase::SOME_CONST) rather than directly as would be allowable if called in the primary file.
Overall, this approach seemed to require the least amount of rewrite of my code to make things into manageable blocks of code.
Maybe this is not the true 'Rails way' but it does seem to work nicely in my particular scenario.
Upvotes: 2
Reputation: 2438
I know I'm answering this a little late, but I've just done this in one of my apps so thought I'd post the solution I used.
Let's this was my model:
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
You can refactor it to:
# 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
and:
# 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
It works perfectly thanks to the extra cleanliness that ActiveSupport::Concern
gives you. Hope this solves this old question.
Upvotes: 10
Reputation: 4072
If you're literally trying to split a class into two files (similar to partial classes in C#), I don't know of a ruby-friendly way to do that.
However, one common way to wind up with classes that have considerable functionality (including a large number of methods) is via Mixins. Modules can be mixed into a class and their methods are literally included in the class.
Upvotes: 1
Reputation: 16441
There's a neat gem called modularity that will do exactly what you're wanting.
A good guide on how to split them properly is on gem-session.
Upvotes: 1