torque 0.0.1

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