resumetools 0.2.7.0

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,307 @@
1
+ # coding: utf-8
2
+
3
+ #--
4
+ # Copyright (c) 2009 Virgil Dimaguila
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person
7
+ # obtaining a copy of this software and associated documentation
8
+ # files (the "Software"), to deal in the Software without
9
+ # restriction, including without limitation the rights to use,
10
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the
12
+ # Software is furnished to do so, subject to the following
13
+ # conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25
+ # OTHER DEALINGS IN THE SOFTWARE.
26
+ #++
27
+
28
+ module ResumeTools
29
+
30
+ # = Resume
31
+ # Represents a single resume document
32
+ class Resume
33
+
34
+ # The full name of the subject of this resume
35
+ attr_accessor :full_name
36
+
37
+ # The subject's URL
38
+ attr_accessor :url
39
+
40
+ # The subject's e-mail address
41
+ attr_accessor :email
42
+
43
+ # The subject's telephone number
44
+ attr_accessor :telephone
45
+
46
+ # First line of the address
47
+ attr_accessor :address1
48
+
49
+ # Second line of the address
50
+ attr_accessor :address2
51
+
52
+ # The resume's sections
53
+ attr_reader :sections
54
+
55
+ # Creates a +Resume+ and passes it to the given block. Returns the created +Resume+.
56
+ def self.build(opts={}, &block)
57
+ resume = self.new(opts)
58
+ block.call(resume)
59
+ resume
60
+ end
61
+
62
+ # Creates a new +Resume+ with the given properties
63
+ def initialize(props={})
64
+ @full_name = props[:full_name] || ""
65
+ @url = props[:url] || ""
66
+ @email = props[:email] || ""
67
+ @telephone = props[:telephone] || ""
68
+ @address1 = props[:address1] || ""
69
+ @address2 = props[:address2] || ""
70
+ @sections = Array.new
71
+ end
72
+
73
+ # Add section
74
+ def add_section(section)
75
+ self.sections << section
76
+ self
77
+ end
78
+
79
+ # Create new section and add to sections
80
+ def create_section(props={}, &block)
81
+ section = Section.new(props)
82
+ block.call(section) if block
83
+ self.add_section(section)
84
+ self
85
+ end
86
+
87
+ #
88
+ def has_url?
89
+ !self.url.blank?
90
+ end
91
+
92
+ #
93
+ def has_email?
94
+ !self.email.blank?
95
+ end
96
+
97
+ #
98
+ def has_telephone?
99
+ !self.telephone.blank?
100
+ end
101
+
102
+ #
103
+ def has_address1?
104
+ !self.address1.blank?
105
+ end
106
+
107
+ #
108
+ def has_address2?
109
+ !self.address2.blank?
110
+ end
111
+
112
+ #
113
+ def has_sections?
114
+ !self.sections.empty?
115
+ end
116
+
117
+ # Returns an array of lines that has the contact info
118
+ def header_lines
119
+ elements = []
120
+ [:address1, :address2, :telephone, :email, :url].each do |element|
121
+ elements << self.send(element) unless self.send(element).blank?
122
+ end
123
+ lines = []
124
+ elements.each_slice(2) { |pair| lines << pair.join(" • ") }
125
+ lines
126
+ end
127
+
128
+ end #class Resume
129
+
130
+
131
+ # = Section
132
+ # Represents a section in the resume
133
+ class Section
134
+ # Section title
135
+ attr_accessor :title
136
+
137
+ # Section paragraph
138
+ attr_accessor :para
139
+
140
+ # List of periods
141
+ attr_reader :periods
142
+
143
+ # List of items
144
+ attr_reader :items
145
+
146
+ #
147
+ def initialize(props={})
148
+ @title = props[:title] || ""
149
+ @para = props[:para] || ""
150
+ @items = Array.new
151
+ @periods = Array.new
152
+ end
153
+
154
+ #
155
+ def has_paragraph?
156
+ !self.para.blank?
157
+ end
158
+
159
+ def has_para?
160
+ self.has_paragraph?
161
+ end
162
+
163
+ #
164
+ def has_items?
165
+ !self.items.empty?
166
+ end
167
+
168
+ #
169
+ def has_periods?
170
+ !self.periods.empty?
171
+ end
172
+
173
+ #
174
+ def has_title?
175
+ !self.title.blank?
176
+
177
+ end
178
+
179
+ # Creates a period and adds it to the list
180
+ def create_period(props={}, &block)
181
+ period = Period.new(props)
182
+ block.call(period) if block
183
+ self.add_period(period)
184
+ self
185
+ end
186
+
187
+ # Adds a period
188
+ def add_period(period)
189
+ self.periods << period
190
+ end
191
+
192
+ # Ads an item
193
+ def add_item(item)
194
+ self.items << item
195
+ end
196
+
197
+ # Creates an item and adds it to the list
198
+ def create_item(props={}, &block)
199
+ item = Item.new(props)
200
+ block.call(item) if block
201
+ self.add_item(item)
202
+ self
203
+ end
204
+ end
205
+
206
+
207
+ # = Period
208
+ # Represents a period in the section
209
+ class Period
210
+ # Period title
211
+ attr_accessor :title
212
+
213
+ # Period location
214
+ attr_accessor :location
215
+
216
+ # Period organization
217
+ attr_accessor :organization
218
+
219
+ # Period start date
220
+ attr_accessor :dtstart
221
+
222
+ # Period end date
223
+ attr_accessor :dtend
224
+
225
+ # List of items
226
+ attr_reader :items
227
+
228
+ #
229
+ def initialize(props={})
230
+ @title = props[:title] || ""
231
+ @location = props[:location] || ""
232
+ @organization = props[:organization] || ""
233
+ @dtstart = props[:dtstart] || nil
234
+ @dtend = props[:dtend] || nil
235
+ @items = Array.new
236
+ end
237
+
238
+ #
239
+ def has_title?
240
+ !self.title.blank?
241
+ end
242
+
243
+ def has_location?
244
+ !self.location.blank?
245
+ end
246
+
247
+ def has_organization?
248
+ !self.organization.blank?
249
+ end
250
+
251
+ def has_dtstart?
252
+ !self.dtstart.blank?
253
+ end
254
+
255
+ def has_dtend?
256
+ !self.dtend.blank?
257
+ end
258
+
259
+ def has_items?
260
+ !self.items.empty?
261
+ end
262
+
263
+ # Adds an item
264
+ def add_item(item)
265
+ self.items << item
266
+ end
267
+
268
+ # Creates an item and adds it to the list
269
+ def create_item(props={}, &block)
270
+ item = Item.new(props)
271
+ block.call(item) if block
272
+ self.add_item(item)
273
+ item
274
+ end
275
+
276
+ # The period details in a single line
277
+ def line
278
+ elements = []
279
+ elements << self.organization if self.has_organization?
280
+ elements << self.location if self.has_location?
281
+ if self.has_dtstart? && self.has_dtend?
282
+ date = " (#{self.dtstart} - #{self.dtend})"
283
+ elsif self.has_dtstart? || self.has_dtend?
284
+ value = self.has_dtstart? ? self.dtstart : self.dtend
285
+ date = " (#{value})"
286
+ else
287
+ date = ''
288
+ end
289
+ elements.join(", ").concat("#{date}")
290
+ end
291
+ end
292
+
293
+
294
+ # = Item
295
+ # Represents an item in a period or section. Items are usually
296
+ # bulleted items that are listed in order
297
+ class Item
298
+ # The item text
299
+ attr_accessor :text
300
+
301
+ #
302
+ def initialize(props={})
303
+ @text = props[:text] || ""
304
+ end
305
+ end
306
+
307
+ end
@@ -0,0 +1,107 @@
1
+ #--
2
+ # Copyright (c) 2009 Virgil Dimaguila
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person
5
+ # obtaining a copy of this software and associated documentation
6
+ # files (the "Software"), to deal in the Software without
7
+ # restriction, including without limitation the rights to use,
8
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the
10
+ # Software is furnished to do so, subject to the following
11
+ # conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23
+ # OTHER DEALINGS IN THE SOFTWARE.
24
+ #++
25
+
26
+ module ResumeTools
27
+ module TextReader
28
+ class ParseException < Exception
29
+ end
30
+
31
+ module ClassMethods
32
+ require 'treetop'
33
+
34
+ Treetop.load File.join(File.dirname(__FILE__), '..', 'grammars', 'resume.treetop')
35
+
36
+ # Builds a new Resume instance from text
37
+ def from_text(text)
38
+ parser = ::ResumeParser.new
39
+ result = parser.parse(text)
40
+
41
+ if result.nil?
42
+ raise ParseException.new(parser.failure_reason)
43
+ end
44
+
45
+ resume = ResumeTools::Resume.new
46
+ current_section = nil
47
+ current_period = nil
48
+
49
+ result.elements.each do |element|
50
+ case element.data_type
51
+ when :item
52
+ item = ::ResumeTools::Item.new(:text => element.value)
53
+ if current_period
54
+ current_period.add_item(item)
55
+ elsif current_section
56
+ current_section.add_item(item)
57
+ end
58
+ when :period
59
+ current_period = ::ResumeTools::Period.new(:title => element.value)
60
+ current_section.add_period(current_period) if current_section
61
+ when :section
62
+ current_period = nil # Reset period
63
+ current_section = ::ResumeTools::Section.new(:title => element.value)
64
+ resume.add_section(current_section)
65
+ when :period_location
66
+ current_period.location = element.value if current_period
67
+ when :period_organization
68
+ current_period.organization = element.value if current_period
69
+ when :period_dates
70
+ if current_period
71
+ dates = element.value.split("to", 2).map { |d| d.strip }
72
+ if dates.length == 1
73
+ current_period.dtend = dates[0]
74
+ current_period.dtstart = nil
75
+ elsif dates.length == 2
76
+ current_period.dtstart = dates[0]
77
+ current_period.dtend = dates[1]
78
+ end
79
+ end
80
+ when :paragraph
81
+ current_section.para = element.value if current_section
82
+ when :contact_name
83
+ resume.full_name = element.value
84
+ when :contact_telephone
85
+ resume.telephone = element.value
86
+ when :contact_email
87
+ resume.email = element.value
88
+ when :contact_address
89
+ if resume.address1.blank?
90
+ resume.address1 = element.value
91
+ elsif resume.address2.blank?
92
+ resume.address2 = element.value
93
+ end
94
+ when :contact_url
95
+ resume.url = element.value
96
+ end
97
+ end
98
+
99
+ resume
100
+ end
101
+ end
102
+ end
103
+
104
+ class << Resume
105
+ include TextReader::ClassMethods
106
+ end
107
+ end
@@ -0,0 +1,36 @@
1
+ #--
2
+ # Resume Tools, Copyright (c) 2009 Virgil Dimaguila
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person
5
+ # obtaining a copy of this software and associated documentation
6
+ # files (the "Software"), to deal in the Software without
7
+ # restriction, including without limitation the rights to use,
8
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the
10
+ # Software is furnished to do so, subject to the following
11
+ # conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23
+ # OTHER DEALINGS IN THE SOFTWARE.
24
+ #++
25
+
26
+ unless defined? ResumeTools::VERSION
27
+ module ResumeTools
28
+ module VERSION
29
+ MAJOR = 0
30
+ MINOR = 2
31
+ TINY = 7
32
+ BITSY = 0
33
+ STRING = [MAJOR, MINOR, TINY, BITSY].join('.')
34
+ end
35
+ end
36
+ end