I've included the yajl gem in my Gemfile via gem 'yajl-ruby', '~> 1.1.0' however upon calling parser = Yajl::Parser.new in my store controller I receive the error uninitialized constant StoreController::Yajl
If I execute require 'yajl' at the beginning of the controller all is well but I was under the impression that the gem should be available to controllers within the application simply by including in the gem file.
You have to indicate to Bundler the main file of the gem with the require option:
gem 'yajl-ruby', '~> 1.1.0', require: 'yajl'
This is necessary for gems whose name is different that their main file name.
See the Gemfile manual.
Related
I am trying to understand how importing files in ruby works. But I almost spent a whole day
figuring out why I can call DateTime.now in my class where I didn't require 'date, these are all running on docker container instance where I get ruby 3.0.3
What got me stuck is when I tried to make a new ruby file on a new docker container instance ruby 3.0.3. And try calling DateTime.now it doesn't work.
I tried looking for require statement that has the word date in it in the project where I can call DateTime.now but no luck finding.
gem file of the project where I can call DateTime without require statement
gem 'aws-sdk-s3', '~> 1.93'
gem 'mime-types'
gem 'optparse', '~> 0.1.0'
group :development do
gem 'rubocop', '~> 1.12'
end
group :test do
gem 'rspec', '~> 3.10'
gem 'rubocop-rspec', '~> 2.2'
gem 'webmock', '~> 3.12'
end
gem file of the new project where I couldn't call DateTime without require statement
gem 'mime-types'
gem 'optparse', '~> 0.1.0'
gem 'gqli'
group :development do
gem 'rubocop', '~> 1.12'
end
group :test do
gem 'rspec', '~> 3.10'
gem 'rubocop-rspec', '~> 2.2'
gem 'webmock', '~> 3.12'
gem 'test-unit', '~> 3.0'
end
the only difference is the aws and gqli in both project but I tried having both of them have the same gem file it still doesn't work.
When you require the aws-sdk-s3 library, which is part of the aws-sdk-s3 Gem, the library, in turn, requires the aws-sdk-core library:
# frozen_string_literal: true
# WARNING ABOUT GENERATED CODE
#
# This file is generated. See the contributing guide for more information:
# https://github.com/aws/aws-sdk-ruby/blob/version-3/CONTRIBUTING.md
#
# WARNING ABOUT GENERATED CODE
require 'aws-sdk-kms'
require 'aws-sigv4'
require 'aws-sdk-core'
#↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
The aws-sdk-core library, which is part of the aws-sdk-core Gem, in turn, loads the param_converter library using Kernel#require_relative, as you can see here:
# client modules
require_relative 'aws-sdk-core/client_stubs'
require_relative 'aws-sdk-core/async_client_stubs'
require_relative 'aws-sdk-core/eager_loader'
require_relative 'aws-sdk-core/errors'
require_relative 'aws-sdk-core/pageable_response'
require_relative 'aws-sdk-core/pager'
require_relative 'aws-sdk-core/param_converter'
#↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
The param_converter library then, in turn, loads the date library:
# frozen_string_literal: true
require 'stringio'
require 'date'
#↑↑↑↑↑↑↑↑↑↑↑↑↑
The aws-sdk-core Gem does not depend on the date Gem:
> spec.add_dependency('jmespath', '~> 1', '>= 1.6.1') # necessary for secure jmespath JSON parsing
> spec.add_dependency('aws-partitions', '~> 1', '>= 1.651.0') # necessary for new endpoint resolution
> spec.add_dependency('aws-sigv4', '~> 1.5') # necessary for making Aws::STS, SSO, SSOOIDC API calls
> spec.add_dependency('aws-eventstream', '~> 1', '>= 1.0.2') # necessary for binary eventstream
But you don't have to depend on the date Gem in order to use the date library, because the date Gem is a default Gem, which means it is part of Ruby's standard library, gets maintained by the Ruby developers, doesn't need to be installed, doesn't need to be listed as a Gem dependency, and doesn't need to be activated.
So, that's why you don't have to require the date library yourself: because you require a library, which in turn requires another library, which in turn require_relatives another library, which in turn requires the date library.
But the more interesting question is, should you require the date library? And the answer is, Yes, you should. Relying on a require chain like this is extremely brittle: what if some library somewhere in the middle down this chain decides to change its internal structure and, e.g. validate dates using a different library or implement date validation themselves? Then your code breaks for no apparent reason.
The rule that I generally follow is that every script must stand on its own, i.e. every script must require all of its dependencies.
Now, in larger applications, this might get annoying, and might even lead to high startup latencies. A good example would be framework or a DSL, where it is simply expected that the framework or the DSL provides a set of libraries for you that are already pre-loaded. Imagine, you had to require every active_support library, active_model library, active_relation library, etc. you are using in a Ruby on Rails application. That would be stupid: if you write a Ruby on Rails model, you know that active_model is loaded, you don't need to load it again.
So, a slightly relaxed rule is: every script that is intended to be required or executed by a client must stand on its own. Scripts that are internal to the application may rely on internal knowledge about which libraries are already required.
I'm writing a gem aipp which exposes a few executables, nothing fancy here:
#!/usr/bin/env ruby
require 'aipp'
AIPP::NOTAM::Executable.new(File.basename($0)).run
Parts of the gem optionally use database adapters (pg or ruby-mysql gem). But since these can be a pain in the butt when the gem is used on the cloud, I'd like to really make them optional and not require them as runtime dependency in the .gemspec.
So I require them conditionally at runtime:
require 'pg' if ENV['AIPP_POSTGRESQL_URL']
require 'mysql' if ENV['AIPP_MYSQL_URL']
Unfortunately, that doesn't work as expected. When either of the environment variables is set and the executable is used, the require fails – probably because there's no dependency declared.
I've tried an inline Gemfile on the executable like the following. Works in development (repo checkout), but not when the gem is intalled via Rubygems:
#!/usr/bin/env ruby
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'aipp'
gem 'pg', '~> 1' if ENV['AIPP_POSTGRESQL_URL']
gem 'ruby-mysql', '~> 3' if ENV['AIPP_MYSQL_URL']
end
AIPP::NOTAM::Executable.new(File.basename($0)).run
What's the correct approach to require gems which are not listed as runtime dependency but installed additionally (via gem install or Gemfile)?
Maybe somebody knows of an existing gem which has already solved this problem somehow.
Thanks for your help!
I would use bundler groups for that.
gemfile do
source 'https://rubygems.org'
gem 'aipp'
group :database do
gem 'pg', '~> 1'
gem 'ruby-mysql'
end
end
There is more info about how to use them in the link.
I'm doing some messing around to try to understand better how bundler works. I have just three files in my working directory:
Gemfile Gemfile.lock test.rb
All the Gemfile has is gem "slop" and test.rb looks like this:
puts Slop.parse
When I run bundle exec test.rb I get a NameError due to not having a require statement:
[ec2-user#xx my_app]$ bundle exec ruby test.rb
test.rb:1:in `<main>': uninitialized constant Slop (NameError)
But if I run bundle console, Bundler loads the gem correctly and I can run Slop.parse from the console without having to explicitly type require "slop":
[ec2-user#xx my_app]$ bundle console
irb(main):001:0> Slop.parse
=> #<Slop::Result:0x00000001339838...
So what am I missing? I was under the impression that since I don't have require: false in my Gemfile, Slop should be loaded when I run bundle exec ruby test.rb and I shouldn't need to put the require "slop" line in the file.
You need to config bundler to require all gems on your Gemfile like this:
require 'rubygems'
require 'bundler/setup'
Bundler.require(:default)
Check the docs at http://bundler.io/v1.12/bundler_setup.html
I was under the impression that since I don't have require: false in
my Gemfile, Slop should be loaded when I run bundle exec ruby test.rb
and I shouldn't need to put the require "slop" line in the file.
The bundler docs say:
Specify your dependencies in a Gemfile in your project's root:
source 'https://rubygems.org'
gem 'nokogiri' #<======HERE
Inside your app, load up the bundled environment:
require 'rubygems'
require 'bundler/setup'
# require your gems as usual
require 'nokogiri' #<========AND HERE
As for this:
I was under the impression that since I don't have require: false in
my Gemfile, Slop should be loaded when I run bundle exec ruby test.rb
and I shouldn't need to put the require "slop" line in the file.
The bundler docs are horrible on this point. As far as I can tell, :require => false is a Rails specific thing, which is used to decrease load times on project startup. In a rails app, specifying require: false means that the gem won't be loaded until you manually require the gem. If you don't specify :require => false, then the gem will be loaded automatically--however that is because rails code is written to do that automatic loading. Your app has no code that performs a similar function.
Edit: Made a mistake while testing. So here's the way it works: In a non rails app, e.g. in your test.rb, if you want to automatically require all the gems specified in your Gemfile, you need to write:
Bundler.require :default
The Bundler docs mention that in the fine print here:
Specify :require => false to prevent bundler from requiring the gem,
but still install it and maintain dependencies.
gem 'rspec', :require => false
gem 'sqlite3'
In order to require gems in your Gemfile, you will need to call Bundler.require in your application.
I'm not sure why that requirement was only mentioned in conjunction with require: false instead of being stated at the outset.
And, in your Gemfile if you specify:
gem 'slop', :require => false
(as well as Bundler.require :default in test.rb), then you also have to explicitly require the slop gem in test.rb:
require 'slop'
In other words, Bundler.require :default automatically requires all the gems in your Gemfile except the gems marked with require: false. For gems marked with require: false, you have to manually write require 'gem_name' in your app.
Therefore, neydroid posted the correct solution.
* In your Gemfile, you can nest gems in groups, which affects how Bundler.require() works. See the Bundler docs on groups.
You should add the require "slop" inside your test.rb
I am using :path => '/path/to/gem' functionality of bundler to build and use a modified upstream gem, which uses Rake::FileList in its .gemspec.
At this stage, the bundle I'm installing is not yet activated, or maybe the order things are installed forbids bundler from using rake.
I am using ruby 1.8.7.
My Gemfile:
source 'http://rubygems.org'
gem "rake"
gem "foreign_gem", :path => '/home/user/src/foreign_gem'
The error I get:
$ bundle install
Unfortunately, a fatal error has occurred. Please see the Bundler
troubleshooting documentation at http://bit.ly/bundler-issues. Thanks!
/home/ilya/src/foreign_gem/foreign_gem.gemspec:11: uninitialized constant FileList (NameError)
from /home/user/.rbenv/versions/1.8.7-p358/lib/ruby/site_ruby/1.8/rubygems/specification.rb:426:in 'initialize'
from /home/user/src/foreign_gem/foreign_gem.gemspec:1:in 'new'
from /home/user/src/foreign_gem/foreign_gem.gemspec:1
You should be able to add require 'rake' at the top of your foreign_gem.gemspec file in order to use FileList.
I don't know if this is a best practice, but it should work.
Does this:
gem 'whenever', require: false
mean that the gem needs to be installed, or does it mean it is not required?
This means install the gem, but do not call require when you start Bundler. So you will need to manually call
require "whenever"
if you want to use the library.
If you were to do
gem "whenever", require: "whereever"
then bundler would download the gem named whenever, but would call
require "whereever"
This is often used if the name of library to require is different than the name of the gem.
You use :require => false when you want the gem to be installed but not "required".
So in the example you gave:
gem 'whenever', :require => false
when someone runs bundle install the whenever gem would be installed as with gem install whenever. Whenever is used to create cron jobs by running a rake task but isn't usually used from within the rails (or other framework if not rails) application.
So you can use :require => false for anything that you need to run from the command line but don't need within your code.
require: false tells Bundler.require not to require that specific gem: the gem must be required explicitly via require 'gem'.
This option does not affect:
bundle install: the gem will get installed regardless
the require search path setup by bundler.
Bundler adds things to the path when you do either of:
Bundle.setup
which is called by require bundler/setup
which is called by bundle exec
Example
Gemfile
source 'https://rubygems.org'
gem 'haml'
gem 'faker', require: false
main.rb
# Fail because we haven't done Bundler.require yet.
# bundle exec does not automatically require anything for us,
# it only puts them in the require path.
begin Haml; rescue NameError; else raise; end
begin Faker; rescue NameError; else raise; end
# The Bundler object is automatically required on `bundle exec`.
Bundler.require
Haml
# Not required because of the require: false on the Gemfile.
# THIS is what `require: false` does.
begin Faker; rescue NameError; else raise; end
# Faker is in the path because Bundle.setup is done automatically
# when we use `bundle exec`. This is not affected by `require: false`.
require 'faker'
Faker
Then the following won't raise exceptions:
bundle install --path=.bundle
bundle exec ruby main.rb
On GitHub for you to play with it.
Rails usage
As explained in the initialization tutorial, the default Rails template runs on startup:
config/boot.rb
config/application.rb
config/boot.rb contains:
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
which does the require 'bundler/setup' and sets up the require path.
config/application.rb does:
Bundler.require(:default, Rails.env)
which actually requires the gems.
Whenever you specify a Gem in your Gemfile and run bundle install, bundler will go and install specified gem and load code for that Gem in you app by putting require 'whenever' this way bundler will load code for all of your Gems in your Rails app, and you can call any method from any Gem without any pain, like you do most of the time.
but Gems like whenever, faker or capistrano are something which you do not need in your app code you need whenever code in your schedule.rb file to manage crons and capistrano code in deploy.rb file to customize deployment recipe so you need not to load code for these gems in your app code
and wherever you want to call any method from these Gems you can manually require thsese gems by yourself by putting require "whenever" . so you put :require => false in your Gemfile for these Gems, this way bundler will install that Gem but not load code for that Gem itself, you can do it whenever you want by simply putting like require 'whenever' in your case.
Analogy to Explain
## Gemfile
gem "university_degree", require: false
gem "dealing_with_boss"
"dealing_with_boss" - loaded into memory and ready to go.
degree gem - not "needed"....you need to manually require it, in order to use it.
In order to require gems in your Gemfile, you will need to call Bundler.require.
You can prevent bundler from requiring the gem with require: false, but it will still install and maintain the gem. Check this out for a more detailed explanation.