torque 0.0.1

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.
@@ -0,0 +1,108 @@
1
+ class Torque
2
+
3
+ ##
4
+ # Stores the data to represent a Pivotal Tracker story
5
+ class Story
6
+
7
+ ##
8
+ # The current state of the story (finished, accepted, etc)
9
+ attr_reader :current_state
10
+
11
+ ##
12
+ # The date that the story was accepted, or nil if it has not been accepted yet
13
+ attr_reader :date_accepted
14
+
15
+ ##
16
+ # The story description
17
+ attr_reader :description
18
+
19
+ ##
20
+ # The estimate for the story
21
+ attr_reader :estimate
22
+
23
+ ##
24
+ # The labels for the story
25
+ attr_reader :labels
26
+
27
+ ##
28
+ # The name of the story
29
+ attr_reader :name
30
+
31
+ ##
32
+ # The ID of the story's project
33
+ attr_reader :project_id
34
+
35
+ ##
36
+ # The story's ID
37
+ attr_reader :story_id
38
+
39
+ ##
40
+ # The story's type (feature, chore, etc)
41
+ attr_reader :story_type
42
+
43
+ ##
44
+ # @param html_hash A hash (of html elements keyed by their tags) generated from Pivotal Tracker story html
45
+ #
46
+ # Returns a new story whose fields were parsed from the html_hash provided
47
+ def self.create(html_hash)
48
+ Story.new(html_hash).parse
49
+ end
50
+
51
+ ##
52
+ # html_hash:: A hash (of html elements keyed by their tags) generated from Pivotal Tracker story html
53
+ #
54
+ # Creates a new story without parsing its fields
55
+ def initialize(html_hash)
56
+ @html_hash = html_hash
57
+ end
58
+
59
+ # If the second input is an empty array, returns the first input
60
+ # Else returns the index 0 of the second
61
+ def handle_nil(default, value)
62
+ (value.nil? ? default : value)
63
+ end
64
+ private :handle_nil
65
+
66
+ ##
67
+ # Parses the story's fields from its html hash
68
+ def parse
69
+ html_hash = @html_hash
70
+
71
+ # Default values
72
+ @current_state = ""
73
+ @date_accepted = nil # Will be a date
74
+ @description = ""
75
+ @estimate = -1
76
+ @labels = "" # Will be an array
77
+ @name = ""
78
+ @project_id = -1
79
+ @story_id = -1
80
+ @story_type = ""
81
+
82
+ @current_state = handle_nil(@current_state, html_hash["current_state"])
83
+ @description = handle_nil(@description, html_hash["description"])
84
+ @name = handle_nil(@name, html_hash["name"])
85
+ @story_type = handle_nil(@story_type, html_hash["story_type"])
86
+
87
+ @estimate = Integer(handle_nil(@estimate, html_hash["estimate"]))
88
+ @project_id = Integer(handle_nil(@project_id, html_hash["project_id"]))
89
+ @story_id = Integer(handle_nil(@story_id, html_hash["id"]))
90
+
91
+ @labels = handle_nil(@labels, html_hash["labels"])
92
+ @labels = @labels.split(",")
93
+
94
+ date_acceptedString = html_hash["accepted_at"]
95
+ if(date_acceptedString != nil && date_acceptedString != "")
96
+ @date_accepted = Date.strptime(date_acceptedString, "%Y/%m/%d")
97
+ end
98
+
99
+ # Returns the generated story
100
+ self
101
+ end
102
+
103
+ def to_s()
104
+ return "Story("+@name+", "+"#{@story_id}"+")"
105
+ end
106
+
107
+ end
108
+ end
@@ -0,0 +1,230 @@
1
+ require_relative "file_system"
2
+ require_relative "error/missing_torque_info_file_error"
3
+ require 'yaml'
4
+
5
+ class Torque
6
+
7
+ ##
8
+ # Parses the raw contents of the torque info file
9
+ # Stores each of the fields in .torqueinfo.yaml as publicly accessible fields
10
+ class TorqueInfoParser
11
+
12
+ ##
13
+ # The email_address field (String)
14
+ attr_reader :email_address
15
+
16
+ ##
17
+ # The email_password field (String)
18
+ attr_reader :email_password
19
+
20
+ ##
21
+ # The email_to field (String or Array)
22
+ attr_reader :email_to
23
+
24
+ ##
25
+ # The project field (Fixnum)
26
+ attr_reader :project
27
+
28
+ ##
29
+ # The output_dir field (String)
30
+ attr_reader :output_dir
31
+
32
+ ##
33
+ # The token field (String)
34
+ attr_reader :token
35
+
36
+ ##
37
+ # @param file_path The path to the .torqueinfo.yaml file
38
+ # @param file_system An instance of the FileSystem class
39
+ #
40
+ # Creates a new parser. Does not parse the file
41
+ def initialize(file_path = "./.torqueinfo.yaml", file_system = FileSystem.new)
42
+
43
+ @file_path = file_path
44
+ @fs = file_system
45
+ end
46
+
47
+ ##
48
+ # Parses the file, storing the results as public instnance fields
49
+ # Stores each field with no corresponding value in the file as 'nil'
50
+ # Returns self
51
+ def parse
52
+
53
+ if !@fs.path_exist?(@file_path)
54
+ directory = File.expand_path(File.dirname(@file_path))
55
+ raise MissingTorqueInfoFileError.new "'#{directory}' is not configured for Torque"
56
+ end
57
+
58
+ torque_info_hash = {}
59
+ begin
60
+ torque_info_hash = YAML::load(@fs.file_read(@file_path)) || {}
61
+ rescue Psych::SyntaxError
62
+ # If cannot parse, ignore contents
63
+ end
64
+
65
+ @email_address = torque_info_hash["email_address"]
66
+ @email_password = torque_info_hash["email_password"]
67
+ @email_to = torque_info_hash["email_to"]
68
+ @project = torque_info_hash["project"]
69
+ @output_dir = torque_info_hash["output_dir"]
70
+ @token = torque_info_hash["token"]
71
+
72
+ self
73
+ end
74
+
75
+ ##
76
+ # Sets the specified field to the specified value in the file on disk
77
+ def set(field, value)
78
+ file_string = @fs.file_read(@file_path)
79
+ new_file_string = set_string(field, value, file_string)
80
+ @fs.file_write(@file_path, new_file_string)
81
+ end
82
+
83
+ ##
84
+ # Adds a list of values to a sequence belonging to field on disk
85
+ def add(field, values)
86
+ file_string = @fs.file_read(@file_path)
87
+ new_file_string = add_string(field, values, file_string)
88
+ @fs.file_write(@file_path, new_file_string)
89
+ end
90
+
91
+ ##
92
+ # Adds a list of values to a sequence belonging to field, eliminating duplicates, on disk
93
+ # Returns a list of all values that were added (were not duplicates)
94
+ def add_no_duplicates(field, values)
95
+ file_string = @fs.file_read(@file_path)
96
+ values_copy = values.clone
97
+
98
+ new_file_string = add_no_duplicates_string(field, values_copy, file_string)
99
+ @fs.file_write(@file_path, new_file_string)
100
+ values_copy
101
+ end
102
+
103
+ ##
104
+ # Removes a field to a sequence of values on disk
105
+ # Returns a list of the values that were removed (could be found)
106
+ def rm(field, values)
107
+ file_string = @fs.file_read(@file_path)
108
+ values_copy = values.clone
109
+
110
+ new_file_string = rm_string(field, values_copy, file_string)
111
+ @fs.file_write(@file_path, new_file_string)
112
+ values_copy
113
+ end
114
+
115
+
116
+ private
117
+
118
+ # Util method. Changes the first array to the second array in place
119
+ def set_array_in_place(array1, array2)
120
+ array1.delete_if {true}
121
+ array1.concat(array2)
122
+ end
123
+
124
+ # Util method. Converts a yaml field to an applicable array
125
+ def to_array(value)
126
+ if value.is_a? Array; value
127
+ elsif value.is_a? String; [value]
128
+ elsif value.is_a? NilClass; []
129
+ else; []
130
+ end
131
+ end
132
+
133
+ # Alters the string contents of a file, setting the specified field to the specified value
134
+ def set_string(field, value, file_string)
135
+
136
+ file_yaml = YAML.load(file_string) || {}
137
+ file_yaml[field] = value
138
+ file_yaml.to_yaml
139
+
140
+ end
141
+
142
+ # Alters the string contents of a file, adding all elements of 'values' to field 'field'
143
+ # Returns the new string contents
144
+ def add_string(field, values, file_string)
145
+
146
+ return file_string if values.empty?
147
+
148
+ file_yaml = YAML.load(file_string) || {}
149
+ file_yaml[field] = to_array(file_yaml[field])
150
+ file_yaml[field].concat(values)
151
+ file_yaml.to_yaml
152
+
153
+ end
154
+
155
+ # Alters the string contents of a file, adding all elements of 'values' to field 'field' ignoring duplicates
156
+ # At finish, 'values' contains all values that were added (were not duplicates)
157
+ # Returns the new string contents
158
+ def add_no_duplicates_string(field, values, file_string)
159
+
160
+ return file_string if values.empty?
161
+
162
+ file_yaml = YAML.load(file_string) || {}
163
+ file_yaml[field] = to_array(file_yaml[field])
164
+ file_yaml[field].delete(nil)
165
+
166
+ values.uniq!
167
+
168
+ index = 0
169
+ while index < values.length
170
+ value = values[index]
171
+
172
+ if file_yaml[field].member? value
173
+ values.delete_at(index)
174
+ index-=1
175
+ else
176
+ file_yaml[field] << value
177
+ end
178
+
179
+ index+=1
180
+ end
181
+
182
+ file_yaml.to_yaml
183
+
184
+ end
185
+
186
+ # Alters the string contents of a file so that value is added to a sequence belonging to field
187
+ # At finish, 'values' contains all values that were removed (could be found)
188
+ # Returns the new string contents
189
+ def rm_string(field, values, file_string)
190
+
191
+ return file_string if values.empty?
192
+
193
+ file_yaml = YAML.load(file_string) || {}
194
+
195
+ # The field does not exist
196
+ if file_yaml[field].is_a? NilClass
197
+ set_array_in_place(values, [])
198
+ return file_string
199
+
200
+ # The field is a single value
201
+ elsif file_yaml[field].is_a? String
202
+ if values.member? file_yaml[field]
203
+ set_array_in_place(values, [ file_yaml[field] ])
204
+ file_yaml.delete(field)
205
+ else
206
+ set_array_in_place(values, [])
207
+ end
208
+
209
+ # The field is a sequence
210
+ else
211
+ index = 0;
212
+ while index < values.length
213
+ value = values[index]
214
+ if file_yaml[field].member? value
215
+ file_yaml[field].delete(value)
216
+ else
217
+ values.delete_at(index)
218
+ index -= 1
219
+ end
220
+ index += 1
221
+ end
222
+
223
+ file_yaml.delete(field) if file_yaml[field].empty?
224
+ end
225
+
226
+ file_yaml.to_yaml
227
+ end
228
+
229
+ end
230
+ end
@@ -0,0 +1,24 @@
1
+ class Torque
2
+ class Version
3
+
4
+ version = {}
5
+ File.read(File.join(File.dirname(__FILE__), '../', '../', 'VERSION')).each_line do |line|
6
+ type, value = line.chomp.split(":")
7
+ next if type =~ /^\s+$/ || value =~ /^\s+$/
8
+ version[type] = value
9
+ end
10
+
11
+ MAJOR = version['major']
12
+ MINOR = version['minor']
13
+ PATCH = version['patch']
14
+
15
+ STRING = [MAJOR, MINOR, PATCH].compact.join('.')
16
+
17
+ ##
18
+ # The string representing the current version of Torque (eg "1.9.2")
19
+ def self.string
20
+ STRING
21
+ end
22
+
23
+ end
24
+ end
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: torque
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Nico Adams, Scrimmage
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-08-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: highline
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.6.19
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.6.19
27
+ - !ruby/object:Gem::Dependency
28
+ name: mail
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 2.5.4
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: 2.5.4
41
+ - !ruby/object:Gem::Dependency
42
+ name: nokogiri
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.6.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.6.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: mocha
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Compiles Pivotal Tracker stories into a local document to help generate
84
+ release notes
85
+ email:
86
+ - nico@wescrimmage.com
87
+ executables:
88
+ - torque
89
+ extensions: []
90
+ extra_rdoc_files:
91
+ - README.md
92
+ files:
93
+ - bin/config
94
+ - bin/email
95
+ - bin/project
96
+ - bin/torque
97
+ - lib/torque/date_settings.rb
98
+ - lib/torque/error/invalid_project_error.rb
99
+ - lib/torque/error/invalid_token_error.rb
100
+ - lib/torque/error/missing_output_directory_error.rb
101
+ - lib/torque/error/missing_project_error.rb
102
+ - lib/torque/error/missing_token_error.rb
103
+ - lib/torque/error/missing_torque_info_file_error.rb
104
+ - lib/torque/error/pivotal_api_error.rb
105
+ - lib/torque/file_system.rb
106
+ - lib/torque/mailer.rb
107
+ - lib/torque/pivotal.rb
108
+ - lib/torque/pivotal_html_parser.rb
109
+ - lib/torque/project/project.rb
110
+ - lib/torque/project/project_manager.rb
111
+ - lib/torque/record_pathname_settings.rb
112
+ - lib/torque/settings.rb
113
+ - lib/torque/story.rb
114
+ - lib/torque/torque_info_parser.rb
115
+ - lib/torque/version.rb
116
+ - lib/torque.rb
117
+ - Gemfile
118
+ - LICENSE
119
+ - README.md
120
+ - VERSION
121
+ homepage: https://github.com/Scrimmage/torque
122
+ licenses:
123
+ - MIT
124
+ metadata: {}
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 2.0.3
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: Generates release notes for a project from Pivotal Tracker stories
145
+ test_files: []