Installed gem much slower than source - ruby

Running an installed gem is much slower than running its local source counterpart.
Installed gem:
$ time wmctile switch_to Thunderbird
real 0m0.682s
user 0m0.491s
sys 0m0.091s
Local source:
$ time ./work/wmctile/bin/wmctile switch_to Thunderbird
real 0m0.197s
user 0m0.118s
sys 0m0.064s
Why? Could it be because of RVM, or is this a "feature" of Ruby gems in general? Is there a way to speed it up?
This is the generated bin file:
$ which wmctile
/home/some_user_name/.rvm/gems/ruby-2.1.2/bin/wmctile
$ cat $( which wmctile )
#!/usr/bin/env ruby_executable_hooks
#
# This file was generated by RubyGems.
#
# The application 'wmctile' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
version = ">= 0"
if ARGV.first
str = ARGV.first
str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then
version = $1
ARGV.shift
end
end
gem 'wmctile', version
load Gem.bin_path('wmctile', 'wmctile', version)

RVM puts the proper directories for your Ruby version and gemset in the path whenever the RVM Ruby is set. My PATH begins with this:
/Users/kbennett/.rvm/gems/ruby-2.3.0/bin
/Users/kbennett/.rvm/gems/ruby-2.3.0#global/bin
/Users/kbennett/.rvm/rubies/ruby-2.3.0/bin
/Users/kbennett/.rvm/bin
So, I think it's the OS and not Ruby itself that is responsible for the delay. You could test this by putting a simple shell script file in that gem bin directory, and calling it with and without its absolute location to see if you get the same difference.

Related

Force .rb file running under specific ruby versions

I write a ruby script in a .rb file. It uses latest Ruby features (version 2.7). Is there any way to force this .rb file can only be executed in a specific Ruby version range? For example, the first line of a .rb file could be:
#! ruby 2.7+
# This .rb file can only be run with Ruby version 2.7 or above
Use the gem semantic to handle parsing the current Ruby version:
require 'semantic'
# Require >= 2.7 < 3
exit unless Semantic::Version.new(RUBY_VERSION).satisfies?('~> 2.7')
# Require >= 2.7, including 3 and above
exit unless Semantic::Version.new(RUBY_VERSION).satisfies?('>= 2.7')
This requires you to use bundler and a Gemfile with your app.
Other comparators are listed in the source code for the gem:
if ['<', '>', '<=', '>='].include?(comparator)
satisfies_comparator? comparator, pad_version_string(other_version_string)
elsif comparator == '~>'
pessimistic_match? other_version_string
else
tilde_matches? other_version_string
end
This will allow you to fine-tune your version requirements.
Naively,
unless RUBY_VERSION[0, 3] == "2.7"
puts "You need 2.7")
exit
end

Why is force_encoding("BINARY") used here?

When we install Rails, we get this rails "executable":
#!/usr/bin/env ruby
#
# This file was generated by RubyGems.
#
# The application 'railties' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
version = ">= 0"
if ARGV.first
str = ARGV.first
str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then
version = $1
ARGV.shift
end
end
gem 'railties', version
load Gem.bin_path('railties', 'rails', version)
I'm wondering what the point of doing force_encoding("BINARY") is on that String. What possible values could it be that force_encoding is necessary? I would think that people would only specify versions using numbers and letters here.
This isn't a rails specific thing - it's a wrapper rubygems will generate for any ruby executable in a gem. The call to force_encoding was added in 6bf71914
The reason for the change is that the first argument might not be a version at all - we want to test if it is a version, but it could be anything and we don't want the regex check to blow up. For example some executables accept a list of file names as arguments, and those file names could be invalid in the default external encoding used by ruby.
There is a bit more discussion on the issue which prompted this change.

Add a Ruby command-line application to /usr/bin?

I have written a shell program in Ruby. Now I want to add it to my bin directory so that I can call the program by running $ my-rb-prog ....
First I tried to symlink my file into /usr/bin but then it said that it couldn't load the modules that I required.
On my second try, I tried to build a gem out of my project which worked fine, but I can still not access my shell program. After that I installed the gem. Here's what my gemspec looks like:
# -*- encoding: utf-8 -*-
$:.unshift(File.join(File.dirname(__FILE__), "/lib"))
require 'webcheck'
Gem::Specification.new do |s|
s.name = "webcheck"
s.version = WebCheck::VERSION
s.platform = Gem::Platform::RUBY
s.authors = ["Victor Jonsson"]
s.email = ["kontakt#victorjonsson.se"]
s.homepage = "http://victorjonsson.se"
s.summary = %q{Check your website man!}
s.description = %q{Just check it!}
s.required_ruby_version = '>= 1.9.3'
s.add_dependency "httparty", "~> 0.12.0"
s.post_install_message = "Just check it!"
s.files = `git ls-files`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
end
I thought that I would get access to my shell program, which is a Ruby file located in the bin directory inside my project, after that I had installed the gem but it clearly isn't that easy.
This is my first day of coding Ruby if you can't tell.
First, add a "she-bang" string as first line of your file. It will allow the shell to run the file. It should be:
#!/usr/bin/env ruby
Then give execution permissions to the file:
$ chmod +x your_file_name.rb
Now you can run your application:
./your_file_name.rb
Also you can add the path to the directory with this script to the PATH variable and run the application from anywhere you want.
# You may do this in ~/.bashrc file
PATH=$PATH:path/to/dir/with/script/
Don't forget to add #!/usr/bin/env ruby to the top of your Ruby script.
"Making a Ruby Script Executable" is a really good tutorial on making your executable available system wide, without the use of a gem.

best practices for transfering ruby gems collection from one machine to another

i want to copy all of my gems from one machine (foo) to another (bar). both machines are identical except that foo has a bunch of gems installed and bar does not (bar cannot get onto the internet).
i copied /usr/local/lib/ruby/gems from foo to bar, and now bar recognizes that the gems are installed.
root#bar # gem list
*** LOCAL GEMS ***
keybox (1.2.1)
rake (0.9.2.2)
... and so on
if i try to run them from their native location, they work as expected
root#bar # cd /usr/local/lib/ruby/gems/1.8/gems/keybox-1.2.1/bin
root#bar # ./keybox --version
keybox: version 1.2.1
i would like to have all gems under a common bin dir, like they are on foo
root#bar # find /usr/local/lib/ruby/gems/1.8/gems/*/bin -type f | xargs -I baz ln -s baz /usr/local/bin/
as expected, this creates symbolic links from each gem's location to /usr/local/bin/
for some gems (like rake) everything works as expected, for others (like keybox) it does not work. for some gems, there's a difference between the script in the gem dir and the one that gets installed under /usr/local/bin.
root#foo # diff /usr/local/lib/ruby/gems/1.8/gems/keybox-1.2.1/bin/keybox /usr/local/bin/keybox
1,4c1,7
< #!/usr/bin/env ruby
< ########################################################################
< #
< ########################################################################
---
> #!/usr/local/bin/ruby18
> #
> # This file was generated by RubyGems.
> #
> # The application 'keybox' is installed as part of a gem, and
> # this file is here to facilitate running it.
> #
6,17c9
< #----------------------------------------------------------------------
< # bootstrap
< #----------------------------------------------------------------------
< begin
< require 'keybox'
< require 'keybox/application/password_safe'
< rescue LoadError
< path = File.expand_path(File.join(File.dirname(__FILE__),"..","lib"))
< raise if $:.include? path
< $: << path
< retry
< end
---
> require 'rubygems'
19,21c11
< #----------------------------------------------------------------------
< # instantiate the program and pass it the commandline parameters
< #----------------------------------------------------------------------
---
> version = ">= 0"
23,24c13,16
< keybox = Keybox::Application::PasswordSafe.new(ARGV)
< keybox.run
---
> if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
> version = $1
> ARGV.shift
> end
25a18,19
> gem 'keybox', version
> load Gem.bin_path('keybox', 'keybox', version)
since i'm already copying gems from one machine to another i could just as easily copy the public scripts as well, but is there a more "gemish" way to do it? is there a gem command that will regenerate the public scripts as has apparently been done in the case of keybox?
If you installed the gems via ports, the following should work
Make a list of all gems, like pkg_info | grep rubygem | cut -d ' ' -f 1
Use 'pkg_create -Rnb' to create packages from locally installed ports (and necessary dependencies).
Copy the packages to the target machine
Install them there with pkg_add.
Try using the following command:
gem pristine --all
That will reinstall the gems from the cached source (which I believe you copied), and therefore should recreate script wrappers, recompile extensions, etc.
You can also check the documentation to adjust the command to suit your needs (there's a flag to just restore the executables, if that's all you want to do).

irb history not working

in ~/.irbrc i have these lines:
require 'irb/ext/save-history'
#History configuration
IRB.conf[:SAVE_HISTORY] = 100
IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.irb-save-history"
and yet when i run irb and hit the up arrow nothing happens. also the irb history file specified is not getting created and nothing is logged to it.
irb history works in Debian Linux out of the box. There's no etc/irbrc, nor do I have a ~/.irbrc. So, hmmmm.
This person put a bit more in his irbrc than you did. Do you suppose the ARGV.concat could be the missing piece?
require 'irb/completion'
require 'irb/ext/save-history'
ARGV.concat [ "--readline", "--prompt-mode", "simple" ]
IRB.conf[:SAVE_HISTORY] = 100
IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.irb-save-history"
I don't have an answer for you why the above doesn't work, but I did find a file, /etc/irbrc on my system (OS X - Snow Leopard, Ruby 1.8.7) that does provide a working, persistent history for me. So two pieces of advice: i) check your /etc/irbrc (or equivalent) to make sure that there isn't anything in there that might interfere with your settings, and ii) try out the settings below to see if you can get history working that way.
# Some default enhancements/settings for IRB, based on
# http://wiki.rubygarden.org/Ruby/page/show/Irb/TipsAndTricks
unless defined? ETC_IRBRC_LOADED
# Require RubyGems by default.
require 'rubygems'
# Activate auto-completion.
require 'irb/completion'
# Use the simple prompt if possible.
IRB.conf[:PROMPT_MODE] = :SIMPLE if IRB.conf[:PROMPT_MODE] == :DEFAULT
# Setup permanent history.
HISTFILE = "~/.irb_history"
MAXHISTSIZE = 100
begin
histfile = File::expand_path(HISTFILE)
if File::exists?(histfile)
lines = IO::readlines(histfile).collect { |line| line.chomp }
puts "Read #{lines.nitems} saved history commands from '#{histfile}'." if $VERBOSE
Readline::HISTORY.push(*lines)
else
puts "History file '#{histfile}' was empty or non-existant." if $VERBOSE
end
Kernel::at_exit do
lines = Readline::HISTORY.to_a.reverse.uniq.reverse
lines = lines[-MAXHISTSIZE, MAXHISTSIZE] if lines.nitems > MAXHISTSIZE
puts "Saving #{lines.length} history lines to '#{histfile}'." if $VERBOSE
File::open(histfile, File::WRONLY|File::CREAT|File::TRUNC) { |io| io.puts lines.join("\n") }
end
rescue => e
puts "Error when configuring permanent history: #{e}" if $VERBOSE
end
ETC_IRBRC_LOADED=true
end
This is a known bug with a patch available. Easiest solution is to overwrite save-history.rb:
/usr/lib/ruby/1.8/irb/ext/save-history.rb
with a fixed version:
http://pastie.org/513500
or to do it in one go:
wget -O /usr/lib/ruby/1.8/irb/ext/save-history.rb http://pastie.org/pastes/513500/download
Check to make sure you built ruby with libreadline as irb history seems to not work without it.
This may also happen if you have extra irb config file, e.g. ~/.irbrc. If this is the case, copy the content from liwp's answer to the extra config and it should work.
I had the same problem on ubuntu 20.04 and fixed it by running:
gem install irb

Resources