Split model in few files

Hi,

I have the following (newbie) problem: in my 'app/models' folder is a class (User) with more then 1000 lines of code. I want to split the model in different files into a subfolder (a separat file for validations, filter, additional methods, ...). How can I do this as simple as possible? Can I extend the class directly (see below) or is it necessary to include modules (the files can contain class and instance methods)?

e.g.

# app/models/user.rb def User   # elementary code end

# app/models/user/validations.rb def User   # validations end

# app/models/user/attributes.rb def User   # additional attributes end

... The require 'user/validations' etc. does not work for me (?). It runs well in the console but not for the rails application (the class content is only found at the first request – I extended the load_path with config.load_paths << "#{RAILS_ROOT}/app/models/user" in the environment.rb).

Hi,

I have the following (newbie) problem: in my 'app/models' folder is a class (User) with more then 1000 lines of code. I want to split the model in different files into a subfolder (a separat file for validations, filter, additional methods, ...). How can I do this as simple as possible? Can I extend the class directly (see below) or is it necessary to include modules (the files can contain class and instance methods)?

e.g.

# app/models/user.rb def User # elementary code end

# app/models/user/validations.rb def User # validations end

# app/models/user/attributes.rb def User # additional attributes end

... The require 'user/validations' etc. does not work for me (?). It runs well in the console but not for the rails application (the class content is only found at the first request – I extended the load_path with config.load_paths << "#{RAILS_ROOT}/app/models/user" in the environment.rb).

You should use require_dependency rather than require. Also I wouldn't add models/user to the load path: if you follow this approach with several models then doing just require 'validations' could pick any of them (depending on the order in which you add those folders to the load path).

Lastly have a google around for concerned_with

Fred

Use modules, which are included not required. This is much cleaner than overriding the class as you are doing above. You might find that you can factor out some functionality such that other classes can use it as well. Put the modules in lib and keep your models folder with just the class definitions.

S. K. wrote:

Hi,

I have the following (newbie) problem: in my 'app/models' folder is a class (User) with more then 1000 lines of code. I want to split the model in different files into a subfolder (a separat file for validations, filter, additional methods, ...). How can I do this as simple as possible? Can I extend the class directly (see below)

Yes, but then Rails' autoloader may not find all the pieces unless you explicitly requre them.

or is it necessary to include modules (the files can contain class and instance methods)?

It would be better to include modules. That also means you can reuse the modules in other classes.

But why the heck do you have 1000 LOC in your model to begin with? Smells like something more fundamental is wrong.

Best,

A module include sounds fine but I got the following problem with the sample code:

# app/models/user.rb def User   include UserValidations   # elementary code end

# app/models/user/user_validations.rb module UserValidations   # validations end

uninitialized constant User::UserValidations

I can solve this if I require the modules first (see below). It works, but doesn't makes sense for me...

# app/models/user.rb def User   require 'user/user_validations'   include UserValidations   # elementary code end

ah yeah, you'll need to require all of the module files. Don't do this in your model though, do it in a file in config/initializers.rb.

thanks a lot :wink:

[Please quote when replying.]

Max Williams wrote:

ah yeah, you'll need to require all of the module files.

No! Put them in app/models or in lib, where Rails will find them automatically.

Don't do this in your model though, do it in a file in config/initializers.rb.

No! If you need to do an explicit require, put it where you need it.

Best,

Max Williams wrote:

Use modules, which are included not required. This is much cleaner than overriding the class as you are doing above. You might find that you can factor out some functionality such that other classes can use it as well. Put the modules in lib and keep your models folder with just the class definitions.

I am trying to do just that. I have a large model with lots of attributes - I am not trying to factor out code to share between models, I just want to break the model up into a few files for sanity's sake.

But if I, for example, do this:

  class Project < ActiveRecord::Base     include ProjectValidations   end

  module ProjectValidations     validates_presence_of :project_name   end

then I get the following error:

  undefined method `validates_presence_of' for ProjectValidations:Module

Suggestions, anyone?

You want to do this, as its the class that needs to evaluate this method, not the module.

class Project < ActiveRecord::Base include ProjectValidations end

module ProjectValidations validates_presence_of :project_name end

module ProjectValidations

  def self.included(base)      base.send :validates_presence_of, :project_name   end

end

RobL

]> But if I, for example, do this:

Rob Lacey wrote:

You want to do this, as its the class that needs to evaluate this method, not the module.

module ProjectValidations

  def self.included(base)      base.send :validates_presence_of, :project_name   end

end

Thanks for the pointer, Rob. But I've got a lot more of these than the simple use case shows, and I'd have to modify a lot of code to modularize things that way. After some more experimentation, I've come up with this:

  def self.included(base)     base.class_eval {       validates_presence_of :project_name       ...     }   end

This way I can just cut/paste all of the validations from the original model class file into the base.class_eval block; much less mucking around.