If you've ever looked into how to create Gems, you've probably seen a bunch of ways to do that. Project generators like Hoe, Jeweler, and the like offer some nice ways to get started, but they may often be overkill for many projects. If you're just starting out, why not learn to do it from scratch?
In this talk, we'll create our own gem from scratch, using only things that are provided by Ruby, its standard library, and RubyGems to craft a simple gem.
You'll learn how to set up a project, how to write and run tests, how to use Rake to quickly build the gem, and even how to create a gem that installs an executable command-line program.
3. The (normal) Process
• Come up with an idea
• write some code
• Release a gem
• Beg people to write tests for you
twitter: bphogan
email: brianhogan at napcs.com
4. Our process
• Come up with an idea and write some code
• Throw all that code away
• Rewrite it with tests as we go
• Release it as a Gem
• Enjoy fame and fortune
twitter: bphogan
email: brianhogan at napcs.com
5. Grabatar
(a gem to grab Gravatar URLs)
http://www.gravatar.com/avatar/
6ef8cb7cd7cd58077f0b57e4fa49a969
twitter: bphogan
email: brianhogan at napcs.com
6. Step one: Build tested,
working code
twitter: bphogan
email: brianhogan at napcs.com
7. The basic structure of a
Gem
gemname.gemspec
lib/
gemname
version.rb
anotherfile.rb
gemname.rb
twitter: bphogan
email: brianhogan at napcs.com
9. Test::Unit
test
somefile_test.rb
twitter: bphogan
email: brianhogan at napcs.com
10. Write the test
test/grabatar_test.rb
require 'test/unit'
require 'lib/grabatar'
class GrabatarTest < Test::Unit::TestCase
def test_displays_version
assert Grabatar::VERSION
end
end
twitter: bphogan
email: brianhogan at napcs.com
11. Implement the code
lib/grabatar/version.rb
module Grabatar
VERSION = "0.0.2"
end
twitter: bphogan
email: brianhogan at napcs.com
12. Add to the main Ruby file:
lib/grabatar.rb
require 'grabatar/version'
twitter: bphogan
email: brianhogan at napcs.com
14. Write the test first...
test/gravatar_helper.rb
require 'test/unit'
require 'grabatar'
class GravatarTest < Test::Unit::TestCase
def test_builds_gravatar_url
g = Grabatar::Gravatar.new("bphogan@gmail.com")
assert_equal "http://www.gravatar.com/avatar/
6ef8cb7cd7cd58077f0b57e4fa49a969", g.avatar_url
end
end
twitter: bphogan
email: brianhogan at napcs.com
15. Then write the code
lib/grabatar/gravatar.rb
module Grabatar
class Gravatar
require 'digest/md5'
def initialize(email)
@email = email
end
def avatar_url
hash = Digest::MD5.hexdigest(@email)
"http://www.gravatar.com/avatar/#{hash}"
end
end
end
twitter: bphogan
email: brianhogan at napcs.com
16. Then write the code
lib/grabatar/gravatar.rb
module Grabatar
class Gravatar
require 'digest/md5'
def initialize(email)
@email = email
end
def avatar_url
hash = Digest::MD5.hexdigest(@email)
"http://www.gravatar.com/avatar/#{hash}"
end
end
end
twitter: bphogan
email: brianhogan at napcs.com
17. Then write the code
lib/grabatar/gravatar.rb
module Grabatar
class Gravatar
require 'digest/md5'
def initialize(email)
@email = email
end
def avatar_url
hash = Digest::MD5.hexdigest(@email)
"http://www.gravatar.com/avatar/#{hash}"
end
end
end
twitter: bphogan
email: brianhogan at napcs.com
18. Then write the code
lib/grabatar/gravatar.rb
module Grabatar
class Gravatar
require 'digest/md5'
def initialize(email)
@email = email
end
def avatar_url
hash = Digest::MD5.hexdigest(@email)
"http://www.gravatar.com/avatar/#{hash}"
end
end
end
twitter: bphogan
email: brianhogan at napcs.com
19. Step two: Make A
Gemspec
twitter: bphogan
email: brianhogan at napcs.com
20. Gemspecs are easy.
grabatar.gemspec
require 'lib/grabatar/version'
Gem::Specification.new do |s|
s.name = "grabatar"
s.version = Grabatar::VERSION
s.summary = "Fetch avatar image URLs from Gravatar"
s.authors = ["Brian Hogan"]
s.email = ["bphogan@gmail.com"]
s.files = [
"lib/grabatar.rb",
"lib/grabatar/version.rb",
"lib/grabatar/gravatar.rb",
"lib/grabatar/railtie.rb",
"lib/grabatar/view_helpers.rb"
]
end
twitter: bphogan
email: brianhogan at napcs.com
21. Gemspecs are easy.
grabatar.gemspec
require 'lib/grabatar/version'
Gem::Specification.new do |s|
s.name = "grabatar"
s.version = Grabatar::VERSION
s.summary = "Fetch avatar image URLs from Gravatar"
s.authors = ["Brian Hogan"]
s.email = ["bphogan@gmail.com"]
s.files = [
"lib/grabatar.rb",
"lib/grabatar/version.rb",
"lib/grabatar/gravatar.rb",
"lib/grabatar/railtie.rb",
"lib/grabatar/view_helpers.rb"
]
end
twitter: bphogan
email: brianhogan at napcs.com
22. Gemspecs are easy.
grabatar.gemspec
require 'lib/grabatar/version'
Gem::Specification.new do |s|
s.name = "grabatar"
s.version = Grabatar::VERSION
s.summary = "Fetch avatar image URLs from Gravatar"
s.authors = ["Brian Hogan"]
s.email = ["bphogan@gmail.com"]
s.files = [
"lib/grabatar.rb",
"lib/grabatar/version.rb",
"lib/grabatar/gravatar.rb",
"lib/grabatar/railtie.rb",
"lib/grabatar/view_helpers.rb"
]
end
twitter: bphogan
email: brianhogan at napcs.com
23. Gemspecs are easy.
grabatar.gemspec
require 'lib/grabatar/version'
Gem::Specification.new do |s|
s.name = "grabatar"
s.version = Grabatar::VERSION
s.summary = "Fetch avatar image URLs from Gravatar"
s.authors = ["Brian Hogan"]
s.email = ["bphogan@gmail.com"]
s.files = [
"lib/grabatar.rb",
"lib/grabatar/version.rb",
"lib/grabatar/gravatar.rb",
"lib/grabatar/railtie.rb",
"lib/grabatar/view_helpers.rb"
]
end
twitter: bphogan
email: brianhogan at napcs.com
30. Test first
test/view_helpers_test.rb
require 'test/unit'
require 'grabatar/gravatar'
require 'grabatar/view_helpers'
class ViewHelpers < Test::Unit::TestCase
include Grabatar::ViewHelpers
def test_generates_avatar_url
assert_equal
"http://www.gravatar.com/avatar
/6ef8cb7cd7cd58077f0b57e4fa49a969",
gravatar_url_for("bphogan@gmail.com")
end
end
twitter: bphogan
email: brianhogan at napcs.com
31. Test first
test/view_helpers_test.rb
require 'test/unit'
require 'grabatar/gravatar'
require 'grabatar/view_helpers'
class ViewHelpers < Test::Unit::TestCase
include Grabatar::ViewHelpers
def test_generates_avatar_url
assert_equal
"http://www.gravatar.com/avatar
/6ef8cb7cd7cd58077f0b57e4fa49a969",
gravatar_url_for("bphogan@gmail.com")
end
end
twitter: bphogan
email: brianhogan at napcs.com
32. Create a helper
lib/grabatar/view_helpers.rb
module Grabatar
module ViewHelpers
def gravatar_url_for(email)
Grabatar::Gravatar.new(email).avatar_url
end
end
end
twitter: bphogan
email: brianhogan at napcs.com
34. Include the Helper
lib/grabatar/railtie.rb
module Grabatar
class Railtie < Rails::Railtie
initializer "grabatar.view_helpers" do
ActionView::Base.send :include, ViewHelpers
end
end
end
twitter: bphogan
email: brianhogan at napcs.com
35. Only load the Railtie if we have Rails
lib/grabatar.rb
require 'grabatar/railtie' if defined?(Rails)
twitter: bphogan
email: brianhogan at napcs.com
36. Making a CLI tool
twitter: bphogan
email: brianhogan at napcs.com
37. Add a bin folder with
the file!
twitter: bphogan
email: brianhogan at napcs.com
38. The CLI program
bin/grabatar
#!/usr/bin/env ruby
require 'grabatar'
puts "Grabatar v#{Grabatar::VERSION}"
email = ARGV[0]
if email.nil?
puts "Usage: grabatar <youremail@server.com>"
else
g = Grabatar::Gravatar.new(email)
puts g.avatar_url
end
twitter: bphogan
email: brianhogan at napcs.com
39. The CLI program
bin/grabatar
#!/usr/bin/env ruby
require 'grabatar'
puts "Grabatar v#{Grabatar::VERSION}"
email = ARGV[0]
if email.nil?
puts "Usage: grabatar <youremail@server.com>"
else
g = Grabatar::Gravatar.new(email)
puts g.avatar_url
end
twitter: bphogan
email: brianhogan at napcs.com
40. The CLI program
bin/grabatar
#!/usr/bin/env ruby
require 'grabatar'
puts "Grabatar v#{Grabatar::VERSION}"
email = ARGV[0]
if email.nil?
puts "Usage: grabatar <youremail@server.com>"
else
g = Grabatar::Gravatar.new(email)
puts g.avatar_url
end
twitter: bphogan
email: brianhogan at napcs.com
41. Running it
$ grabatar bphogan@gmail.com
Grabatar v0.0.2
http://www.gravatar.com/avatar/6ef8cb7cd7cd58077f0b57e4fa49a969
twitter: bphogan
email: brianhogan at napcs.com
42. Who wants code?
http://github.com/napcs/grabatar
twitter: bphogan
email: brianhogan at napcs.com
43. Thanks!
http://spkr8.com/t/8181
twitter: bphogan
email: brianhogan at napcs.com
Editor's Notes
\n
Who here knows what a Gem is? Who's built a gem?\n
\n
\n
Simple gem that fetches the Gravatar URL based on the given email\n
This is the easy part.\n
a lib folder, a "main" file that includes our other files\n
Because they make for good code.\n
Because it's there. I think Gems should try to have as few dependencies as possible.\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
By hand. \n
\n
\n
\n
Lists all the files you wish to release with your Gem.\n
It sure is handy, but its generated manifest is overkill. It depends on git. Maybe it&#x2019;s not bad for you, but it sure makes me uncomfortable. It also requires that I use Git. I can remove these lines trivially. But it&#x2019;s still uncomfortable.\n
Things that automatically build manifests can (and do) accidentally include things like this, and more. And you can't put exceptions in for every possible type of IDE backup file or hidden folder a contributor might have.\n
This way, you ensure that what's in the Gem is what you want, and what your contributors want. \n
\n
We can make our gems work with Rails. Let's make a really simple Rails helper\n
Our helper will be a module, so we write a test that includes the module and tests that it returns the right result.\n
Our helper will be a module, so we write a test that includes the module and tests that it returns the right result.\n
\n
We create a new class that inherits from Railtie and then mix our module into the view helpers\n