gemspec extensions - conditionally install gem - ruby

I'm forking the gem and I'd like to add some conditional dependencies to gemspec file, depending on ruby version. I found that it could by done using spec.extensions. So in gem directory I created a file: ext/mkrf_conf.rb:
require 'rubygems/dependency_installer.rb'
begin
Gem::Command.build_args = ARGV
rescue NoMethodError
end
inst = Gem::DependencyInstaller.new
begin
if RUBY_VERSION < "2.1.0"
inst.install "activerecord", ">2.0", "< 4.0"
else
inst.install "activerecord", ">4.0"
end
rescue
exit(1)
end
f = File.open(File.join(File.dirname(__FILE__), "Rakefile"), "w") # create dummy rakefile to indicate success
f.write("task :default\n")
f.close
Then I added the extension to gemspec file:
spec.extensions << 'ext/mkrf_conf.rb'
I created a test project, with Gemfile when I have:
gem 'my_gem', path: '/Users/xxx/Documents/my_projects/my_gem'
I'd like to test if this is work, but when I do bundle install, activerecord is not installing at all. As this extension is not exist. Can somebody can point me what I'm doing wrong. Thanks!

This might be not an exact answer on your question, but I wonder whether you see that Gemfile is a plain ruby file. That said, you might simply drop your logic in your Gemfile:
if RUBY_VERSION < "2.1.0"
gem "activerecord", ">2.0", "< 4.0"
else
gem "activerecord", ">4.0"
end
gem ...
Hope it solves your issue.

Related

How can I install a gem (via bundler using gemspec) before parsing the gemspec?

I have a gem that exists for the purpose of helping with versioning. It's useful to have this gem available when defining the version in the gemspec file.
The problem, however, is that running bundle install first causes the gemspec to be parsed, which results in an error because the required gem isn't installed yet.
I can get around it by running gem install <other_gem> before bundle install, but I'd much prefer bundler manage it, especially when taking into account that I'm using a custom gem server.
I've tried adding the gem to the Gemfile directly before the gemspec line, but no luck.
Gemfile:
source 'https://my.gemserver.com/gems'
gemspec
mygem.gemspec:
require 'external/dependency'
Gem::Specification.new do |spec|
spec.name = 'mygem'
spec.version = External::Dependency.version_helper
....
spec.add_development_dependency 'external-dependency'
end
EDIT:
Another workaround is to rescue the LoadError and specify a default version if the dependency isn't loaded. Also, not ideal
begin
require 'external/dependency'
rescue LoadError; end
Gem::Specification.new do |spec|
spec.name = 'mygem'
spec.version = defined?(External::Dependency) ? External::Dependency.version_helper : ''
....
spec.add_development_dependency 'external-dependency'
end
I think you're stuck with gem install. But I would solve this by adding that step to the Dockerfile I use for the project.
Maybe it's possible to do something like this using rbenv or rvm? Haven't used either of those since migrating to Docker, but rvm gemset is kind of a bootstrap...
I got around it by making the gemspec install the gem during a bundle update or install.
EXTERNAL_DEPENDENCY = Gem::Dependency.new('external-dependency', '~> 0.1')
if File.basename($0) == 'bundle' && ARGV.include?('update') || ARGV.include?('install')
require 'rubygems/dependency_installer'
Gem::DependencyInstaller.new.install(EXTERNAL_DEPENDENCY)
end
and then...
spec.add_development_dependency EXTERNAL_DEPENDENCY.name, EXTERNAL_DEPENDENCY.requirements_list

How to unzip a file with ruby

Okay I've found the following code for unzippping a file with Ruby.
def unzip_file (file, destination)
Zip::ZipFile.open(file_path) { |zip_file|
zip_file.each { |f|
f_path=File.join("destination_path", f.name)
FileUtils.mkdir_p(File.dirname(f_path))
zip_file.extract(f, f_path) unless File.exist?(f_path)
}
}
end
Above this I'm using the following to ensure the needed gems are installed.
begin
require 'rubygems'
rescue LoadError
'gem install rubygems'
end
begin
require 'zip/zip'
rescue LoadError
'gem install rubyzip'
end
So when I call unzip_file I get the following error:
in `unzip_file': uninitialized constant Zip (NameError)
What am I doing wrong?
Thanks!
Beware: the sample script will also unpack symlinks, and will unpack ../../../../etc/passwd without complaining. The rubyzip gem expects you to do your own pathname laundering.
Note that in rubyzip 1.1.4, Zip::Zipfile was renamed to Zip::File.
The problem with installing the gem that way is that you're shelling out to another process with:
`gem install rubyzip`
and after that finishes installing the gem, your current irb session still won't see it. You'd have to reload irb with exec "irb" and then calling require 'zip' again.
Note: those are backticks not single quotes.
Try this:
begin
require 'zip'
rescue LoadError
`gem install rubyzip`
exec "irb"
retry
end
For me require 'zip' works. I have rubyzip-1.1.2
Now you should be able to use Zip
Also, the gem command is rubygems. So you can't install rubygems with itself. It should already be installed, but if not try this: http://rubygems.org/pages/download

the irbrc config file doesn't work for ruby 2 + rails 4

The irbrc config file works on the rails 3.2 + ruby 1.9
Today I checked out a project, using rails 4 + ruby 2,
and I found it didn't load the .irbrc file (I put the file under my home directory)
When I ran into irb or rails console
What's the problem?
irbrc file
require 'irb/completion'
require 'hirb' ; Hirb.enable
ARGV.concat ["--readline", "--prompt-mode", "simple"]
IRB.conf[:SAVE_HISTORY] = 10000
IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.irb_history"
ActiveRecord::Base.logger.level = 1 # Avoid log in Rails console
ActiveRecord::Base.logger = Logger.new STDOUT #顯示 SQL statements
ActiveRecord::Base.connection.tables
def drop_tbl (tblname)
ActiveRecord::Migration.drop_table(eval(":"+tblname))
end
def show_tbls
tbls = ActiveRecord::Base.connection.tables
tbls.each { |tbl|
puts "#{tbl} #{tbl_name(tbl)}"
}
end
def tbl_name(name)
name.singularize.humanize.split().map{|x| x.capitalize}.join()
end
def cols (tblname)
cols = eval("#{tblname}.column_names")
ap(cols)
end
Gemfile in the project
source 'https://rubygems.org'
ruby '2.0.0'
gem 'bootstrap-sass'
gem 'coffee-rails'
gem 'rails'
gem 'haml-rails'
gem 'sass-rails'
gem 'uglifier'
gem 'jquery-rails'
group :development do
gem 'sqlite3'
gem 'pry'
gem 'pry-nav'
gem 'thin'
gem "better_errors"
gem "binding_of_caller"
end
group :production do
gem 'pg'
gem 'rails_12factor'
end
I believe the irb config file doesn’t get loaded in rails console if the file is erroneous. Run ruby ~/.irbrc to make sure that it evaluates without complaint. Additionally, you can verify that your console is even sourcing your irbrc by running $LOAD_PATH.index{|s| s.include?('irbrc')} within your console.
You're using Pry. Try renaming the file to .pryrc.

Ruby: What does 'require: false' in Gemfile mean?

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.

ruby: code to install gem if missing

is there some ruby code I can use to install a gem from a local file, if that gem is not installed?
i'm thinking it would look something like:
if !gem_installed("some gem name")
system "gem install -l local_copy.gem"
end
i don't know if anything exists that lets me check for gems like this or not...
Checking availability is covered in this previous StackOverflow Quesiton
begin
gem "somegem"
# with requirements
gem "somegem", ">=2.0"
rescue Gem::LoadError
# not installed
end
or
matches = Gem.source_index.find_name(gem.name, gem.version_requirements)
As for the install, it looks like rails uses the system for gem install also
puts %x(#{cmd})
This is my way of doing this
['json','date','mail'].each { |req|
begin
gem req
rescue Gem::LoadError
puts " -> install gem " + req
Gem.install(req)
gem req
end
require req
}

Resources