pivotal_shell 0.1.0 → 0.2.0

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 CHANGED
@@ -2,3 +2,4 @@ pkg/*
2
2
  *.gem
3
3
  .bundle
4
4
  .pivotalrc
5
+ .pivotal_cache
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pivotal_shell (0.1.0)
5
- pivotal-tracker (= 0.3)
4
+ pivotal_shell (0.2.0)
5
+ pivotal-tracker (> 0.3)
6
+ sqlite3
6
7
 
7
8
  GEM
8
9
  remote: http://rubygems.org/
@@ -13,17 +14,17 @@ GEM
13
14
  libxml-ruby (1.1.4)
14
15
  mime-types (1.16)
15
16
  nokogiri (1.4.3.1)
16
- pivotal-tracker (0.3.0)
17
+ pivotal-tracker (0.3.1)
17
18
  builder
18
19
  happymapper (>= 0.3.2)
19
20
  nokogiri (~> 1.4.3.1)
20
21
  rest-client (~> 1.6.0)
21
22
  rest-client (1.6.1)
22
23
  mime-types (>= 1.16)
24
+ sqlite3 (1.3.3)
23
25
 
24
26
  PLATFORMS
25
27
  ruby
26
28
 
27
29
  DEPENDENCIES
28
- pivotal-tracker (= 0.3)
29
30
  pivotal_shell!
@@ -1,6 +1,6 @@
1
1
  # pivotal_shell
2
2
 
3
- A command-line wrapper for [Pivotal Tracker](http://www.pivotaltracker.com)
3
+ A command-line client for [Pivotal Tracker](http://www.pivotaltracker.com)
4
4
 
5
5
  ## Installation
6
6
 
@@ -58,8 +58,27 @@ Finish story
58
58
 
59
59
  pivotal finish 123456
60
60
 
61
+ ## Caching
62
+
63
+ `pivotal-shell` caches story and user information into a local database. This can greatly speed up execution since Pivotal Tracker can be *slooow* sometimes.
64
+ The database is synchronised with the actual project data each 15 minutes (that is, if you call a `pivotal` command and the database is older that 15 minutes, it's updated).
65
+ To change this interval, use the `refresh_interval` parameter in the global or project `.pivotalrc`; the value is in minutes; set it to `-1` to completely disable updating, which can be
66
+ useful if you're going to do it by `cron`.
67
+
68
+ There are two commands related to caching:
69
+
70
+ pivotal update # update stories from the server
71
+
72
+ pivotal reload # completely reinitialize the database; use this in case of bugs
73
+
74
+ You can add this to your crontab to enable autoupdate:
75
+
76
+ 0,15,30,45 * * * * cd /your/project/path && pivotal update
77
+
61
78
  ## TODO
62
79
 
63
- Commit (with git, all comments after the story id go to git, story id gets appended to comments)
80
+ * Commit (with git, all comments after the story id go to git, story id gets appended to comments)
64
81
 
65
82
  pivotal commit 123456 "some more comments"
83
+
84
+ * Refactor caching code
@@ -3,6 +3,7 @@
3
3
  require 'optparse'
4
4
  require 'pivotal_shell'
5
5
  require 'pivotal_shell/configuration'
6
+ require 'pivotal_shell/cache'
6
7
  #require 'active_support/core_ext'
7
8
 
8
9
  PivotalShell::Configuration.load
@@ -12,6 +13,8 @@ commands = [
12
13
  ['story', 'show information about a specific story'],
13
14
  ['start', 'start a story'],
14
15
  ['finish', 'finish a story'],
16
+ ['update', 'update stories status from the server'],
17
+ ['reload', 'reload entire project from the server'],
15
18
  ]
16
19
 
17
20
  banner = "Pivotal command-line client #{PivotalShell::VERSION}\nUsage: pivotal COMMAND PARAMS\n\nAvailable commands:\n\n#{commands.map{|c, d| "#{c} - #{d}"}.join("\n")}\n\nRun pivotal COMMAND --help for more info on a specific command\n\n"
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env sh
2
+
3
+ pivotal commit $1
@@ -4,6 +4,9 @@ module PivotalShell
4
4
  class Exception < StandardError; end
5
5
  end
6
6
 
7
+ require 'pivotal_shell/version'
8
+ require 'pivotal_shell/configuration'
9
+ require 'pivotal_shell/cache'
7
10
  require 'pivotal_shell/command'
8
11
 
9
12
  # fixing pivotal-tracker's options encoding; it did not work with array filters
@@ -14,11 +17,11 @@ module PivotalTracker
14
17
  options_strings = []
15
18
  # remove options which are not filters, and encode them as such
16
19
  [:limit, :offset].each do |o|
17
- options_strings << "#{CGI.escape(o.to_s)}=#{CGI.escape(options.delete(o))}" if options[o]
20
+ options_strings << "#{CGI.escape(o.to_s)}=#{CGI.escape(options.delete(o).to_s)}" if options[o]
18
21
  end
19
22
  # assume remaining key-value pairs describe filters, and encode them as such.
20
23
  filters_string = options.map do |key, value|
21
- "#{CGI.escape(key.to_s)}%3A#{CGI.escape([value].flatten.map{|v| v.include?(' ') ? '"'+v+'"' : v}.join(','))}"
24
+ "#{CGI.escape(key.to_s)}%3A#{CGI.escape([value].flatten.map{|v| v=v.to_s; v.include?(' ') ? '"'+v+'"' : v}.join(','))}"
22
25
  end
23
26
  options_strings << "filter=#{filters_string.join('+')}" unless filters_string.empty?
24
27
  return "?#{options_strings.join('&')}"
@@ -0,0 +1,145 @@
1
+ require 'sqlite3'
2
+ require 'pivotal_shell/configuration'
3
+ require 'pivotal_shell/cache/story'
4
+ require 'pivotal_shell/cache/user'
5
+
6
+ class PivotalShell::Cache
7
+ STORY_ATTRIBUTES=%w(requested_by_id name id current_state accepted_at labels url estimate description created_at owned_by_id story_type)
8
+
9
+ attr_reader :db
10
+
11
+ def initialize(filename)
12
+ @filename = filename
13
+ @db = SQLite3::Database.new(@filename)
14
+ @db.results_as_hash = true
15
+ create_tables
16
+ refresh_if_needed
17
+ end
18
+
19
+ def inspect
20
+ "#<PivotalShell::Cache #{@filename}>"
21
+ end
22
+
23
+ def refresh
24
+ load_stories(:modified_since => self[:last_updated_at])
25
+ end
26
+
27
+ def reload
28
+ db.execute('DELETE FROM users')
29
+ db.execute('DELETE FROM stories')
30
+ @users = nil
31
+ load_stories
32
+ end
33
+
34
+ def [](key)
35
+ resultset = db.execute('SELECT value FROM settings WHERE name=?', key.to_s)
36
+ resultset.empty? ? nil : YAML.load(resultset.first['value'])
37
+ end
38
+
39
+ def []=(key, value)
40
+ db.execute('REPLACE INTO settings (name, value) VALUES (?,?)', key.to_s, YAML.dump(value))
41
+ value
42
+ end
43
+
44
+
45
+ protected
46
+
47
+ def users
48
+ @users ||= load_users_from_cache
49
+ end
50
+
51
+ def refresh_if_needed
52
+ updated_at = self[:last_updated_at]
53
+ if updated_at.nil?
54
+ puts 'Retrieving all stories from Pivotal Tracker. This could take a while...'
55
+ load_stories
56
+ else
57
+ refresh if (PivotalShell::Configuration.refresh_interval>0) && (self[:last_updated_at]+PivotalShell::Configuration.refresh_interval*60 < Time.now)
58
+ end
59
+ end
60
+
61
+ def load_stories(options={})
62
+ # Pivotal Tracker API limits one stories call to 3000 results. Thus we need to paginate.
63
+ all_stories = []
64
+ limit = 3000
65
+ offset = 0
66
+ begin
67
+ new_stories = PivotalShell::Configuration.project.stories.all(options.merge(:limit => limit, :offset => offset))
68
+ all_stories += new_stories unless new_stories.empty?
69
+ offset+=limit
70
+ end until new_stories.empty?
71
+ save_stories_to_cache(all_stories)
72
+ self[:last_updated_at] = Time.now
73
+ end
74
+
75
+ def load_users
76
+ PivotalShell::Configuration.project.memberships.all.each do |membership|
77
+ if row = db.execute('SELECT id FROM users WHERE name=?', membership.name).first
78
+ db.execute('UPDATE USERS SET role=?, initials=?, email=? WHERE id=?', membership.role, membership.initials, membership.email, row['id'])
79
+ else
80
+ db.execute('INSERT INTO USERS (name, role, initials, email) VALUES(?,?,?,?)', membership.name, membership.role, membership.initials, membership.email)
81
+ end
82
+ end
83
+ @users = load_users_from_cache
84
+ end
85
+
86
+ def load_users_from_cache
87
+ db.execute('SELECT id,name,role,initials,email FROM users').inject({}) do |hash,row|
88
+ hash[row['name'].to_s] = row
89
+ hash
90
+ end
91
+ end
92
+
93
+ def user_id_by_name(name)
94
+ name=name.to_s
95
+ return nil if name==''
96
+ user = users[name]
97
+ if user.nil?
98
+ load_users
99
+ user = users[name]
100
+ if user.nil?
101
+ db.execute('INSERT INTO USERS (name, role, initials, email) VALUES(?,"","","")', name)
102
+ load_users_from_cache
103
+ user=users[name]
104
+ end
105
+ end
106
+ user && user['id']
107
+ end
108
+
109
+ def save_stories_to_cache(stories)
110
+ insert_query = 'INSERT INTO stories (url, name, description, story_type, estimate, labels, current_state, requested_by_id, owned_by_id, created_at, accepted_at, id) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'
111
+ update_query = 'UPDATE stories SET url=?, name=?, description=?, story_type=?, estimate=?, labels=?, current_state=?, requested_by_id=?, owned_by_id=?, created_at=?, accepted_at=? WHERE id=?'
112
+ stories.each do |story|
113
+ requested_by_id = user_id_by_name(story.requested_by)
114
+ owned_by_id = user_id_by_name(story.owned_by)
115
+ created_at = story.created_at && story.created_at.strftime("%Y-%m-%d %H:%M:%S")
116
+ accepted_at = story.accepted_at && story.accepted_at.strftime("%Y-%m-%d %H:%M:%S")
117
+ query = db.execute('SELECT id FROM stories WHERE id=?', story.id).first ? update_query : insert_query
118
+ db.execute query, story.url, story.name, story.description, story.story_type, story.estimate, story.labels, story.current_state, requested_by_id, owned_by_id, created_at, accepted_at, story.id
119
+ end
120
+ end
121
+
122
+ def create_tables
123
+ db.execute('CREATE TABLE IF NOT EXISTS settings (name VARCHAR NOT NULL, value VARCHAR)')
124
+ db.execute('CREATE UNIQUE INDEX IF NOT EXISTS settings_name ON settings(name)')
125
+ db.execute('CREATE TABLE IF NOT EXISTS users (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, role VARCHAR NOT NULL, name VARCHAR NOT NULL, initials VARCHAR NOT NULL, email VARCHAR NOT NULL)')
126
+ db.execute('CREATE UNIQUE INDEX IF NOT EXISTS users_name ON users(name)')
127
+ db.execute(<<SQL
128
+ CREATE TABLE IF NOT EXISTS stories (
129
+ id INTEGER NOT NULL PRIMARY KEY,
130
+ url VARCHAR NOT NULL,
131
+ name VARCHAR NOT NULL,
132
+ description TEXT,
133
+ story_type VARCHAR,
134
+ estimate INTEGER,
135
+ labels VARCHAR,
136
+ current_state VARCHAR,
137
+ requested_by_id INTEGER,
138
+ owned_by_id INTEGER,
139
+ created_at DATETIME,
140
+ accepted_at DATETIME
141
+ )
142
+ SQL
143
+ )
144
+ end
145
+ end
@@ -0,0 +1,69 @@
1
+ class PivotalShell::Cache
2
+ class Story
3
+ ATTRIBUTES=%w(requested_by_id name id current_state accepted_at labels url estimate description created_at owned_by_id story_type)
4
+ STATUSES = %w(unscheduled unstarted started finished delivered accepted rejected)
5
+ TYPES = %w(feature bug chore)
6
+
7
+ ATTRIBUTES.each do |attribute|
8
+ attr_reader attribute
9
+ end
10
+
11
+ def initialize(source={})
12
+ if source.is_a? PivotalTracker::Story
13
+ raise 'TODO'
14
+ elsif source.is_a? Hash
15
+ create_from_hash(source)
16
+ end
17
+ end
18
+
19
+ def self.find(id)
20
+ hash = PivotalShell::Configuration.cache.db.execute("SELECT * FROM stories WHERE id=?", id).first
21
+ hash && new(hash)
22
+ end
23
+
24
+ def self.all(params)
25
+ conditions = []
26
+ query_params = []
27
+ if params[:unowned]
28
+ conditions << "owned_by_id IS NULL"
29
+ elsif params[:owner] && (owner = PivotalShell::Cache::User.find(params[:owner]))
30
+ conditions << "owned_by_id==?"
31
+ query_params << owner.id
32
+ end
33
+
34
+ if params[:state]
35
+ params[:state] = [params[:state]].flatten
36
+ conditions << "current_state IN (#{(["?"]*params[:state].length).join(',')})"
37
+ query_params << params[:state]
38
+ end
39
+
40
+ if params[:type]
41
+ params[:type] = [params[:type]].flatten
42
+ conditions << "story_type IN (#{(["?"]*params[:type].length).join(',')})"
43
+ query_params << params[:type]
44
+ end
45
+
46
+ query = 'SELECT * FROM stories'
47
+ query << ' WHERE '+conditions.map{|c| "(#{c})"}.join(' AND ') unless conditions.empty?
48
+ puts query.inspect
49
+ PivotalShell::Configuration.cache.db.execute(query, query_params).map {|r| new(r)}
50
+ end
51
+
52
+ def owned_by
53
+ return nil if owned_by_id.nil?
54
+ @owned_by ||= PivotalShell::Cache::User.find(owned_by_id)
55
+ end
56
+
57
+ def requested_by
58
+ return nil if requested_by_id.nil?
59
+ @requested_by ||= PivotalShell::Cache::User.find(requested_by_id)
60
+ end
61
+
62
+ protected
63
+ def create_from_hash(hash)
64
+ PivotalShell::Cache::Story::ATTRIBUTES.each do |attribute|
65
+ self.instance_variable_set("@#{attribute}", hash[attribute])
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,33 @@
1
+ class PivotalShell::Cache
2
+ class User
3
+ ATTRIBUTES=%w(name id initials email)
4
+
5
+ ATTRIBUTES.each do |attribute|
6
+ attr_reader attribute
7
+ end
8
+
9
+ def initialize(source={})
10
+ if source.is_a? PivotalTracker::Membership
11
+ raise 'TODO'
12
+ elsif source.is_a? Hash
13
+ create_from_hash(source)
14
+ end
15
+ end
16
+
17
+ def to_s
18
+ name
19
+ end
20
+
21
+ def self.find(id)
22
+ hash = PivotalShell::Configuration.cache.db.execute("SELECT * FROM users WHERE id=? OR initials=? OR name=? OR email=?", id, id, id, id).first
23
+ hash && new(hash)
24
+ end
25
+
26
+ protected
27
+ def create_from_hash(hash)
28
+ PivotalShell::Cache::User::ATTRIBUTES.each do |attribute|
29
+ self.instance_variable_set("@#{attribute}", hash[attribute])
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,4 +1,8 @@
1
1
  class PivotalShell::Command
2
2
  def initialize(options)
3
3
  end
4
+
5
+ def execute
6
+ raise "Override me!"
7
+ end
4
8
  end
@@ -0,0 +1,16 @@
1
+ module PivotalShell::Commands
2
+ class PivotalShell::Commands::Commit < PivotalShell::Command
3
+ def initialize(options)
4
+ @message = options[0]
5
+ end
6
+
7
+ def execute
8
+ puts 'Doesnt work yet! Sorry.'
9
+ #exit 0 if (message =~ /\[#\d+\]/) || (message =~ /merge/i) # there is already a task ID in the message, or it is a merge
10
+
11
+ #input = File.open('/dev/tty', 'r')
12
+
13
+ #PivotalShell::Configuration.cache.update
14
+ end
15
+ end
16
+ end
@@ -29,6 +29,7 @@ module PivotalShell::Commands
29
29
  puts 'Story is not in a started state: '+@story.name
30
30
  else
31
31
  @story.update(:current_state => 'started')
32
+ PivotalShell::Configuration.cache.refresh
32
33
  end
33
34
  end
34
35
  end
@@ -0,0 +1,19 @@
1
+ module PivotalShell::Commands
2
+ class PivotalShell::Commands::Reload < PivotalShell::Command
3
+ def initialize(options)
4
+ opts = OptionParser.new do |opts|
5
+ opts.banner = "Completely reload the stories database from Pivotal Tracker"
6
+
7
+ opts.on_tail('--help', 'Show this help') do
8
+ puts opts
9
+ exit
10
+ end
11
+ opts.parse!(options)
12
+ end
13
+ end
14
+
15
+ def execute
16
+ PivotalShell::Configuration.cache.reload
17
+ end
18
+ end
19
+ end
@@ -31,6 +31,7 @@ module PivotalShell::Commands
31
31
  puts 'Story is already finished: '+@story.name
32
32
  else
33
33
  @story.update(:current_state => 'started')
34
+ PivotalShell::Configuration.cache.refresh
34
35
  end
35
36
  end
36
37
  end
@@ -5,8 +5,6 @@ module PivotalShell::Commands
5
5
  def initialize(options)
6
6
  @options = {:params => {}}
7
7
 
8
- available_statuses = %w(unscheduled unstarted started finished delivered accepted rejected)
9
- available_types = %w(features bugs chores)
10
8
 
11
9
  opts = OptionParser.new do |opts|
12
10
  opts.banner = "List Pivotal stories\nUsage: pivotal stories [options]\n\nThe default is to show all unfinished stories assigned to yourself\n\nDisplay format:\n [id]\n type: Feature/Bug/Chore\n estimate: * (irrelevant)/0/1/2/3\n state: . (unscheduled)/Unstarted/Started/Finished/Delivered/Accepted/Rejected\n title\n\nOptions:"
@@ -16,17 +14,17 @@ module PivotalShell::Commands
16
14
  @options[:all] = true
17
15
  end
18
16
 
19
- available_statuses.each do |status|
17
+ PivotalShell::Cache::Story::STATUSES.each do |status|
20
18
  opts.on("--#{status}", "Show #{status} stories") do
21
19
  @options[:params][:state] ||= []
22
20
  @options[:params][:state] << status
23
21
  end
24
22
  end
25
23
 
26
- available_types.each do |type|
27
- opts.on("--#{type}", "Show #{type}") do
24
+ PivotalShell::Cache::Story::TYPES.each do |type|
25
+ opts.on("--#{type}s", "Show #{type}") do
28
26
  @options[:params][:type] ||= []
29
- @options[:params][:type] << type[0..-2] # chomp s
27
+ @options[:params][:type] << type
30
28
  end
31
29
  end
32
30
 
@@ -35,7 +33,7 @@ module PivotalShell::Commands
35
33
  end
36
34
 
37
35
  opts.on('--unowned', 'Show tasks not assigned to anyone') do
38
- @options[:unowned] = true
36
+ @options[:params][:unowned] = true
39
37
  end
40
38
 
41
39
  opts.on('--anyone', 'Show tasks assigned to anyone') do
@@ -58,10 +56,7 @@ module PivotalShell::Commands
58
56
  end
59
57
 
60
58
  def execute
61
- stories = PivotalShell::Configuration.project.stories.all(@options[:params])
62
- if @options[:unowned]
63
- stories.reject!{|s| !s.owned_by.nil?}
64
- end
59
+ stories = PivotalShell::Cache::Story.all(@options[:params])
65
60
 
66
61
  puts stories.empty? ? 'No stories!' : stories.map{|s| "#{("[#{s.id}]").rjust 12} #{PivotalShell::Configuration.icon(s.story_type, s.current_state, s.estimate)} #{s.name.strip}"}.join("\n")
67
62
  end
@@ -22,11 +22,12 @@ module PivotalShell::Commands
22
22
  end
23
23
 
24
24
  def execute
25
- @story = PivotalShell::Configuration.project.stories.find(@story_id)
25
+ @story = PivotalShell::Cache::Story.find(@story_id)
26
26
  if @story.nil?
27
27
  puts 'Story not found'
28
28
  else
29
29
  puts ["[#{@story.id}] - #{@story.name}",
30
+
30
31
  "State: #{@story.current_state}",
31
32
  "Owner: #{@story.owned_by}",
32
33
  "Creator: #{@story.requested_by}",
@@ -0,0 +1,19 @@
1
+ module PivotalShell::Commands
2
+ class PivotalShell::Commands::Update < PivotalShell::Command
3
+ def initialize(options)
4
+ opts = OptionParser.new do |opts|
5
+ opts.banner = "Update the stories database from Pivotal Tracker"
6
+
7
+ opts.on_tail('--help', 'Show this help') do
8
+ puts opts
9
+ exit
10
+ end
11
+ opts.parse!(options)
12
+ end
13
+ end
14
+
15
+ def execute
16
+ PivotalShell::Configuration.cache.refresh
17
+ end
18
+ end
19
+ end
@@ -2,9 +2,13 @@ require 'yaml'
2
2
  require 'pivotal_tracker'
3
3
 
4
4
  module PivotalShell::Configuration
5
+ DEFAULTS = {
6
+ 'refresh_interval' => 15
7
+ }
8
+
5
9
  def self.load
6
10
  @global_config = YAML.load_file(global_config_path)
7
- @project_config = YAML.load_file(project_config_path)
11
+ @project_config = YAML.load_file(File.join(project_config_path,'.pivotalrc'))
8
12
  PivotalTracker::Client.token = @global_config['api_token']
9
13
  end
10
14
 
@@ -12,10 +16,18 @@ module PivotalShell::Configuration
12
16
  @project ||= PivotalTracker::Project.find(@project_config['project_id'])
13
17
  end
14
18
 
19
+ def self.cache
20
+ @cache ||= PivotalShell::Cache.new(File.join(project_config_path,'.pivotal_cache'))
21
+ end
22
+
15
23
  def self.me
16
24
  @me ||= @project_config['me']
17
25
  end
18
26
 
27
+ def self.refresh_interval
28
+ @refresh_interval ||= @project_config['refresh_interval'] || @global_config['refresh_interval'] || DEFAULTS['refresh_interval']
29
+ end
30
+
19
31
  def self.global_config_path
20
32
  @global_config_path ||= File.expand_path('~/.pivotalrc')
21
33
  end
@@ -45,7 +57,7 @@ module PivotalShell::Configuration
45
57
  end
46
58
 
47
59
  def self.icon(type, status, estimate)
48
- type_icon(type) + ' ' + estimate_icon(estimate) + ' ' + status_icon(status)
60
+ type_icon(type).to_s + ' ' + estimate_icon(estimate).to_s + ' ' + status_icon(status).to_s
49
61
  end
50
62
 
51
63
  private
@@ -58,7 +70,7 @@ private
58
70
  if dirs.empty? || File.join(dirs, '.pivotalrc')==global_config_path
59
71
  raise PivotalShell::Exception.new('No project .pivotalrc found')
60
72
  else
61
- File.join(dirs, '.pivotalrc')
73
+ File.join(dirs)
62
74
  end
63
75
  end
64
76
  end
@@ -1,3 +1,3 @@
1
1
  module PivotalShell
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -11,7 +11,8 @@ Gem::Specification.new do |s|
11
11
  s.homepage = "https://github.com/leonid-shevtsov/pivotal_shell"
12
12
  s.summary = %q{A command-line client for Pivotal Tracker}
13
13
 
14
- s.add_dependency 'pivotal-tracker', '=0.3'
14
+ s.add_dependency 'pivotal-tracker', '>0.3'
15
+ s.add_dependency 'sqlite3'
15
16
 
16
17
  s.files = `git ls-files`.split("\n")
17
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pivotal_shell
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
5
- prerelease: false
4
+ hash: 23
5
+ prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Leonid Shevtsov
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-12-06 00:00:00 +02:00
18
+ date: 2011-02-12 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -24,7 +24,7 @@ dependencies:
24
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
25
  none: false
26
26
  requirements:
27
- - - "="
27
+ - - ">"
28
28
  - !ruby/object:Gem::Version
29
29
  hash: 13
30
30
  segments:
@@ -33,6 +33,20 @@ dependencies:
33
33
  version: "0.3"
34
34
  type: :runtime
35
35
  version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: sqlite3
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :runtime
49
+ version_requirements: *id002
36
50
  description:
37
51
  email:
38
52
  - leonid@shevtsov.me
@@ -49,12 +63,19 @@ files:
49
63
  - README.markdown
50
64
  - Rakefile
51
65
  - bin/pivotal
66
+ - examples/commit-msg
52
67
  - lib/pivotal_shell.rb
68
+ - lib/pivotal_shell/cache.rb
69
+ - lib/pivotal_shell/cache/story.rb
70
+ - lib/pivotal_shell/cache/user.rb
53
71
  - lib/pivotal_shell/command.rb
72
+ - lib/pivotal_shell/commands/commit.rb
54
73
  - lib/pivotal_shell/commands/finish.rb
74
+ - lib/pivotal_shell/commands/reload.rb
55
75
  - lib/pivotal_shell/commands/start.rb
56
76
  - lib/pivotal_shell/commands/stories.rb
57
77
  - lib/pivotal_shell/commands/story.rb
78
+ - lib/pivotal_shell/commands/update.rb
58
79
  - lib/pivotal_shell/configuration.rb
59
80
  - lib/pivotal_shell/version.rb
60
81
  - pivotal_shell.gemspec
@@ -88,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
109
  requirements: []
89
110
 
90
111
  rubyforge_project:
91
- rubygems_version: 1.3.7
112
+ rubygems_version: 1.5.0
92
113
  signing_key:
93
114
  specification_version: 3
94
115
  summary: A command-line client for Pivotal Tracker