How I write a gemspec.
Tags: ruby, gems, gemspec, bundler
Published: 2020-05-10 07:43:40

**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.

```ruby
# 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.*

```ruby
# 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.

```ruby
require_relative 'lib/lumise/version'
```

Said file would look something like this:

```ruby
# 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.

```ruby
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](naming) ideally.

```ruby
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.

```ruby
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.

```ruby
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.

```ruby
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.

```ruby
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.

```ruby
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.

```ruby
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.

[naming]: https://guides.rubygems.org/name-your-gem/