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 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
 
@@ -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 #{string}"
29
+ end or raise Error, "Unrecogizable feature #{identifier}"
26
30
  end
27
31
 
28
32
  def local_body
29
- File.read(@filename) if @filename
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 :created_at, :iteration, :url, :labels
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
@@ -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
- "Token" => @token,
22
- "Accept" => "application/xml",
23
- "Content-type" => "application/xml"
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 = new(Dir.getwd)
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
- l = features
138
- args.map! {|arg| story(arg)}
139
- args.replace(stories) if args.empty?
140
- args.each do |remote|
141
- body = "# http://www.pivotaltracker.com/story/show/#{remote.id}\n" <<
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
- story = story(arg)
157
- story.transition!("started") if %w(unstarted rejected).include?(story.current_state)
100
+ feature(arg).pull
158
101
  end
159
- pull(*args)
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.2"
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.2
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: