The title of this question may need renaming. Any suggestions?
In my Rails application there was an old Module in the /lib folder called foo_blaster.rb. I then installed my gem named foo_blaster.rb. None of the code in the gem executed until I deleted the original FooBlaster module. Why is this? Shouldn't Ruby combine the two Modules?
The Modules are the same
# lib/foo_blaster.rb
module FooBlaster
class Characters
puts "some characters"
end
end
# foo_blaster gem files
module FooBlaster
class Users
end
end
FooBlaster::Characters #=> some characters
FooBlaster::Users #=> NameError: uninitialized constant FooBlaster::Users
What am I missing here?
Rails 2.3.18,
Ruby 1.8.7-p358
If you open up a console (with script/console) and examine the $LOAD_PATH variable, you should see something resembling this:
>> $LOAD_PATH
[
# some entries...
[12] "/your/rails/root/app",
[13] "/your/rails/root/app/controllers",
[14] "/your/rails/root/app/models",
[15] "/your/rails/root/app/helpers",
[16] "/your/rails/root/lib",
# more entries...
[27] "/your/ruby/path/lib/ruby/1.8/gems/foo_blaster-0.0.1/lib"
# rest of entries
]
When you require 'foo_blaster' (which may be done implicitly by Rails, Bundler, or perhaps some other means), Ruby searches the locations in $LOAD_PATH sequentially until it finds the file foo_blaster.rb, loads that file, and stops. In your case, it finds the file in your application's /lib directory. The file from your gem will never be loaded.
If you want to load both files, all you have to do is give them different names and make sure to require them both. For instance, you might rename the file in your application to foo_blaster_extensions.rb, then add an initializer to load it:
# config/initializers/extend_foo_blaster.rb
require 'foo_blaster_extensions'
If there is a case when you need same name file in your core app. You should follow best practices while creating gem i.e., you should namespace your gem. So in that case no conflicts will occur with core application.
Related
Is there a way to tell Rails that all files in a certain folder are contained in a certain namespace?
Ie.
I have a file bar.rb in app/foo. Rails will assume this file defines Bar, but instead I want this file to define Foo::Bar.
I know I can achieve this by adding my root to Rails' autoload paths, but that isn't a real solution. Is there any other way I can tell Rails that all files within app/foo reside in the Foo namespace?
EDIT: File tree for clarification
app
assets
controllers
models
foo
bar.rb
quux.rb
I would like to be able to define Foo::Bar and Foo::Quux in respectively bar.rb and quux.rb, while also using Rails autoloading. Without having to resort to the tree structure as below:
app
assets
controllers
models
foo
foo
bar.rb
quux.rb
You can autoload files with namespaces corresponding to directories inside /app by adding /app to your autoload paths in your application config.
# config/application.rb
config.autoload_paths << "#{config.root}/app"
I'm not sure whether or not this is what the author of the question meant by
I know I can achieve this by adding my root to Rails' autoload paths, but that isn't a real solution.
But this is definitely a real solution and the correct one. Doing this is fine and not at all unusual in my experience.
There are only minor side effects which are the cost of adopting the inconsistent naming conventions you want. If you refer to an undefined constant that matches the name of another directory in the app you'll get a slightly different error message.
# Models::Foo => LoadError (Unable to autoload constant Models::Foo, expected /app/models/foo.rb to define it)
But if you have existing namespaces that match directories in app loading will still work fine. Here's what Rails is doing:
It takes the paths added to config.autoload_paths and adds them to the defaults (the directories under app: app/models, app/controllers, app/foo etc). Then when a constant is referenced that is not already loaded Rails proceeds through those paths, looking for paths that match the constants. So when you reference Foo::Bar it looks for app/models/foo/bar.rb, app/controllers/foo/bar.rb etc. until it finds a file that defined Foo::Bar. All we're doing is adding app/foo/bar.rb to that lookup.
Rails doesn't assume namespace, it assumes path to the source file depending on a namespace, so:
$ cat app/foo/bar.rb
module Foo
class Bar
def bar
puts "i'm here"
end
end
end
$ rails c
2.2.0 :007 > b=Foo::Bar.new
=> #<Foo::Bar:0x00000005272770>
2.2.0 :008 > b.bar
i'm here
=> nil
You can try adding the folder you Rail's autoload paths configuration
# config/application.rb
# Rails 4
config.autoload_paths << Rails.root.join("app", "foo")
# Rails 5+
config.eager_load_paths << Rails.root.join("app", "foo")
I have written a generator which creates the following ruby file and folder:
app/tests/test.rb
in the test.rb file I have a Test class which looks like this:
class Test < MyCustomModule::MyCustomClass::Base
...
end
Now, I want to use its functionality in one of the show.html.erb files creating new instance like this:
Test.new(...).render(...).html_safe
but I am getting the following error:
uninitialized constant MyCustomModule::MyCustomClass::Base
I have use the following answer to link my gem and my rails application. It seems to work as I am able to use the generator, but the gem module and class are not seen in the rails application.
Could anyone tell how to fix this issue?
I have try to follow the tips posted here but still nothing changed:
Adding config.autoload_paths += Dir["#{config.root}/lib/**/"] in application.rb file
I have created my gem structure looking at CarrierWave gem, so the naming should be correct
I try to disable config.threadsafe! but it is already disabled since config.cache_classes and config.eager_load are set to false in development
DEPRECATION WARNING: config.threadsafe! is deprecated. Rails
applications behave by default as thread safe in production as long as
config.cache_classes and config.eager_load are set to true.
Also, looking at adding-asset-to-your-gems rails documentation, it is said that:
A good example of this is the jquery-rails gem which comes with Rails
as the standard JavaScript library gem. This gem contains an engine
class which inherits from Rails::Engine. By doing this, Rails is
informed that the directory for this gem may contain assets and the
app/assets, lib/assets and vendor/assets directories of this engine
are added to the search path of Sprockets.
So, I have done this, and put my model class file in assets folder, but the result is the same.
The following screenshots demonstrate my real case:
The screenshot below displays my gem file structure
Here you can see how I am loading the gem in my Rails application Gemfile:
gem 'thumbnail_hover_effect', '0.0.3', github: 'thumbnail_hover_effec/thumbnail_hover_effec', branch: 'master'
Then I am using the gem generator a ruby file with a cutstom name in app/thumbnails/test.rb folder with the following code:
class Test < ThumbnailHoverEffect::Image::Base
...
end
and trying to use the Test class gives me uninitialized constant ThumbnailHoverEffect::Image::Base error.
Back in the gem files, these are how the thumbnail_hover_effect file looks like
require 'thumbnail_hover_effect/version'
require 'thumbnail_hover_effect/engine'
require 'thumbnail_hover_effect/image'
module ThumbnailHoverEffect
# Your code goes here...
end
and hoe the image file looks like:
module ThumbnailHoverEffect
#
class Image
...
end
end
From what you've posted here there is no ThumbnailHoverEffect::Image::Base defined. Rails autoloading conventions (which you should not be depending on a gem btw, more on that later) would be looking for this file in thumbnail_hover_effect/image/base.rb, but the directory structure you printed does not have that. Of course you could define the class in thumbnail_hover_effect/image.rb and it would work, but the abridged snippet you posted does not show that. So where is ThumbnailHoverEffect::Image::Base defined?
If it's in thumbnail_hover_effect/image/base.rb then that would indicate the file is not being loaded. You can sanity check this by putting a puts 'loading this stupid file' at the top of thumbnail_hover_effect/image/base.rb. That will allow you to bisect the problem by seeing whether there is a problem with your definition of the class, or whether the problem is with loading the proper files. Debugging is all about bisecting the problem.
I have the simple following code, which is working in a ruby (not rails) app:
require 'gmail'
Gmail.new('my_account', 'my_password') do |gmail|
end
I am able to get a connection to the Gmail account and do some stuff in there.
However, I want to use this Gem in a Rails app, and therefore I have tried adding the following into the Gemfile:
gem "ruby-gmail", "0.2.1"
gem "mime", "0.1"
However, when I try to use this in a rake task, like this:
task :scrap_receipts_gmail => :environment do
Gmail.new('my_account', 'my_password') do |gmail|
puts gmail.inspect
end
end
I get the following error:
uninitialized constant Object::Gmail
This is solved if I add require 'gmail'. My question is:
Why would I have to require gmail, if I have already specified that in the Gemfile?
The module/class namespace has to match the directory structure. For example, in lib/foo/bar.rb, if and only if the namespace is Foo::Bar can it be auto loaded by Rails, otherwise you have to require it explicitly.
In this case, Gmail is defined as a class, which doesn't match the directory structure. If Gmail was defined as a module (namespace ::Gmail matchs directory structure), then you'll never need to explicitly require "gmail".
So there's this great plugin I've gotten used to using in my Rails 2 projects called Bootstrapper. It essentially duplicates the functionality of the seeds.rb file, but I like it because it lets you break up your bootstrap process into concise chunks.
Anyway, I've gone so far as to fork the project and attempt to turn it into a Rails 3 gem. I've been able to get the gem to initialize and register the rake tasks and generators OK. However, I'm running into a problem with the Bootstrapper class itself. It won't load in the Rails project unless it's in a module.
That is, if I place the Bootstrapper class in a file by itself and require that file in my Railtie, then in my Rails app, it can't find the Bootstrapper class. If I put the class in a module and call Bootstrapper::Bootstrapper everything is peachy.
The code that actually requires the Bootstrapper class is this:
ActiveSupport.on_load :active_record do
require 'bootstrapper/bootstrapper'
end
The source is available here:
http://github.com/jrmehle/bootstrapper/tree/make_gem
Autoload paths actually has an annoying feature of following filesystem paths. For example in your lib or extras (depending on what you autoload) you might have the following file structure:
lib/bootstrapper/bootstrapper.rb
# in this case, Bootstrapper::Bootstrapper.class = Class in rails c
# ie: you don't get a NameError exception
More specifically,
lib/bootstrappers/bootstrapper.rb
# Bootstrapper::Bootstrapper => NameError
# Bootstrappers::Bootstrapper => works
If you really want the other way, you can move everything into your lib/bootstrapper.rb source file but meh, I don't like doing that, that's not how gems are organized. In rails3, you'll find the autoloading pretty nice once you use modules everywhere (which can be painful).
Rails3 uses /extras instead of /lib but it's not required, it's just the default (commented out) from rails new. To switch, you just autoload extras instead of lib.
I have a engine style Rails plugin from which I can create a gem using Jeweler. But when I require it in my Rails environment (or erb) the models within the plugin are not loaded. I have followed a number of tutorials and read just about everything on the subject.
# environment.rb
config.gem 'myengine'
# in irb
require 'myengine'
I have unpacked the gem and verified that all files are present. My init.rb has been moved to a new folder called 'rails' as per. All files in 'lib' are automatically added to the $LOAD_PATH, so require 'myengine' runs lib/myengine.rb. I verified this by putting a puts 'hello' within.
Is it because of the physical presence of plugins in a known place that Rails can add all the models, controller etc. to the relevant load_paths? Do I need to replicate this manually when using a gem?
Would gemspec require_paths be a way of adding additional paths other than lib? I assume however that Rails does not just require every single file, but loads them on demand hence the need for the filename and class name to match?
%w{ models controllers helpers }.each do |dir|
path = File.join(File.dirname(__FILE__), 'app', dir) + '/'
$LOAD_PATH << path
puts 'requiring'
Dir.new(path).entries.each do |file|
if file =~ /\.rb/
puts file
require file
end
end
end
By adding the above to lib/myengine.rb all the models/controllers are required. But like I said in my question this is unlikely to be a good way forward.
Offhand I'd say the part about adding those directories to the search path is right on. What you shouldn't need to do is require each file manually (as you allude to in your last sentence). What Rails does when you reference a non-existent constant is to search for a file with the same name (underscored of course) in the load path.
If for some reason you can not abide by the constraint (think about it long and hard) then you are going to need to dig deeper into Rails and see how the reloading mechanism works so you can tie into it properly in development mode.
The problem was the files (in app) where not being added to the gem because when using Jeweler it only automatically adds files to required_paths which are committed to git.