tpope-pickler 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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: