Disclaimer: I made this article as a first attempt at writing; expect it to improve. Please don't doubt on commenting or asking for questions or improvements.
A gemspec
is basically a gem's manifesto, it is a file describing what your gem is, does, and contains.
This is how I write a .gemspec
file, and I'll explain you why.
# frozen_string_literal: true
require_relative 'lib/lumise/version'
Gem::Specification.new do |spec|
spec.name = 'lumise'
spec.version = Lumise::Version
spec.authors = ['vaporyhumo']
spec.email = ['roanvilina@gmail.com']
spec.summary = 'Some Summary'
spec.description = 'Some Description'
spec.license = 'Unlicense'
spec.homepage = 'https://github.com/vaporyhumo/lumise'
spec.metadata = {
'homepage_uri' => spec.homepage
'bug_tracker_uri' => spec.homepage + '/issues',
'changelog_uri' => spec.homepage + '/blob/master/CHANGELOG.md',
'documentation_uri' => 'https://www.rubydoc.info/gems/lumise',
'source_code_uri' => spec.homepage
}
spec.bindir = 'exe'
spec.executables = ['lumise']
spec.files = Dir['lib/**/*'] # Dir['lib/**/{*,.*}']
spec.required_ruby_version = '>= 2.4.0'
spec.add_dependency 'thor', '~> 1.0'
end
First off, we declare String
s to be immutable within the file.
Basically every modern ruby file starts with this one.
# frozen_string_literal: true
Then we require the file that contains the gem's version.
I prefer using require_relative
than adding lib
to the $LOAD_PATH
, which is something else you might find out there.
require_relative 'lib/lumise/version'
Said file would look something like this:
# frozen_string_literal: true
module Lumise
VERSION = '0.1.0'
end
This is done mainly because you want a single authoritative source of truth for your gem's version. Update once, reflect everywhere.
Then we create the actual specifications, every gemspec needs to create this object, and specify all configurations within the given block.
Gem::Specification.new do |spec|
...
end
In the first chunk, we specify the gem's name, and reference the gem's version.
The name should be compliant with RubyGems
recommendations ideally.
spec.name = 'lumise'
spec.version = Lumise::Version
The constant Lumise::Version
is available from the file required before.
Next, we add contact information, by providing a list of authors, and a list of emails.
spec.authors = ['vaporyhumo']
spec.email = ['roanvilina@gmail.com']
Then we describe our gem a little bit by providing: a short summary, a longer description, the license for using, modifying or sharing the gem, and a homepage, that should point to the gem's git repository if nothing else.
spec.summary = 'Some Summary'
spec.description = 'Some Description'
spec.license = 'Unlicense'
spec.homepage = 'https://github.com/vaporyhumo/lumise'
Later, we provide some metadata, that are actually useful links that will be displayed in the RubyGems
page of your gem.
spec.metadata = {
'homepage_uri' => spec.homepage
'bug_tracker_uri' => spec.homepage + '/issues',
'changelog_uri' => spec.homepage + '/blob/master/CHANGELOG.md',
'documentation_uri' => 'https://www.rubydoc.info/gems/lumise',
'source_code_uri' => spec.homepage
}
We can use the already defined spec.homepage
variable here!~
Following this, we will specify which files should be packed when building
the gem, and which executables should be installed with it.
spec.bindir = 'exe'
spec.executables = ['lumise']
spec.files = Dir['lib/**/*'] # Dir['lib/**/{*,.*}']
Specifying the bindir
is particularly important, since by default it will point to the bin
directory, which you might use for scripts meant to help us in the development cycle of the gem, but are not meant to be consumed by the final user. Usually exe
is used instead.
When using bundler to create your gem, the bin
directory will hold the
setup
and console
scripts, so remember to change it!
The executables
variable will hold an array with the filenames of the scripts that you do want the gem to provide; these live under the formerly specified bindir
directory.
You must manually specify all of the files that should be included in the
built gem; any kind of array with file names will do, I usually just use Dir
to include all of the lib
directory and nothing else. You can find the rest on the repo.
Some people use git ls-files
and then filter the results...
I find this unnecessary, when probably you just need files under lib
.
Going forward, we should specify what versions of ruby the gem will work with.
I like to keep this one (1) minor below the last supported version, so people upgrading can still use the gem, though I think I wouldn't mind dropping that support if by any chance I needed to.
spec.required_ruby_version = '>= 2.4.0'
Using a CI to check for this compatibility is a good idea, since checking
manually can be a hassle.
Lastly, we add our gem's dependencies. Always specifying the required versions.
spec.add_dependency 'thor', '~> 1.0'
Of course, you would skip this step if no dependencies are needed.
Some people like to also add development dependencies in the gemspec
; I don't.
I put all my development dependencies in the Gemfile
, mostly because they have a lot to do with my development cycle and little to nothing to do about the actual gem.
Top comments (2)
It's very clear <3
Very useful @vaporyhumo Thanks!