slurper 0.2.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -12,7 +12,7 @@ Works great with slurper.vim! (http://github.com/alowe/vim-slurper)
12
12
  future version unintentionally.
13
13
  * Commit, do not mess with rakefile, version, or history.
14
14
  (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)
15
- * Send me a pull request. Bonus points for topic branches.
15
+ * Send me a pull request.
16
16
 
17
17
  == Setup
18
18
 
@@ -20,7 +20,7 @@ Works great with slurper.vim! (http://github.com/alowe/vim-slurper)
20
20
 
21
21
  == Config
22
22
 
23
- Create a story_defaults.yml file in your working directory for project stories.
23
+ Slurper requires a slurper_config.yml file in your working directory. This file contains your Tracker API and story requestor information.
24
24
 
25
25
  === Example
26
26
 
@@ -32,31 +32,72 @@ The project_id tells tracker which project to add your stories to. It can be fou
32
32
 
33
33
  The token can be found on your personal profile page in Pivotal Tracker.
34
34
 
35
- The name, labels and description fields provide slurper with default values for story titles and descriptoins if you don't provide them in your stories.slurper file.
36
-
37
35
  The requested_by field should be the name of your project stakeholder exactly as it appears in tracker.
38
36
 
39
37
  == Usage
40
38
 
41
- Create a stories.slurper file and compose your stories in the slurper story format. In your working directory use the slurp command to import your stories from the stories.slurper file into Pivotal Tracker. If you have multiple story files for a project, stories.slurper and stories2.slurperfor example you can explicitly provide which file to slurp.
39
+ Create a stories.slurper file and compose your stories in the slurper story format. In your working directory use the slurp command to import your stories from the stories.slurper file into Pivotal Tracker. Slurper looks for a stories.slurper file in your current directory by default, however, you can provide an alternate story source file if necessary.
42
40
 
43
- $slurp stories2.slurper
41
+ Default
44
42
 
45
- === Example
43
+ $slurp ~/stories.slurper
44
+
45
+ Also valid
46
+
47
+ $slurp ~/special_stories.slurper
48
+
49
+ Or even
50
+
51
+ $slurp ~/mystories.txt
46
52
 
53
+ === Example stories.slurper
54
+
55
+ ==
56
+ story_type:
57
+ chore
58
+ name:
59
+ Set Up Staging Environment
60
+ description:
61
+ Set up and configure staging environment for approval of stories
62
+
63
+ labels:
64
+
47
65
  ==
48
- name
49
- User Create Something
50
- description
51
- In order to accomplish some business value
52
- As a User
53
- I want to create something
54
-
55
- labels
56
- things
66
+ story_type:
67
+ feature
68
+ name:
69
+ Campaign Manager Does Something
70
+ description:
71
+ In order to get some value
72
+ As a campaign manager
73
+ I want to do something
74
+
75
+ - can do something
76
+
77
+ labels:
78
+ campaign managers
57
79
  ==
80
+ story_type:
81
+ release
82
+ name:
83
+ Big Release
84
+ description:
85
+ This release marks a lot of awesome functionality
86
+
87
+ labels:
88
+
89
+ ==
90
+ story_type:
91
+ bug
92
+ name:
93
+ I did something and nothing happened
94
+ description:
95
+ When I do something, another thing is supposed to happen but I see an error screen instead.
96
+
97
+ labels:
98
+
58
99
 
59
- Note: stories must have == before and after each story.
100
+ Note: the story source file is whitespace-sensitive. Be sure the value for each key phrase is indented with two spaces beneath each key phrase. Also, start each story with a double-equals on its own line.
60
101
 
61
102
 
62
103
  Credit - Wes Gibbs (http://wgibbs.github.com) thought of and wrote slurper as a ruby script. It was later packaged and released as a gem by his fellow Rocketeers after using it and finding it extremely handy.
data/bin/slurp CHANGED
@@ -1,11 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # Slurps stories from the given file (stories.txt by default) and creates
3
+ # Slurps stories from the given file (stories.slurper by default) and creates
4
4
  # Pivotal Tracker stories from them. Useful during story carding sessions
5
5
  # when you want to capture a number of stories quickly without clicking
6
6
  # your way through the Tracker UI.
7
7
 
8
- # Note that if you include labels in stories.txt, they don't appear
8
+ # Default story values and API token information should be provided in a
9
+ # ~/.slurper.yml file.
10
+
11
+ # Note that if you include labels in stories.slurper, they don't appear
9
12
  # immediately in Tracker. You'll have to refresh Tracker after a few seconds
10
13
  # to see them.
11
14
 
@@ -23,17 +26,4 @@ end.parse!
23
26
 
24
27
  story_file = ARGV.empty? ? "stories.slurper" : ARGV[0]
25
28
 
26
- story_lines = Array.new
27
- stories = Array.new
28
- IO.foreach(story_file) do |line|
29
- if line[0,2] != "=="
30
- story_lines << line
31
- else
32
- stories << Story.new.parse(story_lines)
33
- story_lines.clear
34
- end
35
- end
36
-
37
- stories.reverse! unless options[:reverse]
38
-
39
- stories.each { |story| story.save }
29
+ Slurper.slurp(story_file, options[:reverse])
data/lib/slurper.rb CHANGED
@@ -1 +1,58 @@
1
1
  require 'story'
2
+
3
+ class Slurper
4
+ attr_accessor :story_file, :stories
5
+
6
+ def self.slurp(story_file, reverse = true)
7
+ slurper = new(story_file)
8
+ slurper.yamlize_story_file
9
+ slurper.stories.reverse! if reverse
10
+ slurper.create_stories
11
+ end
12
+
13
+ def initialize(story_file)
14
+ self.story_file = story_file
15
+ end
16
+
17
+ def yamlize_story_file
18
+ self.stories = YAML.load(
19
+ IO.read(story_file).
20
+ gsub(/^/, " ").
21
+ gsub(/ ==.*/, "- !ruby/object:Story\n attributes:").
22
+ gsub(/ description:$/, " description: |")
23
+ )
24
+ scrub_descriptions
25
+ end
26
+
27
+ def create_stories
28
+ puts "Preparing to slurp #{stories.size} stories into Tracker..."
29
+ stories.each_with_index do |story, index|
30
+ begin
31
+ story.save
32
+ puts "#{index+1}. #{story.name}"
33
+ rescue ActiveResource::ServerError, ActiveResource::ResourceNotFound => e
34
+ msg = "Slurp failed on story "
35
+ if story.attributes["name"]
36
+ msg << story.attributes["name"]
37
+ else
38
+ msg << "##{options[:reverse] ? index + 1 : stories.size - index }"
39
+ end
40
+ puts msg + ". Error: #{e}"
41
+ end
42
+ end
43
+ end
44
+
45
+ protected
46
+
47
+ def scrub_descriptions
48
+ stories.each do |story|
49
+ if story.respond_to? :description
50
+ story.description = story.description.gsub(" ", "").gsub(" \n", "\n")
51
+ end
52
+ if story.respond_to?(:description) && story.description == ""
53
+ story.attributes["description"] = nil
54
+ end
55
+ end
56
+ end
57
+
58
+ end
data/lib/story.rb CHANGED
@@ -2,8 +2,8 @@ require 'active_resource'
2
2
 
3
3
  class Story < ActiveResource::Base
4
4
 
5
- @@defaults = YAML.load_file('story_defaults.yml')
6
- self.site = "http://www.pivotaltracker.com/services/v2/projects/#{@@defaults['project_id']}"
5
+ @@defaults = YAML.load_file('slurper_config.yml')
6
+ self.site = "http://www.pivotaltracker.com/services/v3/projects/#{@@defaults['project_id']}"
7
7
  headers['X-TrackerToken'] = @@defaults.delete("token")
8
8
  attr_accessor :story_lines
9
9
 
@@ -13,58 +13,4 @@ class Story < ActiveResource::Base
13
13
  load(@@defaults.merge(attributes))
14
14
  end
15
15
 
16
- def parse(story_lines)
17
- @story_lines = story_lines
18
- parse_name
19
- parse_description
20
- parse_labels
21
- self
22
- end
23
-
24
- private
25
-
26
- def parse_name
27
- @story_lines.each_with_index do |line, i|
28
- if start_of_value?(line, 'name')
29
- if starts_with_whitespace?(@story_lines[i+1])
30
- @attributes["name"] = @story_lines[i+1].strip
31
- else
32
- @attributes.delete("name")
33
- end
34
- end
35
- end
36
- end
37
-
38
- def parse_description
39
- @story_lines.each_with_index do |line, i|
40
- if start_of_value?(line, 'description')
41
- desc = Array.new
42
- while((next_line = @story_lines[i+=1]) && starts_with_whitespace?(next_line)) do
43
- desc << next_line
44
- end
45
- desc.empty? ? @attributes.delete("description") : @attributes["description"] = desc.join.gsub(/^ +/, "").gsub(/^\t+/, "")
46
- end
47
- end
48
- end
49
-
50
- def parse_labels
51
- @story_lines.each_with_index do |line, i|
52
- if start_of_value?(line, 'labels')
53
- if starts_with_whitespace?(@story_lines[i+1])
54
- @attributes["labels"] = @story_lines[i+1].strip
55
- else
56
- @attributes.delete("labels")
57
- end
58
- end
59
- end
60
- end
61
-
62
- def starts_with_whitespace?(line)
63
- line && line[0,1] =~ /\s/
64
- end
65
-
66
- def start_of_value?(line, attribute)
67
- line[0,attribute.size] == attribute
68
- end
69
-
70
16
  end
@@ -1,18 +1,14 @@
1
1
  require 'rubygems'
2
2
  require 'spec'
3
- require 'story'
3
+ require 'slurper'
4
4
 
5
- describe Story do
6
-
7
- it ".parse should return a reference to the story" do
8
- story = Story.new
9
- story.parse("").should == story
10
- end
5
+ describe Slurper do
11
6
 
12
7
  context "deals with leading/trailing whitespace" do
13
8
  before do
14
- story_lines = IO.readlines(File.dirname(__FILE__) + "/whitespacey_story.txt")
15
- @story = Story.new.parse(story_lines)
9
+ slurper = Slurper.new(File.join(File.dirname(__FILE__), "fixtures", "whitespacey_story.slurper"))
10
+ slurper.yamlize_story_file
11
+ @story = slurper.stories.first
16
12
  end
17
13
 
18
14
  it "strips whitespace from the name" do
@@ -26,8 +22,9 @@ describe Story do
26
22
 
27
23
  context "given values for all attributes" do
28
24
  before do
29
- story_lines = IO.readlines(File.dirname(__FILE__) + "/full_story.txt")
30
- @story = Story.new.parse(story_lines)
25
+ slurper = Slurper.new(File.join(File.dirname(__FILE__), "fixtures", "full_story.slurper"))
26
+ slurper.yamlize_story_file
27
+ @story = slurper.stories.first
31
28
  end
32
29
 
33
30
  it "parses the name correctly" do
@@ -35,18 +32,23 @@ describe Story do
35
32
  end
36
33
 
37
34
  it "parses the description correctly" do
38
- @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"
35
+ @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"
39
36
  end
40
37
 
41
38
  it "parses the label correctly" do
42
39
  @story.labels.should == "money,power,fame"
43
40
  end
41
+
42
+ it "parses the story type correctly" do
43
+ @story.story_type.should == "feature"
44
+ end
44
45
  end
45
46
 
46
47
  context "given only a name" do
47
48
  before do
48
- story_lines = IO.readlines(File.dirname(__FILE__) + "/name_only.txt")
49
- @story = Story.new.parse(story_lines)
49
+ slurper = Slurper.new(File.join(File.dirname(__FILE__), "fixtures", "name_only.slurper"))
50
+ slurper.yamlize_story_file
51
+ @story = slurper.stories.first
50
52
  end
51
53
 
52
54
  it "should parse the name correctly" do
@@ -57,27 +59,29 @@ describe Story do
57
59
 
58
60
  context "given empty attributes" do
59
61
  before do
60
- story_lines = IO.readlines(File.dirname(__FILE__) + "/empty_attributes.txt")
61
- @story = Story.new.parse(story_lines)
62
+ slurper = Slurper.new(File.join(File.dirname(__FILE__), "fixtures", "empty_attributes.slurper"))
63
+ slurper.yamlize_story_file
64
+ @story = slurper.stories.first
62
65
  end
63
66
 
64
67
  it "should not set any name" do
65
- @story.attributes.keys.should_not include("name")
68
+ @story.name.should be_nil
66
69
  end
67
70
 
68
71
  it "should not set any description" do
69
- @story.attributes.keys.should_not include("description")
72
+ @story.description.should be_nil
70
73
  end
71
74
 
72
75
  it "should not set any labels" do
73
- @story.attributes.keys.should_not include("labels")
76
+ @story.labels.should be_nil
74
77
  end
75
78
  end
76
79
 
77
80
  context "given attributes with spaces" do
78
81
  before do
79
- story_lines = IO.readlines(File.dirname(__FILE__) + "/quoted_attributes.txt")
80
- @story = Story.new.parse(story_lines)
82
+ slurper = Slurper.new(File.join(File.dirname(__FILE__), "fixtures", "quoted_attributes.slurper"))
83
+ slurper.yamlize_story_file
84
+ @story = slurper.stories.first
81
85
  end
82
86
 
83
87
  it "should set the description correctly" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slurper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wes Gibbs
@@ -12,7 +12,7 @@ autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
14
 
15
- date: 2010-01-09 00:00:00 -05:00
15
+ date: 2010-02-05 00:00:00 -05:00
16
16
  default_executable: slurp
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
@@ -67,4 +67,4 @@ signing_key:
67
67
  specification_version: 3
68
68
  summary: takes a formatted story file and puts it on Pivotal Tracker
69
69
  test_files:
70
- - spec/story_spec.rb
70
+ - spec/slurper_spec.rb