tpope-pickler 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -4,6 +4,7 @@ Synchronize user stories in Pivotal Tracker with Cucumber features.
4
4
 
5
5
  == Getting started
6
6
 
7
+ gem install tpope-pickler --source=http://gems.github.com
7
8
  echo "api_token: ..." > ~/.tracker.yml
8
9
  echo "project_id: ..." > ~/my/app/features/tracker.yml
9
10
 
@@ -34,6 +35,14 @@ List all stories matching the given query.
34
35
 
35
36
  Show details for the story referenced by the id.
36
37
 
38
+ pickler start <id>
39
+
40
+ Pull a given feature and change its state to started.
41
+
42
+ pickler finish <id>
43
+
44
+ Push a given feature and change its state to finished.
45
+
37
46
  == Disclaimer
38
47
 
39
48
  No warranties, expressed or implied.
@@ -0,0 +1,71 @@
1
+ class Pickler
2
+ class Feature
3
+ URL_REGEX = %r{\bhttp://www\.pivotaltracker\.com/\S*/(\d+)\b}
4
+ attr_reader :pickler
5
+
6
+ def initialize(pickler, identifier)
7
+ @pickler = pickler
8
+ case identifier
9
+ when nil, /^\s+$/
10
+ raise Error, "No feature given"
11
+
12
+ when /^#{URL_REGEX}$/, /^(\d+)$/
13
+ @id = $1.to_i
14
+
15
+ when /\.feature$/
16
+ if File.exist?(identifier)
17
+ @filename = identifier
18
+ end
19
+
20
+ else
21
+ if File.exist?(path = pickler.features_path("#{identifier}.feature"))
22
+ @filename = path
23
+ end
24
+
25
+ end or raise Error, "Unrecogizable feature #{string}"
26
+ end
27
+
28
+ def local_body
29
+ File.read(@filename) if @filename
30
+ end
31
+
32
+ def filename
33
+ unless defined?(@filename)
34
+ @filename = Dir[pickler.features_path("**","*.feature")].detect do |f|
35
+ File.read(f)[/#\s*#{URL_REGEX}/,1].to_i == @id
36
+ end
37
+ end
38
+ @filename
39
+ end
40
+
41
+ def to_s
42
+ local_body || story.to_s
43
+ end
44
+
45
+ def push
46
+ return if story.to_s == local_body.to_s
47
+ story.to_s = local_body
48
+ story.save
49
+ end
50
+
51
+ def finish
52
+ story.current_state = "finished" unless story.complete?
53
+ story.to_s = local_body
54
+ story.save
55
+ end
56
+
57
+ def id
58
+ unless defined?(@id)
59
+ @id = if id = local_body.to_s[/#\s*#{URL_REGEX}/,1]
60
+ id.to_i
61
+ end
62
+ end
63
+ @id
64
+ end
65
+
66
+ def story
67
+ @story ||= @pickler.project.story(id) if id
68
+ end
69
+
70
+ end
71
+ end
@@ -14,6 +14,22 @@ class Pickler
14
14
  super(attributes)
15
15
  end
16
16
 
17
+ def transition!(state)
18
+ raise Pickler::Tracker::Error, "Invalid state #{state}", caller unless STATES.include?(state)
19
+ self.current_state = state
20
+ if id
21
+ xml = "<story><current-state>#{state}</current-state></story>"
22
+ error = tracker.request_xml(:put, resource_url, xml).fetch("errors",{})["error"] || true
23
+ else
24
+ error = save
25
+ end
26
+ raise Pickler::Tracker::Error, Array(error).join("\n"), caller unless error == true
27
+ end
28
+
29
+ def complete?
30
+ %w(finished delivered accepted).include?(current_state)
31
+ end
32
+
17
33
  def tracker
18
34
  project.tracker
19
35
  end
@@ -64,24 +80,24 @@ class Pickler
64
80
 
65
81
  def destroy
66
82
  if id
67
- request = tracker.request_xml(:delete, "/projects/#{project.id}/stories/#{id}", to_xml)
68
- raise Error, request["message"], caller if request["success"] != "true"
83
+ response = tracker.request_xml(:delete, "/projects/#{project.id}/stories/#{id}", to_xml)
84
+ raise Error, response["message"], caller if response["success"] != "true"
69
85
  @attributes["id"] = nil
70
86
  self
71
87
  end
72
88
  end
73
89
 
90
+ def resource_url
91
+ ["/projects/#{project.id}/stories",id].compact.join("/")
92
+ end
93
+
74
94
  def save
75
- if id
76
- request = tracker.request_xml(:put, "/projects/#{project.id}/stories/#{id}", to_xml)
77
- else
78
- request = tracker.request_xml(:post, "/projects/#{project.id}/stories", to_xml)
79
- end
80
- if request["success"] == "true"
81
- initialize(project, request["story"])
95
+ response = tracker.request_xml(id ? :put : :post, resource_url, to_xml)
96
+ if response["success"] == "true"
97
+ initialize(project, response["story"])
82
98
  true
83
99
  else
84
- Array(request["errors"]["error"])
100
+ Array(response["errors"]["error"])
85
101
  end
86
102
  end
87
103
 
data/lib/pickler.rb CHANGED
@@ -39,8 +39,12 @@ class Pickler
39
39
  pickler.push(*argv)
40
40
  when 'pull'
41
41
  pickler.pull(*argv)
42
+ when 'start'
43
+ pickler.start(argv.first)
44
+ when 'finish'
45
+ pickler.finish(argv.first)
42
46
  when 'help', '--help', '-h', '', nil
43
- puts 'pickler commands: show <id>, search <query>, push, pull'
47
+ puts 'pickler commands: [show|start|finish] <id>, search <query>, push, pull'
44
48
  else
45
49
  $stderr.puts "pickler: unknown command #{first}"
46
50
  exit 1
@@ -107,19 +111,33 @@ class Pickler
107
111
  Cucumber.language['scenario']
108
112
  end
109
113
 
110
- def remote_features
114
+ def stories
111
115
  project.stories(scenario_word, :includedone => true).select do |s|
112
116
  s.to_s =~ /^\s*#{Regexp.escape(scenario_word)}:/ && parser.parse(s.to_s)
113
117
  end
114
118
  end
115
119
 
116
- def local_features
117
- Dir[features_path('**','*.feature')].map {|f| LocalFeature.new(self,f)}
120
+ def features(*args)
121
+ if args.any?
122
+ args.map {|a| feature(a)}
123
+ else
124
+ Dir[features_path('**','*.feature')].map {|f|feature(f)}.select {|f|f.id}
125
+ end
126
+ end
127
+
128
+ def feature(string)
129
+ Feature.new(self,string)
130
+ end
131
+
132
+ def story(string)
133
+ feature(string).story
118
134
  end
119
135
 
120
- def pull
121
- l = local_features
122
- remote_features.each do |remote|
136
+ def pull(*args)
137
+ l = features
138
+ args.map! {|arg| story(arg)}
139
+ args.replace(stories) if args.empty?
140
+ args.each do |remote|
123
141
  body = "# http://www.pivotaltracker.com/story/show/#{remote.id}\n" <<
124
142
  normalize_feature(remote.to_s)
125
143
  if local = l.detect {|f| f.id == remote.id}
@@ -133,13 +151,23 @@ class Pickler
133
151
  nil
134
152
  end
135
153
 
136
- def push
137
- local_features.select do |local|
138
- next unless local.id
139
- remote = local.story
140
- next if remote.to_s == local.to_s
141
- remote.to_s = local.to_s
142
- remote.save
154
+ def start(*args)
155
+ args.each do |arg|
156
+ story = story(arg)
157
+ story.transition!("started") if %w(unstarted rejected).include?(story.current_state)
158
+ end
159
+ pull(*args)
160
+ end
161
+
162
+ def push(*args)
163
+ features(*args).each do |feature|
164
+ feature.push
165
+ end
166
+ end
167
+
168
+ def finish(*args)
169
+ features(*args).each do |feature|
170
+ feature.finish
143
171
  end
144
172
  end
145
173
 
@@ -162,33 +190,7 @@ class Pickler
162
190
  new
163
191
  end
164
192
 
165
- class LocalFeature
166
- attr_reader :pickler, :filename
167
-
168
- def initialize(pickler, filename)
169
- @pickler = pickler
170
- @filename = filename
171
- end
172
-
173
- def to_s
174
- File.read(@filename)
175
- end
176
-
177
- def id
178
- unless defined?(@id)
179
- @id = if id = to_s[%r{#\s*http://www\.pivotaltracker\.com/\S*?/(\d+)},1]
180
- id.to_i
181
- end
182
- end
183
- @id
184
- end
185
-
186
- def story
187
- @pickler.project.story(id) if id
188
- end
189
-
190
- end
191
-
192
193
  end
193
194
 
195
+ require 'pickler/feature'
194
196
  require 'pickler/tracker'
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.1"
3
+ s.version = "0.0.2"
4
4
 
5
5
  s.summary = "PIvotal traCKer Liaison to cucumbER"
6
6
  s.description = "Synchronize between Cucumber and Pivotal Tracker"
@@ -15,6 +15,7 @@ Gem::Specification.new do |s|
15
15
  "pickler.gemspec",
16
16
  "bin/pickler",
17
17
  "lib/pickler.rb",
18
+ "lib/pickler/feature.rb",
18
19
  "lib/pickler/tracker.rb",
19
20
  "lib/pickler/tracker/project.rb",
20
21
  "lib/pickler/tracker/story.rb"
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.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Pope
@@ -44,6 +44,7 @@ files:
44
44
  - pickler.gemspec
45
45
  - bin/pickler
46
46
  - lib/pickler.rb
47
+ - lib/pickler/feature.rb
47
48
  - lib/pickler/tracker.rb
48
49
  - lib/pickler/tracker/project.rb
49
50
  - lib/pickler/tracker/story.rb