virgild-resumetools 0.2.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.
- data/CHANGES +0 -0
- data/LICENSE +22 -0
- data/README.md +203 -0
- data/Rakefile +38 -0
- data/examples/sample.pdf +1751 -0
- data/examples/sample.resume +150 -0
- data/lib/grammars/resume.treetop +389 -0
- data/lib/resume/pdf.rb +158 -0
- data/lib/resume/resume.rb +231 -0
- data/lib/resume/text_reader.rb +107 -0
- data/lib/resumetools/version.rb +35 -0
- data/lib/resumetools.rb +34 -0
- data/spec/grammar_spec.rb +93 -0
- data/spec/read_resume_spec.rb +61 -0
- data/spec/rendering_pdf_spec.rb +37 -0
- data/spec/resume_spec.rb +213 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +39 -0
- data/tasks/default.rake +1 -0
- data/tasks/gem.rake +77 -0
- data/tasks/package.rake +9 -0
- data/tasks/rdoc.rake +29 -0
- data/tasks/rspec.rake +16 -0
- metadata +128 -0
@@ -0,0 +1,231 @@
|
|
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
|
+
|
28
|
+
# = Resume
|
29
|
+
# Represents a single resume document
|
30
|
+
class Resume
|
31
|
+
|
32
|
+
# The title of the resume
|
33
|
+
attr_accessor :title
|
34
|
+
|
35
|
+
# The name (or label) for this resume
|
36
|
+
attr_accessor :name
|
37
|
+
|
38
|
+
# The full name of the subject of this resume
|
39
|
+
attr_accessor :full_name
|
40
|
+
|
41
|
+
# The subject's URL
|
42
|
+
attr_accessor :url
|
43
|
+
|
44
|
+
# The subject's e-mail address
|
45
|
+
attr_accessor :email
|
46
|
+
|
47
|
+
# The subject's telephone number
|
48
|
+
attr_accessor :telephone
|
49
|
+
|
50
|
+
# First line of the address
|
51
|
+
attr_accessor :address1
|
52
|
+
|
53
|
+
# Second line of the address
|
54
|
+
attr_accessor :address2
|
55
|
+
|
56
|
+
# The resume's sections
|
57
|
+
attr_reader :sections
|
58
|
+
|
59
|
+
# Creates a +Resume+ and passes it to the given block. Returns the created +Resume+.
|
60
|
+
def self.build(&block)
|
61
|
+
resume = self.new
|
62
|
+
yield resume
|
63
|
+
resume
|
64
|
+
end
|
65
|
+
|
66
|
+
# Creates a new +Resume+ with the given properties
|
67
|
+
def initialize(props={})
|
68
|
+
@title = props[:title] || ""
|
69
|
+
@name = props[:name] || "Unnamed Resume"
|
70
|
+
@full_name = props[:full_name] || ""
|
71
|
+
@url = props[:url] || ""
|
72
|
+
@email = props[:email] || ""
|
73
|
+
@telephone = props[:telephone] || ""
|
74
|
+
@address1 = props[:address1] || ""
|
75
|
+
@address2 = props[:address2] || ""
|
76
|
+
@sections = Array.new
|
77
|
+
end
|
78
|
+
|
79
|
+
# Add section
|
80
|
+
def add_section(section)
|
81
|
+
self.sections << section
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
# Create new section and add to sections
|
86
|
+
def create_section(props={}, &block)
|
87
|
+
section = Section.new(props)
|
88
|
+
yield section if block_given?
|
89
|
+
self.add_section(section)
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
def has_url?
|
95
|
+
!self.url.blank?
|
96
|
+
end
|
97
|
+
|
98
|
+
#
|
99
|
+
def has_email?
|
100
|
+
!self.email.blank?
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
def has_telephone?
|
105
|
+
!self.telephone.blank?
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
def has_address1?
|
110
|
+
!self.address1.blank?
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
def has_address2?
|
115
|
+
!self.address2.blank?
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
# = Section
|
122
|
+
# Represents a section in the resume
|
123
|
+
class Section
|
124
|
+
# Section title
|
125
|
+
attr_accessor :title
|
126
|
+
|
127
|
+
# Section paragraph
|
128
|
+
attr_accessor :para
|
129
|
+
|
130
|
+
# List of periods
|
131
|
+
attr_reader :periods
|
132
|
+
|
133
|
+
# List of items
|
134
|
+
attr_reader :items
|
135
|
+
|
136
|
+
#
|
137
|
+
def initialize(props={})
|
138
|
+
@title = props[:title] || ""
|
139
|
+
@para = props[:para] || ""
|
140
|
+
@items = Array.new
|
141
|
+
@periods = Array.new
|
142
|
+
end
|
143
|
+
|
144
|
+
# Creates a period and adds it to the list
|
145
|
+
def create_period(props={}, &block)
|
146
|
+
period = Period.new(props)
|
147
|
+
yield period if block_given?
|
148
|
+
self.add_period(period)
|
149
|
+
self
|
150
|
+
end
|
151
|
+
|
152
|
+
# Adds a period
|
153
|
+
def add_period(period)
|
154
|
+
self.periods << period
|
155
|
+
end
|
156
|
+
|
157
|
+
# Ads an item
|
158
|
+
def add_item(item)
|
159
|
+
self.items << item
|
160
|
+
end
|
161
|
+
|
162
|
+
# Creates an item and adds it to the list
|
163
|
+
def create_item(props={}, &block)
|
164
|
+
item = Item.new(props)
|
165
|
+
yield item if block_given?
|
166
|
+
self.add_item(item)
|
167
|
+
self
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
# = Period
|
173
|
+
# Represents a period in the section
|
174
|
+
class Period
|
175
|
+
# Period title
|
176
|
+
attr_accessor :title
|
177
|
+
|
178
|
+
# Period location
|
179
|
+
attr_accessor :location
|
180
|
+
|
181
|
+
# Period organization
|
182
|
+
attr_accessor :organization
|
183
|
+
|
184
|
+
# Period start date
|
185
|
+
attr_accessor :dtstart
|
186
|
+
|
187
|
+
# Period end date
|
188
|
+
attr_accessor :dtend
|
189
|
+
|
190
|
+
# List of items
|
191
|
+
attr_reader :items
|
192
|
+
|
193
|
+
#
|
194
|
+
def initialize(props={})
|
195
|
+
@title = props[:title] || ""
|
196
|
+
@location = props[:location] || ""
|
197
|
+
@organization = props[:organization] || ""
|
198
|
+
@dtstart = props[:dtstart] || nil
|
199
|
+
@dtend = props[:dtend] || nil
|
200
|
+
@items = Array.new
|
201
|
+
end
|
202
|
+
|
203
|
+
# Adds an item
|
204
|
+
def add_item(item)
|
205
|
+
self.items << item
|
206
|
+
end
|
207
|
+
|
208
|
+
# Creates an item and adds it to the list
|
209
|
+
def create_item(props={}, &block)
|
210
|
+
item = Item.new(props)
|
211
|
+
yield item if block_given?
|
212
|
+
self.add_item(item)
|
213
|
+
item
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
# = Item
|
219
|
+
# Represents an item in a period or section. Items are usually
|
220
|
+
# bulleted items that are listed in order
|
221
|
+
class Item
|
222
|
+
# The item text
|
223
|
+
attr_accessor :text
|
224
|
+
|
225
|
+
#
|
226
|
+
def initialize(props={})
|
227
|
+
@text = props[:text] || ""
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
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,35 @@
|
|
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 = 0
|
32
|
+
STRING = [MAJOR, MINOR, TINY].join('.')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/resumetools.rb
ADDED
@@ -0,0 +1,34 @@
|
|
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
|
+
$:.unshift(File.dirname(__FILE__))
|
27
|
+
|
28
|
+
require "extlib"
|
29
|
+
require "uuidtools"
|
30
|
+
|
31
|
+
require "resumetools/version"
|
32
|
+
require "resume/resume"
|
33
|
+
require "resume/text_reader"
|
34
|
+
require "resume/pdf"
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
require "treetop"
|
3
|
+
|
4
|
+
Treetop.load File.join(File.dirname(__FILE__), "../lib/grammars/resume.treetop")
|
5
|
+
|
6
|
+
describe "Resume Grammar" do
|
7
|
+
before(:all) do
|
8
|
+
resume_file = "sample.resume"
|
9
|
+
@parser = ResumeParser.new
|
10
|
+
@resume_txt = File.read(File.join(File.dirname(__FILE__), "../examples/#{resume_file}"))
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should have a parser" do
|
14
|
+
@parser.should be_instance_of(ResumeParser)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "after parsing sample file" do
|
18
|
+
def elements_with_type(type)
|
19
|
+
@elements.reject { |e| e.data_type != type }
|
20
|
+
end
|
21
|
+
|
22
|
+
before(:all) do
|
23
|
+
@result = @parser.parse(@resume_txt)
|
24
|
+
@elements = @result.elements
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should parse our file" do
|
28
|
+
@result.should_not be_nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should have 33 items" do
|
32
|
+
elements_with_type(:item).length.should == 33
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should have 2 paragraphs" do
|
36
|
+
elements_with_type(:paragraph).length.should == 2
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should have 7 sections" do
|
40
|
+
elements_with_type(:section).length.should == 7
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should have contact name" do
|
44
|
+
elements_with_type(:contact_name).length.should == 1
|
45
|
+
elements_with_type(:contact_name)[0].value.should == "Thomas B. Seeker"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should have contact telephone" do
|
49
|
+
elements_with_type(:contact_telephone).length.should == 1
|
50
|
+
elements_with_type(:contact_telephone)[0].value.should == "(410) 555-1212"
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should have contact addresses" do
|
54
|
+
elements_with_type(:contact_address).length.should == 2
|
55
|
+
elements_with_type(:contact_address)[0].value.should == "1234 Northern Star Circle"
|
56
|
+
elements_with_type(:contact_address)[1].value.should == "Baltimore, MD 12345"
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should have contact e-mail" do
|
60
|
+
elements_with_type(:contact_email).length.should == 1
|
61
|
+
elements_with_type(:contact_email)[0].value.should == "seeker@nettcom.com"
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should have contact URL" do
|
65
|
+
elements_with_type(:contact_url).length.should == 1
|
66
|
+
elements_with_type(:contact_url)[0].value.should == "http://nettcom.com/tseeker"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should have 5 periods" do
|
70
|
+
elements_with_type(:period).length.should == 5
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should have 5 period organizations" do
|
74
|
+
elements_with_type(:period_organization).length.should == 5
|
75
|
+
elements_with_type(:period_organization).map { |o| o.value }.should == [
|
76
|
+
"Computer Engineering Corporation",
|
77
|
+
"Business Consultants, Inc.",
|
78
|
+
"US Army Infantry",
|
79
|
+
"Air Force Institute of Technology (AFIT)",
|
80
|
+
"University of California, Los Angeles (UCLA)"
|
81
|
+
]
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should have 3 period locations" do
|
85
|
+
elements_with_type(:period_location).length.should == 3
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should have 3 period dates" do
|
89
|
+
elements_with_type(:period_dates).length.should == 3
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe "Creating a resume from a text" do
|
4
|
+
before do
|
5
|
+
sample = File.join(File.dirname(__FILE__), '..', 'examples', 'sample.resume')
|
6
|
+
text = File.read(sample)
|
7
|
+
@resume = ResumeTools::Resume.from_text(text)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should create a resume" do
|
11
|
+
@resume.should be_instance_of(ResumeTools::Resume)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should have the correct contact info" do
|
15
|
+
@resume.full_name.should == "Thomas B. Seeker"
|
16
|
+
@resume.address1.should == "1234 Northern Star Circle"
|
17
|
+
@resume.address2.should == "Baltimore, MD 12345"
|
18
|
+
@resume.telephone.should == "(410) 555-1212"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should have 7 sections" do
|
22
|
+
@resume.should have(7).sections
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should have sections in order" do
|
26
|
+
@resume.sections.map { |s| s.title }.should == [
|
27
|
+
"Career Goal",
|
28
|
+
"Qualifications Summary",
|
29
|
+
"Technical Skills",
|
30
|
+
"Professional Experience",
|
31
|
+
"Education",
|
32
|
+
"Specialized Training",
|
33
|
+
"Certification, Honors, and Professional Affiliations"
|
34
|
+
]
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should have a paragraph in the Career Goal section" do
|
38
|
+
@resume.sections[0].para.should_not be_blank
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should have a paragraph in the Qualifications Summary section" do
|
42
|
+
@resume.sections[1].para.should_not be_blank
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should have 4 items in the Technical Skills section" do
|
46
|
+
@resume.sections[2].should have(4).items
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should have 3 periods in the Professional Experience section" do
|
50
|
+
@resume.sections[3].should have(3).periods
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should have 4 items in the Systems Engineer period" do
|
54
|
+
@resume.sections[3].periods[0].should have(4).items
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should have proper dates" do
|
58
|
+
@resume.sections[3].periods[0].dtstart.should == "1993"
|
59
|
+
@resume.sections[3].periods[0].dtend.should == "Present"
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,37 @@
|
|
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
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
27
|
+
|
28
|
+
describe "Rendering a Resume to PDF" do
|
29
|
+
before do
|
30
|
+
@resume = ResumeTools::Resume.new
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should render to PDF" do
|
34
|
+
result = @resume.render_pdf
|
35
|
+
result.should_not be_nil
|
36
|
+
end
|
37
|
+
end
|