torque 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +13 -6
- data/VERSION +1 -1
- data/lib/torque.rb +31 -17
- data/lib/torque/field_filter.rb +136 -0
- data/lib/torque/pivotal.rb +5 -7
- data/lib/torque/pivotal_html_parser.rb +54 -25
- data/lib/torque/settings.rb +10 -20
- data/lib/torque/story.rb +7 -5
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14355f09eb1d9a65345ed81c053ceb353f3990e3
|
4
|
+
data.tar.gz: 7f6e191c2a67318615be764f1f99de4a959abb67
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7641002a1af10500ca46689627c7de5aac7c8d78f8423e17c9506c949ea5d29e653845fb7f2c6fbb3bfafab6111ac8ed807339302bf9b4a412cdbb1699bfc882
|
7
|
+
data.tar.gz: ba240db9ca4829a3f92d90900032b9ab8b93b09118b2a21529253da23cf84d8eae03b40964a97874197976d73752308d903b796ab3aaaea9ef28f037705f67af
|
data/README.md
CHANGED
@@ -4,18 +4,18 @@ Torque
|
|
4
4
|
Introduction
|
5
5
|
------------
|
6
6
|
|
7
|
-
Torque is a command line utility
|
7
|
+
Torque is a command line utility that compiles data from your Pivotal Tracker project into a set of release notes for that project.
|
8
8
|
|
9
|
-
At start, Torque requests a Pivotal Tracker API token,
|
9
|
+
At start, Torque requests a Pivotal Tracker API token, providing it access to info from your Pivotal Tracker projects. It then requests a project for which to generate release notes.
|
10
10
|
|
11
|
-
|
11
|
+
After this one time configuration, Torque can begin generating notes. To do this, it requests data on accepted stories from your Pivotal project and compiles these stories into a first-draft release notes document. Depending on how consistently your team uses Pivotal Tracker, this document will contain:
|
12
12
|
|
13
13
|
* A record of each feature that was added to the project since the last time Torque was run
|
14
14
|
* A description of the feature
|
15
15
|
* The date that this feature was accepted
|
16
16
|
* A link to the Pivotal Tracker story that tracks this feature
|
17
17
|
|
18
|
-
Torque's default behavior is to document all the stories that were accepted since the last time
|
18
|
+
Torque's default behavior is to document all the stories that were accepted since the last time it was run. For instance, if Torque was run on June 1st and then on June 10th, it would generate release notes from the stories that were accepted between June 1st and June 10th (inclusive). This is designed to make it easy to generate release notes each time new features are pushed to production: If Torque is run at the time of each new release, then it will generate notes for all the features that were added since the previous release. If needed, Torque can also generate notes for a custom date range or for any number of past project iterations.
|
19
19
|
|
20
20
|
Torque was designed and tested in a Ruby 2.0 environment.
|
21
21
|
|
@@ -23,11 +23,12 @@ Current Features
|
|
23
23
|
----------------
|
24
24
|
|
25
25
|
* Automatically compiles all Pivotal Tracker stories accepted between the current date and the last time Torque was run
|
26
|
+
* Store a record of each document on generation
|
26
27
|
* Compile stories accepted within a custom date range
|
27
|
-
* Stores a record of each document as it's generated
|
28
28
|
* Filtering stories by label, owner or story type
|
29
|
+
* Generate notes for specific number of completed project iterations
|
29
30
|
* Configuration script that automatically sets up a directory to use Torque
|
30
|
-
*
|
31
|
+
* Display all of your current Pivotal Tracker projects and easily switch between them
|
31
32
|
* Ability to email the finished release notes from a personal email account to a pre-specified mailing list. (Currently can only send from accounts hosted by Gmail)
|
32
33
|
|
33
34
|
Note on Confidentiality
|
@@ -86,6 +87,12 @@ To filter your results by story label, owner or type:
|
|
86
87
|
|
87
88
|
Note: The Pivotal Tracker API currently only supports "AND" searches for these fields. A search for multiple labels, for example, will turn up only the stories that match every label. ("OR" searches may or may not be added with the release of the PT API v5, which should arrive soon.)
|
88
89
|
|
90
|
+
To generate notes for X number of completed iterations of your project:
|
91
|
+
|
92
|
+
# torque --iterations X
|
93
|
+
|
94
|
+
Note that a run with iterations currently disables filters, and it is incompatible with custom date ranges.
|
95
|
+
|
89
96
|
To email the release notes from a personal email to Torque's mailing list:
|
90
97
|
|
91
98
|
# torque --email
|
data/VERSION
CHANGED
data/lib/torque.rb
CHANGED
@@ -41,24 +41,40 @@ class Torque
|
|
41
41
|
notes_string += "Release Notes\n"
|
42
42
|
notes_string += "Project #{@settings.project} - '#{project_name}'\n"
|
43
43
|
|
44
|
-
if @settings.
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
44
|
+
if @settings.iterations
|
45
|
+
# Past iterations header
|
46
|
+
notes_string += "Last "
|
47
|
+
notes_string += @settings.iterations == 1 \
|
48
|
+
? notes_string += "iteration"
|
49
|
+
: notes_string += "#{@settings.iterations} iterations"
|
50
|
+
notes_string += "\n"
|
51
|
+
|
52
|
+
elsif @settings.custom_date_range
|
53
|
+
# Custom date range header
|
49
54
|
notes_string += "(custom "+@settings.accept_from.strftime(@date_format)+" - "+@settings.accept_to.strftime(@date_format)+")\n"
|
55
|
+
|
50
56
|
else
|
57
|
+
# Default date range header
|
51
58
|
notes_string += "("+@settings.accept_from.strftime(@date_format)+" - "+@settings.accept_to.strftime(@date_format)+")\n"
|
52
59
|
end
|
60
|
+
|
61
|
+
if @settings.filters_on
|
62
|
+
# Filters header
|
63
|
+
notes_string += "Filter '#{@settings.filter_string}'\n"
|
64
|
+
end
|
53
65
|
|
54
66
|
notes_string += "\n"
|
55
67
|
notes_string += "These notes were generated with Torque, a Pivotal Tracker command line utility\n"
|
56
68
|
notes_string += "\n"
|
57
69
|
|
70
|
+
html_parser = PivotalHTMLParser.new
|
71
|
+
html_parser.add_date_filter(@settings.accept_from, @settings.accept_to) unless @settings.iterations
|
72
|
+
html_parser.add_field_filters(@settings.filters) if @settings.filters_on
|
73
|
+
|
58
74
|
if iterations
|
59
75
|
# Adds each iteration
|
60
76
|
|
61
|
-
iteration_list =
|
77
|
+
iteration_list = html_parser.process_project_iterations(project_html)
|
62
78
|
|
63
79
|
iteration_list = Iteration.sort_list(iteration_list)
|
64
80
|
iteration_list.each do |iteration|
|
@@ -75,10 +91,16 @@ class Torque
|
|
75
91
|
end
|
76
92
|
end
|
77
93
|
|
94
|
+
num_stories = 0
|
95
|
+
iteration_list.each {|iteration| num_stories += iteration.stories.length}
|
96
|
+
|
97
|
+
print_if_verbose "[Torque]"
|
98
|
+
print_if_verbose "[Torque] Added #{num_stories} stories"
|
99
|
+
|
78
100
|
else
|
79
101
|
# Adds each story
|
80
102
|
|
81
|
-
stories =
|
103
|
+
stories = html_parser.process_project(project_html)
|
82
104
|
|
83
105
|
stories = Story.sort_list(stories)
|
84
106
|
stories.each do |story|
|
@@ -151,7 +173,7 @@ class Torque
|
|
151
173
|
|
152
174
|
print_if_not_silent "Current project: #{@settings.project} - '#{project_name}'"
|
153
175
|
|
154
|
-
print_if_not_silent "Filter:
|
176
|
+
print_if_not_silent "Filter: \"#{@settings.filter_string}\"" if @settings.filters_on
|
155
177
|
|
156
178
|
if @settings.iterations
|
157
179
|
print_if_not_silent "Generating release notes: Last #{@settings.iterations} iterations..."
|
@@ -165,18 +187,10 @@ class Torque
|
|
165
187
|
end
|
166
188
|
|
167
189
|
pivotal = Pivotal.new(@settings.token)
|
168
|
-
|
169
|
-
project_html = ""
|
170
|
-
|
171
190
|
if @settings.iterations
|
172
|
-
puts "WARNING: Filters are currently not supported when running with iterations" if @settings.filters_on
|
173
191
|
project_html = pivotal.get_project_iterations(@settings.project, @settings.iterations)
|
174
192
|
else
|
175
|
-
|
176
|
-
project_html = pivotal.get_project_stories(@settings.project, @settings.filter_string)
|
177
|
-
else
|
178
|
-
project_html = pivotal.get_project_stories(@settings.project)
|
179
|
-
end
|
193
|
+
project_html = pivotal.get_project_stories(@settings.project)
|
180
194
|
end
|
181
195
|
|
182
196
|
iterations_on = (!@settings.iterations.nil?)
|
@@ -0,0 +1,136 @@
|
|
1
|
+
class Torque
|
2
|
+
|
3
|
+
##
|
4
|
+
# Parses and stores a field filter for stories
|
5
|
+
class FieldFilter
|
6
|
+
|
7
|
+
##
|
8
|
+
# The field that the FieldFilter filters over
|
9
|
+
attr_reader :field
|
10
|
+
|
11
|
+
##
|
12
|
+
# The contents of the filter
|
13
|
+
attr_reader :contents
|
14
|
+
|
15
|
+
##
|
16
|
+
# @param field A symbol representing the field to filter
|
17
|
+
# @param contents The contents of the field filter
|
18
|
+
#
|
19
|
+
# Current options for the field are
|
20
|
+
# * :label
|
21
|
+
# * :owner
|
22
|
+
# * :type
|
23
|
+
#
|
24
|
+
# An :owner filter with no spaces in it will filter separately by first/middle/last name. For instance,
|
25
|
+
# "Adam" would match "Adam Barnes" or "John Adam Smith" or "Joe Quincy Adam".
|
26
|
+
#
|
27
|
+
# The contents should be a list of values speparated by "," or "+", where AND is signified by "+" and OR is signified
|
28
|
+
# by ",", and "+" has a higher precedence than ",". For example,
|
29
|
+
#
|
30
|
+
# "ios+android,ios+web"
|
31
|
+
#
|
32
|
+
# translates to
|
33
|
+
#
|
34
|
+
# (ios AND android) OR (ios AND web)
|
35
|
+
#
|
36
|
+
def initialize(field, contents="")
|
37
|
+
@field = field
|
38
|
+
@contents = contents
|
39
|
+
|
40
|
+
# Represents evaluator as a 2-level array, @evaluator
|
41
|
+
#
|
42
|
+
# (ios AND android) OR (ios AND web) <=>
|
43
|
+
# [["ios", "android"], ["ios", "web"]]
|
44
|
+
parse_contents
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Returns true if the filter includes the story, false if it excludes it
|
49
|
+
def eval(story)
|
50
|
+
|
51
|
+
# Determines the field to evaluate
|
52
|
+
|
53
|
+
story_field = nil
|
54
|
+
if @field == :label
|
55
|
+
story_field = story.labels
|
56
|
+
elsif @field == :owner
|
57
|
+
story_field = story.owner
|
58
|
+
elsif @field == :type
|
59
|
+
story_field = story.type
|
60
|
+
else
|
61
|
+
# Invalid filter
|
62
|
+
return true
|
63
|
+
end
|
64
|
+
|
65
|
+
# Evaluates the story's field
|
66
|
+
|
67
|
+
or_eval = false
|
68
|
+
@evaluator.each do |or_element|
|
69
|
+
|
70
|
+
and_eval = true
|
71
|
+
or_element.each do |and_element|
|
72
|
+
|
73
|
+
if @field == :label
|
74
|
+
and_eval &&= (story_field.member? and_element)
|
75
|
+
|
76
|
+
elsif @field == :owner
|
77
|
+
# Special case: "owner" filter
|
78
|
+
# If the filter element has no spaces in it...
|
79
|
+
# Then it is assumed to be a first/middle/last name, not a full name
|
80
|
+
if !and_element.match(" ")
|
81
|
+
and_eval &&= (story_field.split(" ").member? and_element)
|
82
|
+
else
|
83
|
+
and_eval &&= (story_field == and_element)
|
84
|
+
end
|
85
|
+
|
86
|
+
else # @field == :type
|
87
|
+
and_eval &&= (story_field == and_element)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
or_eval ||= and_eval
|
93
|
+
end
|
94
|
+
|
95
|
+
or_eval
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Returns a string representation of the FieldFilter
|
101
|
+
def to_s
|
102
|
+
"#{@field}: #{@contents}"
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
# Parses @contents and stores the result in @evaluator, a 2-level array
|
109
|
+
# Returns @evaluator
|
110
|
+
def parse_contents
|
111
|
+
|
112
|
+
# Handles special case: empty contents
|
113
|
+
if @contents == ""
|
114
|
+
@evaluator = [[]]
|
115
|
+
return @evaluator
|
116
|
+
end
|
117
|
+
|
118
|
+
# Splits all strings into arrays
|
119
|
+
@evaluator = contents
|
120
|
+
@evaluator = @evaluator.split ","
|
121
|
+
@evaluator.length.times do |i|
|
122
|
+
@evaluator[i] = @evaluator[i].split "+"
|
123
|
+
end
|
124
|
+
|
125
|
+
# Strips all array elements
|
126
|
+
@evaluator.each do |element1|
|
127
|
+
element1.each do |element2|
|
128
|
+
element2.strip!
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
@evaluator
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
data/lib/torque/pivotal.rb
CHANGED
@@ -56,7 +56,7 @@ class Torque
|
|
56
56
|
elsif response.code != "200"
|
57
57
|
raise PivotalAPIError.new(
|
58
58
|
"The Pivotal Tracker API responded with an unexpected error code: #{response.code}. Check your " \
|
59
|
-
"API token, and/or
|
59
|
+
"API token, internet connection, and/or pivotaltracker.com"
|
60
60
|
)
|
61
61
|
end
|
62
62
|
|
@@ -65,12 +65,11 @@ class Torque
|
|
65
65
|
|
66
66
|
##
|
67
67
|
# @param project The ID of the Pivotal Tracker project from which to get data
|
68
|
-
# @param filter_string A string filter for the stories
|
69
68
|
#
|
70
69
|
# Sends a request through the Pivotal Tracker API
|
71
70
|
#
|
72
71
|
# Returns a string of html data from Pivotal Tracker with data on the stories for the given project
|
73
|
-
def get_project_stories(project
|
72
|
+
def get_project_stories(project)
|
74
73
|
|
75
74
|
# Polls story data from pivotal tracker
|
76
75
|
host="pivotaltracker.com"
|
@@ -78,7 +77,6 @@ class Torque
|
|
78
77
|
url="http://www.pivotaltracker.com/services/v3/projects/#{project}/stories"
|
79
78
|
headers={'X-TrackerToken'=>@token}
|
80
79
|
|
81
|
-
url += "?filter=#{filter_string} includedone:true" if filter_string
|
82
80
|
url = URI.escape(url)
|
83
81
|
|
84
82
|
response=Net::HTTP.new(host, port).get(url, headers)
|
@@ -93,7 +91,7 @@ class Torque
|
|
93
91
|
elsif response.code != "200"
|
94
92
|
raise PivotalAPIError.new(
|
95
93
|
"The Pivotal Tracker API responded with an unexpected error code: #{response.code}. Check your " \
|
96
|
-
+ "project ID, API token, and/or
|
94
|
+
+ "project ID, API token, internet connection, and/or pivotaltracker.com"
|
97
95
|
)
|
98
96
|
|
99
97
|
end
|
@@ -107,7 +105,7 @@ class Torque
|
|
107
105
|
#
|
108
106
|
# Sends a request throgh the Pivotal Tracker API
|
109
107
|
#
|
110
|
-
# Returns a string of html data from Pivotal Tracker with data on
|
108
|
+
# Returns a string of html data from Pivotal Tracker with data on finished iterations of a project
|
111
109
|
def get_project_iterations(project, number=1)
|
112
110
|
|
113
111
|
# Polls story data from pivotal tracker
|
@@ -130,7 +128,7 @@ class Torque
|
|
130
128
|
elsif !(["2","3"].member? response.code[0])
|
131
129
|
raise PivotalAPIError.new(
|
132
130
|
"The Pivotal Tracker API responded with an unexpected error code: #{response.code}. Check your " \
|
133
|
-
+ "project ID, API token, and/or
|
131
|
+
+ "project ID, API token, internet connection, and/or pivotaltracker.com"
|
134
132
|
)
|
135
133
|
|
136
134
|
end
|
@@ -10,35 +10,41 @@ require_relative 'story'
|
|
10
10
|
class Torque
|
11
11
|
class PivotalHTMLParser
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
# Returns a list of Story objects parsed from project_html_string whose date_accepted fields are within acceptable
|
19
|
-
# bounds (least recent to most recent date accepted)
|
20
|
-
def process_project_date_filter(project_html_string, accept_from, accept_to)
|
21
|
-
|
22
|
-
story_list = process_project(project_html_string)
|
23
|
-
story_list.select! {
|
24
|
-
|story|
|
25
|
-
(!story.date_accepted.nil? \
|
26
|
-
&& story.date_accepted >= accept_from \
|
27
|
-
&& story.date_accepted <= accept_to)
|
28
|
-
}
|
13
|
+
def initialize
|
14
|
+
@date_filter = false
|
15
|
+
@field_filters = false
|
16
|
+
end
|
29
17
|
|
30
|
-
|
18
|
+
##
|
19
|
+
# @param accept_from A Date marking the lower bound for the date_accepted field of a story (inclusive)
|
20
|
+
# @param accept_to A Date marking the upper bound for the date_accepted field of a story (inclusive)
|
21
|
+
#
|
22
|
+
# Adds a date filter to stories, excluding them if they lie outside the provided dates
|
23
|
+
def add_date_filter(accept_from, accept_to)
|
24
|
+
@date_filter = true
|
25
|
+
@accept_from = accept_from
|
26
|
+
@accept_to = accept_to
|
27
|
+
end
|
31
28
|
|
29
|
+
##
|
30
|
+
# @param field_filter_list A list of FieldFilter objects to use
|
31
|
+
#
|
32
|
+
# Adds field filters to stories, excluding them if they do not pass all filters in field_filter_list
|
33
|
+
def add_field_filters(field_filter_list)
|
34
|
+
@field_filters = true
|
35
|
+
@field_filter_list = field_filter_list
|
32
36
|
end
|
33
37
|
|
34
38
|
##
|
35
39
|
# @param project_html_string An html string containing the story data for a Pivotal Tracker project
|
36
40
|
#
|
37
41
|
# Returns a list of all Story objects parsed from project_html_string (least recent to most recent date accepted)
|
42
|
+
#
|
43
|
+
# Applies during processing any filters that have been added
|
38
44
|
def process_project(project_html_string)
|
39
45
|
|
40
46
|
project_element = Nokogiri::HTML(project_html_string)
|
41
|
-
|
47
|
+
process_stories_element(project_element)
|
42
48
|
end
|
43
49
|
|
44
50
|
|
@@ -62,7 +68,7 @@ class Torque
|
|
62
68
|
|
63
69
|
private
|
64
70
|
|
65
|
-
# story_element: A Nokogiri::XML::Element representing a story (root element
|
71
|
+
# story_element: A Nokogiri::XML::Element representing a story (root element "story")
|
66
72
|
#
|
67
73
|
# Returns a Story object based off the information in the element
|
68
74
|
def process_story_element(story_element)
|
@@ -77,23 +83,26 @@ class Torque
|
|
77
83
|
Story.create(story_hash)
|
78
84
|
end
|
79
85
|
|
80
|
-
#
|
86
|
+
# stories_element: A Nokogiri::XML::Element representing a collection of stories (root element "stories")
|
81
87
|
#
|
82
88
|
# Returns a list of all Story objects parsed from project_element (least recent to most recent date accepted)
|
83
|
-
|
89
|
+
#
|
90
|
+
# Excludes stories that fall outside of any added filters
|
91
|
+
def process_stories_element(stories_element)
|
84
92
|
|
85
|
-
story_html_array =
|
93
|
+
story_html_array = stories_element.search('story')
|
86
94
|
story_list = []
|
87
95
|
|
88
96
|
story_html_array.each do
|
89
97
|
|story_element|
|
90
|
-
|
98
|
+
story = process_story_element(story_element)
|
99
|
+
story_list << story if passes_filters(story)
|
91
100
|
end
|
92
101
|
|
93
102
|
story_list
|
94
103
|
end
|
95
104
|
|
96
|
-
# iteraton_element: A Nokogiri::XML::Element representing an iteration (root element
|
105
|
+
# iteraton_element: A Nokogiri::XML::Element representing an iteration (root element "iteration")
|
97
106
|
#
|
98
107
|
# Returns an Interation object based off the object in the element
|
99
108
|
def process_iteration_element(iteration_element)
|
@@ -106,12 +115,32 @@ class Torque
|
|
106
115
|
if child.name == "id"
|
107
116
|
iter_number = child.children[0].text
|
108
117
|
elsif child.name == "stories"
|
109
|
-
stories =
|
118
|
+
stories = process_stories_element(child)
|
110
119
|
end
|
111
120
|
end
|
112
121
|
|
113
122
|
Iteration.new(iter_number, stories)
|
114
123
|
end
|
115
124
|
|
125
|
+
# story: A Story object
|
126
|
+
#
|
127
|
+
# True if story passes the list of filters, else false
|
128
|
+
def passes_filters(story)
|
129
|
+
|
130
|
+
if @date_filter
|
131
|
+
return false if story.date_accepted.nil?
|
132
|
+
return false unless (story.date_accepted <= @accept_to)
|
133
|
+
return false unless (story.date_accepted >= @accept_from)
|
134
|
+
end
|
135
|
+
|
136
|
+
if @field_filters
|
137
|
+
@field_filter_list.each do |filter|
|
138
|
+
return false unless filter.eval(story)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
return true
|
143
|
+
end
|
144
|
+
|
116
145
|
end
|
117
146
|
end
|
data/lib/torque/settings.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'date'
|
2
2
|
require_relative 'date_settings'
|
3
|
+
require_relative 'field_filter'
|
3
4
|
require_relative 'file_system'
|
4
5
|
require_relative 'record_pathname_settings'
|
5
6
|
require_relative 'torque_info_parser'
|
@@ -50,15 +51,15 @@ class Torque
|
|
50
51
|
attr_reader :email_to
|
51
52
|
|
52
53
|
##
|
53
|
-
#
|
54
|
+
# A string representing the current filter
|
54
55
|
attr_reader :filter_string
|
55
56
|
|
56
57
|
##
|
57
|
-
# A
|
58
|
+
# A list of field filters in use
|
58
59
|
attr_reader :filters
|
59
60
|
|
60
61
|
##
|
61
|
-
# True if filters are being used for
|
62
|
+
# True if field filters are being used for stories, else false
|
62
63
|
attr_reader :filters_on
|
63
64
|
|
64
65
|
##
|
@@ -123,26 +124,15 @@ class Torque
|
|
123
124
|
|
124
125
|
# Parses story filters from the options
|
125
126
|
|
126
|
-
@filters =
|
127
|
-
|
128
|
-
@filters
|
129
|
-
@filters
|
130
|
-
@filters
|
127
|
+
@filters = []
|
128
|
+
|
129
|
+
@filters << FieldFilter.new(:label, @options[:label]) if @options[:label]
|
130
|
+
@filters << FieldFilter.new(:owner, @options[:owner]) if @options[:owner]
|
131
|
+
@filters << FieldFilter.new(:type, @options[:type]) if @options[:type]
|
131
132
|
|
132
133
|
@filters_on = !@filters.empty?
|
133
134
|
|
134
|
-
|
135
|
-
|
136
|
-
@filter_string = ""
|
137
|
-
@filters.each do |field, args|
|
138
|
-
args.split(",").each do |arg|
|
139
|
-
arg.strip!
|
140
|
-
@filter_string += "#{field}:"
|
141
|
-
@filter_string += ( (arg.include? " ") ? "\"#{arg}\" " : "#{arg} " )
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
@filter_string.strip!
|
135
|
+
@filter_string = @filters.map {|f| f.to_s}.join ", "
|
146
136
|
|
147
137
|
# Parses and processes data from .torqueinfo.yaml
|
148
138
|
|
data/lib/torque/story.rb
CHANGED
@@ -28,6 +28,10 @@ class Torque
|
|
28
28
|
# The name of the story
|
29
29
|
attr_reader :name
|
30
30
|
|
31
|
+
##
|
32
|
+
# The owner of the story
|
33
|
+
attr_reader :owner
|
34
|
+
|
31
35
|
##
|
32
36
|
# The ID of the story's project
|
33
37
|
attr_reader :project_id
|
@@ -77,6 +81,7 @@ class Torque
|
|
77
81
|
@estimate = -1
|
78
82
|
@labels = "" # Will be an array
|
79
83
|
@name = ""
|
84
|
+
@owner = ""
|
80
85
|
@project_id = -1
|
81
86
|
@story_id = -1
|
82
87
|
@type = ""
|
@@ -84,8 +89,9 @@ class Torque
|
|
84
89
|
@current_state = handle_nil(@current_state, html_hash["current_state"])
|
85
90
|
@description = handle_nil(@description, html_hash["description"])
|
86
91
|
@name = handle_nil(@name, html_hash["name"])
|
92
|
+
@owner = handle_nil(@owner, html_hash["owned_by"])
|
87
93
|
@type = handle_nil(@type, html_hash["story_type"])
|
88
|
-
|
94
|
+
|
89
95
|
@estimate = Integer(handle_nil(@estimate, html_hash["estimate"]))
|
90
96
|
@project_id = Integer(handle_nil(@project_id, html_hash["project_id"]))
|
91
97
|
@story_id = Integer(handle_nil(@story_id, html_hash["id"]))
|
@@ -109,9 +115,5 @@ class Torque
|
|
109
115
|
sorted
|
110
116
|
end
|
111
117
|
|
112
|
-
def to_s
|
113
|
-
return "Story("+@name+", "+"#{@story_id}"+")"
|
114
|
-
end
|
115
|
-
|
116
118
|
end
|
117
119
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: torque
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nico Adams, Scrimmage
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-08-
|
11
|
+
date: 2013-08-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: highline
|
@@ -102,6 +102,7 @@ files:
|
|
102
102
|
- lib/torque/error/missing_token_error.rb
|
103
103
|
- lib/torque/error/missing_torque_info_file_error.rb
|
104
104
|
- lib/torque/error/pivotal_api_error.rb
|
105
|
+
- lib/torque/field_filter.rb
|
105
106
|
- lib/torque/file_system.rb
|
106
107
|
- lib/torque/iteration.rb
|
107
108
|
- lib/torque/mailer.rb
|