yapt 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/Gemfile +0 -1
- data/README.md +23 -1
- data/lib/yapt/config.rb +36 -0
- data/lib/yapt/filter.rb +0 -2
- data/lib/yapt/move.rb +28 -0
- data/lib/yapt/request.rb +26 -9
- data/lib/yapt/story.rb +90 -3
- data/lib/yapt/templates/detail.erb +12 -0
- data/lib/yapt/templates/simple.erb +3 -0
- data/lib/yapt/version.rb +1 -1
- data/lib/yapt/view.rb +15 -61
- data/lib/yapt.rb +58 -12
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23de5c03a7ac130c7f824eb67fb29f3e16f33a52
|
4
|
+
data.tar.gz: 1cc941043e57e0e3557d506bb2f176d0dddf7d1f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a700985a6d0e2e93665ffa341acec9b17c01cdc498c2f2e2d0274411f8918ef897f1b9689766d8f64076f5b6923fc4c41e944f540fc57298a01e15f2d89df9fb
|
7
|
+
data.tar.gz: db5d1a4cfb1d4d4e237838dc490532375ae6dfe1bb64f2ce29718e87220dbd58c6ef772531ac11898b8da0e785a5312ac0896fb67092e53b56b0a2798c3f85a9
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -8,13 +8,35 @@ A command line Pivotal Tracker client. In early days.
|
|
8
8
|
|
9
9
|
## Usage
|
10
10
|
|
11
|
-
|
11
|
+
Put these in a yaml .yapt file in your project or ~ directory:
|
12
12
|
|
13
13
|
* api_token
|
14
14
|
* project_id
|
15
15
|
|
16
16
|
yapt list "created_since=last friday" limit=5 keyword
|
17
17
|
|
18
|
+
Use a different template with v(iew)=
|
19
|
+
|
20
|
+
```
|
21
|
+
yapt list v=detail
|
22
|
+
yapt list view=simple
|
23
|
+
```
|
24
|
+
|
25
|
+
Check out an individual story...
|
26
|
+
|
27
|
+
```
|
28
|
+
yapt show 12345
|
29
|
+
```
|
30
|
+
|
31
|
+
Move it ...
|
32
|
+
|
33
|
+
```
|
34
|
+
yapt move 12345 54321 # move 12345 just above 54321
|
35
|
+
yapt move 12345 tback # move 12345 to top of backlog
|
36
|
+
```
|
37
|
+
|
38
|
+
Other keywords: ticebox, tice, bback, tbacklog, bice, bicebox
|
39
|
+
|
18
40
|
## Contributing
|
19
41
|
|
20
42
|
1. Fork it
|
data/lib/yapt/config.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Yapt
|
4
|
+
class Config
|
5
|
+
attr_reader :project_path
|
6
|
+
def initialize(project_path)
|
7
|
+
@project_path = project_path
|
8
|
+
end
|
9
|
+
|
10
|
+
def project_id
|
11
|
+
config.fetch('project_id')
|
12
|
+
end
|
13
|
+
|
14
|
+
def api_token
|
15
|
+
config.fetch('api_token')
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def load_or_hash(path)
|
21
|
+
YAML.load_file(path) rescue Hash.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def config
|
25
|
+
@config ||= user_config.merge(project_config)
|
26
|
+
end
|
27
|
+
|
28
|
+
def project_config
|
29
|
+
load_or_hash("#{project_path}/.yapt")
|
30
|
+
end
|
31
|
+
|
32
|
+
def user_config
|
33
|
+
load_or_hash("#{Dir.home}/.yapt")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/yapt/filter.rb
CHANGED
data/lib/yapt/move.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Yapt
|
2
|
+
class Move
|
3
|
+
def self.setup(id, target_id)
|
4
|
+
to_move = Story.find(id)
|
5
|
+
target = Story.find(target_id)
|
6
|
+
new(to_move, target)
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :to_move, :target
|
10
|
+
def initialize(to_move, target)
|
11
|
+
@to_move, @target = to_move, target
|
12
|
+
end
|
13
|
+
|
14
|
+
def description
|
15
|
+
"Move #{to_move.id} just above #{target.id}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def params
|
19
|
+
{
|
20
|
+
before_id: target.id
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def execute!
|
25
|
+
Request.new("stories/#{to_move.id}", params, :put).result
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/yapt/request.rb
CHANGED
@@ -2,24 +2,41 @@ require "rest-client"
|
|
2
2
|
|
3
3
|
module Yapt
|
4
4
|
class Request
|
5
|
+
attr_reader :method, :path, :params
|
5
6
|
def initialize(path, params = {}, method = :get)
|
6
|
-
@
|
7
|
+
@method, @path, @params = method, path, params
|
7
8
|
end
|
8
9
|
|
9
10
|
def result
|
10
|
-
|
11
|
+
request(method, params)
|
11
12
|
end
|
12
13
|
|
13
|
-
def
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
params: params,
|
18
|
-
"X-TrackerToken" => Yapt.api_token
|
14
|
+
def request(method, payload)
|
15
|
+
options = {
|
16
|
+
"X-TrackerToken" => Yapt.api_token,
|
17
|
+
"Content-Type" => "application/json"
|
19
18
|
}
|
19
|
+
response_handling = ->(response, request, result, &block) {
|
20
|
+
case response.code
|
21
|
+
when 200
|
22
|
+
JSON.parse(response.to_s)
|
23
|
+
else
|
24
|
+
puts "Non-200 response!"
|
25
|
+
puts response.to_s
|
26
|
+
end
|
27
|
+
}
|
28
|
+
if method == :get
|
29
|
+
RestClient.get(url, options.merge(params: payload), &response_handling)
|
30
|
+
else
|
31
|
+
RestClient.send(method, url, payload, options, &response_handling)
|
32
|
+
end
|
20
33
|
end
|
21
34
|
|
22
|
-
def
|
35
|
+
def url
|
36
|
+
"#{self.class.base_url}/#{path}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.base_url
|
23
40
|
"https://www.pivotaltracker.com/
|
24
41
|
services/v5/projects/#{Yapt.project_id}".gsub(/\s+/,'')
|
25
42
|
end
|
data/lib/yapt/story.rb
CHANGED
@@ -1,11 +1,98 @@
|
|
1
1
|
module Yapt
|
2
2
|
class Story
|
3
3
|
def self.find(options = ["limit=5"])
|
4
|
+
return find_one(options) if options.kind_of?(String)
|
4
5
|
params = Filter.parse(options)
|
5
|
-
puts View.headline(params[:filter])
|
6
|
-
puts
|
7
6
|
results = Request.new("stories", params, :get).result
|
8
|
-
|
7
|
+
results.collect {|r| new(r) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.find_one(id)
|
11
|
+
case id
|
12
|
+
when /\A\d+\Z/ then find_by_id(id)
|
13
|
+
when 'tback' then top_of_backlog
|
14
|
+
when 'tbacklog' then top_of_backlog
|
15
|
+
when 'tice' then top_of_icebox
|
16
|
+
when 'ticebox' then top_of_icebox
|
17
|
+
|
18
|
+
when 'bback' then bottom_of_backlog
|
19
|
+
when 'bbacklog' then bottom_of_backlog
|
20
|
+
when 'bice' then bottom_of_icebox
|
21
|
+
when 'bicebox' then bottom_of_icebox
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.find_by_id(id)
|
26
|
+
new(Request.new("stories/#{id}", {}, :get).result)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.top_of_backlog
|
30
|
+
find(["state:unstarted", "limit:1"]).first
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.top_of_icebox
|
34
|
+
find(["state:unscheduled", "limit:1"]).first
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.bottom_of_backlog
|
38
|
+
find(["state:unstarted"]).last
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.bottom_of_icebox
|
42
|
+
find(["state:unscheduled"]).last
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.just_url(id)
|
46
|
+
if id
|
47
|
+
"#{base_site_url}/stories/#{id}"
|
48
|
+
else
|
49
|
+
base_site_url
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.images_url(id)
|
54
|
+
"#{just_url(id)}/images"
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.base_site_url
|
58
|
+
"https://www.pivotaltracker.com/s/projects/#{Yapt.project_id}"
|
59
|
+
end
|
60
|
+
|
61
|
+
attr_reader :raw_story
|
62
|
+
def initialize(raw_story)
|
63
|
+
@raw_story = raw_story
|
64
|
+
end
|
65
|
+
|
66
|
+
[:url, :story_type, :description, :id,
|
67
|
+
:current_state, :labels, :owned_by_id, :created_at, :kind,
|
68
|
+
:project_id, :requested_by_id, :updated_at, :name].each do |attr|
|
69
|
+
define_method attr do
|
70
|
+
raw_story[attr.to_s]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def owner_initials
|
75
|
+
if owned_by_id
|
76
|
+
"Owner: #{Member.find(owned_by_id).initials}"
|
77
|
+
else
|
78
|
+
"No owner"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def requester_initials
|
83
|
+
"Requester: #{Member.find(requested_by_id).initials}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def created_at_display
|
87
|
+
"Created: #{time_display(created_at)}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def updated_at_display
|
91
|
+
"Updated: #{time_display(updated_at)}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def time_display(time)
|
95
|
+
Time.parse(time).strftime("%a %d%b %I:%M")
|
9
96
|
end
|
10
97
|
end
|
11
98
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<% @stories.each do |story| -%>
|
2
|
+
<%= story.id %> | <%= story.name %>
|
3
|
+
<%= story.description %>
|
4
|
+
<%= '-' * 100 %>
|
5
|
+
<%= story.created_at_display %> | <%= story.updated_at_display %> | <%= story.current_state %> | <%= story.requester_initials %> | <%= story.owner_initials %>
|
6
|
+
<%= '=' * 100 %>
|
7
|
+
<% end %>
|
8
|
+
<% if false %>
|
9
|
+
[:url, :story_type, :description, :id,
|
10
|
+
:current_state, :labels, :owned_by_id, :created_at, :kind,
|
11
|
+
:project_id, :requested_by_id, :updated_at, :name].each do |attr|
|
12
|
+
<% end %>
|
data/lib/yapt/version.rb
CHANGED
data/lib/yapt/view.rb
CHANGED
@@ -1,75 +1,29 @@
|
|
1
|
-
require
|
1
|
+
require 'erb'
|
2
2
|
|
3
3
|
module Yapt
|
4
4
|
class View
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
:project_id, :requested_by_id, :updated_at, :name
|
10
|
-
|
11
|
-
def self.headline(str)
|
12
|
-
"About to display: #{str}"
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.display(stories)
|
16
|
-
stories.inject("") do |str, story|
|
17
|
-
str + new(story).to_s
|
18
|
-
end
|
5
|
+
def self.extract_display_config(args)
|
6
|
+
display = args.detect {|a| a =~ /\Av(iew)?[=:]/ }
|
7
|
+
args.delete(display)
|
8
|
+
display ? display.split(/[=:]/).last : nil
|
19
9
|
end
|
20
10
|
|
21
|
-
|
22
|
-
|
23
|
-
@story = OpenStruct.new(story)
|
11
|
+
def initialize(stories)
|
12
|
+
@stories = stories
|
24
13
|
end
|
25
14
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
].inject("") do |str, element|
|
32
|
-
str += "#{send(element)}"
|
33
|
-
element == :nl ? str : "#{str} | "
|
34
|
-
end.gsub(/\| $/, '')
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
def owner_initials
|
40
|
-
if owned_by_id
|
41
|
-
"Owner: #{Member.find(owned_by_id).initials}"
|
15
|
+
def display(template_name)
|
16
|
+
template_path = "#{template_dir}/#{template_name}.erb"
|
17
|
+
if File.exists?(template_path)
|
18
|
+
template = IO.read template_path
|
19
|
+
ERB.new(template, 0, '-').result(binding)
|
42
20
|
else
|
43
|
-
"
|
21
|
+
raise "#{template_path} missing!"
|
44
22
|
end
|
45
23
|
end
|
46
24
|
|
47
|
-
def
|
48
|
-
"
|
49
|
-
end
|
50
|
-
|
51
|
-
def created_at_display
|
52
|
-
"Created: #{time_display(created_at)}"
|
53
|
-
end
|
54
|
-
|
55
|
-
def updated_at_display
|
56
|
-
"Updated: #{time_display(updated_at)}"
|
57
|
-
end
|
58
|
-
|
59
|
-
def time_display(time)
|
60
|
-
Time.parse(time).strftime("%a %d%b %I:%M")
|
61
|
-
end
|
62
|
-
|
63
|
-
def id_name
|
64
|
-
"#{story.id} | #{story.name}\n"
|
65
|
-
end
|
66
|
-
|
67
|
-
def nl
|
68
|
-
"\n"
|
25
|
+
def template_dir
|
26
|
+
"#{File.dirname(__FILE__)}/templates"
|
69
27
|
end
|
70
28
|
end
|
71
29
|
end
|
72
|
-
# ["url", "story_type", "description", "id", "current_state",
|
73
|
-
# "labels", "owned_by_id", "created_at", "kind", "project_id",
|
74
|
-
# "requested_by_id", "updated_at", "name"]
|
75
|
-
|
data/lib/yapt.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require '
|
2
|
-
|
1
|
+
require 'pry'
|
2
|
+
require 'highline/import'
|
3
3
|
|
4
4
|
module Yapt
|
5
5
|
autoload :VERSION, "yapt/version"
|
@@ -8,13 +8,19 @@ module Yapt
|
|
8
8
|
autoload :View, "yapt/view"
|
9
9
|
autoload :Member, "yapt/member"
|
10
10
|
autoload :Request, "yapt/request"
|
11
|
+
autoload :Config, "yapt/config"
|
12
|
+
autoload :Move, "yapt/move"
|
13
|
+
|
14
|
+
def self.config
|
15
|
+
@config ||= Config.new(Dir.pwd)
|
16
|
+
end
|
11
17
|
|
12
18
|
def self.project_id
|
13
|
-
|
19
|
+
config.project_id
|
14
20
|
end
|
15
21
|
|
16
22
|
def self.api_token
|
17
|
-
|
23
|
+
config.api_token
|
18
24
|
end
|
19
25
|
|
20
26
|
def self.cache_duration
|
@@ -30,20 +36,60 @@ module Yapt
|
|
30
36
|
end
|
31
37
|
|
32
38
|
class Runner < Boson::Runner
|
33
|
-
|
39
|
+
attr_reader :start_time
|
40
|
+
def initialize
|
41
|
+
@start_time = Time.now
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
34
45
|
def list(*args)
|
46
|
+
display_config = View.extract_display_config(args)
|
47
|
+
@stories = Story.find(args)
|
48
|
+
display_config ||= (@stories.length > 1) ? "simple" : "detail"
|
49
|
+
output View.new(@stories).display(display_config)
|
50
|
+
end
|
51
|
+
|
52
|
+
def show(id)
|
53
|
+
@story = Story.find(id)
|
54
|
+
output View.new([@story]).display("detail")
|
55
|
+
end
|
56
|
+
|
57
|
+
def open(id = nil)
|
58
|
+
system_open Story.just_url(id)
|
59
|
+
end
|
60
|
+
|
61
|
+
def images(id)
|
62
|
+
system_open Story.images_url(id)
|
63
|
+
end
|
64
|
+
|
65
|
+
def move(id, destination)
|
66
|
+
mover = Move.setup(id, destination)
|
35
67
|
puts
|
36
|
-
puts
|
37
|
-
puts
|
38
|
-
|
68
|
+
puts mover.description
|
69
|
+
puts View.new([mover.to_move, mover.target]).display("simple")
|
70
|
+
|
71
|
+
permission = ask("Make this move? ").downcase
|
72
|
+
if %w(y yes).include?(permission)
|
73
|
+
mover.execute!
|
74
|
+
puts "Moved!"
|
75
|
+
else
|
76
|
+
puts "Aborted. Oh well."
|
77
|
+
end
|
39
78
|
end
|
40
79
|
|
41
|
-
|
42
|
-
|
80
|
+
private
|
81
|
+
|
82
|
+
def system_open(whatever)
|
83
|
+
puts "Opening: #{whatever}"
|
84
|
+
system "open #{whatever}"
|
43
85
|
end
|
44
86
|
|
45
|
-
def
|
46
|
-
puts
|
87
|
+
def output(str)
|
88
|
+
puts
|
89
|
+
puts str
|
90
|
+
puts
|
91
|
+
puts "Took #{Time.now - start_time} seconds."
|
92
|
+
puts
|
47
93
|
end
|
48
94
|
end
|
49
95
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yapt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Schenkman-Moore
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-01-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: boson
|
@@ -109,10 +109,14 @@ files:
|
|
109
109
|
- Rakefile
|
110
110
|
- bin/yapt
|
111
111
|
- lib/yapt.rb
|
112
|
+
- lib/yapt/config.rb
|
112
113
|
- lib/yapt/filter.rb
|
113
114
|
- lib/yapt/member.rb
|
115
|
+
- lib/yapt/move.rb
|
114
116
|
- lib/yapt/request.rb
|
115
117
|
- lib/yapt/story.rb
|
118
|
+
- lib/yapt/templates/detail.erb
|
119
|
+
- lib/yapt/templates/simple.erb
|
116
120
|
- lib/yapt/version.rb
|
117
121
|
- lib/yapt/view.rb
|
118
122
|
- yapt.gemspec
|