git-pivotal 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -12,14 +12,14 @@ begin
12
12
  gemspec.description = "A collection of git utilities to ease integration with Pivotal Tracker"
13
13
  gemspec.email = "jeff@trydionel.com"
14
14
  gemspec.homepage = "http://github.com/trydionel/git-pivotal"
15
- gemspec.authors = ["Jeff Tucker"]
16
-
17
- gemspec.executables = ["git-pick"]
15
+ gemspec.authors = ["Jeff Tucker", "Sam Stokes"]
18
16
 
19
17
  gemspec.add_dependency "nokogiri"
20
18
  gemspec.add_dependency "rest-client"
19
+ gemspec.add_dependency "builder"
21
20
 
22
21
  gemspec.add_development_dependency "rspec"
22
+ gemspec.add_development_dependency "rcov"
23
23
  gemspec.add_development_dependency "mocha"
24
24
  end
25
25
 
@@ -29,7 +29,7 @@ rescue
29
29
  end
30
30
 
31
31
  Spec::Rake::SpecTask.new do |t|
32
- t.warning = true
32
+ t.spec_opts = ['--color']
33
33
  t.rcov = true
34
34
  t.rcov_opts = ['--exclude', 'gems']
35
35
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.2.0
data/bin/git-bug ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+
5
+ require 'pivotal'
6
+ require 'commands/bug'
7
+
8
+ exit Commands::Bug.new(STDIN, STDOUT, *ARGV).run!
data/bin/git-feature ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+
5
+ require 'pivotal'
6
+ require 'commands/feature'
7
+
8
+ exit Commands::Feature.new(STDIN, STDOUT, *ARGV).run!
data/bin/git-finish ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+
5
+ require 'pivotal'
6
+ require 'commands/finish'
7
+
8
+ exit Commands::Finish.new(STDIN, STDOUT, *ARGV).run!
data/bin/git-pick CHANGED
@@ -3,6 +3,7 @@
3
3
  $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
4
 
5
5
  require 'pivotal'
6
- require 'commands/pick'
6
+ require 'commands/feature'
7
7
 
8
- exit Pick.new(*ARGV).run!
8
+ STDOUT.puts "DEPRECATION WARNING: git pick has been deprecated. Please use git feature instead."
9
+ exit Commands::Feature.new(STDIN, STDOUT, *ARGV).run!
data/git-pivotal.gemspec CHANGED
@@ -5,15 +5,14 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{git-pivotal}
8
- s.version = "0.1.3"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Jeff Tucker"]
12
- s.date = %q{2010-01-30}
13
- s.default_executable = %q{git-pick}
11
+ s.authors = ["Jeff Tucker", "Sam Stokes"]
12
+ s.date = %q{2010-02-14}
14
13
  s.description = %q{A collection of git utilities to ease integration with Pivotal Tracker}
15
14
  s.email = %q{jeff@trydionel.com}
16
- s.executables = ["git-pick"]
15
+ s.executables = ["git-bug", "git-feature", "git-finish", "git-pick"]
17
16
  s.extra_rdoc_files = [
18
17
  "LICENSE"
19
18
  ]
@@ -23,20 +22,33 @@ Gem::Specification.new do |s|
23
22
  "LICENSE",
24
23
  "Rakefile",
25
24
  "VERSION",
25
+ "bin/git-bug",
26
+ "bin/git-feature",
27
+ "bin/git-finish",
26
28
  "bin/git-pick",
27
29
  "git-pivotal.gemspec",
30
+ "lib/commands/base.rb",
31
+ "lib/commands/bug.rb",
32
+ "lib/commands/feature.rb",
33
+ "lib/commands/finish.rb",
28
34
  "lib/commands/pick.rb",
29
35
  "lib/pivotal.rb",
30
36
  "lib/pivotal/api.rb",
31
37
  "lib/pivotal/associations.rb",
38
+ "lib/pivotal/attributes.rb",
32
39
  "lib/pivotal/base.rb",
33
40
  "lib/pivotal/collection.rb",
34
41
  "lib/pivotal/project.rb",
35
42
  "lib/pivotal/story.rb",
36
43
  "readme.markdown",
37
- "spec/commands/pick_spec.rb",
44
+ "spec/commands/base_spec.rb",
45
+ "spec/commands/bug_spec.rb",
46
+ "spec/commands/feature_spec.rb",
47
+ "spec/factories.rb",
48
+ "spec/factory.rb",
38
49
  "spec/pivotal/api_spec.rb",
39
50
  "spec/pivotal/associations_spec.rb",
51
+ "spec/pivotal/attributes_spec.rb",
40
52
  "spec/pivotal/base_spec.rb",
41
53
  "spec/pivotal/collection_spec.rb",
42
54
  "spec/pivotal/project_spec.rb",
@@ -49,9 +61,14 @@ Gem::Specification.new do |s|
49
61
  s.rubygems_version = %q{1.3.5}
50
62
  s.summary = %q{A collection of git utilities to ease integration with Pivotal Tracker}
51
63
  s.test_files = [
52
- "spec/commands/pick_spec.rb",
64
+ "spec/commands/base_spec.rb",
65
+ "spec/commands/bug_spec.rb",
66
+ "spec/commands/feature_spec.rb",
67
+ "spec/factories.rb",
68
+ "spec/factory.rb",
53
69
  "spec/pivotal/api_spec.rb",
54
70
  "spec/pivotal/associations_spec.rb",
71
+ "spec/pivotal/attributes_spec.rb",
55
72
  "spec/pivotal/base_spec.rb",
56
73
  "spec/pivotal/collection_spec.rb",
57
74
  "spec/pivotal/project_spec.rb",
@@ -66,18 +83,24 @@ Gem::Specification.new do |s|
66
83
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
67
84
  s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
68
85
  s.add_runtime_dependency(%q<rest-client>, [">= 0"])
86
+ s.add_runtime_dependency(%q<builder>, [">= 0"])
69
87
  s.add_development_dependency(%q<rspec>, [">= 0"])
88
+ s.add_development_dependency(%q<rcov>, [">= 0"])
70
89
  s.add_development_dependency(%q<mocha>, [">= 0"])
71
90
  else
72
91
  s.add_dependency(%q<nokogiri>, [">= 0"])
73
92
  s.add_dependency(%q<rest-client>, [">= 0"])
93
+ s.add_dependency(%q<builder>, [">= 0"])
74
94
  s.add_dependency(%q<rspec>, [">= 0"])
95
+ s.add_dependency(%q<rcov>, [">= 0"])
75
96
  s.add_dependency(%q<mocha>, [">= 0"])
76
97
  end
77
98
  else
78
99
  s.add_dependency(%q<nokogiri>, [">= 0"])
79
100
  s.add_dependency(%q<rest-client>, [">= 0"])
101
+ s.add_dependency(%q<builder>, [">= 0"])
80
102
  s.add_dependency(%q<rspec>, [">= 0"])
103
+ s.add_dependency(%q<rcov>, [">= 0"])
81
104
  s.add_dependency(%q<mocha>, [">= 0"])
82
105
  end
83
106
  end
@@ -0,0 +1,63 @@
1
+ require 'optparse'
2
+
3
+ module Commands
4
+ class Base
5
+
6
+ attr_accessor :input, :output, :options
7
+
8
+ def initialize(input=STDIN, output=STDOUT, *args)
9
+ @input = input
10
+ @output = output
11
+ @options = {}
12
+
13
+ parse_gitconfig
14
+ parse_argv(*args)
15
+ end
16
+
17
+ def put(string, newline=true)
18
+ @output.print(newline ? string + "\n" : string) unless options[:quiet]
19
+ end
20
+
21
+ def sys(cmd)
22
+ put cmd if options[:verbose]
23
+ system cmd
24
+ end
25
+
26
+ def get(cmd)
27
+ put cmd if options[:verbose]
28
+ `#{cmd}`
29
+ end
30
+
31
+ def run!
32
+ unless options[:api_token] && options[:project_id]
33
+ put "Pivotal Tracker API Token and Project ID are required"
34
+ return 1
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def parse_gitconfig
41
+ token = get("git config --get pivotal.api-token").strip
42
+ id = get("git config --get pivotal.project-id").strip
43
+ name = get("git config --get pivotal.full-name").strip
44
+
45
+ options[:api_token] = token unless token == ""
46
+ options[:project_id] = id unless id == ""
47
+ options[:full_name] = name unless name == ""
48
+ end
49
+
50
+ def parse_argv(*args)
51
+ OptionParser.new do |opts|
52
+ opts.banner = "Usage: git pick [options]"
53
+ opts.on("-k", "--api-key=", "Pivotal Tracker API key") { |k| options[:api_token] = k }
54
+ opts.on("-p", "--project-id=", "Pivotal Trakcer project id") { |p| options[:project_id] = p }
55
+ opts.on("-n", "--full-name=", "Pivotal Trakcer full name") { |n| options[:full_name] = n }
56
+ opts.on("-q", "--quiet", "Quiet, no-interaction mode") { |q| options[:quiet] = q }
57
+ opts.on("-v", "--[no-]verbose", "Run verbosely") { |v| options[:verbose] = v }
58
+ opts.on_tail("-h", "--help", "This usage guide") { put opts; exit 0 }
59
+ end.parse!(args)
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,19 @@
1
+ require 'commands/pick'
2
+
3
+ module Commands
4
+ class Bug < Pick
5
+
6
+ def type
7
+ "bug"
8
+ end
9
+
10
+ def plural_type
11
+ "bugs"
12
+ end
13
+
14
+ def branch_suffix
15
+ "bugfix"
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ require 'commands/pick'
2
+
3
+ module Commands
4
+ class Feature < Pick
5
+
6
+ def type
7
+ "feature"
8
+ end
9
+
10
+ def plural_type
11
+ "features"
12
+ end
13
+
14
+ def branch_suffix
15
+ "feature"
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,41 @@
1
+ require 'commands/base'
2
+
3
+ module Commands
4
+ class Finish < Base
5
+
6
+ def run!
7
+ super
8
+
9
+ # clunky way to get branch name... need better method
10
+ current_branch = get('git status | head -1').gsub(/^.+On branch /, '').chomp
11
+ story_id = current_branch[/\d+/]
12
+ unless story_id
13
+ put "Branch name must contain a Pivotal Tracker story id"
14
+ return 1
15
+ end
16
+
17
+ api = Pivotal::Api.new(:api_token => options[:api_token])
18
+ project = api.projects.find(:id => options[:project_id])
19
+ story = project.stories.find(:id => story_id)
20
+
21
+ put "Marking Story #{story_id} as finished..."
22
+ if story.update_attributes(:current_state => :finished)
23
+
24
+ target_branch = "develop"
25
+ put "Merging #{current_branch} into #{target_branch}"
26
+ sys "git checkout #{target_branch}"
27
+ sys "git merge --no-ff #{current_branch}"
28
+
29
+ put "Removing #{current_branch} branch"
30
+ sys "git branch -d #{current_branch}"
31
+
32
+ return 0
33
+ else
34
+ put "Unable to mark Story #{story_id} as finished"
35
+
36
+ return 1
37
+ end
38
+ end
39
+
40
+ end
41
+ end
data/lib/commands/pick.rb CHANGED
@@ -1,100 +1,61 @@
1
- require 'optparse'
1
+ require 'commands/base'
2
2
 
3
- class Pick
3
+ module Commands
4
+ class Pick < Base
4
5
 
5
- attr_accessor :options
6
-
7
- def initialize(*args)
8
- @options = {}
9
- parse_gitconfig
10
- parse_argv(*args)
11
- end
6
+ def type
7
+ raise Error "must define in subclass"
8
+ end
9
+
10
+ def plural_type
11
+ raise Error "must define in subclass"
12
+ end
12
13
 
13
- def run!
14
- unless options[:api_token] && options[:project_id]
15
- puts "Pivotal Tracker API Token and Project ID are required"
16
- return 1
14
+ def branch_suffix
15
+ raise Error "must define in subclass"
17
16
  end
18
17
 
19
- puts "Retrieving latest stories from Pivotal Tracker..."
20
- api = Pivotal::Api.new(:api_token => options[:api_token])
18
+ def run!
19
+ super
20
+
21
+ put "Retrieving latest #{plural_type} from Pivotal Tracker..."
22
+ api = Pivotal::Api.new(:api_token => options[:api_token])
21
23
 
22
- project = api.projects.find(:id => options[:project_id])
23
- story = project.stories.find(:conditions => { :story_type => :feature, :current_state => :unstarted }, :limit => 1).first
24
+ project = api.projects.find(:id => options[:project_id])
25
+ story = project.stories.find(:conditions => { :story_type => type, :current_state => :unstarted }, :limit => 1).first
24
26
 
25
- unless story
26
- puts "No stories available!"
27
- return 0
28
- end
27
+ unless story
28
+ put "No stories available!"
29
+ return 0
30
+ end
29
31
 
30
- puts "Story: #{story.name}"
31
- puts "URL: #{story.url}"
32
+ put "Story: #{story.name}"
33
+ put "URL: #{story.url}"
32
34
 
33
- suffix = "feature"
34
- unless options[:quiet]
35
- print "Enter branch name (will be prepended by #{story.id}) [feature]: "
36
- suffix = gets.chomp
35
+ put "Updating story status in Pivotal Tracker..."
36
+ if story.start!(:owned_by => options[:full_name])
37
+
38
+ suffix = branch_suffix
39
+ unless options[:quiet]
40
+ put "Enter branch name (will be prepended by #{story.id}) [#{suffix}]: ", false
41
+ suffix = input.gets.chomp
37
42
 
38
- suffix = "feature" if suffix == ""
39
- end
43
+ suffix = "feature" if suffix == ""
44
+ end
40
45
 
41
- puts "Creating branch..."
42
- branch = "#{story.id}-#{suffix}"
43
- create_branch branch
44
-
45
- puts "Updating story status in Pivotal Tracker..."
46
- story.start!(:owned_by => options[:full_name])
47
-
48
- return 0
49
- end
50
-
51
- private
52
-
53
- def sys(cmd)
54
- puts cmd if options[:verbose]
55
- system cmd
56
- end
57
-
58
- def get(cmd)
59
- puts cmd if options[:verbose]
60
- `#{cmd}`
61
- end
62
-
63
- def parse_gitconfig
64
- token = get("git config --get pivotal.api-token").strip
65
- id = get("git config --get pivotal.project-id").strip
66
- name = get("git config --get pivotal.full-name").strip
46
+ branch = "#{story.id}-#{suffix}"
47
+ if get("git branch").match(branch).nil?
48
+ put "Creating #{branch} branch..."
49
+ sys "git checkout -b #{branch}"
50
+ end
67
51
 
68
- options[:api_token] = token if token
69
- options[:project_id] = id if id
70
- options[:full_name] = name if name
71
- end
72
-
73
- def parse_argv(*args)
74
- OptionParser.new do |opts|
75
- opts.banner = "Usage: git pick [options]"
76
- opts.on("-k", "--api-key=", "Pivotal Tracker API key") { |k| options[:api_token] = k }
77
- opts.on("-p", "--project-id=", "Pivotal Trakcer project id") { |p| options[:project_id] = p }
78
- opts.on("-n", "--full-name=", "Pivotal Trakcer full name") { |n| options[:full_name] = n }
79
- opts.on("-q", "--quiet", "Quiet, no-interaction mode") { |q| options[:quiet] = q }
80
- opts.on("-v", "--[no-]verbose", "Run verbosely") { |v| options[:verbose] = v }
81
- opts.on_tail("-h", "--help", "This usage guide") { puts opts; exit 0 }
82
- end.parse!(args)
83
- end
84
-
85
- def agrees?(selection)
86
- selection =~ /y/i || selection == ""
87
- end
88
-
89
- def create_branch(branch)
90
- unless branch_exists?(branch)
91
- puts "Creating #{branch} branch..."
92
- sys "git checkout -b #{branch}"
52
+ return 0
53
+ else
54
+ put "Unable to mark story as started"
55
+
56
+ return 1
57
+ end
93
58
  end
94
- end
95
59
 
96
- def branch_exists?(branch)
97
- !get("git branch").match(branch).nil?
98
60
  end
99
-
100
61
  end
@@ -0,0 +1,25 @@
1
+ module Pivotal
2
+ module Attributes
3
+
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+
7
+ class << base
8
+ attr_accessor :attributes
9
+ end
10
+ end
11
+
12
+ module ClassMethods
13
+ def has_attributes(*attributes)
14
+ @attributes = attributes.map(&:to_s)
15
+
16
+ attributes.each do |attribute|
17
+ define_method attribute do
18
+ parsed_resource.xpath("*/" + attribute.to_s).text
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ end
25
+ end
data/lib/pivotal/base.rb CHANGED
@@ -6,9 +6,9 @@ require 'builder'
6
6
  module Pivotal
7
7
  class Base
8
8
  include Pivotal::Associations
9
+ include Pivotal::Attributes
9
10
 
10
11
  attr_accessor :resource, :xml
11
- undef id
12
12
 
13
13
  def initialize(params = {})
14
14
  params.each do |key, value|
@@ -24,13 +24,19 @@ module Pivotal
24
24
  @parsed_resource ||= Nokogiri::XML(xml)
25
25
  end
26
26
 
27
- def method_missing(method, *args)
28
- parsed_resource.css(method.to_s).text
29
- end
30
-
31
27
  def update_attributes(options = {})
32
- @xml = resource.put generate_xml(options)
33
- return self
28
+ begin
29
+ resource.put generate_xml(options) do |response|
30
+ if response.code == 200
31
+ @xml = response.body
32
+ return true
33
+ else
34
+ return false
35
+ end
36
+ end
37
+ rescue RestClient::Exception
38
+ return false
39
+ end
34
40
  end
35
41
 
36
42
  class << self
@@ -58,9 +64,7 @@ module Pivotal
58
64
 
59
65
  def allowed_keys(options = {})
60
66
  options.reject do |key, _|
61
- !%w[id story_type url estimate current_state
62
- description name requested_by owned_by
63
- created_at accepted_at labels].include? key.to_s
67
+ !self.class.attributes.include? key.to_s
64
68
  end
65
69
  end
66
70
 
@@ -50,7 +50,7 @@ module Pivotal
50
50
 
51
51
  def build_collection_from_xml(xml = "")
52
52
  Nokogiri::XML(xml).xpath(component_class.xpath).map do |item|
53
- item_id = item.xpath("//id").text
53
+ item_id = item.xpath("id").text
54
54
  component_class.new :resource => resource[item_id], :xml => item.to_xml
55
55
  end
56
56
  end
@@ -2,6 +2,13 @@ module Pivotal
2
2
  class Project < Base
3
3
 
4
4
  has_collection :stories, :of => Pivotal::Story
5
+ has_attributes :id, :name, :iteration_length, :week_start_day,
6
+ :point_scale, :account, :velocity_scheme,
7
+ :current_velocity, :initial_velocity,
8
+ :number_of_iterations_to_show, :labels,
9
+ :allow_attachments, :public, :use_https,
10
+ :bugs_and_chores_are_estimatable, :commit_mode,
11
+ :last_activity_at, :memberships, :integrations
5
12
 
6
13
  end
7
14
  end
data/lib/pivotal/story.rb CHANGED
@@ -1,9 +1,35 @@
1
1
  module Pivotal
2
2
  class Story < Base
3
3
 
4
+ has_attributes :id, :story_type, :url, :estimate, :current_state,
5
+ :description, :name, :requested_by, :owned_by,
6
+ :created_at, :accepted_at, :labels
7
+
4
8
  def start!(options = {})
9
+ return false if feature? && unestimated?
10
+
5
11
  update_attributes(options.merge(:current_state => :started))
6
12
  end
13
+
14
+ def feature?
15
+ story_type == "feature"
16
+ end
17
+
18
+ def bug?
19
+ story_type == "bug"
20
+ end
21
+
22
+ def chore?
23
+ story_type == "chore"
24
+ end
25
+
26
+ def release?
27
+ story_type == "release"
28
+ end
29
+
30
+ def unestimated?
31
+ estimate == "unestimated"
32
+ end
7
33
 
8
34
  end
9
35
  end
data/lib/pivotal.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require File.join(File.dirname(__FILE__),'pivotal', 'collection')
2
2
  require File.join(File.dirname(__FILE__),'pivotal', 'associations')
3
+ require File.join(File.dirname(__FILE__),'pivotal', 'attributes')
3
4
 
4
5
  require File.join(File.dirname(__FILE__),'pivotal','base')
5
6
  require File.join(File.dirname(__FILE__),'pivotal','story')
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+ require 'commands/base'
3
+
4
+ describe Commands::Base do
5
+
6
+ before(:each) do
7
+ @input = mock('input')
8
+ @output = mock('output')
9
+
10
+ # stub out git config requests
11
+ Commands::Base.any_instance.stubs(:get).with { |v| v =~ /git config/ }.returns("")
12
+ end
13
+
14
+ it "should set the api key with the -k option" do
15
+ @pick = Commands::Base.new(@input, @output,"-k", "1234")
16
+ @pick.options[:api_token].should == "1234"
17
+ end
18
+
19
+ it "should set the api key with the --api-token= option" do
20
+ @pick = Commands::Base.new(@input, @output,"--api-key=1234")
21
+ @pick.options[:api_token].should == "1234"
22
+ end
23
+
24
+ it "should set the project id with the -p option" do
25
+ @pick = Commands::Base.new(@input, @output,"-p", "1")
26
+ @pick.options[:project_id].should == "1"
27
+ end
28
+
29
+ it "should set the project id with the --project-id= option" do
30
+ @pick = Commands::Base.new(@input, @output,"--project-id=1")
31
+ @pick.options[:project_id].should == "1"
32
+ end
33
+
34
+ it "should set the full name with the -n option" do
35
+ @pick = Commands::Base.new(@input, @output,"-n", "Jeff Tucker")
36
+ @pick.options[:full_name].should == "Jeff Tucker"
37
+ end
38
+
39
+ it "should set the full name with the --full-name= option" do
40
+ @pick = Commands::Base.new(@input, @output,"--full-name=Jeff Tucker")
41
+ @pick.options[:full_name].should == "Jeff Tucker"
42
+ end
43
+
44
+ it "should set the quiet flag with the -q option" do
45
+ @pick = Commands::Base.new(@input, @output,"-q")
46
+ @pick.options[:quiet].should be_true
47
+ end
48
+
49
+ it "should set the quiet flag with the --quiet option" do
50
+ @pick = Commands::Base.new(@input, @output,"--quiet")
51
+ @pick.options[:quiet].should be_true
52
+ end
53
+
54
+ it "should set the verbose flag with the -v option" do
55
+ @pick = Commands::Base.new(@input, @output,"-v")
56
+ @pick.options[:verbose].should be_true
57
+ end
58
+
59
+ it "should set the verbose flag with the --verbose option" do
60
+ @pick = Commands::Base.new(@input, @output,"--verbose")
61
+ @pick.options[:verbose].should be_true
62
+ end
63
+
64
+ it "should unset the verbose flag with the --no-verbose option" do
65
+ @pick = Commands::Base.new(@input, @output,"--no-verbose")
66
+ @pick.options[:verbose].should be_false
67
+ end
68
+
69
+ it "should print a message if the API token is missing" do
70
+ @output.expects(:print).with("Pivotal Tracker API Token and Project ID are required\n")
71
+
72
+ @pick = Commands::Base.new(@input, @output, "-p", "1")
73
+ @pick.run!
74
+ end
75
+
76
+ it "should print a message if the project ID is missing" do
77
+ @output.expects(:print).with("Pivotal Tracker API Token and Project ID are required\n")
78
+
79
+ @pick = Commands::Base.new(@input, @output, "-k", "1")
80
+ @pick.run!
81
+ end
82
+
83
+ end
@@ -0,0 +1,25 @@
1
+ require 'commands/bug'
2
+ require 'spec_helper'
3
+
4
+ describe Commands::Bug do
5
+
6
+ before(:each) do
7
+ # stub out git config requests
8
+ Commands::Bug.any_instance.stubs(:get).with { |v| v =~ /git config/ }.returns("")
9
+
10
+ @bug = Commands::Bug.new
11
+ end
12
+
13
+ it "should specify its story type" do
14
+ @bug.type.should == "bug"
15
+ end
16
+
17
+ it "should specify a plural for its story types" do
18
+ @bug.plural_type.should == "bugs"
19
+ end
20
+
21
+ it "should specify its branch suffix" do
22
+ @bug.branch_suffix.should == "bugfix"
23
+ end
24
+
25
+ end
@@ -0,0 +1,25 @@
1
+ require 'commands/feature'
2
+ require 'spec_helper'
3
+
4
+ describe Commands::Feature do
5
+
6
+ before(:each) do
7
+ # stub out git config requests
8
+ Commands::Feature.any_instance.stubs(:get).with { |v| v =~ /git config/ }.returns("")
9
+
10
+ @feature = Commands::Feature.new
11
+ end
12
+
13
+ it "should specify its story type" do
14
+ @feature.type.should == "feature"
15
+ end
16
+
17
+ it "should specify a plural for its story types" do
18
+ @feature.plural_type.should == "features"
19
+ end
20
+
21
+ it "should specify its branch suffix" do
22
+ @feature.branch_suffix.should == "feature"
23
+ end
24
+
25
+ end
data/spec/factories.rb ADDED
@@ -0,0 +1,13 @@
1
+ require File.join(File.dirname(__FILE__), 'factory')
2
+
3
+ Factory.define :story, {
4
+ :id => 1,
5
+ :current_state => :unstarted,
6
+ :story_type => :feature,
7
+ :estimate => 1,
8
+ }
9
+
10
+ Factory.define :project, {
11
+ :id => 1,
12
+ :name => "Project"
13
+ }
data/spec/factory.rb ADDED
@@ -0,0 +1,26 @@
1
+ class Factory
2
+ class << self
3
+ def factories
4
+ @@factories ||= {}
5
+ end
6
+
7
+ def define(type, attributes)
8
+ factories[type] = attributes
9
+ end
10
+ end
11
+ end
12
+
13
+
14
+ def Factory(type, attrs = {})
15
+ defaults = Factory.factories[type]
16
+ attrs = defaults.merge(attrs)
17
+
18
+ markup = Builder::XmlMarkup.new
19
+ markup.__send__(type) do
20
+ attrs.each do |attribute, value|
21
+ markup.__send__(attribute, value.to_s)
22
+ end
23
+ end
24
+
25
+ markup.target!
26
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Pivotal::Attributes do
4
+
5
+ it "should load the #has_attributes method onto a class" do
6
+ @base = Class.new
7
+ @base.should_not respond_to(:has_attributes)
8
+
9
+ @base.send :include, Pivotal::Attributes
10
+ @base.should respond_to(:has_attributes)
11
+ end
12
+
13
+ it "should add methods specified in #has_attributes to the object" do
14
+ @base = Class.new
15
+ @base.send :include, Pivotal::Attributes
16
+
17
+ @base.new.should_not respond_to(:one)
18
+ @base.has_attributes :one
19
+ @base.new.should respond_to(:one)
20
+ end
21
+
22
+ it "should add the attributes specified in #has_attributes to the class #attributes list" do
23
+ @base = Class.new
24
+ @base.send :include, Pivotal::Attributes
25
+
26
+ @base.attributes.should be_nil
27
+ @base.has_attributes :one
28
+ @base.attributes.should == ["one"]
29
+ end
30
+
31
+ end
@@ -20,11 +20,6 @@ describe Pivotal::Base do
20
20
  @base.parsed_resource.should be_a(Nokogiri::XML::Document)
21
21
  end
22
22
 
23
- it "should forward undefined methods to the XML parse tree" do
24
- @base.should_not respond_to(:id)
25
- @base.id.should == "1"
26
- end
27
-
28
23
  it "should present the class's item xpath" do
29
24
  @base.class.xpath.should == "//api"
30
25
  end
@@ -37,7 +32,12 @@ describe Pivotal::Base do
37
32
 
38
33
  before(:each) do
39
34
  @xml = "<api><current_state>started</current_state></api>"
40
- @base.resource.expects(:put).with(@xml).returns(@xml)
35
+ @response = mock("Response")
36
+ @response.stubs(:code).returns(200)
37
+ @response.stubs(:body).returns(@xml)
38
+
39
+ @base.resource.expects(:put).with(@xml).yields(@response)
40
+ @base.class.has_attributes :current_state
41
41
  end
42
42
 
43
43
  it "should be able to update the remote resource with a hash of string values" do
@@ -55,7 +55,16 @@ describe Pivotal::Base do
55
55
  it "should update the stored xml with the new remote model" do
56
56
  lambda {
57
57
  @base.update_attributes(:current_state => "started")
58
- }.should change(@base, :xml).to(@xml)
58
+ }.should change(@base, :xml).to(@response.body)
59
+ end
60
+
61
+ it "should return true if the response code is 200" do
62
+ @base.update_attributes(:current_state => :started).should == true
63
+ end
64
+
65
+ it "should return false if the response code is not 200" do
66
+ @response.stubs(:code).returns(422)
67
+ @base.update_attributes(:current_state => :started).should == false
59
68
  end
60
69
 
61
70
  end
@@ -4,6 +4,7 @@ describe Pivotal::Collection do
4
4
 
5
5
  before(:each) do
6
6
  @api = pivotal_api
7
+ RestClient::Resource.any_instance.stubs(:get).returns("<project><id>1</id></project>")
7
8
  end
8
9
 
9
10
  it "should find a single item given an id" do
@@ -3,8 +3,7 @@ require 'spec_helper'
3
3
  describe Pivotal::Project do
4
4
 
5
5
  before(:each) do
6
- @api = pivotal_api
7
- @project = Pivotal::Project.new :resource => @api.resource["projects"][1]
6
+ @project = Pivotal::Project.new :resource => pivotal_api.resource["projects"][1]
8
7
  end
9
8
 
10
9
  it "should be connected to the project resource" do
@@ -16,4 +15,20 @@ describe Pivotal::Project do
16
15
  @project.stories.component_class.should == Pivotal::Story
17
16
  end
18
17
 
18
+ [:id, :name, :iteration_length, :week_start_day,
19
+ :point_scale, :account, :velocity_scheme,
20
+ :current_velocity, :initial_velocity,
21
+ :number_of_iterations_to_show, :labels,
22
+ :allow_attachments, :public, :use_https,
23
+ :bugs_and_chores_are_estimatable, :commit_mode,
24
+ :last_activity_at, :memberships, :integrations].map(&:to_s).each do |method|
25
+ it "should have an accessor method for #{method}" do
26
+ @project.methods.should include(method)
27
+ end
28
+
29
+ it "should include #{method} in the list of valid attributes" do
30
+ @project.class.attributes.should include(method)
31
+ end
32
+ end
33
+
19
34
  end
@@ -2,28 +2,93 @@ require 'spec_helper'
2
2
 
3
3
  describe Pivotal::Story do
4
4
 
5
+ def story_type(type = "feature")
6
+ Factory(:story, :story_type => type)
7
+ end
8
+
5
9
  before(:each) do
6
- @api = pivotal_api
7
- @project = Pivotal::Project.new :resource => @api.resource["projects"][1]
8
- @story = Pivotal::Story.new :resource => @project.resource["stories"][1]
10
+ @story = Pivotal::Story.new :resource => pivotal_api.resource["projects"][1]["stories"][1]
9
11
  end
10
12
 
11
13
  it "should be connected to the story resource" do
12
14
  @story.resource.url.should == "https://www.pivotaltracker.com/services/v3/projects/1/stories/1"
13
15
  end
14
16
 
15
- it "should be able to mark the story as started" do
16
- @xml = "<story><current_state>started</current_state></story>"
17
- @story.resource.expects(:put).with(@xml)
17
+ [:id, :story_type, :url, :estimate, :current_state,
18
+ :description, :name, :requested_by, :owned_by,
19
+ :created_at, :accepted_at, :labels].map(&:to_s).each do |method|
20
+ it "should have an accessor method for #{method}" do
21
+ @story.methods.should include(method)
22
+ end
23
+
24
+ it "should include #{method} in the list of valid attributes" do
25
+ @story.class.attributes.should include(method)
26
+ end
27
+
28
+ it "should return the proper value when #{method} is called" do
29
+ @story.xml = Factory(:story, method.to_sym => "Test Result")
30
+ @story.send(method).should == "Test Result"
31
+ end
32
+ end
33
+
34
+ it "should specify whether the story is a feature" do
35
+ @story.xml = story_type "feature"
36
+
37
+ @story.should be_a_feature
38
+ @story.should_not be_a_bug
39
+ @story.should_not be_a_chore
40
+ @story.should_not be_a_release
41
+ end
42
+
43
+ it "should specify whether the story is a bug" do
44
+ @story.xml = story_type "bug"
45
+
46
+ @story.should_not be_a_feature
47
+ @story.should be_a_bug
48
+ @story.should_not be_a_chore
49
+ @story.should_not be_a_release
50
+ end
51
+
52
+ it "should specify whether the story is a chore" do
53
+ @story.xml = story_type "chore"
18
54
 
55
+ @story.should_not be_a_feature
56
+ @story.should_not be_a_bug
57
+ @story.should be_a_chore
58
+ @story.should_not be_a_release
59
+ end
60
+
61
+ it "should specify whether the story is a release" do
62
+ @story.xml = story_type "release"
63
+
64
+ @story.should_not be_a_feature
65
+ @story.should_not be_a_bug
66
+ @story.should_not be_a_chore
67
+ @story.should be_a_release
68
+ end
69
+
70
+ it "should be able to mark the story as started" do
71
+ @xpath = "//current_state = 'started'"
72
+ @story.resource.expects(:put).with { |xml| Nokogiri::XML(xml).xpath(@xpath) }
19
73
  @story.start!
20
74
  end
21
75
 
22
76
  it "should be able to update other attributes when marking the story as started" do
23
- @xml = "<story><current_state>started</current_state><owned_by>Jeff Tucker</owned_by></story>"
24
- @story.resource.expects(:put).with(@xml)
25
-
77
+ # Check the XML contains the right options.
78
+ # Can't just check the XML string, because the elements may be in a
79
+ # different order (because it's built from a hash).
80
+ @xpath = "//current_state = 'started' and //owned_by = 'Jeff Tucker'"
81
+ @story.resource.expects(:put).with {|xml| Nokogiri.XML(xml).xpath(@xpath) }
26
82
  @story.start!(:owned_by => "Jeff Tucker")
27
83
  end
28
84
 
29
- end
85
+ it "should return false if attempting to start an unestimated feature story" do
86
+ # bad_response = mock("Response")
87
+ # bad_response.stubs(:code).returns(422)
88
+ # RestClient::Resource.any_instance.stubs(:put).yields(bad_response)
89
+
90
+ @story.xml = Factory(:story, :story_type => :feature, :estimate => :unestimated)
91
+ @story.start!.should be_false
92
+ end
93
+
94
+ end
data/spec/spec_helper.rb CHANGED
@@ -2,20 +2,14 @@ require 'rubygems'
2
2
  require 'mocha'
3
3
  require 'builder'
4
4
  require 'pivotal'
5
- require 'commands/pick'
5
+ require File.join(File.dirname(__FILE__), 'factories')
6
6
 
7
7
  Spec::Runner.configure do |config|
8
8
  config.mock_with :mocha
9
9
  end
10
10
 
11
- def demo_xml
12
- Builder::XmlMarkup.new.project do |project|
13
- project.id 1
14
- end
15
- end
16
-
17
11
  def stub_connection_to_pivotal
18
- RestClient::Resource.any_instance.stubs(:get).returns(demo_xml)
12
+ RestClient::Resource.any_instance.stubs(:get).returns("")
19
13
  end
20
14
 
21
15
  def pivotal_api
metadata CHANGED
@@ -1,16 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-pivotal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Tucker
8
+ - Sam Stokes
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
12
 
12
- date: 2010-01-30 00:00:00 -05:00
13
- default_executable: git-pick
13
+ date: 2010-02-14 00:00:00 -05:00
14
+ default_executable:
14
15
  dependencies:
15
16
  - !ruby/object:Gem::Dependency
16
17
  name: nokogiri
@@ -32,6 +33,16 @@ dependencies:
32
33
  - !ruby/object:Gem::Version
33
34
  version: "0"
34
35
  version:
36
+ - !ruby/object:Gem::Dependency
37
+ name: builder
38
+ type: :runtime
39
+ version_requirement:
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ version:
35
46
  - !ruby/object:Gem::Dependency
36
47
  name: rspec
37
48
  type: :development
@@ -42,6 +53,16 @@ dependencies:
42
53
  - !ruby/object:Gem::Version
43
54
  version: "0"
44
55
  version:
56
+ - !ruby/object:Gem::Dependency
57
+ name: rcov
58
+ type: :development
59
+ version_requirement:
60
+ version_requirements: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
45
66
  - !ruby/object:Gem::Dependency
46
67
  name: mocha
47
68
  type: :development
@@ -55,6 +76,9 @@ dependencies:
55
76
  description: A collection of git utilities to ease integration with Pivotal Tracker
56
77
  email: jeff@trydionel.com
57
78
  executables:
79
+ - git-bug
80
+ - git-feature
81
+ - git-finish
58
82
  - git-pick
59
83
  extensions: []
60
84
 
@@ -66,20 +90,33 @@ files:
66
90
  - LICENSE
67
91
  - Rakefile
68
92
  - VERSION
93
+ - bin/git-bug
94
+ - bin/git-feature
95
+ - bin/git-finish
69
96
  - bin/git-pick
70
97
  - git-pivotal.gemspec
98
+ - lib/commands/base.rb
99
+ - lib/commands/bug.rb
100
+ - lib/commands/feature.rb
101
+ - lib/commands/finish.rb
71
102
  - lib/commands/pick.rb
72
103
  - lib/pivotal.rb
73
104
  - lib/pivotal/api.rb
74
105
  - lib/pivotal/associations.rb
106
+ - lib/pivotal/attributes.rb
75
107
  - lib/pivotal/base.rb
76
108
  - lib/pivotal/collection.rb
77
109
  - lib/pivotal/project.rb
78
110
  - lib/pivotal/story.rb
79
111
  - readme.markdown
80
- - spec/commands/pick_spec.rb
112
+ - spec/commands/base_spec.rb
113
+ - spec/commands/bug_spec.rb
114
+ - spec/commands/feature_spec.rb
115
+ - spec/factories.rb
116
+ - spec/factory.rb
81
117
  - spec/pivotal/api_spec.rb
82
118
  - spec/pivotal/associations_spec.rb
119
+ - spec/pivotal/attributes_spec.rb
83
120
  - spec/pivotal/base_spec.rb
84
121
  - spec/pivotal/collection_spec.rb
85
122
  - spec/pivotal/project_spec.rb
@@ -114,9 +151,14 @@ signing_key:
114
151
  specification_version: 3
115
152
  summary: A collection of git utilities to ease integration with Pivotal Tracker
116
153
  test_files:
117
- - spec/commands/pick_spec.rb
154
+ - spec/commands/base_spec.rb
155
+ - spec/commands/bug_spec.rb
156
+ - spec/commands/feature_spec.rb
157
+ - spec/factories.rb
158
+ - spec/factory.rb
118
159
  - spec/pivotal/api_spec.rb
119
160
  - spec/pivotal/associations_spec.rb
161
+ - spec/pivotal/attributes_spec.rb
120
162
  - spec/pivotal/base_spec.rb
121
163
  - spec/pivotal/collection_spec.rb
122
164
  - spec/pivotal/project_spec.rb
@@ -1,66 +0,0 @@
1
- require 'spec_helper'
2
- require 'commands/pick'
3
-
4
- describe Pick do
5
-
6
- before(:each) do
7
- # stub out git config requests
8
- Pick.any_instance.expects(:get).times(3).with { |v| v =~ /git config/}.returns("")
9
- end
10
-
11
- it "should set the api key with the -k option" do
12
- @pick = Pick.new("-k", "1234")
13
- @pick.options[:api_token].should == "1234"
14
- end
15
-
16
- it "should set the api key with the --api-token= option" do
17
- @pick = Pick.new("--api-key=1234")
18
- @pick.options[:api_token].should == "1234"
19
- end
20
-
21
- it "should set the project id with the -p option" do
22
- @pick = Pick.new("-p", "1")
23
- @pick.options[:project_id].should == "1"
24
- end
25
-
26
- it "should set the project id with the --project-id= option" do
27
- @pick = Pick.new("--project-id=1")
28
- @pick.options[:project_id].should == "1"
29
- end
30
-
31
- it "should set the full name with the -n option" do
32
- @pick = Pick.new("-n", "Jeff Tucker")
33
- @pick.options[:full_name].should == "Jeff Tucker"
34
- end
35
-
36
- it "should set the full name with the --full-name= option" do
37
- @pick = Pick.new("--full-name=Jeff Tucker")
38
- @pick.options[:full_name].should == "Jeff Tucker"
39
- end
40
-
41
- it "should set the quiet flag with the -q option" do
42
- @pick = Pick.new("-q")
43
- @pick.options[:quiet].should be_true
44
- end
45
-
46
- it "should set the quiet flag with the --quiet option" do
47
- @pick = Pick.new("--quiet")
48
- @pick.options[:quiet].should be_true
49
- end
50
-
51
- it "should set the verbose flag with the -v option" do
52
- @pick = Pick.new("-v")
53
- @pick.options[:verbose].should be_true
54
- end
55
-
56
- it "should set the verbose flag with the --verbose option" do
57
- @pick = Pick.new("--verbose")
58
- @pick.options[:verbose].should be_true
59
- end
60
-
61
- it "should unset the verbose flag with the --no-verbose option" do
62
- @pick = Pick.new("--no-verbose")
63
- @pick.options[:verbose].should be_false
64
- end
65
-
66
- end