is it possible to get a gemspec from a .gem archive? - ruby

This might be really naive as I have never had to do this before but is there a way to generate a .gemspec file from a .gem? Like the opposite of gem build xxx.

A .gem file is basically a .tar archive. Inside this .tar archive you will have 2 important files:
data.tar.gz - containing the source code and possibly a .gemspec file (this is not a guarantee though)
metadata.gz - basically a .gemspec, but in a YAML format which you won't be easily able to convert back

Related

What is the built gemspec file (with version) for?

When I run the command
$: gem build example_gem.gemspec
I get a file called example_gem-0.0.0.gem. What is in this file and what is it for?
Does this file play a part with how gems get included into Ruby code that requires them once the gem is already installed on the machine?
I noticed that when I go into my INSTALLATION_DIRECTORY, I can see the gems in folders with names matching matching the format of this versioned gem file, but inside the directory, I only see the other library code, gemspec file, etc.
Does this mean it is for uploading/downloading/installing only?
The .gem file is, well, the Gem. It contains the Gemspec (more precisely, a Marshal serialized version of the object graph of the Gemspec, rather than the executable .gemspec while which generates that object graph) as well as a compressed archive of all the files that are part of that gem (listed in the Gemspec). It also contains a cryptographically secure checksum and an optional cryptographic signature.
When you install a .gem, RubyGems checks the cryptographic checksum to make sure the Gem was not tampered with, checks the optional signature to make sure the Gem was created by who you think it was, reads the Gemspec to figure out what to do with the Gem, and unpacks it into the $GEM_HOME directory.
Kernel#require simply goes through the search path until it finds a file matching the name you provided and runs the file. It knows nothing about Gems. (It does know how to find the unpacked Gem directory, though.)
[Note: this is somewhat simplified. Kernel#require may have implementation-specific features, for example, YARV's Kernel#require also knows how to load dynamic libraries (e.g. .dlls, .sos, .dylibs, depending on the OS), JRuby's Kernel#require also knows how to load .jars, and so on.]

How to extract a .rar archive with Ruby?

I need to unpack an .rar archive with Ruby. I could not find a gem though.
I discovered the rar gem which only allows to create an archive. How can I extract a rar file, not just create it?
After doing some additional reading on the subject it seems that any gems that were for this are basically abandoned. But, you can brew install unrar and use that from Ruby system('unrar l your_file.rar').
ffi-libarchive provides a gem-based solution. It works for rar files even though they don't specifically mention it (see issue #151). Add this to your application's Gemfile:
gem 'ffi-libarchive'
and then execute:
$ bundle

Preserve Empty Directories in a Gem Build

I have a Gem I'm developing - My Gemspecs builds the files for the gem from git:
spec.files = `git ls-files -z`.split("\x0")
The Gem itself has an empty directory in the lib folder, which i've added a .keep to preserve via git
my_gem/
- lib/
- some_empty_folder/
- .keep
But when I install the gem to my system I see that some_empty_folder gets removed.
According to specification refrence: http://guides.rubygems.org/specification-reference/#files
files
Files included in this gem. You cannot append to this accessor, you
must assign to it.
Only add files you can require to this list, not directories, etc.
Directories are automatically stripped from this list when building a
gem, other non-files cause an error.
I was hoping the .keep file would preserve this empty structure, but I've hacked around it by adding an empty .rb file. What is the best way to work around this issue?
Instead of the .keep, you could instead add a .gitignore to the empty folder.
# Ignore all items in this directory
*
# Except this .gitignore file
!.gitignore
This will always preserve that folder as an empty structure.

Skipping some files when building new gem

I've never built my own gem before, so this is a totally new experience. I've built a library for my employer, which has to connect to a database of ours in order to run some of my tests. I want to make sure this config.yml file isn't included in the gem when I publish it. I've added it to the .gitignore file, as I know to do that, but is there some other change I have to make to the .gemspec maybe? Or does bundler only include files it knows about when releasing the library to the public? Just trying to be cautious, first time publishing open source code!
Specification after #Oleander replied:
Running git ls-files in the command line doesn't include the file I want to remove, and my spec.require_paths in the .gemspec file reads like this: ["lib"]. The spec directory isn't there. Does that mean the config.yml file won't be included when I release the gem?
Remove config.yml from s.files in your project.gemspec file inside your project and the file wont show up in your .gem-file.
s.files = `git ls-files`.split($/)
s.files -= ["spec/config.yml"]

How can I copy a directory inside a zip archive to a second zip archive using rubyzip?

I have a .zip archive containing several directories. Using the rubyzip gem I would like to reach into the .zip archive, copy a specified directory (and its contents) and move the directory into a second .zip archive.
Ideally I would not have to extract the contents of the first .zip archive, then re-zip them into a second archive. I'm hoping there is a way to use methods provided in the rubyzip gem.
After checking with one of the maintainers of the rubyzip gem, I have learned that this is not possible.
The RubyZip library must have been updated since then to support it. This worked for me.
require 'rubygems'
require 'zip' # gem 'rubyzip', '>= 1.0'
Zip::File.open('large.zip', false) do |input|
Zip::File.open('small.zip', true) do |output|
input.glob('my_folder_name/*') do |entry|
entry.get_input_stream do |input_entry_stream|
output.get_output_stream(entry.name) do |output_entry_stream|
# you could also chunk this, rather than reading it all at once.
output_entry_stream.write(input_entry_stream.read)
end
end
end
end
end
For versions of RubyZip < 1.0, require 'zip/zip' instead, and use Zip::ZipFile instead of Zip::File
This is a bit of a brute force method (and may or may not work for your application), but you could copy the entire first zip file and then use rubyzip methods to delete everything but the target directory from the copied file.
Theoretically what you are asking should be possible if you are using deflate compression (which stores every file as an individually compressed item).

Resources