yapt 0.0.1 → 0.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7f3d3e6e3a120cd87f9c1cc545edd9ac08eb22ac
4
- data.tar.gz: 2257eedb5a60cc3bc2ee63cf40c878c92f9e9ec6
3
+ metadata.gz: 23de5c03a7ac130c7f824eb67fb29f3e16f33a52
4
+ data.tar.gz: 1cc941043e57e0e3557d506bb2f176d0dddf7d1f
5
5
  SHA512:
6
- metadata.gz: 839df234bc2acc4db310d0abc5994a505dfb3ce95902e572b0a62d5bbbe7a0afa51d1935cf2520cd07b15098f9484e1e2f68b6eab4512db68e211b8da15b70e6
7
- data.tar.gz: 9aecd0b88395d255541d8b39361a3c9cc69af75a3c43dd588bd6df64581b65be57f772530a0b63646079d1bbd9bc0fc99ba67c7a17fba3995d62dfaac0c466b8
6
+ metadata.gz: a700985a6d0e2e93665ffa341acec9b17c01cdc498c2f2e2d0274411f8918ef897f1b9689766d8f64076f5b6923fc4c41e944f540fc57298a01e15f2d89df9fb
7
+ data.tar.gz: db5d1a4cfb1d4d4e237838dc490532375ae6dfe1bb64f2ce29718e87220dbd58c6ef772531ac11898b8da0e785a5312ac0896fb67092e53b56b0a2798c3f85a9
data/.gitignore CHANGED
@@ -15,5 +15,5 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
- .env
18
+ .yapt
19
19
  .yapt_member_cache
data/Gemfile CHANGED
@@ -3,5 +3,4 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in yapt.gemspec
4
4
  gemspec
5
5
 
6
- gem "dotenv"
7
6
  gem "pry"
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
- Environment variables:
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
@@ -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
@@ -1,4 +1,3 @@
1
- require 'pry'
2
1
  require "chronic"
3
2
 
4
3
  module Yapt
@@ -6,7 +5,6 @@ module Yapt
6
5
  def self.parse(args)
7
6
  args.inject({filter: ""}) do |query, arg|
8
7
  filter = new(arg)
9
- # binding.pry
10
8
  if filter.filter?
11
9
  query[:filter] += "#{filter.to_filter} "
12
10
  else
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
- @text_result = send(method, path, params)
7
+ @method, @path, @params = method, path, params
7
8
  end
8
9
 
9
10
  def result
10
- JSON.parse(@text_result)
11
+ request(method, params)
11
12
  end
12
13
 
13
- def get(path, params={})
14
- url = "#{base_url}/#{path}"
15
- RestClient.get url,
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 base_url
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
- View.display(results)
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 %>
@@ -0,0 +1,3 @@
1
+ <% @stories.each do |story| -%>
2
+ <%= story.id %> | <%= story.name %>
3
+ <% end %>
data/lib/yapt/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Yapt
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/yapt/view.rb CHANGED
@@ -1,75 +1,29 @@
1
- require "ostruct"
1
+ require 'erb'
2
2
 
3
3
  module Yapt
4
4
  class View
5
- extend Forwardable
6
-
7
- def_delegators :@story, :url, :story_type, :description, :id,
8
- :current_state, :labels, :owned_by_id, :created_at, :kind,
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
- attr_reader :story
22
- def initialize(story)
23
- @story = OpenStruct.new(story)
11
+ def initialize(stories)
12
+ @stories = stories
24
13
  end
25
14
 
26
- def to_s
27
- [:id, :story_type, :current_state, :name, :nl,
28
- :created_at_display, :updated_at_display,
29
- :owner_initials, :requester_initials,
30
- :nl, :nl
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
- "No owner"
21
+ raise "#{template_path} missing!"
44
22
  end
45
23
  end
46
24
 
47
- def requester_initials
48
- "Requester: #{Member.find(requested_by_id).initials}"
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 'dotenv'
2
- Dotenv.load
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
- @project_id ||= ENV['project_id']
19
+ config.project_id
14
20
  end
15
21
 
16
22
  def self.api_token
17
- @api_token ||= ENV['api_token']
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
- # option :urgent, type: :boolean
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 Story.find(args)
37
- puts
38
- puts args.inspect
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
- def members
42
- Member.cache
80
+ private
81
+
82
+ def system_open(whatever)
83
+ puts "Opening: #{whatever}"
84
+ system "open #{whatever}"
43
85
  end
44
86
 
45
- def moo
46
- puts "MOOOO"
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.1
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: 2013-12-30 00:00:00.000000000 Z
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