No F*cking Idea

Common answer to everything

Building Gem With Bundler

| Comments

Building a new gem ( ruby library ) with bundler is easy task. I found a lot of tutorial on this topic in the web but none of them was covering more then just generating scaffold and packing up the gem. I will try to uncover a bit more. So at the end of this post you will be able to generate new gem, build it and it will have ready support for rspec.

First of all you need bundler. To install bundler just type gem install bundler. Probably most of you guys have it already :) but just in case.

Generating new gem

To generate gem scaffold all you have to do is call bundle gem <gem name> like here

1
2
3
4
5
6
7
8
9
10
bundle gem doctor_toons
      create  doctor_toons/Gemfile
      create  doctor_toons/Rakefile
      create  doctor_toons/LICENSE
      create  doctor_toons/README.md
      create  doctor_toons/.gitignore
      create  doctor_toons/doctor_toons.gemspec
      create  doctor_toons/lib/doctor_toons.rb
      create  doctor_toons/lib/doctor_toons/version.rb
Initializating git repo in /private/tmp/doctor_toons

This will create directory named doctor_toons with:

  • Gemfile where we specify production and development gems
  • Rakefile with our rake tasks, you need to remember that rake is not part of Gemfile if you are using ruby < 1.9.2
  • License file with license, GPL, BSD, MIT, LOL choose one and put it in by default MIT.
  • it initializes git repo inside this directory and adds .gitignore with default ignores
.gitignore
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
λ cat .gitignore
*.gem
*.rbc
.bundle
.config
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
  • lib directory for your code !!!
  • < name >.gemspec file with whole gemspec for our gem.

First thing we need to do is set gems we will be using while building our lib. Initial Gemfile looks like this

1
2
3
4
5
λ cat Gemfile
source 'https://rubygems.org'

# Specify your gem's dependencies in doctor_toons.gemspec
gemspec

All production needed gems needs to be added before gemspec. So if we will want to add rspec as development dependency we need to put it after gemspec term in Gemfile. Eg. if we want to use mongo 1.6.2 as production dependency and rspec 2.9.0 as our development dependency we should do it like this.

cat Gemfile
1
2
3
4
5
6
7
8
9
source 'https://rubygems.org'

gem 'mongo', '~> 1.6.2'
gem 'bson',  '~> 1.6.2'

# Specify your gem's dependencies in doctor_toons.gemspec
gemspec

gem 'rspec', '~> 2.9.0'

Now we can simply bundle all dependencies for our gem by hitting. bundle install you should see something like this

1
2
3
4
5
6
7
8
9
10
11
12
λ bundle install
Fetching gem metadata from https://rubygems.org/....
Using bson (1.6.2)
Using diff-lcs (1.1.3)
Using doctor_toons (0.0.1) from source at /private/tmp/doctor_toons
Using mongo (1.6.2)
Using rspec-core (2.9.0)
Using rspec-expectations (2.9.1)
Using rspec-mocks (2.9.0)
Using rspec (2.9.0)
Using bundler (1.1.0)
Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.

Building gem!

Ok now we are ready for next step, building gem! To do it we call gem build < name >.gemspec so lets do it

1
2
3
λ gem build doctor_toons.gemspec
ERROR:  While executing gem ... (Gem::InvalidSpecificationException)
    "FIXME" or "TODO" is not a description

Error. This means that we need to supply few things before we will build this gem. First of all we need to emacs < name >.gemspec to edit the file with gemspec and set few things. By default this file should look like this

doctor_toons.gemspec
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# -*- encoding: utf-8 -*-
require File.expand_path('../lib/doctor_toons/version', __FILE__)

Gem::Specification.new do |gem|
  gem.authors       = ["JakubOboza"]
  gem.email         = ["jakub.oboza@gmail.com"]
  gem.description   = %q{TODO: Write a gem description}
  gem.summary       = %q{TODO: Write a gem summary}
  gem.homepage      = ""

  gem.executables   = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
  gem.files         = `git ls-files`.split("\n")
  gem.test_files    = `git ls-files -- {test,spec,features}/*`.split("\n")
  gem.name          = "doctor_toons"
  gem.require_paths = ["lib"]
  gem.version       = DoctorToons::VERSION
end

So lets edit description and summary and build again our gem. gem build doctor_toons.gemspec

1
2
3
4
5
6
λ gem build doctor_toons.gemspec
WARNING:  no homepage specified
  Successfully built RubyGem
  Name: doctor_toons
  Version: 0.0.1
  File: doctor_toons-0.0.1.gem

Yeah now we can build our gem :) Lets have a look at gemspec gem spec doctor_toons-0.0.1.gem before we will be adding rspec to it :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
 λ gem spec doctor_toons-0.0.1.gem
--- !ruby/object:Gem::Specification
name: doctor_toons
version: !ruby/object:Gem::Version
  version: 0.0.1
  segments:
  hash:
platform: ruby
authors:
- JakubOboza
autorequire:
bindir: bin
cert_chain: []
date: 2012-04-11 00:00:00.000000000Z
dependencies: []
description: He sleeps with a gun, but he loves his son
email:
- jakub.oboza@gmail.com
executables: []
extensions: []
extra_rdoc_files: []
files:
- .gitignore
- Gemfile
- LICENSE
- README.md
- Rakefile
- doctor_toons.gemspec
- lib/doctor_toons.rb
- lib/doctor_toons/version.rb
homepage: ''
licenses: []
post_install_message:
rdoc_options: []
require_paths:
- lib
required_ruby_version: !ruby/object:Gem::Requirement
  none: false
  requirements:
  - - ! '>='
    - !ruby/object:Gem::Version
      version: '0'
      segments:
      hash:
required_rubygems_version: !ruby/object:Gem::Requirement
  none: false
  requirements:
  - - ! '>='
    - !ruby/object:Gem::Version
      version: '0'
      segments:
      hash:
requirements: []
rubyforge_project:
rubygems_version: 1.8.15
signing_key:
specification_version: 3
summary: Psycho dad
test_files: []

Everything is fine.

Adding testing support

Before we will be wiriting any library code its nice to add support for testing framework. I like name of rspec and rspec as a lib. It lets you define in form of tests specification for your library.

Rspec

Adding rspec to our project is easy. All we need to do it create directory called spec and file inside of it called spec_helper.rb like this

1
2
λ mkdir spec
λ touch spec/spec_helper.rb

Now we need to edit spec_helper.rb and add few things.

1
2
3
4
5
6
7
8
require 'rubygems'
require 'bundler/setup'
# our gem
require 'doctor_toons'

RSpec.configure do |config|

end

This is bare minimum spec_helper.rb file we can have. All we do is require on rubygems, bundler setup and our gem doctor_toons :). To make it easy to use we should expand our rake with a spec task! like this

Rakefile
1
2
3
4
5
6
7
8
#!/usr/bin/env rake
require "bundler/gem_tasks"

require 'rspec/core/rake_task'

RSpec::Core::RakeTask.new('spec')

task :default => :spec

This will add rspec task called rake spec that will run all our specs :) Now last thing we need to add first initial spec to our spec folder so we will be able to test it.

spec/doctor_toons_spec.rb
1
2
3
4
5
6
7
8
9
10
11
require 'spec_helper'

describe DoctorToons do

  it "should rock" do
    lambda do
      DoctorToons.rock()
    end.should_not raise_error
  end

end

Remember spec file names has to end with _spec.rb

Now if you will run rake spec you should see something like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Users/kuba/.rvm/rubies/ruby-1.9.2-p290/bin/ruby -S rspec ./spec/doctor_toons_spec.rb
F

Failures:

  1) DoctorToons should rock
     Failure/Error: lambda do
       expected no Exception, got #<NoMethodError: undefined method `rock' for DoctorToons:Module>
     # ./spec/doctor_toons_spec.rb:6:in `block (2 levels) in <top (required)>'

Finished in 0.00218 seconds
1 example, 1 failure

Failed examples:

rspec ./spec/doctor_toons_spec.rb:5 # DoctorToons should rock
rake aborted!
/Users/kuba/.rvm/rubies/ruby-1.9.2-p290/bin/ruby -S rspec ./spec/doctor_toons_spec.rb failed

Tasks: TOP => spec
(See full trace by running task with --trace)

Wooow works :) now you can implement your library to rock the world!

Few useful things

If you want to have colorful output like me :). You can add .rspec file in top dir of project with content like this

.rspec
1
2
--color
--format documentation

First line adds color output and second line is format out spec output, it is nice because its the most verbose output. You can easy see where spec failed. When you are on early stage of the project and you have few describes like 50-100 it often helps to develop faster.

Summary

This is just intro post about my upcoming series on building tools that you need :). Fingers crossed :)

-Cheers!

Comments