Optional runtime dependency for Ruby gem with executables - ruby

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.

Related

No require 'date' statement on my ruby file but I can call DateTime.now

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.

'Bundler.require' requiring all gems?

Trying to figure out Ruby's Bundler library. My understanding is we can require only certain groups, but my setup seems to be loading gems specified in other groups as well.
Gemfile
source 'https://rubygems.org'
# Specify your gem's dependencies in apple.gemspec
gemspec
group :production do
gem 'mail'
gem 'bundler'
gem 'pry'
gem 'commander'
gem 'fastlane'
gem 'spaceship'
gem 'highline'
gem 'terminal-table'
gem 'clipboard'
gem 'date'
gem 'mysql2'
gem 'fileutils'
gem 'redis'
gem 'json'
gem 'logger'
gem 'jira-ruby', :require => 'jira-ruby'
end
group :jenkins do
gem 'terminal-table'
gem 'pry'
gem 'mail'
gem 'jira-ruby'
gem 'spaceship'
end
test.rb
require 'pry'
binding.pry
require 'rubygems'
require 'bundler/setup'
Bundler.require(:jenkins) # I want this step to require only gems listed under 'jenkins' group in `Gemfile`.
...
When I run the code, it seems Bundler.require(:jenkins) step seems to be requiring all gems specified in Gemfile,
I am still in the process of understanding Bundler, pardon me if the question I asked is too obvious. Does anyone know how to only load the gems from bundler groups? Thanks in advance!!
This is kind of confusing, but as far as I can tell from using it myself, it only requires the gems in the given group even though it lists all of them.
It's pretty easy to see this for yourself, just try using one from the production group
Bundler.require(:jenkins)
Date.new # Should error
Really it seems like you may have noticed this yourself, but you just didn't realize.. If it was including all of them you wouldn't have needed to require pry in test.rb :)

Rubymotion, adding interface builder: running a rake task from gem gives "Don't know how to build task 'ib'"

I have a Ruby Motion project, and I want to add Interface Builder to it.
I've added the gem:
gem 'ib'
But when I run bundle exec rake ib, I get Don't know how to build task 'ib'
Does any one know what I might be doing wrong? here is my gemfile:
source "https://rubygems.org"
gem "rake"
gem "ProMotion", '~> 2.0'
gem "ProMotion-push", git: 'git#github.com:BananaNeil/ProMotion-push.git', :branch => 'actionable-push-notifications'
gem "cocoapods"
gem "motion-cocoapods"
gem 'xcodeproj'
gem "bubble-wrap-http", git: 'git#github.com:BananaNeil/BubbleWrap-HTTP.git', branch: 'allow_invalid_ssl_certs'
gem "bubble-wrap"
gem "sugarcube" # monkeypatch all the things
gem "motion-yaml"
gem "motion-stump"
gem 'houston'
# Enter debugger with simple syntax
gem 'dbt' #-----> break
# Add pretty print
gem 'motion-pp'
# Handle address book for us
gem 'motion-addressbook'
gem 'ib'
Make sure to require 'ib' inside the Rakefile, either with Bundler or manually for each gem.
And if you use Bundler, you might need to remove the begin/catch guard, because it will silence all import related errors.

How can I avoid bundlers warning about multiple sources when I have all gems in my .gemspec?

In my own gem, I have a Gemfile that looks basically like this:
source 'https://my.gemserver.com'
source 'https://rubygems.org'
gemspec
My .gemspec has all dependencies listed as add_dependency and add_development_dependency.
As of Bundler 1.8, I get the warning:
Warning: this Gemfile contains multiple primary sources. Using `source` more than
once without a block is a security risk, and may result in installing unexpected gems.
To resolve this warning, use a block to indicate which gems should come from the
secondary source. To upgrade this warning to an error,
run `bundle config disable_multisource true`.
Is there a way to resolve this warning (without muting via bundle config)? I cannot find anything about a source option in the Rubygems specification.
No, you'll either need to mute the warning or add the source block to your Gemfile with the specific gems you want to come from your private server. There isn't a need to duplicate the ones that come from rubygems.org (or you could do it the other way around, if you depend on more private gems than public ones, and your private gems do not themselves depend on public ones).
The problem is that the gemspec format has no support for specifying the source for each gem, so without duplicating them into the Gemfile, there is no way to specify which gems come from each source.
Kind of sad, but one has to move it out to Gemfile :-(
Gemfile:
source 'https://my.gemserver.com' do
your_gem1
your_gem2
#...
end
source 'https://rubygems.org'
gemspec
but then, if some of your gems should be included in :development or :test group, following could be used
Gemfile:
your_gem1, :source => 'https://my.gemserver.com'
#...
group :development do
your_gem2, :source => 'https://my.gemserver.com'
#...
end
source 'https://rubygems.org'
gemspec
To elaborate on the discussion on the bundler issue, as previous answers have stated, you must include the gem in you Gemfile. However, you only need to specify the version of the gem in your .gemspec. If you change versions more often than private dependencies this isn't a terrible solution.
Reference the gem without version in Gemfile:
# Gemfile
source 'https://rubygems.org'
source 'https://xxx#gem.fury.io/me/' do
gem 'my-private-dependency'
end
gemspec
Reference the gem with version specification in the .gemspec:
# my-gem.gemspec
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
Gem::Specification.new do |spec|
spec.add_dependency 'my-private-dependency', '~> 0.1.5'
end

Can't make sqlite3 work on Ruby on Rails

I just installed ruby on my windows 7 computer. I installed rails and sqlite3 with the gem. I then made my app work on local BUT I still seem to have problems with sqlite3. When I try this:
rake db:create
the only thing i get is an error:
Please install the sqlite3 adapter: "gem install activerecord-sqlite3-adapter" (sqlite3
is not part of the bundle. Add it to the GemFile).
I've been doing some digging here and there, and I could make this error go away adding this line to my GemFile:
gem "sqlite3", group: :sqlite3
And i got a new error:
no driver for sqlite3 found
I tried the 'bundle' command and I have both sqlite3 and sqlite3-ruby, I reinstalled everything but the problem won't go away. This is my gemFile, I hope it helps:
source 'https://rubygems.org'
gem 'rails', '3.2.12'
# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'
#I tried this too, but nothig changes
#gem 'sqlite3-ruby', :require => 'sqlite3'
gem 'sqlite3-ruby', '1.2.5', :require => 'sqlite3'
gem "sqlite3", group: :sqlite3
# Gems used only for assets and not required
# in production environments by default.
group :assets do
gem 'sass-rails', '~> 3.2.3'
gem 'coffee-rails', '~> 3.2.1'
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem 'therubyracer', :platforms => :ruby
gem 'uglifier', '>= 1.0.3'
end
gem 'jquery-rails'
I really don't know what to do. It's kind of frustrating, it seems like something is not (obviously) properly working with sqlite3, because fixing one error leads to a new error. How can I possibly fix this problem ?
To everybody who's going to have this problem. What I did to fix it was uninstall everything. I though that maybe since I had an updated version of everything, something might not be working properly, maybe some dependencies were wrongly addressed. So I reinstalled everything following this:
Rails Installer Website
Which will make you install everything you need to run your first RoR app. It might not be up to date but it works just fine. Sqlite3 works perfectly now and that was what I needed. I might try to update every single program later, right now I just need something that is actually working. Thanks to everybody else who tried to help.

Resources