Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Ruby Gem Presentation

Creating a gem in 10m

First we will create a new gem scaffold using the bundler gem that comes with Ruby.

$ bundle gem tofu
$ cd tofu
$ subl .

Now we can edit the gemspec file to add metadata to our gem.

So lets actually code something, how about being able to count all the lines of code we have written.

# lib/tofu.rb
require "tofu/version"

module Tofu
  class LineCounter
    def initialize(base_dir)
      @base_dir = base_dir
    end

    def code_files(exts)
      glob = File.join(File.expand_path(@base_dir), "**/*.{#{exts}}")
      Dir[glob].reject {|f| File.directory?(f) }
    end

    def lines(file)
      IO.read(file).lines.count
    end

    def all_lines(exts)
      files = code_files(exts)
      line_count = files.map {|f| lines(f)}.inject(:+)
      file_count = files.size
      return line_count, file_count
    end
  end
end

Now we can test this in irb

$ irb -Ilib
>> require "tofu"
>> t = Tofu::LineCounter.new("~/Box/Dev")
>> t.all_lines('rb,py')

But we don't want to use irb, we want a cool executable like bundler. For this we create something in the bin folder.

#!/usr/bin/env ruby
require "tofu"

t = Tofu::LineCounter.new(ARGV[0] || Dir.pwd)
lines, files = t.all_lines(ARGV[1] || 'rb,py,m,cpp,h')

puts "Found #{lines} lines across #{files} files."

Now see in the gemspec, bundler won't recognize our files until we add them to git.

$ git add -A
$ git commit -m "Initial stuff"

Now we can try out our command.

$ bundle
$ bundle exec tofu
$ bundle exec tofu ~/Box/Dev
$ bundle exec tofu ~/Box/Dev rb,py

Now lets add a dependency to make things snazzy. We just add a line to our gemspec to declare it and then use it in our executable file.

# tofu.gemspec
spec.add_runtime_dependency 'colored'
# bin/tofu
require "colored"
puts "Found #{lines.to_s.blue} lines across #{files.to_s.red} files."

For extra credit let's add snazziness, a unix command can trivially count lines but we can count lines of code by stripping empty lines and comments.

def lines(file)
  count = 0
  IO.foreach(file) do |line|
    # TODO: fix stupid UTF-8 stuff, Oh Lord Matz, forgive me for using rescue nil
    count += 1 unless line.empty? || (line =~ /^\S*(#|\/\/)/) rescue nil
  end
  count
end

Now we want to be able to install this on our computer and our friends so lets package it.

$ rake build
$ rake install
$ rake release # don't run this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment