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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1081effd8b2bc6581372cd2a3bd38540d1b38411
4
- data.tar.gz: bab3ee280120bb1e291e7c608aa57034b885e760
3
+ metadata.gz: 14355f09eb1d9a65345ed81c053ceb353f3990e3
4
+ data.tar.gz: 7f6e191c2a67318615be764f1f99de4a959abb67
5
5
  SHA512:
6
- metadata.gz: 3ac460137f176e2720c94544e3d74d930282901e7708ba6ea3e7d7c5d218ee23c14cd2769391a3fb89bd4537ace39f2b0bd5471a26bd006d47595d3cbfcd5ae8
7
- data.tar.gz: 561657b3d705c34150fffcf6fe2b09d712e753df97ce7ed1ff07052a75f45d55dc1b7547a90230a853131057952d1aa87eeb0b7e07973d059a5d8801c05ea649
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 packaged as a Ruby gem. It compiles online data from a Pivotal Tracker project into a set of release notes for that project.
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, which gives Torque access to information from your Pivotal Tracker projects. Torque then asks the user to choose one of their projects to begin to generate notes from.
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
- Once configured, Torque requests data on the accepted stories from the current Pivotal project. It then compiles these stories into a first-draft release notes document. Depending on how consistently your team uses Pivotal Tracker, this document will contain:
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 Torque 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.
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
- * Project script that automatically displays/switches between your Pivotal Tracker projects
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
@@ -1,3 +1,3 @@
1
1
  major:0
2
- minor:2
2
+ minor:3
3
3
  patch:0
@@ -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.filters_on && !@settings.iterations
45
- notes_string += "Filter '#{@settings.filter_string}'\n"
46
- end
47
-
48
- if @settings.custom_date_range
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 = PivotalHTMLParser.new.process_project_iterations(project_html)
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 = PivotalHTMLParser.new.process_project_date_filter(project_html, @settings.accept_from, @settings.accept_to)
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: '#{@settings.filter_string}'" if @settings.filters_on
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
- if @settings.filters_on
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
@@ -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 internet connection"
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, filter_string=nil)
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 internet connection"
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 previous finished project iterations
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 internet connection"
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
- # @param project_html_string An html string containing the story data for a Pivotal Tracker project
15
- # @param accept_from A Date marking the lower bound for the date_accepted field of a story
16
- # @param accept_to A Date marking the upper bound for the date_accepted field of a story
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
- story_list
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
- process_project_element(project_element)
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 - "story")
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
- # project_element: A Nokogiri::XML::Element representing a project (root element - "stories")
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
- def process_project_element(project_element)
89
+ #
90
+ # Excludes stories that fall outside of any added filters
91
+ def process_stories_element(stories_element)
84
92
 
85
- story_html_array = project_element.search('story')
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
- story_list << process_story_element(story_element)
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 - "iteration")
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 = process_project_element(child)
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
@@ -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
- # True if filters are being used for the stories, else false
54
+ # A string representing the current filter
54
55
  attr_reader :filter_string
55
56
 
56
57
  ##
57
- # A hash (field => contents) of the filters being used
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 the stories, else false
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[:label] = @options[:label] if @options[:label]
129
- @filters[:owner] = @options[:owner] if @options[:owner]
130
- @filters[:type] = @options[:type] if @options[:type]
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
- # Generates a filter string from the story filters
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
 
@@ -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.2.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-07 00:00:00.000000000 Z
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