Namespacing Models

Hello everyone,

Do you feel it? The little shiver down your spine when you think of
"namespacing" in rails? To some thats a common feeling, to people like
myself who enforce absolute organization have already thought about
the need for namespaces in rails.

Now I do understand this is an ongoing effort... to some degree.
Controllers and routes, from my experiences work well, that being,
under a Namespace::Classname convention.

At this stage, we need to solidify a convention for namespaces with
models. It seems the functionality is halfway there and we need some
sort of closure on the issue. As it stands, the easy solution which
many people has been using is to just prefix the class name with the
"namespace". For example,

class SecurityUser < ActiveRecord::Base
end

This also means that associations will also reference the naming
convention :security_users. The name used to prefix the class is used
throughout the application... redundant! However, what happens if the
User class is moved to another "package" or if there is a spelling
mistake? The entire application will require refactoring when it
should only be changed in a few places pertaining to the namespace.
This lends itself for a better solution.

I will agree that for most applications, they don't reach the girth
where they even need to take advantage of this type of functionality,
but Rails is compared against "enterprise" web application platforms
where namespaces are a standard. Name conflicts will happen when
reusing modules throughout various applications.

It's simple.

app/models/security/
app/models/security/user.rb

class Security::User < ActiveRecord::Base
end

by default, the table name should be security_user and the
associations can remain :users. There shouldn't need to be a app/
models/security.rb class that references the table "securities".

I am not a rails core developer, so I do not have the roadmap of the
architecture in mind. But there should be something taken into
consideration. I'd be willing to make the contribution, I'd just like
to hear some more thoughts from other developers, thanks.

Regards,

Peter

At this stage, we need to solidify a convention for namespaces with
models. It seems the functionality is halfway there and we need some
sort of closure on the issue.

I think it's simply a matter of fixing the parts which don't work as expected, and then providing a small section of documention in ActiveRecord::Base about models within modules.

Do you know which parts are currently broken, or don't work as expected? AFAIK associations, STI and autoloading work just fine with namespaced models.

The only thing I can see as possibly confusing is the the current behaviour for table_name:

>> module Security; end
=> nil
>> class Security::User < ActiveRecord::Base; end
=> nil
>> Security::User.table_name
=> "users"

It might be more sensible to have a table_name of "security_users", though it irks me that it's the same result as the table_name for a SecurityUser class.

-- tim

The only problem I have with namespaced models is that STI class names are demodulized when they are stored. It would be great to see this fixed in 2.0. There have been a couple of tickets about it (
http://dev.rubyonrails.org/ticket/6792
) and I’ve got a plugin that fixes the problem, http://svn.spreydon.org.nz/things/rails/plugins/namespaced_models/
.

-Jonathan.

Associations do generally work but you have to be explicit about the class name to avoid some issues, e.g:

class Shop::Basket < ActiveRecord::Base
   has_many :items, :foreign_key => 'basket_id',
                    :class_name => '::Shop::basket::Item'
end

class Shop::basket::Item < ActiveRecord::Base
   belongs_to :basket, :foreign_key => 'basket_id',
                       :class_name => '::Shop::Basket'
   belongs_to :product, :foreign_key => 'product_id',
                        :class_name => '::Shop::Product'
end

To fix this compute_type method could be a bit more intelligent and it'd be possible to drop the explicit class name. There's also a small bug with association extension modules not being able to be defined by passing a block.

The main bug with STI is that the demodulized class name is stored and not the full class name.

Like you said, it mostly works it just needs someone to spend a couple of days going through and addressing all these small issues.

Andrew White

The only thing I can see as possibly confusing is the the current
behaviour for table_name:

>> module Security; end
=> nil
>> class Security::User < ActiveRecord::Base; end
=> nil
>> Security::User.table_name
=> "users"

It might be more sensible to have a table_name of "security_users",
though it irks me that it's the same result as the table_name for a
SecurityUser class.

class Security < ActiveRecord::Base
  class User < ActiveRecord::Base
  end
end

Security::User.table_name

Yep... like how your ol dynamically-generating-an-inner-class-with-associations-to-the-outer-class-at-runtime-mind-f*ck-known-as-acts_as_versioned plugin works :slight_smile: :slight_smile:

I only mentioned it because the Module::Class table_name convention might be the only part that's slightly illogical (rather than have it similar to camping, module prefixed)... but hey, I don't mind keeping it as-is and having people set table_name to "security_users" manually

-- tim

Andrew White wrote:

Do you know which parts are currently broken, or don't work as
expected? AFAIK associations, STI and autoloading work just fine with
namespaced models.

Associations do generally work but you have to be explicit about the
class name to avoid some issues, e.g:

class Shop::Basket < ActiveRecord::Base
   has_many :items, :foreign_key => 'basket_id',
                    :class_name => '::Shop::basket::Item'
end

class Shop::basket::Item < ActiveRecord::Base
   belongs_to :basket, :foreign_key => 'basket_id',
                       :class_name => '::Shop::Basket'
   belongs_to :product, :foreign_key => 'product_id',
                        :class_name => '::Shop::Product'
end

To fix this compute_type method could be a bit more intelligent and
it'd be possible to drop the explicit class name. There's also a
small bug with association extension modules not being able to be
defined by passing a block.

The main bug with STI is that the demodulized class name is stored
and not the full class name.

Like you said, it mostly works it just needs someone to spend a
couple of days going through and addressing all these small issues.

Andrew White

I'm running into some weirdness with namespaced models.

It works perfectly on my dev machine (MacOS X, ruby 1.8.6., rails
1.2.3).

However, on my production machine (Ubuntu, ruby 1.8.4, rails 1.2.3),
when ever I try to access a namespaced class, i.e. X::Y, I get the
following errors:

NameError: uninitialized constant X
        from
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:266:in
`load_missing_constant'
        from
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:452:in
`const_missing'
        from
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:464:in
`const_missing'
        from (irb):1

Is this a known issue with ruby 1.8.4?

Thanks,

Andrew

Andrew,

I've seen problems like this, especially but not limited to when moving
an app from OS X to linux. It's the class auto-loading that's the
culprit. Rails' auto-load mechanism works great when you follow
convention, but if you stray (e.g., heavy use of modules) then
unexpected things may happen. Regarding the difference between OS X and
linux, I suspect this more often than not has something to do with linux
being case-sensitive and OS X being only somewhat case-sensitive. Our
solution has been to explicitly load all of our classes in the order we
want them loaded.

At the bottom of application.rb, we load all the models. For each
module (in the order we want them loaded) we do the following:

Dir["#{RAILS_ROOT}/app/models/{module}/*.rb"].each { |file|
  require_dependency "{module}/#{file[file.rindex('/') + 1...-3]}"
}

Replace {module} with your module name. We do it in application.rb, and
with require_dependency, so that classes continue to reload correctly on
each request.

It's not the _best_ solution in the world, since now _all_ classes load
on _every_ request in development mode, but it works and hasn't caused
us any problems.

Incidentally, this is also the solution to get Object.subclasses_of to
work properly in all cases, as well as finders in STI tables. Before
all classes are loaded these methods can behave unpredictably, as they
are unaware of the entire class hierarchy.

Dan Chak