List of installed gems? - ruby

Is there a Ruby method I can call to get the list of installed gems?
I want to parse the output of gem list.
Is there a different way to do this?

This lists all the gems I have installed.
gem query --local
http://guides.rubygems.org/command-reference/#gem-list
Listing all installed gems

The Gem command is included with Ruby 1.9+ now, and is a standard addition to Ruby pre-1.9.
require 'rubygems'
name = /^/i
dep = Gem::Dependency.new(name, Gem::Requirement.default)
specs = Gem.source_index.search(dep)
puts specs[0..5].map{ |s| "#{s.name} #{s.version}" }
# >> Platform 0.4.0
# >> abstract 1.0.0
# >> actionmailer 3.0.5
# >> actionpack 3.0.5
# >> activemodel 3.0.5
# >> activerecord 3.0.5
Here's an updated way to get a list:
require 'rubygems'
def local_gems
Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.group_by{ |g| g.name }
end
Because local_gems relies on group_by, it returns a hash of the gems, where the key is the gem's name, and the value is an array of the gem specifications. The value is an array of the instances of that gem that is installed, sorted by the version number.
That makes it possible to do things like:
my_local_gems = local_gems()
my_local_gems['actionmailer']
# => [Gem::Specification.new do |s|
# s.authors = ["David Heinemeier Hansson"]
# s.date = Time.utc(2013, 12, 3)
# s.dependencies = [Gem::Dependency.new("actionpack",
# Gem::Requirement.new(["= 4.0.2"]),
# :runtime),
# Gem::Dependency.new("mail",
# Gem::Requirement.new(["~> 2.5.4"]),
# :runtime)]
# s.description = "Email on Rails. Compose, deliver, receive, and test emails using the familiar controller/view pattern. First-class support for multipart email and attachments."
# s.email = "david#loudthinking.com"
# s.homepage = "http://www.rubyonrails.org"
# s.licenses = ["MIT"]
# s.name = "actionmailer"
# s.require_paths = ["lib"]
# s.required_ruby_version = Gem::Requirement.new([">= 1.9.3"])
# s.requirements = ["none"]
# s.rubygems_version = "2.0.14"
# s.specification_version = 4
# s.summary = "Email composition, delivery, and receiving framework (part of Rails)."
# s.version = Gem::Version.new("4.0.2")
# end]
And:
puts my_local_gems.map{ |name, specs|
[
name,
specs.map{ |spec| spec.version.to_s }.join(',')
].join(' ')
}
# >> actionmailer 4.0.2
...
# >> arel 4.0.1,5.0.0
...
# >> ZenTest 4.9.5
# >> zucker 13.1
The last example is similar to the gem query --local command-line, only you have access to all the information for a particular gem's specification.

Both
gem query --local
and
ruby -S gem list --local
list 69 entries
While
ruby -e 'puts Gem::Specification.all_names'
return 82
I used wc -l to get the numbers. Not sure if that is the right way to check. Tried to redirect the output to text files and diffed but that didn't help - will need to compare manually one by one.

There's been a method for this for ages:
ruby -e 'puts Gem::Specification.all_names'

Gem::Specification.map {|a| a.name}
However, if your app uses Bundler it will return only list of dependent local gems. To get all installed:
def all_installed_gems
Gem::Specification.all = nil
all = Gem::Specification.map{|a| a.name}
Gem::Specification.reset
all
end

use this code (in console mode):
Gem::Specification.all_names

Here's a really nice one-liner to print all the Gems along with their version, homepage, and description:
Gem::Specification.sort{|a,b| a.name <=> b.name}.map {|a| puts "#{a.name} (#{a.version})"; puts "-" * 50; puts a.homepage; puts a.description; puts "\n\n"};nil

A more modern version would be to use something akin to the following...
require 'rubygems'
puts Gem::Specification.all().map{|g| [g.name, g.version.to_s].join('-') }
NOTE: very similar the first part of an answer by Evgeny... but due to page formatting, it's easy to miss.

Try it in the terminal:
ruby -S gem list --local

Maybe you can get the files (gems) from the gems directory?
gemsdir = "gems directory"
gems = Dir.new(gemsdir).entries

From within your debugger type $LOAD_PATH to get a list of your gems. If you don't have a debugger, install pry:
gem install pry
pry
Pry(main)> $LOAD_PATH
This will output an array of your installed gems.

Related

Sample ''Hola'" Rubygem push error on windows

I am following the very basic tutorial found here: http://guides.rubygems.org/make-your-own-gem/
hola_username.rb:
class Hola
def self.hi
puts "Hello world!"
end
end
hola_username.gemspec:
Gem::Specification.new do |s|
s.name = 'hola_username'
s.version = '0.0.0'
s.date = '2010-04-28'
s.summary = "Hola!"
s.description = "A simple hello world gem"
s.authors = ["Surname Lastname"]
s.email = 'me.me#gmail.com'
s.files = ["lib/hola_username.rb"]
s.homepage =
'http://rubygems.org/gems/hola_username'
s.license = 'MIT'
end
That really is all there is to the project.
I can build my gem with
gem build .\hola_username.gemspec
I have also tested it by importing and executing the hi function of the Hola class and it works:
PS E:\hola_username> gem install .\hola_username-0.0.0.gem
Successfully installed hola_username-0.0.0
Parsing documentation for hola_username-0.0.0
Done installing documentation for hola_username after 0 seconds
1 gem installed
&
irb(main):001:0> require 'hola_username'
=> true
irb(main):002:0> Hola.hi
Hello world!
=> nil
irb(main):003:0>
But when I try to
gem push .\hola_username-0.0.0.gem
I get:
ERROR: While executing gem ... (Psych::SyntaxError)
(): control characters are not allowed at line 1 column 1
Any ideas?
Edit: I am on a windows 10 machine using ruby 2.0.0p598
Edit v01: Anything I put after gem push will result in the above error, doesn't seem to be a problem with the sample rubygem.
Edit v02: My credentials file that was generated in the .gem folder however stars with hex characters: fffe2d002d00.. Which might be the ones causing trouble?
My credentials file in .gem folder was encoded with UCS2 - Little Endian and converting it to UTF without BOM did the trick.
Although I have absolutey no idea why..

Uninstall all gems which are not in a specified list of Gemfile.lock files

I'd like to clean a little system-house. Essentially,
(Gem.all_system_gems - Bundler.referenced_gems(array_of_gemfile_locks)).each do |gem, ver|
`gem uninstall #{gem} -v #{ver}
end
Any such RubyGems/Bundler methods? Or any known/efficient way of accomplishing the same?
Thanks,
Ben
Bundler has a clean command to remove unused gems.
bundle clean --force
This will remove all gems that are not needed by the current project.
If you want to keep your system's gem repository clean you should consider using the --path option with bundle install. This will allow you to keep project dependencies outside of the system's gem repository.
Caution: Severe brain-damage possible.
I put up a version here explaining each function.
# gem_cleaner.rb
require 'bundler'
`touch Gemfile` unless File.exists?("Gemfile")
dot_lockfiles = [ "/path/to/gemfile1.lock", "/path/to/gemfile2.lock"
# ..and so on...
]
lockfile_parser = ->(path) do
Bundler::LockfileParser.new(File.read(path))
end
lockfile_specs = ->(lockfile) { lockfile.specs.map(&:to_s) }
de_parenthesize = ->(string) { string.gsub(/\,|\(|\)/, "") }
uninstaller = ->(string) do
`gem uninstall #{string.split(" ").map(&de_parenthesize).join(" -v ")}`
end
splitter = ->(string) do
temp = string.split(" ").map(&de_parenthesize)
gem_name = temp.shift
temp.map {|x| "#{gem_name} (#{x})"}
end
# Remove #lazy and #to_a if on Ruby version < 2.0
gems_to_be_kept = dot_lockfiles.lazy.map(&lockfile_parser).map(&lockfile_specs).to_a.uniq
all_installed_gems = `gem list`.split("\n").map(&splitter).flatten
gems_to_be_uninstalled = all_installed_gems - gems_to_be_kept
gems_to_be_uninstalled.map(&uninstaller)
Why did I write this snippet this way? I happened to see this the other day: http://www.confreaks.com/videos/2382-rmw2013-functional-principles-for-oo-development
If you're on *nix or Mac OS, you can put the names of the gems you want to remove in a text file. Then run this command:
xargs gem uninstall < path/to/text/file
xargs is a great tool for processing long lists of files. In this case, it takes the contents of the text file, when its piped in via STDIN, and puts each line read into the command-line of gem uninstall. It will continue to do that until the text file is exhausted.
This code is based off of what #Kashyap answered (Bundler::LockfileParser is a good find). I ended up changing it a bit and wanted to share what I ended up using.
require 'rubygems'
require 'bundler'
LOCK_FILES = %w(/path/to/first/Gemfile.lock /path/to/second/Gemfile.lock)
REVIEWABLE_SHELL_SCRIPT = 'gem_cleaner.csh'
class GemCleaner
def initialize lock_files, output_file
#lock_files = lock_files
#output_file = output_file
end
def lock_file_gems
#lock_files.map do |lock_file|
Bundler::LockfileParser.new(File.read(lock_file)).specs.
map {|s| [s.name, s.version.version] }
end.flatten(1).uniq
end
def installed_gems
Gem::Specification.find_all.map {|s| [s.name, s.version.version] }
end
def gems_to_uninstall
installed_gems - lock_file_gems
end
def create_shell_script
File.open(#output_file, 'w', 0744) do |f|
f.puts "#!/bin/csh"
gems_to_uninstall.sort.each {|g| f.puts "gem uninstall #{g[0]} -v #{g[1]}" }
end
end
end
gc = GemCleaner.new(LOCK_FILES, REVIEWABLE_SHELL_SCRIPT)
gc.create_shell_script
Primary differences are use of Gem::Specification.find_all and output to a shell script so I could review the gems before uninstalling. Oh, and still doing it the old-fashioned OO-way. :)
Leaving selected answer with #Kashyap. Props.

Creating a thread-safe temporary file name

When using Tempfile Ruby is creating a file with a thread-safe and inter-process-safe name. I only need a file name in that way.
I was wondering if there is a more straight forward approach way than:
t = Tempfile.new(['fleischwurst', '.png'])
temp_path = t.path
t.close
t.unlink
Dir::Tmpname.create
You could use Dir::Tmpname.create. It figures out what temporary directory to use (unless you pass it a directory). It's a little ugly to use given that it expects a block:
require 'tmpdir'
# => true
Dir::Tmpname.create(['prefix-', '.ext']) {}
# => "/tmp/prefix-20190827-1-87n9iu.ext"
Dir::Tmpname.create(['prefix-', '.ext'], '/my/custom/directory') {}
# => "/my/custom/directory/prefix-20190827-1-11x2u0h.ext"
The block is there for code to test if the file exists and raise an Errno::EEXIST so that a new name can be generated with incrementing value appended on the end.
The Rails Solution
The solution implemented by Ruby on Rails is short and similar to the solution originally implemented in Ruby:
require 'tmpdir'
# => true
File.join(Dir.tmpdir, "YOUR_PREFIX-#{Time.now.strftime("%Y%m%d")}-#{$$}-#{rand(0x100000000).to_s(36)}-YOUR_SUFFIX")
=> "/tmp/YOUR_PREFIX-20190827-1-wyouwg-YOUR_SUFFIX"
File.join(Dir.tmpdir, "YOUR_PREFIX-#{Time.now.strftime("%Y%m%d")}-#{$$}-#{rand(0x100000000).to_s(36)}-YOUR_SUFFIX")
=> "/tmp/YOUR_PREFIX-20190827-1-140far-YOUR_SUFFIX"
Dir::Tmpname.make_tmpname (Ruby 2.5.0 and earlier)
Dir::Tmpname.make_tmpname was removed in Ruby 2.5.0. Prior to Ruby 2.4.4 it could accept a directory path as a prefix, but as of Ruby 2.4.4, directory separators are removed.
Digging in tempfile.rb you'll notice that Tempfile includes Dir::Tmpname. Inside you'll find make_tmpname which does what you ask for.
require 'tmpdir'
# => true
File.join(Dir.tmpdir, Dir::Tmpname.make_tmpname("prefix-", nil))
# => "/tmp/prefix-20190827-1-dfhvld"
File.join(Dir.tmpdir, Dir::Tmpname.make_tmpname(["prefix-", ".ext"], nil))
# => "/tmp/prefix-20190827-1-19zjck1.ext"
File.join(Dir.tmpdir, Dir::Tmpname.make_tmpname(["prefix-", ".ext"], "suffix"))
# => "/tmp/prefix-20190827-1-f5ipo7-suffix.ext"
Since Dir::Tmpname.make_tmpname was removed in Ruby 2.5.0, this one falls back to using SecureRandom:
require "tmpdir"
def generate_temp_filename(ext=".png")
filename = begin
Dir::Tmpname.make_tmpname(["x", ext], nil)
rescue NoMethodError
require "securerandom"
"#{SecureRandom.urlsafe_base64}#{ext}"
end
File.join(Dir.tmpdir, filename)
end
Since you only need the filename, what about using the SecureRandom for that:
require 'securerandom'
filename = "#{SecureRandom.hex(6)}.png" #=> "0f04dd94addf.png"
You can also use SecureRandom.alphanumeric
I found the Dir:Tmpname solution did not work for me. When evaluating this:
Dir::Tmpname.make_tmpname "/tmp/blob", nil
Under MRI Ruby 1.9.3p194 I get:
uninitialized constant Dir::Tmpname (NameError)
Under JRuby 1.7.5 (1.9.3p393) I get:
NameError: uninitialized constant Dir::Tmpname
You might try something like this:
def temp_name(file_name='', ext='', dir=nil)
id = Thread.current.hash * Time.now.to_i % 2**32
name = "%s%d.%s" % [file_name, id, ext]
dir ? File.join(dir, name) : name
end

Exclude files from FileList on Ruby

i have a rakefile with the following SRC = FileList['md/*.md'] but i want to exclude some files
I have tried
SRC = FileList['md/*.md'].exclude("md/header.md")
SRC = FileList['md/*.md'].exclude(/header/)
SRC = FileList['md/*.md'].exclude(/header.md$/)
But it doesn't work, always list me all files
a example:
in place of my rakefile.rb, I have a directory md with the following contents:
rakefile.rb
md/
index.md
example.md
header.md
I want to list all without header.md
This works fine for me:
p FileList['md/*.md']
#=> ["md/example.md", "md/header.md", "md/index.md"]
p FileList['md/*.md'].exclude("md/header.md")
#=> ["md/example.md", "md/index.md"]
p FileList['md/*.md'].exclude(/header/)
#=> ["md/example.md", "md/index.md"]
p FileList['md/*.md'].exclude(/header.md$/)
#=> ["md/example.md", "md/index.md"]
Not sure what's going on. Perhaps it's the version of Rake and/or ruby that you're using?
I've tested your snippets with ruby-1.8.7 (rake 0.8.7 and rake 0.9.2.2) and with ruby-1.9.2 (rake 0.8.7 and rake 0.9.2.2). All four configurations give the expected output.
Code:
puts "no exculde:"
puts FileList['md/*.md']
puts '---'
puts "exclude('md/header.md'):"
puts FileList['md/*.md'].exclude('md/header.md')
puts '----'
puts "exclude(/header/):"
puts FileList['md/*.md'].exclude(/header/)
puts '----'
puts "exclude(/header.md$/):"
puts FileList['md/*.md'].exclude(/header.md$/)
Output:
no exculde:
md/example.md
md/header.md
md/index.md
---
exclude('md/header.md'):
md/example.md
md/index.md
----
exclude(/header/):
md/example.md
md/index.md
----
exclude(/header.md$/):
md/example.md
md/index.md

Is there a pluralize function in Ruby NOT Rails?

I am writing some Ruby code, not Rails, and I need to handle something like this:
found 1 match
found 2 matches
I have Rails installed so maybe I might be able to add a require clause at the top of the script, but does anyone know of a RUBY method that pluralizes strings? Is there a class I can require that can deal with this if the script isn't Rails but I have Rails installed?
Edit: All of these answers were close but I checked off the one that got it working for me.
Try this method as a helper when writing Ruby, not Rails, code:
def pluralize(number, text)
return text.pluralize if number != 1
text
end
Actually all you need to do is
require 'active_support/inflector'
and that will extend the String type.
you can then do
"MyString".pluralize
which will return
"MyStrings"
for 2.3.5 try:
require 'rubygems'
require 'active_support/inflector'
should get it, if not try
sudo gem install activesupport
and then the requires.
Inflector is overkill for most situations.
def x(n, singular, plural=nil)
if n == 1
"1 #{singular}"
elsif plural
"#{n} #{plural}"
else
"#{n} #{singular}s"
end
end
Put this in common.rb, or wherever you like your general utility functions and...
require "common"
puts x(0, 'result') # 0 results
puts x(1, 'result') # 1 result
puts x(2, 'result') # 2 results
puts x(0, 'match', 'matches') # 0 matches
puts x(1, 'match', 'matches') # 1 match
puts x(2, 'match', 'matches') # 2 matches
I personally like the linguistics gem that is definitely not rails related.
# from it's frontpage
require 'linguistics'
Linguistics.use :en
"box".en.plural #=> "boxes"
"mouse".en.plural #=> "mice"
# etc
This works for me (using ruby 2.1.1 and actionpack 3.2.17):
~$ irb
>> require 'action_view'
=> true
>> include ActionView::Helpers::TextHelper
=> Object
>> pluralize(1, 'cat')
=> "1 cat"
>> pluralize(2, 'cat')
=> "2 cats"
require 'active_support'
require 'active_support/inflector'
inf = ActiveSupport::Inflector::Inflections.new
to get the inflector, not sure how you use it
my solution:
# Custom pluralize - will return text without the number as the default pluralize.
def cpluralize(number, text)
return text.pluralize if number != 1
return text.singularize if number == 1
end
So you can have 'review' returned if you call cpluralize(1, 'reviews')
Hope that helps.
I've defined a helper function for that, I use it for every user editable model's index view :
def ovyka_counter(array, name=nil, plural=nil)
name ||= array.first.class.human_name.downcase
pluralize(array.count, name, plural)
end
then you can call it from the view :
<% ovyka_counter #posts %>
for internationalization (i18n), you may then add this to your locale YAML files :
activerecord:
models:
post: "Conversation"

Resources