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.
CodePudding user response:
When you require
the aws-sdk-s3
library, which is part of the aws-sdk-s3
Gem, the library, in turn, require
s 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 require
s another library, which in turn require_relative
s another library, which in turn require
s 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 require
d 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
.