Skip to content
Browse files

initial commit

  • Loading branch information...
0 parents commit 544e6a590765f6995ccae609a2367518109f8e44 Adam Lowe and Stephen Caudill and Wes Gibbs committed with adamlowe Dec 15, 2009
5 .document
@@ -0,0 +1,5 @@
+README.rdoc
+lib/**/*.rb
+bin/*
+features/**/*.feature
+LICENSE
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2009 Adam
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 README.rdoc
@@ -0,0 +1,17 @@
+= slurper
+
+Description goes here.
+
+== Note on Patches/Pull Requests
+
+* Fork the project.
+* Make your feature addition or bug fix.
+* Add tests for it. This is important so I don't break it in a
+ future version unintentionally.
+* Commit, do not mess with rakefile, version, or history.
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
+* Send me a pull request. Bonus points for topic branches.
+
+== Copyright
+
+Copyright (c) 2009 Adam. See LICENSE for details.
51 Rakefile
@@ -0,0 +1,51 @@
+require 'rubygems'
+require 'rake'
+
+begin
+ require 'jeweler'
+ Jeweler::Tasks.new do |gem|
+ gem.name = "slurper"
+ gem.summary = %Q{takes a formatted story file and puts it on Pivotal Tracker}
+ gem.description = %Q{
+ Slurps stories from the given file (stories.txt by default) and creates
+ Pivotal Tracker stories from them. Useful during story carding sessions
+ when you want to capture a number of stories quickly without clicking
+ your way through the Tracker UI.
+ }
+ gem.email = "info@hashrocket.com"
+ gem.homepage = "http://github.com/hashrocket/slurper"
+ gem.authors = ["Wes Gibbs", "Adam Lowe", "Stephen Caudill", "Tim Pope"]
+ gem.add_development_dependency "rspec", ">= 1.2.9"
+ gem.files = FileList["{bin,lib}/**/*"]
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
+ end
+ Jeweler::GemcutterTasks.new
+rescue LoadError
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
+end
+
+require 'spec/rake/spectask'
+Spec::Rake::SpecTask.new(:spec) do |spec|
+ spec.libs << 'lib' << 'spec'
+ spec.spec_files = FileList['spec/**/*_spec.rb']
+end
+
+Spec::Rake::SpecTask.new(:rcov) do |spec|
+ spec.libs << 'lib' << 'spec'
+ spec.pattern = 'spec/**/*_spec.rb'
+ spec.rcov = true
+end
+
+task :spec => :check_dependencies
+
+task :default => :spec
+
+require 'rake/rdoctask'
+Rake::RDocTask.new do |rdoc|
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
+
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = "test #{version}"
+ rdoc.rdoc_files.include('README*')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
1 VERSION
@@ -0,0 +1 @@
+0.0.0
39 bin/slurp
@@ -0,0 +1,39 @@
+#!/usr/bin/env ruby
+
+# Slurps stories from the given file (stories.txt by default) and creates
+# Pivotal Tracker stories from them. Useful during story carding sessions
+# when you want to capture a number of stories quickly without clicking
+# your way through the Tracker UI.
+
+# Note that if you include labels in stories.txt, they don't appear
+# immediately in Tracker. You'll have to refresh Tracker after a few seconds
+# to see them.
+
+$:.unshift(File.join(File.dirname(File.dirname(__FILE__)),'lib'))
+require 'rubygems'
+require 'slurper'
+require 'optparse'
+
+options = {}
+OptionParser.new do |opts|
+ opts.on("-r", "--reverse", "Reverse story creation order") do |v|
+ options[:reverse] = v
+ end
+end.parse!
+
+story_file = ARGV.empty? ? "stories.txt" : ARGV[0]
+
+story_lines = Array.new
+stories = Array.new
+IO.foreach(story_file) do |line|
+ if line[0,2] != "=="
+ story_lines << line
+ else
+ stories << Story.new.parse(story_lines)
+ story_lines.clear
+ end
+end
+
+stories.reverse! unless options[:reverse]
+
+stories.each { |story| story.save }
1 lib/slurper.rb
@@ -0,0 +1 @@
+require 'story'
70 lib/story.rb
@@ -0,0 +1,70 @@
+require 'activeresource'
+
+class Story < ActiveResource::Base
+
+ @@defaults = YAML.load_file('story_defaults.yml')
+ self.site = "http://www.pivotaltracker.com/services/v2/projects/#{@@defaults['project_id']}"
+ headers['X-TrackerToken'] = @@defaults.delete("token")
+ attr_accessor :story_lines
+
+ def initialize(attributes = {})
+ @attributes = {}
+ @prefix_options = {}
+ load(@@defaults.merge(attributes))
+ end
+
+ def parse(story_lines)
+ @story_lines = story_lines
+ parse_name
+ parse_description
+ parse_labels
+ self
+ end
+
+ private
+
+ def parse_name
+ @story_lines.each_with_index do |line, i|
+ if start_of_value?(line, 'name')
+ if starts_with_whitespace?(@story_lines[i+1])
+ @attributes["name"] = @story_lines[i+1].strip
+ else
+ @attributes.delete("name")
+ end
+ end
+ end
+ end
+
+ def parse_description
+ @story_lines.each_with_index do |line, i|
+ if start_of_value?(line, 'description')
+ desc = Array.new
+ while((next_line = @story_lines[i+=1]) && starts_with_whitespace?(next_line)) do
+ desc << next_line
+ end
+ desc.empty? ? @attributes.delete("description") : @attributes["description"] = desc.join.gsub(/^ +/, "").gsub(/^\t+/, "")
+ end
+ end
+ end
+
+ def parse_labels
+ @story_lines.each_with_index do |line, i|
+ if start_of_value?(line, 'labels')
+ if starts_with_whitespace?(@story_lines[i+1])
+ @attributes["labels"] = @story_lines[i+1].strip
+ else
+ @attributes.delete("labels")
+ end
+ end
+ end
+ end
+
+ def starts_with_whitespace?(line)
+ line && line[0,1] =~ /\s/
+ end
+
+ def start_of_value?(line, attribute)
+ line[0,attribute.size] == attribute
+ end
+
+end
BIN pkg/slurper-0.0.0.gem
Binary file not shown.
50 slurper.gemspec
@@ -0,0 +1,50 @@
+# Generated by jeweler
+# DO NOT EDIT THIS FILE DIRECTLY
+# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+ s.name = %q{slurper}
+ s.version = "0.0.0"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.authors = ["Hashrocket Dev Team"]
+ s.date = %q{2009-12-15}
+ s.default_executable = %q{slurp}
+ s.description = %q{
+ Slurps stories from the given file (stories.txt by default) and creates
+ Pivotal Tracker stories from them. Useful during story carding sessions
+ when you want to capture a number of stories quickly without clicking
+ your way through the Tracker UI.
+ }
+ s.email = %q{adambenjaminlowe@gmail.com}
+ s.executables = ["slurp"]
+ s.extra_rdoc_files = [
+ "LICENSE",
+ "README.rdoc"
+ ]
+ s.files = [
+ "bin/slurp",
+ "lib/slurper.rb",
+ "lib/story.rb"
+ ]
+ s.homepage = %q{http://github.com/hashrocket/slurper}
+ s.rdoc_options = ["--charset=UTF-8"]
+ s.require_paths = ["lib"]
+ s.rubygems_version = %q{1.3.5}
+ s.summary = %q{takes a formatted story file and puts it on Pivotal Tracker}
+
+ if s.respond_to? :specification_version then
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+ s.specification_version = 3
+
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
+ else
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
+ end
+ else
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
+ end
+end
+
3 spec/empty_attributes.txt
@@ -0,0 +1,3 @@
+name
+description
+labels
12 spec/full_story.txt
@@ -0,0 +1,12 @@
+name
+ Profit
+description
+ In order to do something
+ As a role
+ I want to click a thingy
+
+ Acceptance:
+ * do the thing
+ * don't forget the other thing
+labels
+ money,power,fame
2 spec/name_only.txt
@@ -0,0 +1,2 @@
+name
+ Profit
2 spec/quoted_attributes.txt
@@ -0,0 +1,2 @@
+description
+ I have a "quote"
95 spec/story_spec.rb
@@ -0,0 +1,95 @@
+require 'rubygems'
+require 'spec'
+require 'story'
+
+describe Story do
+
+ it ".parse should return a reference to the story" do
+ story = Story.new
+ story.parse("").should == story
+ end
+
+ context "deals with leading/trailing whitespace" do
+ before do
+ story_lines = IO.readlines(File.dirname(__FILE__) + "/whitespacey_story.txt")
+ @story = Story.new.parse(story_lines)
+ end
+
+ it "strips whitespace from the name" do
+ @story.name.should == "Profit"
+ end
+
+ it "strips whitespace from the description" do
+ @story.description.should == "In order to do something\nAs a role\nI want to click a thingy\n"
+ end
+ end
+
+ context "given values for all attributes" do
+ before do
+ story_lines = IO.readlines(File.dirname(__FILE__) + "/full_story.txt")
+ @story = Story.new.parse(story_lines)
+ end
+
+ it "parses the name correctly" do
+ @story.name.should == "Profit"
+ end
+
+ it "parses the description correctly" do
+ @story.description.should == "In order to do something\nAs a role\nI want to click a thingy\n\nAcceptance:\n* do the thing\n* don't forget the other thing\n"
+ end
+
+ it "parses the label correctly" do
+ @story.labels.should == "money,power,fame"
+ end
+ end
+
+ context "given only a name" do
+ before do
+ story_lines = IO.readlines(File.dirname(__FILE__) + "/name_only.txt")
+ @story = Story.new.parse(story_lines)
+ end
+
+ it "should parse the name correctly" do
+ @story.name.should == "Profit"
+ end
+
+ it "should use the default value for the description" do
+ @story.description.should == "In order to \nAs a \nI want \n\nAcceptance:\n* "
+ end
+
+ it "should use the default value for the labels" do
+ @story.labels.should == "slurper"
+ end
+ end
+
+ context "given empty attributes" do
+ before do
+ story_lines = IO.readlines(File.dirname(__FILE__) + "/empty_attributes.txt")
+ @story = Story.new.parse(story_lines)
+ end
+
+ it "should not set any name" do
+ @story.attributes.keys.should_not include("name")
+ end
+
+ it "should not set any description" do
+ @story.attributes.keys.should_not include("description")
+ end
+
+ it "should not set any labels" do
+ @story.attributes.keys.should_not include("labels")
+ end
+ end
+
+ context "given attributes with spaces" do
+ before do
+ story_lines = IO.readlines(File.dirname(__FILE__) + "/quoted_attributes.txt")
+ @story = Story.new.parse(story_lines)
+ end
+
+ it "should set the description correctly" do
+ @story.description.should == "I have a \"quote\"\n"
+ end
+ end
+
+end
6 spec/whitespacey_story.txt
@@ -0,0 +1,6 @@
+name
+ Profit
+description
+ In order to do something
+ As a role
+ I want to click a thingy
7 story_defaults.yml
@@ -0,0 +1,7 @@
+---
+project_id: 1234 # found in the project settings or address bar in Tracker
+token: 123abc123abc123abc # found on your profile page in Tracker
+name: Untitled
+description: "In order to \nAs a \nI want \n\nAcceptance:\n* "
+requested_by: Jane Hashrocket # must be a user on the project
+labels: slurper # these take a few seconds to show up in Tracker

0 comments on commit 544e6a5

Please sign in to comment.
Something went wrong with that request. Please try again.