tpope-pickler 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.
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