How to Add Conditional Rubygem Requirements to a Gem Specification

Conditional ruby gem dependencies within a gemspec

To access rails version, we can use something like below (based on this answer):

require 'rubygems'

rails_gem = Gem::Specification.select {|z| z.name == "rails"}.max_by {|a| a.version}
p rails_gem.version.version
#=> "4.2.5"

Conditional Dependency in Ruby Gemspec

Yes, I would suggest a simple text requirement in spec.requirements. I would also recommend some sort of load-chaining when the gem first loads:

# in init.rb and/or rails/init.rb:
unless Object.const_defined?(:JSON)
begin
require 'json_pure'
rescue LoadError
begin
require 'json-ruby'
rescue LoadError
require 'json'
end
end
end
unless Object.const_defined?(:JSON)
raise "Could not load gem MyGem; did you install one of json_pur, json-ruby, or the C-based json library?"
end

How to create a Rails gem with conditional dependency installation

I wasn't able to really figure out the dependency injection for now. So I tried to take what @engineersmky suggested and created an install for the gems. So for right now I have this as my install generator

require 'generators/base_generator'

module MyGem
module Blueprinter
class InstallGenerator < BaseGenerator
source_root File.expand_path("../../../templates", __FILE__)

# Add blueprinter gem to gemfile after my_gem declaration and bundles the newly declared gem
def install_blueprinter
remove_other_supported_gems('ActiveModelSerializers', 'FastJsonapi')
puts 'Installing Blueprinter...'
insert_into_file('Gemfile',
"\ngem 'blueprinter'",
after: "gem 'dry_serialization', source: 'https://gem.fury.io/my_private_gems/'")
run 'bundle install'
end

def helper_include
copy_api_controller
gsub_file(API_CONTROLLER_PATH, /^\t*(include MyGem::.*)\n/, '')
puts 'Adding include statement to ApiController'
insert_into_file(API_CONTROLLER_PATH,
"\n\tinclude MyGem::Blueprinter",
after: 'class ApiController < ActionController::API'
)
end

end
end
end

Gets called with rails g my_gem::blueprinter::install
This creates an ApiController that inherits from ActionController::Base unless one already exists, inserts gem 'blueprinter' into the gemfile after my_gem's declaration, removes references to other serializer gems, then bundles to install the gem.

I like to separate my controllers into api and application to separate the concerns if I decide on a monolith app :D

Then you can use the dryed up methods that I'm creating in each file for each respective gem :D

Should the Gemspec be packaged with a Ruby Gem?

There is no need to pack a Gemspec with a Ruby gem, in a survey of the gems installed in one of my gem homes I noticed that in fact most gems don't include the gemspec file:

 ~/.rvm/gems/ruby-2.3.7/gems $ find . -name \*.gemspec | wc -l
95
~/.rvm/gems/ruby-2.3.7@happy-backend/gems $ ls | wc -l
318

Why would you want to not pack it?

  • It saves a few bytes in your GEM

Why pack it?

  • It allows for easy unpacking and repackaging of a gem
  • Why not? the gem spec is uploaded to the gem server anyway, so there are no secrets there and if the 'user' really wants it its going to be on the $GEM_HOME/specifications folder


Related Topics



Leave a reply



Submit