resme 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4c7490069bf046c56808342ad427c9f055bba680506486029770761517b92099
4
- data.tar.gz: eb69e1752e4b4f4564e0e8cf9c7f5fa65756f7b3e599073a52d8c49583084776
3
+ metadata.gz: d8abd7d17f0d2b9a777cba680826c21d6ee882f3f3df0c74ba086e1d617d4b6b
4
+ data.tar.gz: b34b33c2f2d0f3beae3bef713efba2e39b05537d8a208380e6392271e0cbcb09
5
5
  SHA512:
6
- metadata.gz: cb0ac162ee646457be8d70f51359de1e7d2ad99ad545c79d1403f565c220e975e5927b0ab2fb00c72346edff04ccc0143282d666a08034f2174e7fe770cefc50
7
- data.tar.gz: 3eb1f599f8ca2abbf274632c3afa0076349a85bc44e7c1b468761112627a086aa5de5385a5d4f79a2f6f438fe343b8f5c8de2979dc1bdacbc10ee7a72a258abb
6
+ metadata.gz: fe74aa311a4dd110c26a0760cf6d2f8cb1ba1ee8e587f2a64d00ff6aa420a2a7f6c37b1eec392ddf1b28abbe49ffb94d6b26d5f43f2797ba9fd7cc9874ebf5c1
7
+ data.tar.gz: 3f2ae60c5c8a4e7f8135aa7f43aafa081f913dc8dd6c1b4c6065606447a41b6afe414ae4f32e0541fc06284227d94628f31f59e80af02aee2147c054032137c8
data/README.org CHANGED
@@ -3,13 +3,8 @@
3
3
  #+DATE: <2020-07-14 Tue>
4
4
  #+STARTUP: showall
5
5
 
6
- * RESME - A Resume Generator
7
- :PROPERTIES:
8
- :CUSTOM_ID: resme---a-resume-generator
9
- :END:
10
-
11
6
  Keep your resume in YAML and output it in various formats, including
12
- org-mode, markdown, json, and the Europass XML format.
7
+ Org Mode, Markdown, JSON, and the Europass XML format.
13
8
 
14
9
  The rendering engine is based on ERB. This simplifies the creation of
15
10
  new output formats (and extending/modifying the YML structure to one's
@@ -46,41 +41,39 @@ Or install it yourself as:
46
41
  Start with:
47
42
 
48
43
  #+BEGIN_EXAMPLE
49
- $ resme init
44
+ resme init
50
45
  #+END_EXAMPLE
51
46
 
52
- whih generates a YML template for your resume in the current directory.
47
+ which generates a YML template for your resume in the current directory.
53
48
  Comments in the YML file should help you fill the various entries.
54
49
  Notice that most entries are optional and you can remove sections which
55
50
  are not relevant for your resume.
56
51
 
57
- You can then generate a resume using one of the existing templates or by
58
- writing your own template (see below).
59
-
60
- To generate a resume in Markdown using the provided template:
61
-
62
- $ resme org [-o output_filename] file.yml ...
52
+ You can now generate a resume using one of the existing formats:
63
53
 
64
- To generate a resume in Markdown using the provided template:
54
+ #+begin_example
55
+ resme --to org resume.yml
56
+ #+end_example
65
57
 
66
- $ resme md [-o output_filename] file.yml ...
58
+ Supported formats include:
67
59
 
68
- To generate a resume in the Europass XML format using the provided
69
- template:
60
+ - =org=: Org Mode format
61
+ - =md=: Markdown format
62
+ - =europass=: Europass format
63
+ (http://interop.europass.cedefop.europa.eu/web-services/remote-upload/)
64
+ - =json=: JSON format (https://jsonresume.org/)
70
65
 
71
- $ resme europass [-o output_filename] file.yml ...
72
-
73
- To generate a resume in the JSON format (https://jsonresume.org/):
74
-
75
- $ resme json [-o output_filename] file.yaml ...
66
+ If you are not satisfied with the provided templates, you can write
67
+ your own (see below). In this case, however, =resme= is mainly an ERB
68
+ renderer.
76
69
 
77
70
  Remarks:
78
71
 
79
- - you can specify more than one YML file in input. This allows you to
72
+ - You can specify more than one YML file in input. This allows you to
80
73
  store data about your resume in different files, if you like to do so
81
74
  (e.g., work experiences could be in one file and talks in another).
82
75
  The YML files are merged before processing them.
83
- - the output filename is optional. If you do not specify one, the resume
76
+ - The output filename is optional. If you do not specify one, the resume
84
77
  is generated to =resume-YYYY.MM.DD.format=, where =YYYY-MM-DD= is
85
78
  today's date and =format= is the chosen output format
86
79
 
@@ -117,14 +110,14 @@ The third and the forth format allows you to enter "partial" dates
117
110
  :END:
118
111
 
119
112
  The resumes are generated from the YML matter using ERB templates. The
120
- output formats should support different backends (OrgMode and Markdown
121
- easily allow for generation of PDFs, HTML, and ODT to mention a few).
122
-
123
- You can define your own templates if you wish to do so.
113
+ provided output formats should support different back ends (Org Mode
114
+ and Markdown easily allow for generation of PDFs, HTML, and ODT, to
115
+ mention a few).
124
116
 
125
- All the data in the resume is made available in the =data= variable.
126
- Thus, for instance, the following code snippets generates a list of all
127
- the work experiences:
117
+ However, if you want, you can define your own templates. All the data
118
+ in the resume is made available in the =data= variable. Thus, for
119
+ instance, the following code snippets generates a list of all the work
120
+ experiences:
128
121
 
129
122
  #+BEGIN_EXAMPLE
130
123
  <% data.work each do |exp| %>
@@ -133,11 +126,11 @@ the work experiences:
133
126
  <% end %>
134
127
  #+END_EXAMPLE
135
128
 
136
- To specify your own ERB template use the option =-t=. Thus, for
137
- instance:
129
+ To specify your own ERB template use the option =-e= (=--erb=). Thus,
130
+ for instance:
138
131
 
139
132
  #+BEGIN_EXAMPLE
140
- $ resme render -t template.md.erb [-o output_filename] file.yaml ...
133
+ $ resme render -e template.md.erb [-o output_filename] file.yaml ...
141
134
  #+END_EXAMPLE
142
135
 
143
136
  uses =template.md.erb= to generate the resume.
@@ -235,6 +228,10 @@ Unknown number of unknown bugs.
235
228
  :CUSTOM_ID: release-history
236
229
  :END:
237
230
 
231
+ - *0.4.0* refactors all generation commands under =generate=, provides
232
+ new filtering options, adds =-e= option (for custom templates), and
233
+ refactors various portions of code. It also revises this document
234
+ and fixes some minor bugs.
238
235
  - *0.3.2* and *0.3.1* fix errors with the Europass format: lists of
239
236
  projects, interests, ... are now properly formatted.
240
237
  - *0.3* introduces output to org-mode, introduces references for the CV,
data/exe/resme CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'resme'
3
+ require "resme"
4
4
 
5
5
  # read-eval-print-step passing all commands and the argument
6
- Resme::CommandSemantics.reps Resme::CommandSyntax.commands, ARGV
6
+ Resme::CommandSemantics.execute Resme::CommandSyntax.commands, ARGV
@@ -1,90 +1,90 @@
1
- require 'resme/version'
2
- require 'readline'
3
- require 'fileutils'
4
- require 'date'
5
- require 'yaml'
6
- require 'erb'
7
- require 'json'
8
- require 'kwalify'
1
+ require "resme/version"
2
+ require "readline"
3
+ require "fileutils"
4
+ require "date"
5
+ require "yaml"
6
+ require "erb"
7
+ require "json"
8
+ require "kwalify"
9
9
 
10
10
  module Resme
11
11
  module CommandSemantics
12
- APPNAME = 'resme'
12
+ APPNAME = "resme"
13
13
  VERSION = Resme::VERSION
14
14
 
15
15
  #
16
16
  # Main App Starts Here!
17
17
  #
18
- def self.version opts = nil, argv = []
18
+ def self.version(opts = nil, argv = [])
19
19
  puts "#{APPNAME} version #{VERSION}"
20
20
  end
21
21
 
22
- def self.man opts = nil, argv = []
23
- path = File.join(File.dirname(__FILE__), "/../../../README.md")
22
+ def self.man(opts = nil, argv = [])
23
+ path = File.join(File.dirname(__FILE__), "/../../../README.org")
24
24
  file = File.open(path, "r")
25
- contents = file.read
26
- puts contents
25
+ puts file.read
27
26
  end
28
27
 
29
- def self.help opts = nil, argv = []
28
+ def self.help(opts = nil, argv = [])
30
29
  all_commands = CommandSyntax.commands
31
30
 
32
31
  if argv != []
33
- argv.map { |x| puts all_commands[x.to_sym][2] }
32
+ argv.map do |x|
33
+ puts all_commands[x.to_sym][:help]
34
+ puts "\n\n"
35
+ end
34
36
  else
35
- puts "#{APPNAME} command [options] [args]"
36
- puts ""
37
- puts "Available commands:"
38
- puts ""
39
- all_commands.keys.each do |key|
40
- puts " " + all_commands[key][0].banner
37
+ puts "#{APPNAME} command [options] [args]\n"
38
+ puts "Available commands:\n"
39
+ all_commands.each_key do |key|
40
+ puts " #{all_commands[key][:options].banner}"
41
41
  end
42
42
  end
43
43
  end
44
44
 
45
- def self.console opts, argv = []
45
+ def self.console(opts, argv = [])
46
46
  all_commands = CommandSyntax.commands
47
47
  all_commands.delete(:console)
48
48
 
49
49
  i = 0
50
50
  while true
51
51
  string = Readline.readline("#{APPNAME}:%03d> " % i, true)
52
- string.gsub!(/^#{APPNAME} /, "") # as a courtesy, remove any leading appname string
53
- if string == "exit" or string == "quit" or string == "." then
54
- exit 0
55
- end
56
- reps all_commands, string.split(' ')
52
+ # as a courtesy, remove any leading appname string
53
+ string.gsub!(/^#{APPNAME} /, "")
54
+ exit 0 if %w[exit quit .].include? string
55
+ execute all_commands, string.split(" ")
57
56
  i = i + 1
58
57
  end
59
58
  end
60
59
 
61
60
  # read-eval-print step
62
- def self.reps all_commands, argv
61
+ # check if argv is in any of all_commands, parse options
62
+ # according to the specification in all_commands and invoke
63
+ # a function in this class to actually do the work
64
+ def self.execute(all_commands, argv)
63
65
  if argv == [] or argv[0] == "--help" or argv[0] == "-h"
64
- CommandSemantics.help
66
+ help
65
67
  exit 0
66
68
  else
67
69
  command = argv[0]
68
- syntax_and_semantics = all_commands[command.to_sym]
69
- if syntax_and_semantics
70
- opts = syntax_and_semantics[0]
71
- function = syntax_and_semantics[1]
72
-
73
- begin
74
- parser = Slop::Parser.new(opts)
70
+ command_spec = all_commands[command.to_sym]
75
71
 
76
- result = parser.parse(argv[1..-1])
77
- options = result.to_hash
78
- arguments = result.arguments
72
+ if command_spec
73
+ command_name = command_spec[:name]
74
+ option_parser = command_spec[:options]
79
75
 
80
- eval "CommandSemantics::#{function}(options, arguments)"
81
- rescue Slop::Error => e
82
- puts "#{APPNAME}: #{e}"
76
+ begin
77
+ argv.shift # remove command name from ARGV
78
+ options = {}
79
+ parser = option_parser.parse!(into: options)
80
+ eval "CommandSemantics::#{command_name}(options, argv)"
83
81
  rescue Exception => e
84
- puts e
82
+ puts "#{APPNAME} error: #{e}"
83
+ puts "Help with \"#{APPNAME} help #{command_name}\""
85
84
  end
86
85
  else
87
- puts "#{APPNAME}: '#{command}' is not a valid command. See '#{APPNAME} help'"
86
+ puts "#{APPNAME} error: "#{command}" is not a valid command."
87
+ puts "List commands with \"#{APPNAME} help\"."
88
88
  end
89
89
  end
90
90
  end
@@ -92,23 +92,18 @@ module Resme
92
92
  #
93
93
  # APP SPECIFIC COMMANDS
94
94
  #
95
- def self.check opts, argv
96
- schema = Kwalify::Yaml.load_file(File.join(File.dirname(__FILE__), "/../templates/schema.yml"))
97
- ## or
98
- # schema = YAML.load_file('schema.yaml')
95
+ def self.check(opts, argv)
96
+ path = File.join(File.dirname(__FILE__), "/../templates/schema.yml")
97
+ schema = Kwalify::Yaml.load_file(path)
99
98
 
100
- ## create validator
99
+ # create validator
101
100
  validator = Kwalify::Validator.new(schema)
102
-
103
- ## load document
101
+ # load document
104
102
  document = Kwalify::Yaml.load_file(argv[0])
105
- ## or
106
- #document = YAML.load_file('document.yaml')
107
-
108
- ## validate
103
+ # validate
109
104
  errors = validator.validate(document)
110
105
 
111
- ## show errors
106
+ # show errors
112
107
  if errors && !errors.empty?
113
108
  for e in errors
114
109
  puts "[#{e.path}] #{e.message}"
@@ -118,14 +113,16 @@ module Resme
118
113
  end
119
114
  end
120
115
 
121
- def self.init opts, argv
116
+ def self.init(opts, argv)
122
117
  output = opts[:output] || "resume.yml"
123
118
  force = opts[:force]
124
- template = File.join(File.dirname(__FILE__), "/../templates/resume.yml")
119
+ path = File.dirname(__FILE__), "/../templates/resume.yml"
120
+ template = File.join(path)
125
121
 
126
- # avoid catastrophy
127
- if File.exist?(output) and not force
128
- puts "Error: file #{output} already exists. Use --force if you want to overwrite it"
122
+ # avoid catastrophe
123
+ if File.exist?(output) && !force
124
+ puts "#{APPNAME} error: file #{output} already exists."
125
+ puts "Use --force if you want to overwrite it."
129
126
  else
130
127
  content = File.read(template)
131
128
  backup_and_write output, content
@@ -133,60 +130,65 @@ module Resme
133
130
  end
134
131
  end
135
132
 
136
- def self.md opts, argv
137
- output = opts[:output] || "resume-#{Date.today}.md"
138
- template = File.join(File.dirname(__FILE__), "/../templates/resume.md.erb")
139
-
140
- render argv, template, output
141
- puts "Resume generated in #{output}"
133
+ def self.list(opts, argv)
134
+ data = {}
135
+ argv.each do |file|
136
+ data = data.merge(YAML.load_file(file, permitted_classes: [Date]))
137
+ end
138
+ puts "Sections included in #{argv.join(", ")}:"
139
+ data.keys.each do |key|
140
+ puts "- #{key}: #{(data[key] || []).size} entries"
141
+ end
142
142
  end
143
143
 
144
- def self.org opts, argv
145
- output = opts[:output] || "resume-#{Date.today}.org"
146
- template = File.join(File.dirname(__FILE__), "/../templates/resume.org.erb")
144
+ def self.generate(opts, argv)
145
+ format = opts[:to] == "europass" ? "xml" : opts[:to]
146
+ output = opts[:output] || "resume-#{Date.today}.#{format}"
147
147
 
148
- render argv, template, output
149
- puts "Resume generated in #{output}"
150
- end
151
-
152
- def self.json opts, argv
153
- output = opts[:output] || "resume-#{Date.today}.json"
154
- template = File.join(File.dirname(__FILE__), "/../templates/resume.json.erb")
148
+ if opts[:erb]
149
+ template = opts[:erb]
150
+ else
151
+ template = File.join(File.dirname(__FILE__), "/../templates/resume.#{format}.erb")
152
+ end
155
153
 
156
- render argv, template, output
157
- puts "Resume generated in #{output}"
158
- end
154
+ skipped_sections = opts[:skip] || []
159
155
 
160
- def self.europass opts, argv
161
- output = opts[:output] || "resume-#{Date.today}.xml"
162
- template = File.join(File.dirname(__FILE__), "/../templates/europass/eu.xml.erb")
156
+ if !File.exists?(template)
157
+ puts "#{APPNAME} error: format #{format} is not understood."
158
+ end
163
159
 
164
- render argv, template, output
160
+ render argv, template, output, skipped_sections
165
161
  puts "Resume generated in #{output}"
166
- puts "Render via, e.g., http://interop.europass.cedefop.europa.eu/web-services/remote-upload/"
162
+
163
+ if format == "xml" then
164
+ puts "Europass XML generated. Render via, e.g., http://interop.europass.cedefop.europa.eu/web-services/remote-upload/"
165
+ end
167
166
  end
168
167
 
169
168
  private
170
169
 
171
- def self.render yml_files, template_filename, output_filename
172
- data = Hash.new
170
+ def self.render(yml_files, template_name, output_name, skipped_sections)
171
+ data = {}
173
172
  yml_files.each do |file|
174
- data = data.merge(YAML.load_file(file))
173
+ data = data.merge(YAML.load_file(file, permitted_classes: [Date]))
174
+ end
175
+ skipped_sections.each do |section|
176
+ data.reject! { |k| k == section }
175
177
  end
176
- template = File.read(template_filename)
177
- output = ERB.new(template, nil, '-').result(binding)
178
+ template = File.read(template_name)
179
+ output = ERB.new(template, trim_mode: "-").result(binding)
178
180
  # it is difficult to write readable ERBs with no empty lines...
179
181
  # we use gsub to replace multiple empty lines with \n\n in the final output
180
182
  output.gsub!(/([\t ]*\n){3,}/, "\n\n")
181
- backup_and_write output_filename, output
183
+ backup_and_write output_name, output
182
184
  end
183
185
 
184
- def self.backup filename
186
+ def self.backup(filename)
185
187
  FileUtils::cp filename, filename + "~"
186
188
  puts "Backup copy #{filename} created in #{filename}~."
187
189
  end
188
190
 
189
- def self.backup_and_write filename, content
191
+ def self.backup_and_write(filename, content)
190
192
  backup(filename) if File.exist?(filename)
191
193
  File.open(filename, "w") { |f| f.puts content }
192
194
  end