resme 0.3.0 → 0.4.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.
- checksums.yaml +4 -4
- data/.gitignore +0 -3
- data/Gemfile +2 -2
- data/README.org +245 -0
- data/exe/resme +2 -2
- data/lib/resme/cli/command_semantics.rb +96 -94
- data/lib/resme/cli/command_syntax.rb +223 -204
- data/lib/resme/renderer/renderer.rb +20 -18
- data/lib/resme/templates/resume.md.erb +3 -3
- data/lib/resme/templates/resume.org.erb +15 -12
- data/lib/resme/templates/resume.xml.erb +495 -0
- data/lib/resme/version.rb +1 -1
- data/resme.gemspec +17 -12
- metadata +22 -33
- data/README.md +0 -192
- data/lib/resme/templates/europass/eu.xml.erb +0 -483
@@ -1,234 +1,253 @@
|
|
1
|
-
require
|
1
|
+
require "optparse"
|
2
2
|
|
3
3
|
module Resme
|
4
4
|
module CommandSyntax
|
5
|
-
#
|
5
|
+
# Return a hash of hashes. Each key is the name of a command and
|
6
|
+
# includes useful information, such as an Option object to
|
7
|
+
# parse options and a documentation string.
|
6
8
|
def self.commands
|
7
|
-
|
8
|
-
|
9
|
-
if method.to_s.include?("_opts")
|
10
|
-
h = h.merge(eval(method.to_s))
|
11
|
-
end
|
9
|
+
cmd_spec = {}
|
10
|
+
methods.each do |method|
|
11
|
+
cmd_spec = cmd_spec.merge eval(method.to_s) if method.to_s.include?("_opts")
|
12
12
|
end
|
13
|
-
|
13
|
+
cmd_spec
|
14
14
|
end
|
15
15
|
|
16
|
-
private
|
17
|
-
|
18
16
|
def self.version_opts
|
19
|
-
opts =
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
17
|
+
opts = OptionParser.new do |opts|
|
18
|
+
opts.banner = "version # print version information"
|
19
|
+
end
|
20
|
+
|
21
|
+
help = <<-EOS
|
22
|
+
NAME
|
23
|
+
#{opts.banner}
|
24
|
+
|
25
|
+
SYNOPSYS
|
26
|
+
#{opts.to_s}
|
27
|
+
|
28
|
+
DESCRIPTION
|
29
|
+
return version information
|
30
|
+
|
31
|
+
EXAMPLES
|
32
|
+
# resme version
|
33
|
+
resme version #{VERSION}
|
34
|
+
EOS
|
35
|
+
|
36
|
+
{
|
37
|
+
version: {
|
38
|
+
name: :version,
|
39
|
+
options: opts,
|
40
|
+
help: help.gsub(" ", "")
|
41
|
+
}
|
42
|
+
}
|
36
43
|
end
|
37
44
|
|
38
45
|
def self.console_opts
|
39
|
-
opts =
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
46
|
+
opts = OptionParser.new do |opts|
|
47
|
+
opts.banner = "console # enter the console"
|
48
|
+
end
|
49
|
+
|
50
|
+
help = <<-EOS
|
51
|
+
NAME
|
52
|
+
#{opts.banner}
|
53
|
+
|
54
|
+
SYNOPSYS
|
55
|
+
#{opts.to_s}
|
56
|
+
|
57
|
+
DESCRIPTION
|
58
|
+
Invoke a console, from which you can more easily run
|
59
|
+
resme commands.
|
60
|
+
|
61
|
+
EXAMPLES
|
62
|
+
resme console
|
63
|
+
resme:000> check
|
64
|
+
resme:001> generate -t md
|
65
|
+
resme:002>
|
66
|
+
EOS
|
67
|
+
|
68
|
+
{
|
69
|
+
console: {
|
70
|
+
name: :console,
|
71
|
+
options: opts,
|
72
|
+
help: help.gsub(" ", "")
|
73
|
+
}
|
74
|
+
}
|
59
75
|
end
|
60
76
|
|
61
77
|
def self.man_opts
|
62
|
-
opts =
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
+
opts = OptionParser.new do |opts|
|
79
|
+
opts.banner = "man # print resme manual page"
|
80
|
+
end
|
81
|
+
|
82
|
+
help = <<-EOS
|
83
|
+
NAME
|
84
|
+
#{opts.banner}
|
85
|
+
|
86
|
+
SYNOPSYS
|
87
|
+
#{opts.to_s}
|
88
|
+
|
89
|
+
DESCRIPTION
|
90
|
+
Print the README file of this gem
|
91
|
+
|
92
|
+
EXAMPLES
|
93
|
+
resme man
|
94
|
+
EOS
|
95
|
+
|
96
|
+
{
|
97
|
+
man: {
|
98
|
+
name: :man,
|
99
|
+
options: opts,
|
100
|
+
help: help.gsub(" ", "")
|
101
|
+
}
|
102
|
+
}
|
78
103
|
end
|
79
104
|
|
80
105
|
def self.help_opts
|
81
|
-
opts =
|
82
|
-
|
83
|
-
|
84
|
-
NAME
|
85
|
-
#{opts.banner}
|
86
|
-
|
87
|
-
SYNOPSYS
|
88
|
-
#{opts.to_s}
|
89
|
-
|
90
|
-
DESCRIPTION
|
91
|
-
Print help about a command
|
92
|
-
|
93
|
-
EXAMPLES
|
94
|
-
resme help
|
95
|
-
resme help generate
|
96
|
-
EOS
|
97
|
-
return { :help => [opts, :help, help] }
|
98
|
-
end
|
99
|
-
|
100
|
-
def self.check_opts
|
101
|
-
opts = Slop::Options.new
|
102
|
-
opts.banner = "check -- Check whether a YAML file conforms with the RESME syntax"
|
103
|
-
help = <<EOS
|
104
|
-
NAME
|
105
|
-
#{opts.banner}
|
106
|
-
|
107
|
-
SYNOPSYS
|
108
|
-
#{opts.to_s}
|
109
|
-
|
110
|
-
DESCRIPTION
|
111
|
-
Check whether a YAML file conforms with the RESME syntax.
|
112
|
-
|
106
|
+
opts = OptionParser.new do |opts|
|
107
|
+
opts.banner = "help [command] # print command usage"
|
108
|
+
end
|
113
109
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
110
|
+
help = <<-EOS
|
111
|
+
NAME
|
112
|
+
#{opts.banner}
|
113
|
+
|
114
|
+
SYNOPSYS
|
115
|
+
#{opts.to_s}
|
116
|
+
|
117
|
+
DESCRIPTION
|
118
|
+
Print help about a command
|
119
|
+
|
120
|
+
EXAMPLES
|
121
|
+
resme help
|
122
|
+
resme help generate
|
123
|
+
EOS
|
124
|
+
{
|
125
|
+
help: {
|
126
|
+
name: :help,
|
127
|
+
options: opts,
|
128
|
+
help: help.gsub(" ", "")
|
129
|
+
}
|
130
|
+
}
|
118
131
|
end
|
119
132
|
|
120
133
|
def self.init_opts
|
121
|
-
opts =
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
EOS
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
help = <<EOS
|
150
|
-
NAME
|
151
|
-
#{opts.banner}
|
152
|
-
|
153
|
-
SYNOPSYS
|
154
|
-
#{opts.to_s}
|
155
|
-
|
156
|
-
DESCRIPTION
|
157
|
-
|
158
|
-
Generate a markdown resume from the YAML input files.
|
159
|
-
|
160
|
-
EXAMPLES
|
161
|
-
|
162
|
-
resme md -o r.md resume.yml
|
163
|
-
EOS
|
164
|
-
return { md: [opts, :md, help] }
|
165
|
-
end
|
166
|
-
|
167
|
-
def self.org_opts
|
168
|
-
opts = Slop::Options.new
|
169
|
-
opts.banner = "org [-o output_filename] file.yml ..."
|
170
|
-
opts.string "-o", "--output", "Output filename"
|
171
|
-
help = <<EOS
|
172
|
-
NAME
|
173
|
-
#{opts.banner}
|
174
|
-
|
175
|
-
SYNOPSYS
|
176
|
-
#{opts.to_s}
|
177
|
-
|
178
|
-
DESCRIPTION
|
179
|
-
|
180
|
-
Generate an org-mode resume from the YAML input files.
|
181
|
-
|
182
|
-
EXAMPLES
|
183
|
-
|
184
|
-
resme md -o r.md resume.yml
|
185
|
-
EOS
|
186
|
-
return { org: [opts, :org, help] }
|
187
|
-
end
|
188
|
-
|
189
|
-
def self.json_opts
|
190
|
-
opts = Slop::Options.new
|
191
|
-
opts.banner = "json [-o output_filename] file.yml ..."
|
192
|
-
opts.string "-o", "--output", "Output filename"
|
193
|
-
help = <<EOS
|
194
|
-
NAME
|
195
|
-
#{opts.banner}
|
196
|
-
|
197
|
-
SYNOPSYS
|
198
|
-
#{opts.to_s}
|
199
|
-
|
200
|
-
DESCRIPTION
|
201
|
-
|
202
|
-
Generate a JSON resume from the YAML input files.
|
203
|
-
|
204
|
-
EXAMPLES
|
205
|
-
|
206
|
-
resme json -o r.md resume.yml
|
207
|
-
EOS
|
208
|
-
return { json: [opts, :json, help] }
|
134
|
+
opts = OptionParser.new do |opts|
|
135
|
+
opts.banner = "init [options] # generate an empty resume.yml file"
|
136
|
+
opts.on("-o", "--output FILENAME", String, "Output filename")
|
137
|
+
opts.on("-f", "--force", FalseClass, "Overwrite existing file (if present)")
|
138
|
+
end
|
139
|
+
|
140
|
+
help = <<-EOS
|
141
|
+
NAME
|
142
|
+
#{opts.banner}
|
143
|
+
|
144
|
+
SYNOPSYS
|
145
|
+
#{opts.to_s}
|
146
|
+
|
147
|
+
DESCRIPTION
|
148
|
+
Generate a YAML template for your resume in the current directory.
|
149
|
+
|
150
|
+
EXAMPLES
|
151
|
+
resme init
|
152
|
+
resme init -o r.yml
|
153
|
+
resme init -o r.yml --force
|
154
|
+
EOS
|
155
|
+
{
|
156
|
+
init: {
|
157
|
+
name: :init,
|
158
|
+
options: opts,
|
159
|
+
help: help.gsub(" ", "")
|
160
|
+
}
|
161
|
+
}
|
209
162
|
end
|
210
163
|
|
211
|
-
def self.
|
212
|
-
opts =
|
213
|
-
|
214
|
-
|
215
|
-
help = <<EOS
|
216
|
-
NAME
|
217
|
-
#{opts.banner}
|
164
|
+
def self.check_opts
|
165
|
+
opts = OptionParser.new do |opts|
|
166
|
+
opts.banner = "check resume.yml # Check syntax of resume.yml"
|
167
|
+
end
|
218
168
|
|
219
|
-
|
220
|
-
|
169
|
+
help = <<-EOS
|
170
|
+
NAME
|
171
|
+
#{opts.banner}
|
172
|
+
|
173
|
+
SYNOPSYS
|
174
|
+
#{opts.to_s}
|
175
|
+
|
176
|
+
DESCRIPTION
|
177
|
+
Check whether a YAML file conforms with the RESME syntax.
|
178
|
+
|
179
|
+
EXAMPLES
|
180
|
+
resme check file.yml
|
181
|
+
EOS
|
182
|
+
|
183
|
+
{
|
184
|
+
check: {
|
185
|
+
name: :check,
|
186
|
+
options: opts,
|
187
|
+
help: help.gsub(" ", "")
|
188
|
+
}
|
189
|
+
}
|
190
|
+
end
|
221
191
|
|
222
|
-
|
192
|
+
def self.list_opts
|
193
|
+
opts = OptionParser.new do |opts|
|
194
|
+
opts.banner = "list resume.yml # List main sections in resume.yml"
|
195
|
+
end
|
223
196
|
|
224
|
-
|
197
|
+
help = <<-EOS
|
198
|
+
NAME
|
199
|
+
#{opts.banner}
|
200
|
+
|
201
|
+
SYNOPSYS
|
202
|
+
#{opts.to_s}
|
203
|
+
|
204
|
+
DESCRIPTION
|
205
|
+
List main sections in resume.yml.
|
206
|
+
|
207
|
+
EXAMPLES
|
208
|
+
resme list file.yml
|
209
|
+
EOS
|
210
|
+
|
211
|
+
{
|
212
|
+
list: {
|
213
|
+
name: :list,
|
214
|
+
options: opts,
|
215
|
+
help: help.gsub(" ", "")
|
216
|
+
}
|
217
|
+
}
|
218
|
+
end
|
225
219
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
220
|
+
def self.generate_opts
|
221
|
+
opts = OptionParser.new do |o|
|
222
|
+
o.banner = "generate [options] resume.yml ... # output resume"
|
223
|
+
o.on("-e", "--erb FILENAME", String, "Template to use")
|
224
|
+
o.on("-t", "--to FORMAT", String, "Output format")
|
225
|
+
o.on("-o", "--output FILENAME", String, "Output filename")
|
226
|
+
o.on("-s", "--skip SECTION,SECTION", Array, "Sections to skip")
|
227
|
+
end
|
232
228
|
|
229
|
+
help = <<-EOS
|
230
|
+
NAME
|
231
|
+
#{opts.banner}
|
232
|
+
|
233
|
+
SYNOPSYS
|
234
|
+
#{opts.to_s}
|
235
|
+
|
236
|
+
DESCRIPTION
|
237
|
+
Generate a resume from the YAML input files.
|
238
|
+
|
239
|
+
EXAMPLES
|
240
|
+
resme generate -t org resume.yml
|
241
|
+
resme generate -t md -o r.md section-1.yml section-2.yml
|
242
|
+
EOS
|
243
|
+
|
244
|
+
{
|
245
|
+
generate: {
|
246
|
+
name: :generate,
|
247
|
+
options: opts,
|
248
|
+
help: help.gsub(" ", "")
|
249
|
+
}
|
250
|
+
}
|
251
|
+
end
|
233
252
|
end
|
234
253
|
end
|
@@ -133,23 +133,26 @@ def has_day input
|
|
133
133
|
end
|
134
134
|
end
|
135
135
|
|
136
|
-
# Access hash keys like they were class methods
|
137
|
-
#
|
136
|
+
# Access hash keys like they were class methods (hash["key"] -> hash.key) and
|
137
|
+
# report errors if key is missing
|
138
138
|
class Hash
|
139
139
|
def method_missing(m)
|
140
140
|
key = m.to_s
|
141
141
|
|
142
|
+
# we put a bit of info about the top level structure of a resume to avoid
|
143
|
+
# extra-long error messages I don't want to print detailed information
|
144
|
+
# about top-level entries missing in the resume
|
145
|
+
top_level_entries = %w[
|
146
|
+
contacts addresses web_presence summary work teaching projects other
|
147
|
+
committees volunteer visits education publications talks awards
|
148
|
+
achievements software skills languages driving interests references
|
149
|
+
]
|
150
|
+
|
142
151
|
# error: nil value
|
143
152
|
if self.has_key? key and self[key] == nil
|
144
153
|
$stderr.puts "[W] The value of key '#{key}' is nil in the following entry:"
|
145
154
|
|
146
|
-
|
147
|
-
# I don't want to print detailed information about top-level entries missing in the resume
|
148
|
-
top_level_entries = [
|
149
|
-
"contacts", "addresses", "web_presence", "summary", "work", "teaching", "projects", "other",
|
150
|
-
"committees", "volunteer", "visits", "education", "publications", "talks", "awards", "achievements",
|
151
|
-
"software", "skills", "languages", "driving", "interests", "references"]
|
152
|
-
if not top_level_entries.include?(key) then
|
155
|
+
unless top_level_entries.include?(key)
|
153
156
|
# $stderr.puts self.to_s
|
154
157
|
self.keys.each do |k|
|
155
158
|
$stderr.puts " #{k}: #{self[k]}"
|
@@ -160,18 +163,17 @@ class Hash
|
|
160
163
|
|
161
164
|
return self[key] if self.has_key? key
|
162
165
|
|
163
|
-
# we get here if the key is not found
|
164
|
-
#
|
165
|
-
# the actual mileage might vary
|
166
|
+
# we get here if the key is not found we report an error, return
|
167
|
+
# "" and continue the actual mileage might vary
|
166
168
|
|
167
|
-
# more error reporting: key not found
|
168
169
|
$stderr.puts "[E] Key '#{key}' not found in the following entry:"
|
169
|
-
|
170
|
-
|
171
|
-
|
170
|
+
unless top_level_entries.include?(key)
|
171
|
+
self.keys.each do |k|
|
172
|
+
$stderr.puts " #{k}: #{self[k]}"
|
173
|
+
end
|
174
|
+
$stderr.puts ""
|
172
175
|
end
|
173
|
-
|
174
|
-
|
176
|
+
|
175
177
|
return ""
|
176
178
|
end
|
177
179
|
end
|
@@ -1,8 +1,8 @@
|
|
1
|
-
# <%= full_name data %>, <%= data.basics
|
1
|
+
# <%= full_name data %>, <%= data.basics&.title %>
|
2
2
|
|
3
3
|
<%
|
4
|
-
addresses = data
|
5
|
-
presence = data
|
4
|
+
addresses = (data["addresses"] || []).map { |x| "**#{x.label}**: #{x.street}, #{x.zip_code} #{x.city}" }
|
5
|
+
presence = (data["contacts"] || []).map { |x| "**#{x.label}:** #{x.value}" } + (data["web_presence"] || []).map { |x| "<" + x.value + ">" }
|
6
6
|
-%>
|
7
7
|
<%= presence.map { |x| "* #{x}" }.join("\n") %>
|
8
8
|
<%= addresses.map { |x| "* #{x}" }.join("\n") %>
|
@@ -1,26 +1,29 @@
|
|
1
|
-
#+TITLE:
|
2
|
-
#+AUTHOR: <%= data
|
1
|
+
#+TITLE: Curriculum Vitae
|
2
|
+
#+AUTHOR: <%= full_name data %>, <%= data.basics&.title %>
|
3
|
+
#+EMAIL: <%= (data["contacts"] || []).select { |x| x.label == "email" }.first&.value %>
|
3
4
|
#+DATE: <<%= Date.today %>>
|
4
5
|
#+STARTUP: showall
|
5
6
|
#+OPTIONS: toc:nil num:nil
|
6
7
|
#+OPTIONS: h:1
|
7
8
|
#+OPTIONS: prop:nil
|
8
9
|
|
10
|
+
<% if data["addresses"] or data["contacts"] %>
|
9
11
|
* Contacts
|
10
12
|
:PROPERTIES:
|
11
|
-
<% data
|
13
|
+
<% (data["addresses"] || []).each do |x| -%>
|
12
14
|
<%= ":#{x.label.upcase}: #{x.street}, #{x.zip_code} #{x.city}" %>
|
13
15
|
<% end -%>
|
14
|
-
<% data
|
16
|
+
<% (data["contacts"] || []).each do |x| -%>
|
15
17
|
<%= ":#{x.label.upcase}: #{x.value}" %>
|
16
18
|
<% end -%>
|
17
19
|
:END:
|
18
20
|
|
19
|
-
<% data
|
21
|
+
<% (data["contacts"] || []).each do |x| -%>
|
20
22
|
| <%= "%-30s" % x.label %> | <%= "%-40s" % x.value %> |
|
21
23
|
<% end -%>
|
24
|
+
<% end %>
|
22
25
|
|
23
|
-
<% if data["
|
26
|
+
<% if data["summary"] -%>
|
24
27
|
* Summary
|
25
28
|
|
26
29
|
<%= reflow_to_string data.summary, 78 -%>
|
@@ -40,7 +43,7 @@
|
|
40
43
|
* Work Experience
|
41
44
|
|
42
45
|
<% (data.work || []).each do |item| -%>
|
43
|
-
**
|
46
|
+
** <%= period item %>: <%= clean [item.role, item.who].join(", ") %>
|
44
47
|
<%= propertify item, " " %>
|
45
48
|
|
46
49
|
<%= reflow_to_string item["summary"], 72, " " -%>
|
@@ -52,7 +55,7 @@
|
|
52
55
|
* Teaching
|
53
56
|
|
54
57
|
<% (data.teaching || []).each do |item| -%>
|
55
|
-
**
|
58
|
+
** <%= period item %>: <%= item.subject %>
|
56
59
|
<%= propertify item, " " %>
|
57
60
|
|
58
61
|
<%= [item["role"], item["school"], item["who"]].join(", ") %>
|
@@ -64,7 +67,7 @@
|
|
64
67
|
* Projects
|
65
68
|
|
66
69
|
<% (data.projects || []).each do |item| -%>
|
67
|
-
**
|
70
|
+
** <%= period item %>: <%= [item.name, item.role].join(", ") %>
|
68
71
|
<%= propertify item, " " %>
|
69
72
|
|
70
73
|
<%= reflow_to_string item["summary"], 72, " " -%>
|
@@ -73,10 +76,10 @@
|
|
73
76
|
<% end -%>
|
74
77
|
|
75
78
|
<% if data["other"] -%>
|
76
|
-
* Other
|
79
|
+
* Other
|
77
80
|
|
78
81
|
<% (data.other || []).each do |item| -%>
|
79
|
-
**
|
82
|
+
** <%= period item %>: <%= [item["who"], item["role"]].join(", ") %>
|
80
83
|
<%= propertify item, " " %>
|
81
84
|
|
82
85
|
<%= reflow_to_string item["summary"], 72, " " -%>
|
@@ -150,7 +153,7 @@
|
|
150
153
|
* <%= group.capitalize %>
|
151
154
|
|
152
155
|
<% (group || []).each do |item| -%>
|
153
|
-
**
|
156
|
+
** <%= item.date %>: <%= [item.title, item.who, item.address].join(", ") %>
|
154
157
|
<%= propertify item, " " %>
|
155
158
|
|
156
159
|
<%= reflow_to_string item.summary, 72, " " -%>
|