tpope-pickler 0.0.2 → 0.0.3
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/README.rdoc +4 -2
- data/lib/pickler/feature.rb +38 -2
- data/lib/pickler/runner.rb +54 -0
- data/lib/pickler/tracker/iteration.rb +30 -0
- data/lib/pickler/tracker/note.rb +23 -0
- data/lib/pickler/tracker/story.rb +16 -4
- data/lib/pickler/tracker.rb +18 -3
- data/lib/pickler.rb +12 -83
- data/pickler.gemspec +5 -2
- metadata +4 -1
data/README.rdoc
CHANGED
@@ -35,9 +35,11 @@ List all stories matching the given query.
|
|
35
35
|
|
36
36
|
Show details for the story referenced by the id.
|
37
37
|
|
38
|
-
pickler start <id>
|
38
|
+
pickler start <id> [basename]
|
39
39
|
|
40
|
-
Pull a given feature and change its state to started.
|
40
|
+
Pull a given feature and change its state to started. If basename is given
|
41
|
+
and no local file exists, features/basename.feature will be created in lieu
|
42
|
+
of features/id.feature.
|
41
43
|
|
42
44
|
pickler finish <id>
|
43
45
|
|
data/lib/pickler/feature.rb
CHANGED
@@ -9,6 +9,10 @@ class Pickler
|
|
9
9
|
when nil, /^\s+$/
|
10
10
|
raise Error, "No feature given"
|
11
11
|
|
12
|
+
when Pickler::Tracker::Story
|
13
|
+
@story = identifier
|
14
|
+
@id = @story.id
|
15
|
+
|
12
16
|
when /^#{URL_REGEX}$/, /^(\d+)$/
|
13
17
|
@id = $1.to_i
|
14
18
|
|
@@ -22,11 +26,11 @@ class Pickler
|
|
22
26
|
@filename = path
|
23
27
|
end
|
24
28
|
|
25
|
-
end or raise Error, "Unrecogizable feature #{
|
29
|
+
end or raise Error, "Unrecogizable feature #{identifier}"
|
26
30
|
end
|
27
31
|
|
28
32
|
def local_body
|
29
|
-
File.read(
|
33
|
+
File.read(filename) if filename
|
30
34
|
end
|
31
35
|
|
32
36
|
def filename
|
@@ -42,6 +46,19 @@ class Pickler
|
|
42
46
|
local_body || story.to_s
|
43
47
|
end
|
44
48
|
|
49
|
+
def pull(default = nil)
|
50
|
+
body = "# http://www.pivotaltracker.com/story/show/#{id}\n" <<
|
51
|
+
normalize_feature(story.to_s)
|
52
|
+
filename = filename() || pickler.features_path("#{default||id}.feature")
|
53
|
+
File.open(filename,'w') {|f| f.puts body}
|
54
|
+
@filename = filename
|
55
|
+
end
|
56
|
+
|
57
|
+
def start(default = nil)
|
58
|
+
story.transition!("started") if story.startable?
|
59
|
+
pull(default)
|
60
|
+
end
|
61
|
+
|
45
62
|
def push
|
46
63
|
return if story.to_s == local_body.to_s
|
47
64
|
story.to_s = local_body
|
@@ -67,5 +84,24 @@ class Pickler
|
|
67
84
|
@story ||= @pickler.project.story(id) if id
|
68
85
|
end
|
69
86
|
|
87
|
+
protected
|
88
|
+
|
89
|
+
def normalize_feature(body)
|
90
|
+
return body unless ast = pickler.parser.parse(body)
|
91
|
+
feature = ast.compile
|
92
|
+
new = ''
|
93
|
+
(feature.header.chomp << "\n").each_line do |l|
|
94
|
+
new << ' ' unless new.empty?
|
95
|
+
new << l.strip << "\n"
|
96
|
+
end
|
97
|
+
feature.scenarios.each do |scenario|
|
98
|
+
new << "\n Scenario: #{scenario.name}\n"
|
99
|
+
scenario.steps.each do |step|
|
100
|
+
new << " #{step.keyword} #{step.name}\n"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
new
|
104
|
+
end
|
105
|
+
|
70
106
|
end
|
71
107
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class Pickler
|
2
|
+
class Runner
|
3
|
+
|
4
|
+
attr_reader :pickler, :argv
|
5
|
+
|
6
|
+
def initialize(argv)
|
7
|
+
@argv = argv
|
8
|
+
@pickler = Pickler.new(Dir.getwd)
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
case first = argv.shift
|
13
|
+
when 'show', /^\d+$/
|
14
|
+
story = pickler.project.story(first == 'show' ? argv.shift : first)
|
15
|
+
puts story
|
16
|
+
when 'search'
|
17
|
+
stories = pickler.project.stories(*argv).group_by {|s| s.current_state}
|
18
|
+
first = true
|
19
|
+
states = Pickler::Tracker::Story::STATES
|
20
|
+
states -= %w(unstarted accepted) if argv.empty?
|
21
|
+
states.each do |state|
|
22
|
+
next unless stories[state]
|
23
|
+
puts unless first
|
24
|
+
first = false
|
25
|
+
puts state.upcase
|
26
|
+
puts '-' * state.length
|
27
|
+
stories[state].each do |story|
|
28
|
+
puts "[#{story.id}] #{story.story_type.capitalize}: #{story.name}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
when 'push'
|
32
|
+
pickler.push(*argv)
|
33
|
+
when 'pull'
|
34
|
+
pickler.pull(*argv)
|
35
|
+
when 'start'
|
36
|
+
pickler.start(argv.first,argv[1])
|
37
|
+
when 'finish'
|
38
|
+
pickler.finish(argv.first)
|
39
|
+
when 'help', '--help', '-h', '', nil
|
40
|
+
puts 'pickler commands: [show|start|finish] <id>, search <query>, push, pull'
|
41
|
+
else
|
42
|
+
$stderr.puts "pickler: unknown command #{first}"
|
43
|
+
exit 1
|
44
|
+
end
|
45
|
+
rescue Pickler::Error
|
46
|
+
$stderr.puts "#$!"
|
47
|
+
exit 1
|
48
|
+
rescue Interrupt
|
49
|
+
$stderr.puts "Interrupted!"
|
50
|
+
exit 130
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Pickler
|
2
|
+
class Tracker
|
3
|
+
class Iteration < Abstract
|
4
|
+
attr_reader :project
|
5
|
+
date_reader :start, :finish
|
6
|
+
|
7
|
+
def initialize(project, attributes = {})
|
8
|
+
@project = project
|
9
|
+
super(attributes)
|
10
|
+
end
|
11
|
+
|
12
|
+
def number
|
13
|
+
@attributes['number'].to_i
|
14
|
+
end
|
15
|
+
alias to_i number
|
16
|
+
|
17
|
+
def range
|
18
|
+
start...finish
|
19
|
+
end
|
20
|
+
|
21
|
+
def succ
|
22
|
+
self.class.new(project, 'number' => number.succ.to_s, 'start' => @attributes['finish'], 'finish' => (finish + (finish - start)).strftime("%b %d, %Y"))
|
23
|
+
end
|
24
|
+
|
25
|
+
def inspect
|
26
|
+
"#<#{self.class.inspect}:#{number.inspect} (#{range.inspect})>"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Pickler
|
2
|
+
class Tracker
|
3
|
+
class Note < Abstract
|
4
|
+
attr_reader :story
|
5
|
+
reader :text, :author
|
6
|
+
date_reader :date
|
7
|
+
|
8
|
+
def initialize(story, attributes = {})
|
9
|
+
@story = story
|
10
|
+
super(attributes)
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_xml
|
14
|
+
@attributes.to_xml(:root => 'note')
|
15
|
+
end
|
16
|
+
|
17
|
+
def inspect
|
18
|
+
"#<#{self.class.inspect}:#{id.inspect}, story_id: #{story.id.inspect}, date: #{date.inspect}, author: #{author.inspect}, text: #{text.inspect}>"
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -3,14 +3,16 @@ class Pickler
|
|
3
3
|
class Story < Abstract
|
4
4
|
|
5
5
|
TYPES = %w(bug feature chore release)
|
6
|
-
STATES = %w(unstarted started finished delivered rejected accepted)
|
6
|
+
STATES = %w(unscheduled unstarted started finished delivered rejected accepted)
|
7
7
|
|
8
|
-
attr_reader :project
|
9
|
-
reader :
|
8
|
+
attr_reader :project, :iteration
|
9
|
+
reader :url, :labels
|
10
|
+
date_reader :created_at, :accepted_at, :deadline
|
10
11
|
accessor :current_state, :name, :description, :estimate, :owned_by, :requested_by, :story_type
|
11
12
|
|
12
13
|
def initialize(project, attributes = {})
|
13
14
|
@project = project
|
15
|
+
@iteration = Iteration.new(project, attributes["iteration"]) if attributes["iteration"]
|
14
16
|
super(attributes)
|
15
17
|
end
|
16
18
|
|
@@ -30,6 +32,10 @@ class Pickler
|
|
30
32
|
%w(finished delivered accepted).include?(current_state)
|
31
33
|
end
|
32
34
|
|
35
|
+
def startable?
|
36
|
+
%w(unscheduled unstarted rejected).include?(current_state)
|
37
|
+
end
|
38
|
+
|
33
39
|
def tracker
|
34
40
|
project.tracker
|
35
41
|
end
|
@@ -69,7 +75,13 @@ class Pickler
|
|
69
75
|
end
|
70
76
|
|
71
77
|
def notes
|
72
|
-
[@attributes["notes"]].flatten.compact
|
78
|
+
[@attributes["notes"]].flatten.compact.map {|n| Note.new(self,n)}
|
79
|
+
end
|
80
|
+
|
81
|
+
def comment!(body)
|
82
|
+
raise ArgumentError if body.strip.empty? || body.size > 5000
|
83
|
+
response = tracker.request_xml(:post, "#{resource_url}/notes",{:text => body}.to_xml(:root => 'note'))
|
84
|
+
Note.new(self, response["note"])
|
73
85
|
end
|
74
86
|
|
75
87
|
def to_xml
|
data/lib/pickler/tracker.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
1
3
|
class Pickler
|
2
4
|
class Tracker
|
3
5
|
|
@@ -18,9 +20,9 @@ class Pickler
|
|
18
20
|
require 'net/http'
|
19
21
|
Net::HTTP.start(ADDRESS) do |http|
|
20
22
|
headers = {
|
21
|
-
"
|
22
|
-
"Accept"
|
23
|
-
"Content-type"
|
23
|
+
"X-TrackerToken" => @token,
|
24
|
+
"Accept" => "application/xml",
|
25
|
+
"Content-type" => "application/xml"
|
24
26
|
}
|
25
27
|
klass = Net::HTTP.const_get(method.to_s.capitalize)
|
26
28
|
http.request(klass.new("#{BASE_PATH}#{path}", headers), *args)
|
@@ -61,6 +63,12 @@ class Pickler
|
|
61
63
|
end
|
62
64
|
end
|
63
65
|
|
66
|
+
def self.date_reader(*methods)
|
67
|
+
methods.each do |method|
|
68
|
+
define_method(method) { value = @attributes[method.to_s] and Date.parse(value) }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
64
72
|
def self.accessor(*methods)
|
65
73
|
reader(*methods)
|
66
74
|
methods.each do |method|
|
@@ -68,6 +76,11 @@ class Pickler
|
|
68
76
|
end
|
69
77
|
end
|
70
78
|
reader :id
|
79
|
+
|
80
|
+
def to_xml(options = nil)
|
81
|
+
@attributes.to_xml({:root => self.class.name.split('::').last.downcase}.merge(options||{}))
|
82
|
+
end
|
83
|
+
|
71
84
|
end
|
72
85
|
|
73
86
|
end
|
@@ -75,3 +88,5 @@ end
|
|
75
88
|
|
76
89
|
require 'pickler/tracker/project'
|
77
90
|
require 'pickler/tracker/story'
|
91
|
+
require 'pickler/tracker/iteration'
|
92
|
+
require 'pickler/tracker/note'
|
data/lib/pickler.rb
CHANGED
@@ -14,47 +14,8 @@ class Pickler
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.run(argv)
|
17
|
-
pickler
|
18
|
-
|
19
|
-
case first = argv.shift
|
20
|
-
when 'show', /^\d+$/
|
21
|
-
story = pickler.project.story(first == 'show' ? argv.shift : first)
|
22
|
-
puts story
|
23
|
-
when 'search'
|
24
|
-
stories = pickler.project.stories(*argv).group_by {|s| s.current_state}
|
25
|
-
first = true
|
26
|
-
states = Tracker::Story::STATES
|
27
|
-
states -= %w(unstarted accepted) if argv.empty?
|
28
|
-
states.each do |state|
|
29
|
-
next unless stories[state]
|
30
|
-
puts unless first
|
31
|
-
first = false
|
32
|
-
puts state.upcase
|
33
|
-
puts '-' * state.length
|
34
|
-
stories[state].each do |story|
|
35
|
-
puts "[#{story.id}] #{story.story_type.capitalize}: #{story.name}"
|
36
|
-
end
|
37
|
-
end
|
38
|
-
when 'push'
|
39
|
-
pickler.push(*argv)
|
40
|
-
when 'pull'
|
41
|
-
pickler.pull(*argv)
|
42
|
-
when 'start'
|
43
|
-
pickler.start(argv.first)
|
44
|
-
when 'finish'
|
45
|
-
pickler.finish(argv.first)
|
46
|
-
when 'help', '--help', '-h', '', nil
|
47
|
-
puts 'pickler commands: [show|start|finish] <id>, search <query>, push, pull'
|
48
|
-
else
|
49
|
-
$stderr.puts "pickler: unknown command #{first}"
|
50
|
-
exit 1
|
51
|
-
end
|
52
|
-
rescue Pickler::Error
|
53
|
-
$stderr.puts "#$!"
|
54
|
-
exit 1
|
55
|
-
rescue Interrupt
|
56
|
-
$stderr.puts "Interrupted!"
|
57
|
-
exit 130
|
17
|
+
require 'pickler/runner'
|
18
|
+
Runner.new(argv).run
|
58
19
|
end
|
59
20
|
|
60
21
|
attr_reader :directory
|
@@ -111,12 +72,6 @@ class Pickler
|
|
111
72
|
Cucumber.language['scenario']
|
112
73
|
end
|
113
74
|
|
114
|
-
def stories
|
115
|
-
project.stories(scenario_word, :includedone => true).select do |s|
|
116
|
-
s.to_s =~ /^\s*#{Regexp.escape(scenario_word)}:/ && parser.parse(s.to_s)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
75
|
def features(*args)
|
121
76
|
if args.any?
|
122
77
|
args.map {|a| feature(a)}
|
@@ -134,29 +89,20 @@ class Pickler
|
|
134
89
|
end
|
135
90
|
|
136
91
|
def pull(*args)
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
normalize_feature(remote.to_s)
|
143
|
-
if local = l.detect {|f| f.id == remote.id}
|
144
|
-
filename = local.filename
|
145
|
-
else
|
146
|
-
next if remote.current_state == 'unstarted'
|
147
|
-
filename = features_path("#{remote.id}.feature")
|
92
|
+
if args.empty?
|
93
|
+
args = project.stories(scenario_word, :includedone => true).reject do |s|
|
94
|
+
s.current_state == 'unstarted'
|
95
|
+
end.select do |s|
|
96
|
+
s.to_s =~ /^\s*#{Regexp.escape(scenario_word)}:/ && parser.parse(s.to_s)
|
148
97
|
end
|
149
|
-
File.open(filename,'w') {|f| f.puts body}
|
150
98
|
end
|
151
|
-
nil
|
152
|
-
end
|
153
|
-
|
154
|
-
def start(*args)
|
155
99
|
args.each do |arg|
|
156
|
-
|
157
|
-
story.transition!("started") if %w(unstarted rejected).include?(story.current_state)
|
100
|
+
feature(arg).pull
|
158
101
|
end
|
159
|
-
|
102
|
+
end
|
103
|
+
|
104
|
+
def start(arg, default = nil)
|
105
|
+
feature(arg).start(default)
|
160
106
|
end
|
161
107
|
|
162
108
|
def push(*args)
|
@@ -173,23 +119,6 @@ class Pickler
|
|
173
119
|
|
174
120
|
protected
|
175
121
|
|
176
|
-
def normalize_feature(body)
|
177
|
-
return body unless ast = parser.parse(body)
|
178
|
-
feature = ast.compile
|
179
|
-
new = ''
|
180
|
-
(feature.header.chomp << "\n").each_line do |l|
|
181
|
-
new << ' ' unless new.empty?
|
182
|
-
new << l.strip << "\n"
|
183
|
-
end
|
184
|
-
feature.scenarios.each do |scenario|
|
185
|
-
new << "\n Scenario: #{scenario.name}\n"
|
186
|
-
scenario.steps.each do |step|
|
187
|
-
new << " #{step.keyword} #{step.name}\n"
|
188
|
-
end
|
189
|
-
end
|
190
|
-
new
|
191
|
-
end
|
192
|
-
|
193
122
|
end
|
194
123
|
|
195
124
|
require 'pickler/feature'
|
data/pickler.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "pickler"
|
3
|
-
s.version = "0.0.
|
3
|
+
s.version = "0.0.3"
|
4
4
|
|
5
5
|
s.summary = "PIvotal traCKer Liaison to cucumbER"
|
6
6
|
s.description = "Synchronize between Cucumber and Pivotal Tracker"
|
@@ -16,9 +16,12 @@ Gem::Specification.new do |s|
|
|
16
16
|
"bin/pickler",
|
17
17
|
"lib/pickler.rb",
|
18
18
|
"lib/pickler/feature.rb",
|
19
|
+
"lib/pickler/runner.rb",
|
19
20
|
"lib/pickler/tracker.rb",
|
20
21
|
"lib/pickler/tracker/project.rb",
|
21
|
-
"lib/pickler/tracker/story.rb"
|
22
|
+
"lib/pickler/tracker/story.rb",
|
23
|
+
"lib/pickler/tracker/iteration.rb",
|
24
|
+
"lib/pickler/tracker/note.rb"
|
22
25
|
]
|
23
26
|
s.add_dependency("activesupport", [">= 2.0.0"])
|
24
27
|
s.add_dependency("cucumber", [">= 0.1.9"])
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tpope-pickler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Pope
|
@@ -45,9 +45,12 @@ files:
|
|
45
45
|
- bin/pickler
|
46
46
|
- lib/pickler.rb
|
47
47
|
- lib/pickler/feature.rb
|
48
|
+
- lib/pickler/runner.rb
|
48
49
|
- lib/pickler/tracker.rb
|
49
50
|
- lib/pickler/tracker/project.rb
|
50
51
|
- lib/pickler/tracker/story.rb
|
52
|
+
- lib/pickler/tracker/iteration.rb
|
53
|
+
- lib/pickler/tracker/note.rb
|
51
54
|
has_rdoc: false
|
52
55
|
homepage: http://github.com/tpope/pickler
|
53
56
|
post_install_message:
|