pivotal-slacker 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "rubygems"
5
+ require "pivotal-tracker"
6
+ require "rainbow"
7
+ require "action_view"
8
+ require "launchy"
9
+ require "pp"
10
+ require "app_config"
11
+ require "formatter"
12
+ include ActionView::Helpers::DateHelper
13
+
14
+ # ----------------------------------------------------------
15
+ # Load config
16
+
17
+ config = nil
18
+ begin
19
+ config = AppConfig.load
20
+ rescue Exception => e
21
+ puts e
22
+ exit 1
23
+ end
24
+
25
+ # ----------------------------------------------------------
26
+ # Init
27
+
28
+ PivotalTracker::Client.token = config.api_key
29
+ project = PivotalTracker::Project.find config.project
30
+
31
+ # ----------------------------------------------------------
32
+ # Main app code
33
+
34
+ require "commander/import"
35
+ require "pivotal-tracker"
36
+
37
+ program :version, "1.0"
38
+ program :description, "Pivotal Tracker command line client."
39
+
40
+ command :mine do |c|
41
+ c.syntax = "pivotal-slacker mine [options]"
42
+ c.description = "List your stories in pivotal tracker."
43
+ c.example "description", "command example"
44
+ c.option "--some-switch", "Some switch that does something"
45
+ c.action do |args, options|
46
+ # Do something or c.when_called Pivotal-slacker::Commands::Mine
47
+
48
+ states = %w{unstarted started finished accepted rejected}
49
+ owned_by = config.user
50
+
51
+ puts "Looking for stories owned by #{owned_by} in states: #{states}".color("#444444")
52
+
53
+ stories = project.stories.all(:owned_by => owned_by, :current_state => states)
54
+ stories.sort! { |a, b| a.created_at <=> b.created_at }
55
+ stories.each do |story|
56
+ id = Formatter.story_id(story.id)
57
+ name = Formatter.story_name(story.name)
58
+ state = Formatter.state(story.current_state)
59
+ requested_by = Formatter.requested_by(story.requested_by)
60
+ created_at = Formatter.time_ago(story.created_at)
61
+
62
+ puts "[#{id}] #{name} ~ #{state}, from #{requested_by}, created #{created_at}"
63
+ end
64
+ end
65
+ end
66
+
67
+ command :open do |c|
68
+ c.syntax = "pivotal-slacker open story_id"
69
+ c.description = "Open a specific Pivotal Tracker story in the browser."
70
+ c.example "Open a story with ID 123", "pivotal-slacker open 123"
71
+ c.action do |args, options|
72
+ story = project.stories.find(args[0].to_i)
73
+ Launchy.open story.url
74
+ end
75
+ end
76
+
77
+ command :show do |c|
78
+ c.syntax = "pivotal-slacker show story_id"
79
+ c.description = "Show the details of a given Pivotal Tracker story."
80
+ c.example "Show a story with ID 123", "pivotal-slacker show 123"
81
+ c.action do |args, options|
82
+ story = project.stories.find(args[0].to_i)
83
+ id = Formatter.story_id(story.id)
84
+ state = Formatter.state(story.current_state)
85
+ requested_by = Formatter.requested_by(story.requested_by)
86
+ created_at = Formatter.time_ago(story.created_at)
87
+
88
+ puts ""
89
+ puts "[#{id}] #{Formatter.story_name(story.name, :heading => true)}"
90
+ puts "#{state}, from #{requested_by}, created #{created_at}"
91
+ puts ""
92
+ puts Formatter.description(story.description)
93
+ puts ""
94
+
95
+ notes = story.notes.all.sort { |a, b| a.noted_at <=> b.noted_at }
96
+ notes.each do |note|
97
+ author = Formatter.note_author(note.author)
98
+ noted_at = Formatter.time_ago(note.noted_at)
99
+ note_text = Formatter.note_text(note.text)
100
+ puts "#{author} (#{noted_at}): #{note_text}"
101
+ end
102
+
103
+ # Extra padding line after notes.
104
+ if notes != nil and notes.size > 0
105
+ puts ""
106
+ end
107
+ end
108
+ end
109
+
110
+ command :start do |c|
111
+ c.syntax = "pivotal-slacker start story_id"
112
+ c.description = "Mark a given Pivotal Tracker story as \"started\"."
113
+ c.example "Start a story with ID 123", "pivotal-slacker start 123"
114
+ c.action do |args, options|
115
+ story_id = args[0].to_i
116
+
117
+ story = project.stories.find(story_id)
118
+ story.update :current_state => "started"
119
+
120
+ puts Formatter.story_action(Formatter.state("started"), story_id, story.name) + "."
121
+ end
122
+ end
123
+
124
+ command :comment do |c|
125
+ c.syntax = "pivotal-slacker comment story_id comment"
126
+ c.description = "Comment on a given Pivotal Tracker story."
127
+ c.example "Comment on story 123", "pivotal-slacker comment 123 \"Due to my skills I will destroy this task.\""
128
+ c.action do |args, options|
129
+ story_id = args[0].to_i
130
+ comment = args[1]
131
+
132
+ story = project.stories.find(story_id)
133
+ story.notes.create :text => comment
134
+
135
+ comment = Formatter.note_text(comment)
136
+ puts "#{Formatter.story_action "Commented on", story_id, story.name}:"
137
+ puts "#{Formatter.note_author(config.user)}: #{comment}"
138
+ end
139
+ end
140
+
141
+ command :finish do |c|
142
+ c.syntax = "pivotal-slacker finish story_id"
143
+ c.description = "Mark a given Pivotal Tracker story as \"finished\"."
144
+ c.example "Finish a story with ID 123", "pivotal-slacker finish 123"
145
+ c.action do |args, options|
146
+ story_id = args[0].to_i
147
+
148
+ story = project.stories.find(story_id)
149
+ story.update :current_state => "finished"
150
+
151
+ puts Formatter.story_action(Formatter.state("finished"), story_id, story.name) + "."
152
+ end
153
+ end
154
+
155
+ command :create do |c|
156
+ c.syntax = "pivotal-slacker create [options]"
157
+ c.description = "Create a new task in Pivotal Tracker."
158
+ c.example "Create a new task", "pivotal-slacker create --chore --owner \"Jimmy Winkerbean\""
159
+ c.option "--feature", "Designate the story as a feature"
160
+ c.option "--bug", "Designate the story as a bug"
161
+ c.option "--chore", "Designate the story as a chore"
162
+ c.option "--release", "Designate the story as a release"
163
+ c.option "--owner STRING", String, "Assigns the story to a user"
164
+ c.option "--name STRING", String, "Name of story"
165
+ c.option "--description STRING", String, "Description of story"
166
+ c.action do |args, options|
167
+ options.default :chore => false, :owner => config.user, :name => nil, :description => nil
168
+
169
+ raise "--name is required" if options.name == nil
170
+ raise "--description is required" if options.description == nil
171
+
172
+ story_type = "feature" if options.feature
173
+ story_type = "bug" if options.bug
174
+ story_type = "chore" if options.chore
175
+ story_type = "release" if options.release
176
+
177
+ puts ""
178
+ puts Formatter.story_name(options.name, :heading => true)
179
+ puts "#{Formatter.attr_descriptor('type is', Formatter.story_type(story_type))}"
180
+ puts "#{Formatter.attr_descriptor('requested by', Formatter.owner(config.user))}"
181
+ puts "#{Formatter.attr_descriptor('owned by', Formatter.owner(options.owner))}"
182
+ puts "#{Formatter.attr_descriptor('description is', Formatter.description(options.description))}"
183
+ puts ""
184
+
185
+ if agree "Really create? (y/n)"
186
+ story = project.stories.create(
187
+ :name => options.name,
188
+ :story_type => story_type,
189
+ :description => options.description,
190
+ :requested_by => config.user,
191
+ :owned_by => options.owner
192
+ )
193
+ puts Formatter.story_action("Created", story.id, story.name)
194
+ else
195
+ puts "Didn't create story."
196
+ end
197
+ end
198
+ end
199
+
@@ -0,0 +1,40 @@
1
+ require "rubygems"
2
+
3
+ class AppConfig
4
+ FILENAME = ".pt.yaml"
5
+
6
+ def self.load
7
+ require "yaml"
8
+
9
+ # Read YAML config or quit.
10
+ config = nil
11
+ begin
12
+ config = YAML.load_file FILENAME
13
+ rescue Exception => e
14
+ raise "No #{FILENAME} file found in current directory; please create one."
15
+ end
16
+
17
+ AppConfig.new :config => config
18
+ end
19
+
20
+ def initialize opts={:config => Hash.new}
21
+ @config = opts[:config]
22
+
23
+ raise "Config must include \"project\" key" if not @config.has_key? "project"
24
+ raise "Config must include \"api_key\" key" if not @config.has_key? "api_key"
25
+ raise "Config must include \"user\" key" if not @config.has_key? "user"
26
+ end
27
+
28
+ def project
29
+ @config["project"]
30
+ end
31
+
32
+ def api_key
33
+ @config["api_key"]
34
+ end
35
+
36
+ def user
37
+ @config["user"]
38
+ end
39
+
40
+ end
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+ require "rubygems"
3
+ require "rainbow"
4
+
5
+ class Formatter
6
+ def self.story_id id
7
+ id.to_s.color("#404040")
8
+ end
9
+
10
+ def self.state state
11
+ if state == "finished"
12
+ # state = state.color('009900').bright #background('009900').color('ffffff')
13
+ " ✓ #{state} ".background('#4C9900').bright.underline
14
+ elsif state == "accepted"
15
+ " ✓ #{state} ".background('#0080ff').color(:black).underline
16
+ else
17
+ state = state.bright # .background(:green).underline
18
+ end
19
+ end
20
+
21
+ def self.requested_by requested_by
22
+ requested_by.color("#3399ff")
23
+ end
24
+
25
+ def self.owner owner
26
+ self.requested_by owner
27
+ end
28
+
29
+ def self.story_name story_name, opts={:heading => false}
30
+ if opts[:heading] == true
31
+ story_name.bright.underline
32
+ else
33
+ story_name
34
+ end
35
+ end
36
+
37
+ def self.time_ago time
38
+ (distance_of_time_in_words(Time.now, time) + " ago").color('#404040')
39
+ end
40
+
41
+ def self.note_author author
42
+ self.requested_by(author)
43
+ end
44
+
45
+ def self.note_text text
46
+ text.bright
47
+ end
48
+
49
+ def self.story_action comment, story_id, story_name
50
+ story_name = Formatter.story_name(story_name, :heading => true)
51
+ "#{comment} #{Formatter.story_id(story_id)}, #{story_name}"
52
+ end
53
+
54
+ def self.story_type type
55
+ type.color '#99004C'
56
+ end
57
+
58
+ def self.attr_descriptor descriptor, attr
59
+ "#{descriptor} → ".color('#333333') + attr
60
+ end
61
+
62
+ def self.description description
63
+ description
64
+ end
65
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pivotal-slacker
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sean Gilbertson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-04 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: If you're overwhelmed with or tired of using the Pivotal Tracker web
15
+ UI, use this!
16
+ email: sean.gilbertson@gmail.com
17
+ executables:
18
+ - pivotal-slacker
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - lib/formatter.rb
23
+ - lib/app_config.rb
24
+ - bin/pivotal-slacker
25
+ homepage: https://github.com/dreki/pivotal-slacker
26
+ licenses: []
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ! '>='
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 1.8.25
46
+ signing_key:
47
+ specification_version: 3
48
+ summary: A command-line client for Pivotal Tracker.
49
+ test_files: []