Skip to content

Instantly share code, notes, and snippets.

@shushugah
Last active October 6, 2017 09:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shushugah/6f90d836b169f06a41eea3a7f576cff9 to your computer and use it in GitHub Desktop.
Save shushugah/6f90d836b169f06a41eea3a7f576cff9 to your computer and use it in GitHub Desktop.
Rails Autoloading Behavior and Module name spacing
# app/models/campaign.rb 
class Campaign; end # This class is completely irrelevent to our code, but has same class name as the other campaign class.

# app/resources/homepage/apple.rb
module Resources
  module Homepage
    class Apple < Campaign
      campaign_method!  # This fails because apple.rb is loaded alphabetically before resources/homepage/campaign.rb 
    end
  end
end

# app/resources/homepage/campaign.rb
module Resources
  module Homepage
    class Campaign
      def self.campaign_method!
        "I should be callable"
      end
    end
  end
end

# app/resources/homepage/yolo.rb
module Resources
  module Homepage
    class Yolo < Resources::Homepage::Campaign
       campaign_method!  # This works because the parent class is unambiguous
    end
  end
end

# app/resources/homepage/zolo.rb
module Resources
  module Homepage
    class Zolo < Campaign
       campaign_method!  # This works by luck, because Zolo is loaded alphabetically after resources/homepage/campaign.rb
    end
  end
end

I was able to debug this issue using rails console

pry(main)> Resources::Homepage::Yolo # returns a constant
=> Resources::Homepage::Yolo
pry(main)> Resources::Homepage::Apple # returns an error message
NameError: undefined local variable or method `campaign_method!' for Resources::Homepage::Apple:Class`

After changing class Apple < Campaign to class Apple < Resources::Homepage::Campaign and reloading the console, the code loads correctly.

Explicitly loading the required file also works, which is what Rails AutoLoader (usually) does correctly for you. The Rails Guides discourages using require or require_relative for auto loaded files. Instead use require_dependency

# resources/homepage/apple.rb
require_dependency 'resources/homepage/campaign' 
module Resources
  module Homepage
    class Apple < Campaign
      campaign_method!  # this works and loads campaign's class method
    end
  end
end
@fanaugen
Copy link

Nice writeup. Yeah, Rails’ autoloading of constants is weird magic, and most Rails developers (including myself) don’t really understand how it works and what the consequences of it are, which causes many subtle bugs to be created.

In general, for all kinds of library "magic": everything it does for you it also does to you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment