tpope-pickler 0.0.7 → 0.0.8

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/lib/pickler.rb CHANGED
@@ -84,9 +84,8 @@ class Pickler
84
84
 
85
85
  def parser
86
86
  require 'cucumber'
87
- require "cucumber/treetop_parser/feature_#@lang"
88
87
  Cucumber.load_language(@lang)
89
- @parser ||= Cucumber::TreetopParser::FeatureParser.new
88
+ @parser ||= Cucumber::Parser::FeatureParser.new
90
89
  end
91
90
 
92
91
  def project_id
@@ -108,11 +107,15 @@ class Pickler
108
107
 
109
108
  def scenario_word
110
109
  parser
111
- Cucumber.language['scenario']
110
+ Cucumber.keyword_hash['scenario']
111
+ end
112
+
113
+ def format
114
+ (config['format'] || :comment).to_sym
112
115
  end
113
116
 
114
117
  def local_features
115
- Dir[features_path('**','*.feature')].map {|f|feature(f)}.select {|f|f.id}
118
+ Dir[features_path('**','*.feature')].map {|f|feature(f)}.select {|f|f.pushable?}
116
119
  end
117
120
 
118
121
  def scenario_features
@@ -39,20 +39,20 @@ class Pickler
39
39
  def filename
40
40
  unless defined?(@filename)
41
41
  @filename = Dir[pickler.features_path("**","*.feature")].detect do |f|
42
- File.read(f)[/#\s*#{URL_REGEX}/,1].to_i == @id
42
+ File.read(f)[/(?:#\s*|@[[:punct:]]?)#{URL_REGEX}/,1].to_i == @id
43
43
  end
44
44
  end
45
45
  @filename
46
46
  end
47
47
 
48
48
  def to_s
49
- local_body || story.to_s
49
+ local_body || story.to_s(pickler.format)
50
50
  end
51
51
 
52
52
  def pull(default = nil)
53
53
  filename = filename() || pickler.features_path("#{default||id}.feature")
54
54
  story = story() # force the read into local_body before File.open below blows it away
55
- File.open(filename,'w') {|f| f.puts story}
55
+ File.open(filename,'w') {|f| f.puts story.to_s(pickler.format)}
56
56
  @filename = filename
57
57
  end
58
58
 
@@ -63,10 +63,26 @@ class Pickler
63
63
  end
64
64
  end
65
65
 
66
+ def pushable?
67
+ id || local_body =~ %r{\A(?:#\s*|@[[:punct:]]?(?:http://www\.pivotaltracker\.com/story/new)?[[:punct:]]?(?:\s+@\S+)*\s*)\n[[:upper:]][[:lower:]]+:} ? true : false
68
+ end
69
+
66
70
  def push
67
- return if story.to_s == local_body.to_s
68
- story.to_s = local_body
69
- story.save
71
+ body = local_body
72
+ return if story.to_s(pickler.format) == body.to_s
73
+ if story
74
+ story.to_s = body
75
+ story.save!
76
+ else
77
+ unless pushable?
78
+ raise Error, "To create a new story, make the first line an empty comment"
79
+ end
80
+ story = pickler.new_story
81
+ story.to_s = body
82
+ @story = story.save!
83
+ body.sub!(/\A(?:#.*\n)?/,"# #{story.url}\n")
84
+ File.open(filename,'w') {|f| f.write body}
85
+ end
70
86
  end
71
87
 
72
88
  def finish
@@ -81,7 +97,7 @@ class Pickler
81
97
 
82
98
  def id
83
99
  unless defined?(@id)
84
- @id = if id = local_body.to_s[/#\s*#{URL_REGEX}/,1]
100
+ @id = if id = local_body.to_s[/(?:#\s*|@[[:punct:]]?)#{URL_REGEX}/,1]
85
101
  id.to_i
86
102
  end
87
103
  end
@@ -201,14 +201,28 @@ class Pickler
201
201
  banner_arguments "<story>"
202
202
  summary "Show details for a story"
203
203
 
204
+ on "--full", "default format" do
205
+ @format = :full
206
+ end
207
+
208
+ on "--raw", "same as the .feature" do
209
+ @format = :raw
210
+ end
211
+
204
212
  process do |*args|
205
213
  case args.size
206
214
  when 0
207
215
  puts "#{pickler.project_id} #{pickler.project.name}"
208
216
  when 1
209
- story = pickler.story(args.first)
210
- paginated_output do
211
- puts_full story
217
+ feature = pickler.feature(args.first)
218
+ story = feature.story
219
+ case @format
220
+ when :raw
221
+ puts feature.story.to_s(pickler.format) if feature.story
222
+ else
223
+ paginated_output do
224
+ puts_full feature.story
225
+ end
212
226
  end
213
227
  else
214
228
  too_many
@@ -284,7 +298,8 @@ class Pickler
284
298
  summary "Upload stories"
285
299
  description <<-EOF
286
300
  Upload the given story or all features with a tracker url in a comment on the
287
- first line.
301
+ first line. Features with a blank comment in the first line will created as
302
+ new stories.
288
303
  EOF
289
304
 
290
305
  process do |*args|
@@ -1,10 +1,11 @@
1
1
  require 'date'
2
+ require 'cgi'
2
3
 
3
4
  class Pickler
4
5
  class Tracker
5
6
 
6
7
  ADDRESS = 'www.pivotaltracker.com'
7
- BASE_PATH = '/services/v1'
8
+ BASE_PATH = '/services/v2'
8
9
  SEARCH_KEYS = %w(label type state requester owner mywork id includedone)
9
10
 
10
11
  class Error < Pickler::Error; end
@@ -50,23 +51,19 @@ class Pickler
50
51
  def request_xml(method, path, *args)
51
52
  response = request(method,path,*args)
52
53
  raise response.inspect if response["Content-type"].split(/; */).first != "application/xml"
53
- Hash.from_xml(response.body)["response"]
54
+ hash = Hash.from_xml(response.body)
55
+ if hash["message"] && (response.code.to_i >= 400 || hash["success"] == "false")
56
+ raise Error, hash["message"], caller
57
+ end
58
+ hash
54
59
  end
55
60
 
56
61
  def get_xml(path)
57
- response = request_xml(:get, path)
58
- unless response["success"] == "true"
59
- if response["message"]
60
- raise Error, response["message"], caller
61
- else
62
- raise "#{path}: #{response.inspect}"
63
- end
64
- end
65
- response
62
+ request_xml(:get, path)
66
63
  end
67
64
 
68
65
  def project(id)
69
- Project.new(self,get_xml("/projects/#{id}")["project"].merge("id" => id.to_i))
66
+ Project.new(self,get_xml("/projects/#{id}")["project"])
70
67
  end
71
68
 
72
69
  class Abstract
@@ -86,7 +83,10 @@ class Pickler
86
83
 
87
84
  def self.date_reader(*methods)
88
85
  methods.each do |method|
89
- define_method(method) { value = @attributes[method.to_s] and Date.parse(value) }
86
+ define_method(method) do
87
+ value = @attributes[method.to_s]
88
+ value.kind_of?(String) ? Date.parse(value) : value
89
+ end
90
90
  end
91
91
  end
92
92
 
@@ -96,7 +96,10 @@ class Pickler
96
96
  define_method("#{method}=") { |v| @attributes[method.to_s] = v }
97
97
  end
98
98
  end
99
- reader :id
99
+
100
+ def id
101
+ id = @attributes['id'] and Integer(id)
102
+ end
100
103
 
101
104
  def to_xml(options = nil)
102
105
  @attributes.to_xml({:dasherize => false, :root => self.class.name.split('::').last.downcase}.merge(options||{}))
@@ -2,13 +2,20 @@ class Pickler
2
2
  class Tracker
3
3
  class Iteration < Abstract
4
4
  attr_reader :project
5
- date_reader :start, :finish
6
5
 
7
6
  def initialize(project, attributes = {})
8
7
  @project = project
9
8
  super(attributes)
10
9
  end
11
10
 
11
+ def start
12
+ Date.parse(@attributes['start'].to_s)
13
+ end
14
+
15
+ def finish
16
+ Date.parse(@attributes['finish'].to_s)
17
+ end
18
+
12
19
  def number
13
20
  @attributes['number'].to_i
14
21
  end
@@ -23,7 +30,7 @@ class Pickler
23
30
  end
24
31
 
25
32
  def succ
26
- self.class.new(project, 'number' => number.succ.to_s, 'start' => @attributes['finish'], 'finish' => (finish + (finish - start)).strftime("%b %d, %Y"))
33
+ self.class.new(project, 'number' => number.succ.to_s, 'start' => @attributes['finish'], 'finish' => (finish + (finish - start)))
27
34
  end
28
35
 
29
36
  def inspect
@@ -3,7 +3,11 @@ class Pickler
3
3
  class Note < Abstract
4
4
  attr_reader :story
5
5
  reader :text, :author
6
- date_reader :date
6
+ date_reader :noted_at
7
+
8
+ def date
9
+ noted_at && Date.new(noted_at.year, noted_at.mon, noted_at.day)
10
+ end
7
11
 
8
12
  def initialize(story, attributes = {})
9
13
  @story = story
@@ -20,7 +20,7 @@ class Pickler
20
20
  path = "/projects/#{id}/stories"
21
21
  path << "?filter=#{CGI.escape(filter)}" if filter
22
22
  response = tracker.get_xml(path)
23
- [response["stories"]["story"]].flatten.compact.map {|s| Story.new(self,s)}
23
+ [response["stories"]].flatten.compact.map {|s| Story.new(self,s)}
24
24
  end
25
25
 
26
26
  def new_story(attributes = {}, &block)
@@ -5,7 +5,7 @@ class Pickler
5
5
  TYPES = %w(bug feature chore release)
6
6
  STATES = %w(unscheduled unstarted started finished delivered rejected accepted)
7
7
 
8
- attr_reader :project, :iteration, :labels
8
+ attr_reader :project, :labels
9
9
  reader :url
10
10
  date_reader :created_at, :accepted_at, :deadline
11
11
  accessor :current_state, :name, :description, :owned_by, :requested_by, :story_type
@@ -17,6 +17,13 @@ class Pickler
17
17
  @labels = normalize_labels(@attributes["labels"])
18
18
  end
19
19
 
20
+ def iteration
21
+ unless current_state == 'unscheduled' || defined?(@iteration)
22
+ @iteration = project.stories(:id => id, :includedone => true).first.iteration
23
+ end
24
+ @iteration
25
+ end
26
+
20
27
  def labels=(value)
21
28
  @labels = normalize_labels(value)
22
29
  end
@@ -72,16 +79,28 @@ class Pickler
72
79
  project.tracker
73
80
  end
74
81
 
75
- def to_s
76
- to_s = "# #{url}\n#{story_type.capitalize}: #{name}\n"
82
+ def to_s(format = :comment)
83
+ to_s = "#{header(format)}\n#{story_type.capitalize}: #{name}\n"
77
84
  description_lines.each do |line|
78
85
  to_s << " #{line}".rstrip << "\n"
79
86
  end
80
87
  to_s
81
88
  end
82
89
 
90
+ def header(format = :comment)
91
+ case format
92
+ when :tag
93
+ "@#{url}#{labels.map {|l| " @#{l.tr('_,',' _')}"}.join}"
94
+ else
95
+ "# #{url}"
96
+ end
97
+ end
98
+
83
99
  def to_s=(body)
84
- body = body.sub(/\A# .*\n/,'')
100
+ if body =~ /\A@https?\b\S*(\s+@\S+)*\s*$/
101
+ self.labels = body[/\A@.*/].split(/\s+/)[1..-1].map {|l| l[1..-1].tr(' _','_,')}
102
+ end
103
+ body = body.sub(/\A(?:[@#].*\n)+/,'')
85
104
  if body =~ /\A(\w+): (.*)/
86
105
  self.story_type = $1.downcase
87
106
  self.name = $2
@@ -136,7 +155,7 @@ class Pickler
136
155
  def destroy
137
156
  if id
138
157
  response = tracker.request_xml(:delete, "/projects/#{project.id}/stories/#{id}", "")
139
- raise Error, response["message"], caller if response["success"] != "true"
158
+ raise Error, response["message"], caller if response["message"]
140
159
  @attributes["id"] = nil
141
160
  self
142
161
  end
@@ -148,7 +167,7 @@ class Pickler
148
167
 
149
168
  def save
150
169
  response = tracker.request_xml(id ? :put : :post, resource_url, to_xml(false))
151
- if response["success"] == "true"
170
+ if response["story"]
152
171
  initialize(project, response["story"])
153
172
  true
154
173
  else
@@ -156,6 +175,14 @@ class Pickler
156
175
  end
157
176
  end
158
177
 
178
+ def save!
179
+ errors = save
180
+ if errors != true
181
+ raise Pickler::Tracker::Error, Array(error).join("\n"), caller
182
+ end
183
+ self
184
+ end
185
+
159
186
  private
160
187
  def normalize_labels(value)
161
188
  Array(value).join(", ").strip.split(/\s*,\s*/)
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.7"
3
+ s.version = "0.0.8"
4
4
 
5
5
  s.summary = "PIvotal traCKer Liaison to cucumbER"
6
6
  s.description = "Synchronize between Cucumber and Pivotal Tracker"
@@ -24,5 +24,5 @@ Gem::Specification.new do |s|
24
24
  "lib/pickler/tracker/note.rb"
25
25
  ]
26
26
  s.add_dependency("activesupport", [">= 2.0.0"])
27
- s.add_dependency("cucumber", [">= 0.1.9"])
27
+ s.add_dependency("cucumber", [">= 0.2.0"])
28
28
  end
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.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Pope
@@ -9,11 +9,12 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-05 00:00:00 -08:00
12
+ date: 2009-03-21 00:00:00 -07:00
13
13
  default_executable: pickler
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
17
+ type: :runtime
17
18
  version_requirement:
18
19
  version_requirements: !ruby/object:Gem::Requirement
19
20
  requirements:
@@ -23,12 +24,13 @@ dependencies:
23
24
  version:
24
25
  - !ruby/object:Gem::Dependency
25
26
  name: cucumber
27
+ type: :runtime
26
28
  version_requirement:
27
29
  version_requirements: !ruby/object:Gem::Requirement
28
30
  requirements:
29
31
  - - ">="
30
32
  - !ruby/object:Gem::Version
31
- version: 0.1.9
33
+ version: 0.2.0
32
34
  version:
33
35
  description: Synchronize between Cucumber and Pivotal Tracker
34
36
  email: ruby@tpope.info