Pat Allan. Cut and Polish: Crafting Gems
-
Upload
svitla-systems-inc -
Category
Technology
-
view
2.586 -
download
2
Transcript of Pat Allan. Cut and Polish: Crafting Gems
Cut and PolishA Guide to
Crafting GemsGood afternoon everyone - I hope you’re all ready to learn about how to write gems!
Pat Allan@pat
freelancing-gods.comMy name’s Pat, yes, I’m from Australia as well - hopefully you can understand me through my accent. Here’s my twitter handle and blog, if you’re interested.
“Use rubygems to publish your gem to
rubygems”
Now really, it’s pretty simple... you just use rubygems to publish your gem to rubygems.Oh wait, that’s almost recursive... let me try again.
“Use rubygems to publish your gem to
rubygems”
Now really, it’s pretty simple... you just use rubygems to publish your gem to rubygems.Oh wait, that’s almost recursive... let me try again.
“Use gem to publish your gem to rubygems”
You use gem to publish your gem to rubygems.Wait, no, that isn’t quite clear either...
“Use gem to publish your gem to rubygems”
You use gem to publish your gem to rubygems.Wait, no, that isn’t quite clear either...
“Use the gem command to publish your gem to
rubygems.org”
You use the gem command to publish your gem to rubygems.org.Okay, that’s a bit better, but it’s still not ideal... this isn’t looking quite so simple any more, is it. Let’s try to clarify things.
noun
a precious or semiprecious stone, esp. when cut and polished or engraved.
|jem|gem
First, what’s a gem? Well, this is the definition anyone who isn’t a developer would expect...
noun
a packaged code library written in the Ruby programming language.
|jem|gem
But we’re talking about bytes and electrons - so for us, a gem is library of Ruby code.
noun
a command line executable distributed with the RubyGems package manager.
|jem|gem
Of course, it’s *also* a command line tool installed as part of rubygems. And what’s rubygems?
noun
a package manager for Ruby libraries.
|ˈro͞obējemz|rubygems
Well, it’s essentially a package manager for gems.
noun
a website that stores published versions of Ruby gems.
|ˈro͞obējemz|rubygems
But it’s also the site that hosts all published versions of gems.If you get confused between these definitions during this talk, please let me know. I’ll try to be as clear as possible.
Why?But first - why should we even care about creating gems?
Why?
re-use codeWell, they let us re-use our own code easily between projects - and you can let others use them as well.
Why?
easy to installThey’re also pretty easy to install - at least, in most cases.
Why?
easy to shareAnd thanks to rubygems, they’re really easy to share! And shared code is happy code.
HistoryWhen did this all begin? Let’s run through a quick history lesson.
History
2003rubyforge.org
rubyforge began in 2003 - before there was the concept of gems.
History
2004rubygems
But that soon followed, the following year, with the release of the rubygems package manager for distributing gems.
“If you have libraries you would like to include, please send the gem !les to either myself or Rich Kilmer”
Chad Fowler, 15 March 2004
Back then, though, the way to get a gem published was to email Chad or Rich - not ideal.
History
gems were magical
And there was still some mystery about constructing gems.
History
permission was required
Even when they moved beyond email to something a little more automated, you still needed permission to publish a new gem - though they weren’t too fussy about allowing people to do this.
History
2009gemcutter.org
But two years ago, gemcutter hit the scene - initially as an alternative gem source.
History
anyone can publish
It made publishing gems easy - a simple shell command - and anyone was allowed to publish gems.
History
rubygems.orgAnd so it was adopted as the default gem source, and migrated to rubygems.org. And Rubyists rejoiced!
GemspecsThat’s all well and good, but I’m supposed to be telling you about how to write gems - and every gem revolves around a gemspec.
Gemspecs
kyiv.gemspecEvery gem has a gemspec - with the gem’s name. This file holds all the settings for your gem, and has a list of every file and dependency.
# kyiv.gemspecGem::Specification.new do |s| s.name = ‘kyiv’ s.version = ‘0.0.1’ s.authors = [‘Pat Allan’] s.email = [‘[email protected]’] s.homepage = ‘’ s.summary = ‘RubyC Kyiv Gem’ s.description = ‘’ s.files = [...] s.test_files = [...] s.executables = [...]end
Here’s an example gemspec. There’s nothing too scary here, it’s standard Ruby, and we’re just going through setting some options.
s.name = ‘kyiv’
Naming
At the top we’ve got the name. You can call your gem whatever you like, but it must be unique. If someone else has already released a gem of the same name, bad luck, find something else to call yours.
Naming
# name‘will_paginate’# requiringrequire ‘will_paginate’# Class/ModuleWillPaginate
Also: keep it lowercase, and as a general rule, use underscores for word separation...
Naming
# name‘cucumber-rails’# requiringrequire ‘cucumber/rails’# Class/ModuleCucumber::Rails
... and hyphens for namespacing. These are not rules, just recommendations - I’ve failed to follow them in the past, as have many others.
Versions
s.version = ‘0.0.1’
Next is the version number of your gem.
Versions
s.version = ‘Major.Minor.Release’
You can choose any numbering system you like, but the vast majority of gems follow the standard Major/Minor/Release system.
Versions
Major VersionsMajor version numbers indicate major changes - if your gem changes its external behaviour or main class and method structure. If people upgrade, are they going to have to change their code that’s using your code? If the answer is definitely yes, that would warrant bumping up the major version number.
Versions
Minor VersionsMinor version numbers are useful to indicate new features and minor changes to behaviour - things which are useful, but won’t require changes for existing usage.
Versions
Release Versions
And finally the release number - you want to change this for bug fixes and very small changes.
Versions
ReferenceI would recommend separating the gem version out into a separate ruby file, so developers can check which version of your gem - within their code - they are using.
Versions
# lib/kyiv/version.rbmodule Kyiv VERSION = ‘0.0.1’end
Such a file would look something like this - with a constant containing our version number.
Versions
# kyiv.gemspecrequire ‘kyiv/version’# ...s.version = Kyiv::VERSION
And then the gemspec would look like this. Note that we’re only requiring the version file, not the entire library - we don’t want to load our entire gem just to determine the version number and generate our gemspec.
Versions
VERSION = ‘1.0.0.beta1’
Here’s an example of releasing a beta version of a gem - if your gem version has any alphabetical characters, then it’s considered a pre-release - whether that’s a beta, release candidate or something else. This means it won’t be installed by default, only when explicitly requested.
Authors, Emails & Homepage
s.authors = [‘Pat Allan’]s.email = [‘[email protected]’]s.homepage = ‘http:// github.com/pat/kyiv’
These settings are pretty self-explanatory - the gem authors’ names and email addresses. Both of these settings accept arrays, so you can list all of the authors.And you can also provide the home page for the gem - at the very least, put the Github repository here.
Summary & Description
s.summary = ‘RubyC Kyiv Gem’s.description = ‘Extended detail’
The gemspec expects both a summary and description - and it complains if they’re the same. The summary should be a very short description of your gem - and the description should be much more detailed.
Files
s.files = [ ‘lib/kyiv.rb’, ‘lib/kyiv/version.rb’, ‘README.textile’, ‘LICENCE’]
Now we get to the core of a gem - a listing of the relevant files. You want to have all files listed here that are required for the gem to work and be used - so, all ruby files, but also the README, LICENCE, and a HISTORY file if you have one too.
Files
lib/kyiv.rb
All files that can be required should exist under the lib directory. You can put them elsewhere, but I’ve never seen a good reason to do that. So just put them in lib.And you want a file there that matches the name of your gem.
Files
Kyiv::Touristlib/kyiv/tourist.rb
Kyiv::Translatorlib/kyiv/translator.rb
All other relevant files should be placed within a gem’s project directory in lib, and namespaced accordingly - you want to avoid conflicts with any other gems.
Files
# Don’t do thislib/kyiv.rblib/tourist.rblib/translator.rb
You don’t want to do anything like this - because what happens if there’s also a gem named ‘tourist’?
Information Files
READMEAlways have a README file in your gem, with some information on how to install and use the gem. You can write it as a plain text file, or Textile or Markdown or RDoc - that’s up to you - but just make sure it’s there and helpful.
Information Files
LICENCEDon’t forget to have a file with your open source licence of choice, too. You’ll find most gems are released under MIT or BSD licences - very few opt for the GPL, which can make it harder for developers to use your gem.
Information Files
HISTORYThis - or a change log - is optional, but recommended. It’s a great way for you and those who use your gem to see what’s changed in each release - and once your gem has been around a while, having that information becomes even more helpful.
Test Files
s.test_files = [ ‘spec/kyiv_spec.rb’, ‘spec/spec_helper.rb’]
And of course you’re writing tests for your gem, right? You can list your test files in your gemspec as well - again, best to follow standard practices here and put them in test or spec directories.
Test Files
Beware Large Test Suites
Now, if your gem’s test suite gets rather large - perhaps you’ve got a stack of fixture files - then it’s better not to include those files in your gem at all - otherwise you’re going to increase the gem size dramatically. That said, this is rare, so in most cases, include your tests.
Test Files
Travis CII highly recommend using Travis CI for continuous integration - it allows you to easily test across different versions of Ruby, including JRuby and Rubinius.
Executables
s.executables = [ ‘bin/kyiv’]
Not all gems will have executables, but some will - and you need to list them in your gemspec accordingly. They should live in the bin directory, and have executable permissions - and no file extension.
Executables
#!/usr/bin/env ruby
require ‘kyiv’Kyiv::CLI.run
Here’s an example of what could go in an executable file - you’ve got your shebang at the top, and then the ruby code that handles the command line interface. I highly recommend keeping this file very small and putting all the logic in a class in your lib directory. This makes your gem code much easier to test, maintain and re-use.
# kyiv.gemspecGem::Specification.new do |s| s.name = ‘kyiv’ s.version = ‘0.0.1’ s.authors = [‘Pat Allan’] s.email = [‘[email protected]’] s.homepage = ‘’ s.summary = ‘RubyC Kyiv Gem’ s.description = ‘’ s.files = [...] s.test_files = [...] s.executables = [...]end
So, going back to our gemspec, that’s our core settings all covered. There are others though - and the most important of these are our dependencies on other gems.
Dependencies
s.add_runtime_dependency ‘rails’, ‘>= 3.0.0’
Gemspecs have a distinction between runtime dependencies - things your gem needs when it is running and being used by others - and development dependencies, which are required to develop the gem. So, your gem may need Rails to work - so you’d add a line like this to your gemspec.
Dependencies
s.add_development_dependency ‘rspec’, ‘>= 2.6.0’
And given I’m using RSpec to test my gem, then I want to have it as a dependency as well - but only a development dependency. It doesn’t need to be installed for users of my gem.
Dependencies
Dependency Versions
It can be a little tricky to determine which version you want your dependencies to require - you don’t want it too restrictive, but you also don’t want it to break in the future.
Dependencies
s.add_runtime_dependency ‘rails’, ‘~> 3.1’
This ensures any release of Rails that is equal to or greater than 3.1 can be used, but not Rails 4 - and going by our major/minor/release version number approach, this should hopefully be safe enough, because Rails shouldn’t go making any dramatic changes until 4.0. Of course, that’s not always true, but you can’t account for everything!
Dependencies
~>I’m guessing most of you have seen this version constraint before - but does everyone understand what it means? I know this is a slight tangent, but just quickly - it’s known as the pessimistic version constraint.
Dependencies
~> 3.1 == [>= 3.1 && < 4.0]
~> 3.0.3 == [>= 3.0.3 && < 3.1]
You can see here that how specific you are is taken into account - it allows for an increase in the last digit specified, but not any before that. Anyway, you can read more about that online - back to dependencies!
Dependencies
s.add_development_dependency ‘rspec’, ‘= 2.6.0’
It doesn’t hurt to be a bit more exact with your development dependencies - as this will allow other contributors to easily get a local copy set up.
Rake TasksTo assist development of your gem, you’re probably going to have a Rakefile in the root of your project with some tasks defined.
# run testsrake spec
# generate documentationrake yard
# default task should run testsrake
These tasks may be for running tests and generating documentation - and perhaps helping with managing releases of the gem as well. It’s generally expected that the default task - which runs when you invoke rake with no argument - should run your entire test suite.
PublishingSo, you’ve got the first version of your gem ready to go, the tests are all green, and now it’s time to share it with the world! How do you do that?
Publishing
gem build kyiv.gemspec#=> kyiv-0.0.1.gem
First things first: let’s generate the actual gem file - which is done by passing our gemspec into the ‘gem build’ command. This packages our gem together - into a file that includes the current version number.
Gem Files
.gem == .tarIf you’re curious, a gem file is just a tar file - and inside that is a compressed copy of your code, and the metadata from the gemspec.
Publishing
gem push kyiv-0.0.1.gem
Getting that gem file up onto rubygems.org is a single command - ‘gem push’. You’ll need to have signed up for an account on rubygems.org, and it’ll prompt you for your details once, and then remember them.But it really is just a matter of typing out that command, and that’ll upload the file!
Publishing
PermanentIt’ll take a few seconds for that gem version to be available to everyone - but once it’s up there, that’s it. You can’t change an existing version of a gem - if you made a mistake, bad luck, fix things and release a new version.
Publishing
(Don’t) YankHowever, if you’ve really screwed up - perhaps released some code that deletes files off of someone’s machine, or has an extremely serious security flaw - then you may want to yank it - which just means no one will be able to install that version of the gem any more.Yanking gems is only for emergencies - it’s unlikely you should ever need to do it.
Publishing
MRI 1.9 CautionFor the most part, developing and deploying on Ruby 1.9 is a smooth experience - and I recommend it - but there can be issues with building gems, because of the different way YAML is parsed. So it’s best to use MRI 1.8 when building the gem at the moment - hopefully soon this won’t be the case.
CommunityAnd once your gem is out there available for everyone to use, you may want to consider a few things...
Community
SupportFirstly, if you do want others to use your gem, then you’re probably going to need to offer some support - whether that’s via GitHub issues, or a mailing list, or some other way.
Community
GitHubSpeaking of GitHub, keep an eye out for pull requests - and encourage others to contribute. Perhaps have some guidelines on this in your README. Don’t be afraid to curate the patches, as well - accept ones you like, and provide feedback on ones you don’t want to merge in immediately. Be honest but friendly.
Community
ReleasesAnd as your gem evolves, you can release new versions. It’s up to you on how often this happens - but try not to go overboard, and try not to break things.
BundlerNow, you may want to use Bundler locally to outline the gems you’re using to develop your gem.
Bundler
RecommendedI certainly recommend this - Bundler is certainly a great way of managing dependencies for any Ruby project.
Bundler
Not RequiredThat said, it’s not required - you can manage these things however you like. Bundler, after all, is a development tool - it will not have any impact on how people use your gem.
Bundler
# Gemfilesource :rubygems
gemspec
There’s no need to double up on gem dependency lists though - given we’re documenting that in our gemspec, Bundler can look at that instead - here’s a Gemfile example.
Bundler
# Gemfilegemspec
gem ‘mysql2’, :platform => :rubygem ‘jdbc-mysql’, :platform => :jruby
Sometimes, though, it’s necessary to be more specific than a gemspec allows - at least, for development dependencies. One example is different libraries for different rubies. If you’re doing this, then best to not list your development dependencies in the gemspec - after all, if anyone’s modifying your gem source, they’ll be using Bundler too.
Bundler
rake release
One last thing to note about Bundler is that it can provide a few helper rake tasks - the most useful of these being release, which builds and pushes a new gem version and tags the release in your git repo and pushes that tag to Github. It’s the tagging that’s particularly useful - the rest is easy enough to do manually.
Bundler
# RakefileBundler::GemHelper. install_tasks
To get those rake tasks, just add this line to your Rakefile.
ToolsOf course, Bundler’s not the only tool around to help you manage and develop gems.
Tools
HoeHoe has been around for years, and provides plugins to manage releasing your gem, as well as testing and documentation and I’m sure a few other bits and pieces as well.
Tools
newgemnewgem has also been around a while, and is built on top of hoe. It can even generate a website for your gem if you wish.
Tools
JewelerJeweler’s not quite as heavy as either hoe or newgem - it just provides a generator for creating the essential gem project files, and some rake tasks to help manage releases.But you don’t need to use any of these - I recommend starting with a vanilla gemspec and see how far you can get.
ExamplesAnd examples of gemspecs - and gems - are everywhere. Look at the gems you’ve installed on your computer, or at code on Github. Don’t be afraid to read other peoples’ code - it’ll make you a better developer, whether or not you’re writing gems.
Examples
bundle open kyiv
A quick way to view gems is to use the `bundle open` command, which can open up any gem in your bundle in your preferred text editor.
Thank YouQuestions?
Anyway, I think that’s enough from me - you can all start writing your own gems now!Does anyone have any questions?