pv 0.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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format=progress
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pv.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rspec'
8
+ gem 'vcr'
9
+ gem 'webmock', '~> 1.8'
10
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Tom Scott
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # PV
2
+
3
+ Use Pivotal Tracker in the shell.
4
+
5
+ ## Installation
6
+
7
+ Install the binary:
8
+
9
+ $ gem install pv
10
+
11
+ Then create `~/.pivotal.yml` with your deets:
12
+
13
+ ```yaml
14
+ username: user@email.com
15
+ password: your-super-secret-password
16
+ project: your-awesome-project-name
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ Here are all of the commands Piv can do:
22
+
23
+ ### pv
24
+
25
+ Simply running `piv` will open your `$PAGER` with the stories
26
+ in your "My Work" tab. Each story is one line and includes the
27
+ title and the ID:
28
+
29
+ 123456 My spoon is too big. (A Banana)
30
+
31
+ ### pv show 123456
32
+
33
+ Show the entire story's details, including tasks. Again, this is
34
+ viewed in your `$PAGER`...
35
+
36
+ Feature 123456 (6 points)
37
+ Requester: A Banana <banana@work.com>
38
+ Owner: John Doe <someone@home.com>
39
+ Status: NOT STARTED
40
+
41
+ My spoon is too big.
42
+
43
+ I AM A BANANA!
44
+
45
+
46
+ ### pv edit 123456 -s {start|finish|deliver|accept|reject|restart|close} -m "message"
47
+
48
+ Edits the status of a given story on Pivotal Tracker, with an optional message. The
49
+ message is appended to the story in comments.
50
+
51
+ ### pv {start|finish|deliver|accept|reject|restart|close} 123456 -m "message"
52
+
53
+ Easier-to-remember aliases for the above command.
54
+
55
+ ### pv create "Story title" --points=3
56
+
57
+ Creates a story on Pivotal Tracker. Opens your `$EDITOR` so you can provide more information
58
+ than simply the title, unless you pass --minimal. Stories are created as features by default,
59
+ but you can override this by passing `--bug` or `--chore`. Note: `--points` values, while
60
+ required if creating a feature, are ignored when creating bugs or chores. They are also not
61
+ required on the command.
62
+
63
+ ### pv help
64
+
65
+ Show a command manual.
66
+
67
+ ## Contributing
68
+
69
+ 1. Fork it
70
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
71
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
72
+ 4. Push to the branch (`git push origin my-new-feature`)
73
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/pv ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.expand_path('./lib')
4
+
5
+ require 'pv'
6
+
7
+ Pv::Command.start
data/lib/pv.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'pv/configuration'
2
+ require 'pv/command'
3
+ require 'pv/version'
4
+ require 'pv/bug_tracker'
5
+ require 'pv/story'
6
+
7
+ module Pv
8
+ def self.config
9
+ @config ||= Pv::Configuration.new
10
+ end
11
+
12
+ def self.tracker
13
+ @tracker ||= Pv::BugTracker.new
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ require 'pivotal_tracker'
2
+
3
+ module Pv
4
+ class BugTracker
5
+ attr_reader :username, :password, :token
6
+
7
+ def initialize with_username=nil, and_password=nil
8
+ @username = with_username || Pv.config.username
9
+ @password = and_password || Pv.config.password
10
+ @token = PivotalTracker::Client.token(username, password)
11
+
12
+ PivotalTracker::Client.use_ssl = true
13
+ end
14
+
15
+ def connected?
16
+ @token != nil
17
+ end
18
+
19
+ def stories for_project_id=nil, and_full_name=nil
20
+ project_id = for_project_id || Pv.config.project_id
21
+ user = and_full_name || Pv.config.name
22
+
23
+ project = PivotalTracker::Project.all.select { |p| p.id == project_id }.first
24
+ project.stories.all(owned_by: user)
25
+ end
26
+ end
27
+ end
data/lib/pv/command.rb ADDED
@@ -0,0 +1,58 @@
1
+ require 'thor'
2
+ require 'active_support/all'
3
+
4
+ module Pv
5
+ class Command < Thor
6
+ include Thor::Actions
7
+
8
+ default_task :log
9
+ desc :log, "Show every story assigned to you on this project."
10
+ def log
11
+ Pv.tracker.stories.each do |story|
12
+ say "[#{story.id}] #{story.name} < #{story.requested_by} >"
13
+ end
14
+ end
15
+
16
+ desc "show STORY_ID", "Show the full text and attributes of a story on this project."
17
+ def show story_id
18
+ story = File.write "/tmp/story", Story.find(story_id).render
19
+ run "less -R /tmp/story"
20
+ end
21
+
22
+ desc "edit STORY_ID", "Edit a story's status on this project."
23
+ method_option :status, default: '', alias: 's'
24
+ method_option :message, default: "", alias: 'm'
25
+ def edit story_id
26
+ story = Story.find story_id
27
+ story.update(status: options[:status]) unless options[:status].blank?
28
+ story.comment! options[:message] unless options[:message].blank?
29
+ end
30
+
31
+ %w(start finish deliver accept reject restart).each do |status|
32
+ desc "#{status} STORY_ID", "#{status.titleize} a story on this project."
33
+ define_method(status) do |story_id|
34
+ options[:status] = status
35
+ edit(story_id)
36
+ end
37
+ end
38
+
39
+ desc :help, "Show all commands"
40
+ def help
41
+ say <<-TEXT
42
+
43
+ pv: pivotal tracker in the shell
44
+
45
+ A simple command-line interface for Pivotal Tracker.
46
+ Configure in ~/.pv:
47
+
48
+ username: 'your-username'
49
+ password: 'secret'
50
+ project_id: 123456
51
+
52
+ Then use the following commands to manage your project:
53
+ TEXT
54
+ say "\n"
55
+ super
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,20 @@
1
+ require 'yaml'
2
+
3
+ module Pv
4
+ class Configuration
5
+ attr_reader :username, :password, :attributes, :project_id, :name
6
+
7
+ def initialize
8
+ @path = File.expand_path "~/.pv"
9
+ @attributes = YAML::load_file @path
10
+ @username = @attributes['username']
11
+ @password = @attributes['password']
12
+ @project_id = @attributes['project_id']
13
+ @name = @attributes['name']
14
+ end
15
+
16
+ def present?
17
+ File.exists? @path
18
+ end
19
+ end
20
+ end
data/lib/pv/story.rb ADDED
@@ -0,0 +1,23 @@
1
+ module Pv
2
+ class Story
3
+ attr_accessor :story_type, :requested_by, :owned_by, :current_state,
4
+ :name, :description, :estimate, :id
5
+
6
+ def initialize from_pivotal_story
7
+ %w(id story_type requested_by owned_by current_state name description estimate).each do |attr|
8
+ self.send "#{attr}=", from_pivotal_story.send(attr)
9
+ end
10
+ end
11
+
12
+ def self.find by_id
13
+ new Pv.tracker.stories.find(by_id).first
14
+ end
15
+
16
+ # Render this Story in plain text.
17
+ def render
18
+ source = IO.read "./lib/templates/story.txt.erb"
19
+ template = ERB.new(source)
20
+ template.result(binding)
21
+ end
22
+ end
23
+ end
data/lib/pv/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Pv
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,8 @@
1
+ <%= story_type.titleize %> <%= id %> (<%= estimate %> points)
2
+ Requested By: <%= requested_by %>
3
+ Assigned To: <%= owned_by %>
4
+ Status: <%= current_state.upcase %>
5
+
6
+ <%= name %>
7
+
8
+ <%= description %>
data/pv.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pv/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "pv"
8
+ gem.version = Pv::VERSION
9
+ gem.authors = ["Tom Scott"]
10
+ gem.email = ["tubbo@psychedeli.ca"]
11
+ gem.description = %q{A command-line interface to Pivotal Tracker.}
12
+ gem.summary = %q{A command-line interface to Pivotal Tracker.}
13
+ gem.homepage = "http://github.com/tubbo/pv"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'pivotal-tracker'
21
+ gem.add_dependency 'thor'
22
+ gem.add_dependency 'active_support'
23
+ gem.add_dependency 'i18n'
24
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'pv/bug_tracker'
3
+
4
+ module Pv
5
+ describe BugTracker do
6
+ subject do
7
+ VCR.use_cassette :pivotal_client_connection do
8
+ Pv::BugTracker.new("john@doe.com", "password")
9
+ end
10
+ end
11
+
12
+ it "connects to pivotal tracker" do
13
+ subject.should be_connected
14
+ end
15
+
16
+ it "gathers the list of stories this user has to accomplish" do
17
+ VCR.use_cassette :pivotal_project_stories_search do
18
+ subject.stories(461255, "Tom Scott").should_not be_empty
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,35 @@
1
+ require 'pv/configuration'
2
+
3
+ module Pv
4
+ describe Configuration do
5
+ before do
6
+ if File.exists? File.expand_path("~/.pv")
7
+ `cp ~/.pv ~/.pv.safe`
8
+ end
9
+
10
+ `cp spec/fixtures/pv.yml ~/.pv`
11
+ end
12
+
13
+ subject { Pv::Configuration.new }
14
+
15
+ it "reads from ~/.pv YAML" do
16
+ subject.should be_present
17
+ subject.attributes.should_not be_empty
18
+ subject.attributes.count.should == 3
19
+ end
20
+
21
+ it "parses a username" do
22
+ subject.username.should == "john@doe.com"
23
+ end
24
+
25
+ it "parses a password" do
26
+ subject.password.should == "password"
27
+ end
28
+
29
+ after do
30
+ if File.exists? File.expand_path("~/.pv.safe")
31
+ `cp ~/.pv.safe ~/.pv`
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,74 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://www.pivotaltracker.com/services/v3/tokens/active
6
+ body:
7
+ encoding: US-ASCII
8
+ string: username=necromancer&password=2AUcBXdL3EYq8E87vJA3qAUNN
9
+ headers:
10
+ Accept:
11
+ - ! '*/*; q=0.5, application/xml'
12
+ Accept-Encoding:
13
+ - gzip, deflate
14
+ Content-Length:
15
+ - '55'
16
+ Content-Type:
17
+ - application/x-www-form-urlencoded
18
+ User-Agent:
19
+ - Ruby
20
+ response:
21
+ status:
22
+ code: 200
23
+ message: !binary |-
24
+ T0s=
25
+ headers:
26
+ !binary "Q29udGVudC1UeXBl":
27
+ - !binary |-
28
+ YXBwbGljYXRpb24veG1sOyBjaGFyc2V0PXV0Zi04
29
+ !binary "VHJhbnNmZXItRW5jb2Rpbmc=":
30
+ - !binary |-
31
+ Y2h1bmtlZA==
32
+ !binary "Q29ubmVjdGlvbg==":
33
+ - !binary |-
34
+ Y2xvc2U=
35
+ !binary "U3RhdHVz":
36
+ - !binary |-
37
+ MjAw
38
+ !binary "WC1Qb3dlcmVkLUJ5":
39
+ - !binary |-
40
+ UGh1c2lvbiBQYXNzZW5nZXIgKG1vZF9yYWlscy9tb2RfcmFjaykgMy4wLjE0
41
+ !binary "Q2FjaGUtQ29udHJvbA==":
42
+ - !binary |-
43
+ cHJpdmF0ZSwgbWF4LWFnZT0wLCBtdXN0LXJldmFsaWRhdGU=
44
+ !binary "WC1SdW50aW1l":
45
+ - !binary |-
46
+ MTE3
47
+ !binary "U2V0LUNvb2tpZQ==":
48
+ - !binary |-
49
+ dF9zZXNzaW9uPUJBaDdCem9QWlhod2FYSmxjMTloZEVsMU9nbFVhVzFsRFNZ
50
+ ckhJRHJNR2RWQmpvZlFHMWhjbk5vWVd4ZmQybDBhRjkxZEdOZlkyOWxjbU5w
51
+ YjI1R09nOXpaWE56YVc5dVgybGtJaVZoTUdRNE1tTmpNbUk0TldVMk1EaG1Z
52
+ ekpqTkdOak9XVXhOR1V3T1RGak9RJTNEJTNELS0zZmJiYmY3MjA5MjMwNTI1
53
+ YTQ3N2U3ZmViNTdiNzRkZjQxZDk0ZGFmOyBwYXRoPS87IHNlY3VyZTsgSHR0
54
+ cE9ubHk=
55
+ !binary "RXRhZw==":
56
+ - !binary |-
57
+ ImQzNzdhNzkwNTE3MTNiYTE3MDliNjNhMzEzNWU5MWY1Ig==
58
+ !binary "U2VydmVy":
59
+ - !binary |-
60
+ bmdpbngvMS4yLjIgKyBQaHVzaW9uIFBhc3NlbmdlciAzLjAuMTQgKG1vZF9y
61
+ YWlscy9tb2RfcmFjayk=
62
+ !binary "Q29udGVudC1FbmNvZGluZw==":
63
+ - !binary |-
64
+ Z3ppcA==
65
+ body:
66
+ encoding: ASCII-8BIT
67
+ string: !binary |-
68
+ H4sIAAAAAAAAAy3MQQ7CIBAAwHtfQbgr3cJWTIDefIF+wO5CiAqmotHfmxrv
69
+ k3HT+3YVL14euRYvYdtLwWWulEvy8nQ8bKycQudavXAJnRAuPTMFBNTnmUY0
70
+ cW8sIEc2o8aBIpHesXXqx1afSbTPnb3MpXHiRQaAfkBwagVO/ecvVvIujYkA
71
+ AAA=
72
+ http_version:
73
+ recorded_at: Sun, 18 Nov 2012 06:21:24 GMT
74
+ recorded_with: VCR 2.3.0