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.