Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Rails try to guess the filename when I `include` a module?

I have the following setup where one of my classes includes a module that's located in another file

The key thing to note is that the module MyBar does not live in a file with a similar name. It lives in my_foo.rb instead.

my_foo.rb

module MyBar
  def self.test
    "This is a test string"
  end
end

some_class.rb

require 'my_foo'

class SomeClass
  include MyBar

  def initialize
    puts MyBar.test
  end
end

When I run this I get a NameError

NameError - uninitialized constant MyBar

It looks like Rails is trying to be smart and assume that because the module name is MyBar it should be located in a file similarly named my_bar.rb.

I tried testing this theory by changing the name to what it expects, and it suddenly worked correctly

my_foo.rb (renamed)-> my_bar.rb

The question is

  1. Why does Rails do this? Seems like it's assuming too much. I get that it's "convention over configuration", but there are several valid reasons to have the filename not match the module name.

  2. How do I override this behavior?

like image 422
user2490003 Avatar asked Sep 25 '15 01:09

user2490003


2 Answers

The app directory is not included in $LOAD_PATH in rails for various reasons (mainly autoloading and class reload while you edit stuff).

If you want to use constants that don't match the file name, you want to locate them in lib directory and require them (lib is included in $LOAD_PATH).

If you really want to include a file in the app directory, you probably want to try with a require Rails.root.join('app', 'models', 'my_foo.rb').to_s. I'm not sure what will break though, you are really going against rails convention.

like image 30
Francesco Belladonna Avatar answered Sep 24 '22 16:09

Francesco Belladonna


This has something to do with how the autoloading works in Rails.

Rails only auto loads when you try to use a new class/module name that matches the file name and located under the /lib/ directory.

To fix your issue, you can put your my_foo.rb file under /lib/ directory and require it like this: require 'lib/my_foo'

See the Rails Documentation for Autoloading and Reloading Constants for more information about how auto loading works in Rails.

like image 83
K M Rakibul Islam Avatar answered Sep 26 '22 16:09

K M Rakibul Islam