pivotal-slacker 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []