Class: Pickler::Tracker::Story

Inherits:
Abstract
  • Object
show all
Defined in:
lib/pickler/tracker/story.rb

Constant Summary collapse

TYPES =
%w(bug feature chore release)
STATES =
%w(unscheduled unstarted started finished delivered rejected accepted)

Instance Attribute Summary collapse

Attributes inherited from Abstract

#attributes

Instance Method Summary collapse

Methods inherited from Abstract

accessor, date_reader, #id, reader

Constructor Details

#initialize(project, attributes = {}) ⇒ Story

Returns a new instance of Story.



13
14
15
16
17
18
# File 'lib/pickler/tracker/story.rb', line 13

def initialize(project, attributes = {})
  @project = project
  @labels = []
  super(attributes)
  @iteration = Iteration.new(project, attributes["iteration"]) if attributes["iteration"]
end

Instance Attribute Details

#labelsObject

Returns the value of attribute labels.



8
9
10
# File 'lib/pickler/tracker/story.rb', line 8

def labels
  @labels
end

#projectObject (readonly)

Returns the value of attribute project.



8
9
10
# File 'lib/pickler/tracker/story.rb', line 8

def project
  @project
end

Instance Method Details

#backlog?(as_of = Date.today) ⇒ Boolean

Returns:

  • (Boolean)


57
58
59
# File 'lib/pickler/tracker/story.rb', line 57

def backlog?(as_of = Date.today)
  iteration && iteration.start >= as_of
end

#comment!(body) ⇒ Object



151
152
153
154
155
156
157
158
# File 'lib/pickler/tracker/story.rb', line 151

def comment!(body)
  response = tracker.request_xml(:post, "#{resource_url}/notes",Pickler.hash_to_xml(:note, :text => body))
  if response["note"]
    Note.new(self, response["note"])
  else
    raise Pickler::Tracker::Error, Array(response["errors"]["error"]).join("\n"), caller
  end
end

#complete?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'lib/pickler/tracker/story.rb', line 70

def complete?
  %w(finished delivered accepted).include?(current_state)
end

#current?(as_of = Date.today) ⇒ Boolean

Returns:

  • (Boolean)


61
62
63
# File 'lib/pickler/tracker/story.rb', line 61

def current?(as_of = Date.today)
  iteration && iteration.include?(as_of)
end

#description_linesObject



123
124
125
126
127
128
129
# File 'lib/pickler/tracker/story.rb', line 123

def description_lines
  array = []
  description.to_s.each_line do |line|
    array << line.chomp
  end
  array
end

#destroyObject



170
171
172
173
174
175
176
177
# File 'lib/pickler/tracker/story.rb', line 170

def destroy
  if id
    response = tracker.request_xml(:delete, "/projects/#{project.id}/stories/#{id}", "")
    raise Error, response["message"], caller if response["message"]
    attributes["id"] = nil
    self
  end
end

#done?(as_of = Date.today) ⇒ Boolean

In a previous iteration

Returns:

  • (Boolean)


66
67
68
# File 'lib/pickler/tracker/story.rb', line 66

def done?(as_of = Date.today)
  iteration && iteration.finish <= as_of
end

#estimateObject



135
136
137
# File 'lib/pickler/tracker/story.rb', line 135

def estimate
  attributes["estimate"].to_i < 0 ? nil : @attributes["estimate"]
end

#estimate=(value) ⇒ Object



139
140
141
# File 'lib/pickler/tracker/story.rb', line 139

def estimate=(value)
  attributes["estimate"] = value.nil? ? -1 : value
end

#finishObject



43
44
45
46
47
48
49
50
51
# File 'lib/pickler/tracker/story.rb', line 43

def finish
  case story_type
  when "bug", "feature"
    self.current_state = "finished" unless complete?
  when "chore", "release"
    self.current_state = "accepted"
  end
  current_state
end

#finish!Object



53
54
55
# File 'lib/pickler/tracker/story.rb', line 53

def finish!
  transition!(finish)
end

#header(format = :tag) ⇒ Object



93
94
95
96
97
98
99
100
# File 'lib/pickler/tracker/story.rb', line 93

def header(format = :tag)
  case format
  when :tag
    "@#{url || "#{project.use_https? ? 'https' : 'http'}://www.pivotaltracker.com/story/new"}#{labels.map {|l| " @#{l.tr(' _','_,')}"}.join}"
  else
    "# #{url}"
  end
end

#iterationObject



20
21
22
23
24
25
# File 'lib/pickler/tracker/story.rb', line 20

def iteration
  unless current_state == 'unscheduled' || defined?(@iteration)
    @iteration = project.stories(:id => id, :includedone => true).first.iteration
  end
  @iteration
end

#notesObject



131
132
133
# File 'lib/pickler/tracker/story.rb', line 131

def notes
  [attributes["notes"]].flatten.compact.map {|n| Note.new(self,n)}
end

#resource_urlObject



179
180
181
# File 'lib/pickler/tracker/story.rb', line 179

def resource_url
  ["/projects/#{project.id}/stories",id].compact.join("/")
end

#saveObject



183
184
185
186
187
188
189
190
191
# File 'lib/pickler/tracker/story.rb', line 183

def save
  response = tracker.request_xml(id ? :put : :post,  resource_url, to_xml(false))
  if response["story"]
    initialize(project, response["story"])
    true
  else
    Array(response["errors"]["error"])
  end
end

#save!Object



193
194
195
196
197
198
199
# File 'lib/pickler/tracker/story.rb', line 193

def save!
  errors = save
  if errors != true
    raise Pickler::Tracker::Error, Array(errors).join("\n"), caller
  end
  self
end

#startable?Boolean

Returns:

  • (Boolean)


74
75
76
# File 'lib/pickler/tracker/story.rb', line 74

def startable?
  %w(unscheduled unstarted rejected).include?(current_state)
end

#suggested_basename(user_override = nil) ⇒ Object



143
144
145
146
147
148
149
# File 'lib/pickler/tracker/story.rb', line 143

def suggested_basename(user_override = nil)
  if user_override.to_s !~ /\A-?\z/
    user_override
  else
    name.to_s.empty? ? id.to_s : name.gsub(/[^\w-]+/,'_').downcase
  end
end

#to_s(format = :tag) ⇒ Object



82
83
84
85
86
87
88
89
90
91
# File 'lib/pickler/tracker/story.rb', line 82

def to_s(format = :tag)
  to_s = "#{header(format)}\n#{story_type.capitalize}: #{name}\n"
  description_lines.each do |line|
    to_s << "  #{line}".rstrip << "\n"
  end
  if to_s !~ /\A[\0-\177]*\z/
    to_s = "# -*- coding: utf-8 -*-\n#{to_s}"
  end
  to_s
end

#to_s=(body) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/pickler/tracker/story.rb', line 102

def to_s=(body)
  if body =~ /\A@https?\b\S*(\s+@\S+)*\s*$/
    self.labels = body[/\A@.*/].split(/\s+/)[1..-1].map {|l| l[1..-1].tr('_,',' _')}
  end
  body = body.sub(/\A(?:[@#].*\n)+/,'')
  if body =~ /\A(\w+): (.*)/
    self.story_type = $1.downcase
    self.name = $2
    description = $'
  else
    self.story_type = "feature"
    self.name = body[/.*/]
    description = $'
  end
  self.description = description.gsub(/\A\n+|\n+\Z/,'') + "\n"
  if description_lines.all? {|l| l.empty? || l =~ /^  /}
    self.description.gsub!(/^  /,'')
  end
  self
end

#to_xml(force_labels = true) ⇒ Object



160
161
162
163
164
165
166
167
168
# File 'lib/pickler/tracker/story.rb', line 160

def to_xml(force_labels = true)
  hash = attributes.reject do |k,v|
    !%w(current_state deadline description estimate name owned_by requested_by story_type).include?(k)
  end
  if force_labels || !id || normalize_labels(attributes["labels"]) != labels
    hash["labels"] = labels.join(", ")
  end
  Pickler.hash_to_xml(:story, hash)
end

#trackerObject



78
79
80
# File 'lib/pickler/tracker/story.rb', line 78

def tracker
  project.tracker
end

#transition!(state) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
# File 'lib/pickler/tracker/story.rb', line 31

def transition!(state)
  raise Pickler::Tracker::Error, "Invalid state #{state}", caller unless STATES.include?(state)
  self.current_state = state
  if id
    xml = "<story><current_state>#{state}</current_state></story>"
    error = tracker.request_xml(:put, resource_url, xml).fetch("errors",{})["error"] || true
  else
    error = save
  end
  raise Pickler::Tracker::Error, Array(error).join("\n"), caller unless error == true
end