28

I have got the following dir structure

models/foo/setting.rb
models/foo.rb

foo.rb content

 module Foo
  def self.table_name_prefix
    'foo_'
  end
 end

and setting.rb content

class Foo::Setting < ActiveRecord::Base
end

As soon as I am calling Foo::Setting.find… I am getting an error SQLException: no such table: settings which is indeed correct because the table is named foo_settings so rails seems to ignore the table prefix specified for the module Foo.

What can I do so that rails considers the prefix?

gorootde
  • 4,003
  • 4
  • 41
  • 83
  • 1
    Which version of Rails are you using? – qerub Jan 18 '12 at 13:43
  • As Qerub mentioned, you should check your Rails version, because if im not mistaken, this feature came in from 3.0 – zsquare Jan 18 '12 at 13:54
  • Did some experimenting. It works if I do a `require 'repsys.rb'` in `models/foo/setting.rb`. Seems like Rails doesn't load `foo.rb`if I am accessing `Foo::Settings`. If requiring is the only solution that would be pretty poor! – gorootde Jan 18 '12 at 14:39
  • I already have some code working correctly in my app, but now I get this error with another namespace. Using rails 3.2.6. – TuteC Aug 07 '12 at 20:50
  • Thanks for the question. Running into the same problem, but only when I run it on heroku, locally it all works fine. I can live with your workaround for now. Did you ever find a better solution? – Hampei Apr 18 '13 at 21:14

4 Answers4

25

You've define a method inside a module (Foo). This doesn't magically define that method on a class nested in that module.

I'd try something like

class Foo < ActiveRecord::Base
  self.abstract_class = true
  self.table_name_prefix = 'foo_'
end

And then inherit from Foo

class Foo::Setting < Foo
...
end
Frederick Cheung
  • 83,189
  • 8
  • 152
  • 174
  • 1
    Rails generated that module and the structure. Are you sure? If so `rails generate` will generate something that is not runnable. – gorootde Jan 18 '12 at 15:09
  • It generates something that's runnable, just not something that does what you want. If you're just namespacing your models then all you need is a mode – Frederick Cheung Jan 18 '12 at 15:23
  • Spree commerce seem to be using this method also.. https://github.com/spree/spree/blob/master/core/app/models/spree.rb But I cant figure out how they've done it – BigFive Jul 19 '12 at 02:22
  • 4
    I don't think this answer is correct. For some of my models it does magically define that method on the nested classes but for others it doesn't? – amoebe Apr 03 '13 at 13:10
  • Sounds like you should ask a fresh question, showing when it works and when it doesn't - there's not really room in comments to do all that – Frederick Cheung Apr 03 '13 at 15:04
  • 3
    wrong, according to [the docs](http://apidock.com/rails/ActiveRecord/Base/table_name_prefix/class) for `table_name_prefix`, the OP code should work. My guess is that it has to do with the autoloader (see my answer). Your answer is still working though, so no downvote ;) – m_x Jun 09 '15 at 13:56
  • ah, yes active record rather than calling just table_name_prefix tries to walk up the module chain calling table_name_prefix all the way – Frederick Cheung Jun 09 '15 at 14:03
  • 2
    the answer below by @m_x is much superior, as it identifies the core of the problem. – igneus Jun 23 '16 at 16:35
  • It is important to note, that if table_name is overrided, table_name_prefix doesn't work https://stackoverflow.com/questions/29174840/activerecord-ignores-table-name-prefix – gayavat May 25 '17 at 08:10
11

This is probably caused by rails' autoloader. When doing this :

module Foo
  class Bar
  end
end

And then trying to use Foo::Bar, the autoloader first tries to locate app/models/foo/bar.rb. The file is loaded, and module Foo is defined here (albeit as a module containing solely Bar) so the autoloader never attempts to load app/models/foo.rb.

This should only happen in development mode, as in production mode all of your files are require'd on startup.

There are two workarounds AFAIK :

Cheat the autoloader

declare your class using class Foo::Bar, to force the autoloader to resolve a constant lookup for Foo.

This has the annoying side effect that the constant lookup inside Bar will NOT be scoped inside Foo, for instance :

# app/models/foo.rb
module Foo
 BAZ = "baz"
end

# app/models/foo/bar.rb
class Foo::Bar
  def baz
    BAZ
  end
end

here, Foo::Bar.new.bazwill fail, unless you reference the constant using Foo::BAZ. This can get really a mess when defining ActiveRecord associations, for instance.

Require the module

using require_dependency :

require_dependency 'foo'
module Foo
  class Bar
  end
end

This is IMHO the right solution, as it does not break the constant lookup, but it is also a bit annoying as you have to add the require statement on top of each namespaced file.

Note :

This bug seems to have been resolved in rails 4. I used the second workaround a lot while on rails 3, but I've tried to reproduce the bug in rails 4 and it does not show up anymore. I think they modified the way the autoloader works... For more info, see the rails guides on autoloading and reloading constants

m_x
  • 12,357
  • 7
  • 46
  • 60
  • Under some cases I have this bug too – GorillaApe Sep 02 '16 at 18:24
  • i still had this bug intermittently show up on a CI run at work with rails 4. so we didn't have to require_dependency we required the module in an initializer. i prefer this way as opposed to needing to require_dependancy because that means any other dev that comes along will need to remember to require dependancy as well. – karina Sep 13 '16 at 22:36
  • Yup, I've still had this crop up in an application in Rails 4 and 5. We deal with it by creating a base model as described in the other answer, but `require_dependency` would work fine too. Kinda frustrating that the Rails generator gives you an unreliable default scheme for this. – Ian Greenleaf Young Oct 24 '19 at 16:06
2

Just create a base class in inside your namespaced model directory and require Foo in it, then extend your models from the base class.

Say I have app/models/foo.rb

module Foo
  def self.table_name_prefix
    'tble_prefix_'
  end
end

Then in app/models/foo/base_record.rb

require_dependency 'foo'
module Foo
  class BaseRecord < ActiveRecord::Base
    self.abstract_class = true
  end
end

Then extend from the BaseRecord

module Foo
  class Bar < BaseRecord

  end
end
papar
  • 1,023
  • 9
  • 17
1

I had the same issue. Solved it by changing either of my application's namespace or the model's.

Take a look at this question. Using the same namespace for the application as for models causes to models not pick up the parent namespace table_name_prefix correctly.

Community
  • 1
  • 1
guapolo
  • 2,443
  • 2
  • 20
  • 19