Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building Ruby gems

Building Ruby gems

Stefano Verna

June 05, 2012
Tweet

More Decks by Stefano Verna

Other Decks in Programming

Transcript

  1. @steffoz stefanoverna.com •A Gem is a software package with a

    name, version, and platform •Gems split out reusable functionality that others can use •Some gems also provide command line utilities Gems are build using the Rubygems tool (included with Ruby itself).
  2. @steffoz stefanoverna.com % tree freewill freewill/ ├── lib/ │ └──

    freewill.rb └── freewill.gemspec So, this is your first gem. Contains information about the gem’s files, test information, platform, version number, author’s email and name. Contains the code of the gem.
  3. @steffoz stefanoverna.com % tree freewill freewill/ ├── bin/ │ └──

    freewill ├── lib/ │ └── freewill.rb ├── test/ │ └── test_freewill.rb ├── README ├── Rakefile └── freewill.gemspec This is a more usual gem. Contains the executables of the gem. Contains the tests of the gem. Contains the docs of the gem. Helps automating gem release, testing and generate docs.
  4. @steffoz stefanoverna.com Gem::Specification.new do |s| s.name = 'freewill' s.version =

    '1.0.0' s.date = '2010-04-27' s.summary = "Freewill!" s.description = "I will choose Freewill!" s.authors = ["Nick Quaranto"] s.email = '[email protected]' s.homepage = 'http://example.com' s.files = ["lib/freewill.rb"] s.add_dependency 'activesupport' end
  5. @steffoz stefanoverna.com › gem build freewill.gemspec Successfully built RubyGem Name:

    freewill Version: 1.0.0 File: redpomo-1.0.0.gem › ls redpomo-1.0.0.gem
  6. @steffoz stefanoverna.com › tar zxvf freewill-1.0.0.gem x data.tar.gz x metadata.gz

    tar zxvf data.tar.gz x lib/freewill.rb [...] › gunzip metadata.gz › cat metadata --- !ruby/object:Gem::Specification name: redpomo version: !ruby/object:Gem::Version version: 0.0.7 prerelease: platform: ruby authors: - Stefano Verna autorequire: bindir: bin [...] All the gem files are placed in an inner data.tar.gz tarball A YAML serialized version of a Ruby class, containing all the gemspec infos.
  7. @steffoz stefanoverna.com require "foo/bar" Loads the given file, returning true

    if successful and false if the feature is already loaded. If the filename does not resolve to an absolute path, it will be searched for in the directories listed in $LOAD_PATH.
  8. @steffoz stefanoverna.com › irb irb(main)> puts $LOAD_PATH ~/.rbenv/versions/1.9.3-p0/lib/ruby/1.9.1 [...] irb(main)>

    require 'active_support' true irb(main)> puts $LOAD_PATH ~/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/i18n-0.6.0/lib ~/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/activesupport-3.2.3/lib ~/.rbenv/versions/1.9.3-p0/lib/ruby/1.9.1
  9. @steffoz stefanoverna.com > redditor > redditor hot|top|new|controversial > redditor hot

    Programming > redditor top fffffffuuuuuuuuuuuu The Goal (we'll miss)
  10. @steffoz stefanoverna.com 1 HTTP Client RestClient Simple HTTP and REST

    client for Ruby, inspired by microframework syntax for specifying actions.
  11. @steffoz stefanoverna.com 1 require 'rest_client' response = RestClient.get 'http://example.com/resource' response.code

    # 200 response.cookies # {"Foo"=>"BAR", "QUUX"=>"QUUUUX"} response.headers # {:content_type=>"text/html; charset=utf-8", :cache_control=>"private" ... response.to_s # \n<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n \"http://www.w3.org/TR/html4/strict.dtd\">\n\n<html ....
  12. @steffoz stefanoverna.com 2 JSON Parser JSON This is a implementation

    of the JSON specification according to RFC 4627
  13. @steffoz stefanoverna.com 2 require 'json' JSON.parse '{ "foo": "bar" }'

    # {"foo"=>"bar"} { foo: "bar" }.to_json # {"foo":"bar"}
  14. @steffoz stefanoverna.com 3 require 'googl' url = Googl.shorten('http://www.zigotto.com') url.short_url #

    "http://goo.gl/ump4S" url.long_url # "http://www.zigotto.com/" url.qr_code # "http://goo.gl/ump4S.qr" url.info # "http://goo.gl/ump4S.info"
  15. @steffoz stefanoverna.com 4 CLI Table Drawer command_line_reporter This gem makes

    it easy to provide a report while your ruby script is executing
  16. @steffoz stefanoverna.com 4 require 'command_line_reporter' include CommandLineReporter table :border =>

    true do row :color => 'red' do column 'Name', :width => 20, :color => 'blue' column 'Address', :width => 20 column 'City', :width => 15 end row do column 'Caeser' column '1 Appian Way' column 'Rome' end row do column 'Richard Feynman' column '1 Golden Gate' column 'Quantum Field' end end
  17. @steffoz stefanoverna.com 5 CLI thor A simple and efficient tool

    for building self- documenting command line utilities
  18. @steffoz stefanoverna.com 5 class App < Thor map "-L" =>

    :list desc "install APP_NAME", "install an app" method_options :version def install(name) version = options[:version] # do something end desc "list [SEARCH]", "list all the apps" def list(search = "") # list everything end end
  19. @steffoz stefanoverna.com Ok, now let's make the world a better

    place. Let's release redditor to the public!
  20. @steffoz stefanoverna.com mine redditor \ -D 'Browse Reddit from CLI'

    \ -a 'Stefano Verna' \ -e '[email protected]' \ --markdown \ --yard \ --bin \ --bundler
  21. @steffoz stefanoverna.com # redditor.gemspec gem.add_dependency 'activesupport' gem.add_dependency 'thor' gem.add_dependency 'command_line_reporter'

    gem.add_dependency 'json' gem.add_dependency 'googl' gem.add_dependency 'rest-client' gem.add_dependency 'launchy'
  22. @steffoz stefanoverna.com # lib/redditor/cli.rb require 'thor' module Redditor class CLI

    < Thor desc "hot", "Shows the hottest stories on Reddit" def hot # COPY AND PASTE? HELL YEAH! end end end
  23. @steffoz stefanoverna.com # spec/acceptance/cli_usage_spec.rb require 'spec_helper' describe "CLI usage" do

    describe "redditor hot" do it "returns the hottest news on Reddit" do redditor "hot" output.should == read_fixture("output/hot.txt") end end end
  24. @steffoz stefanoverna.com # spec/cli_spec.rb require 'spec_helper' require 'redditor/cli' describe Redditor::CLI

    do describe ".hot" do it "fetches /hot.json stories, and prints them out" do stories = double("stories array") Redditor::Story.stub(:find).with(type: :hot).and_return(stories) Redditor::StoriesPrinter.should_receive(:output).with(stories) subject.hot end end end
  25. @steffoz stefanoverna.com # spec/cli_spec.rb require 'spec_helper' require 'redditor/cli' describe Redditor::CLI

    do describe ".hot" do it "fetches /hot.json stories, and prints them out" do stories = double("stories array") Redditor::Story.stub(:find).with(type: :hot).and_return(stories) Redditor::StoriesPrinter.should_receive(:output).with(stories) subject.hot end end end double  generic  term  for  any  kind  of  pretend  object  used  in   place  of  a  real  object  for  testing  purposes.  The  name  comes   from  the  notion  of  a  Stunt  Double  in  movies.
  26. @steffoz stefanoverna.com # spec/cli_spec.rb require 'spec_helper' require 'redditor/cli' describe Redditor::CLI

    do describe ".hot" do it "fetches /hot.json stories, and prints them out" do stories = double("stories array") Redditor::Story.stub(:find).with(type: :hot).and_return(stories) Redditor::StoriesPrinter.should_receive(:output).with(stories) subject.hot end end end
  27. @steffoz stefanoverna.com It's time to start a new inner loop

    to think about what Story#find should do.
  28. @steffoz stefanoverna.com It's time to start a new inner loop

    to think about what Story#find should do.
  29. @steffoz stefanoverna.com # spec/story_spec.rb require 'spec_helper' require 'redditor/story' describe Redditor::Story

    do describe "#find" do it "fetches stories for the specified filter, mapping them into Stories" do url = double("Reddit URL") raw_story = double("story hash") story = double("Story") Redditor::Story.stub(:url_for).with(foo: :bar).and_return(url) Redditor::Story.stub(:fetch).with(url).and_return([ raw_story ]) Redditor::Story.stub(:new).with(raw_story).and_return(story) result = Redditor::Story.find(foo: :bar) result.should have(1).story result.first.should == story end end end
  30. @steffoz stefanoverna.com # lib/redditor/story.rb module Redditor class Story def self.find(filters)

    fetch(url_for(filters)).map do |raw_story| Story.new(raw_story) end end end end