resumer 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5bfc062725a0c56a2f661476ae695ce31ee65ee5f0017352fda23e28b43e00e3
4
+ data.tar.gz: 4fe0cd3ceef03949bcf84a557230a264faa1be4963db308ce31308c9fb5f3346
5
+ SHA512:
6
+ metadata.gz: a792f865b82b176899b54f0a1debdae2a1094a37524c34df6dec50b142303cdcb3e1b14c25e35124f5e59f830848f68f0d82ace12454c197ba1ef273c921d315
7
+ data.tar.gz: 907b6d433ce95c2e5b4fdcf88411e3ce2398a2195e4946e8d49c8c08c29ead79f6199a4816bc13ed66ad2927552646d53a1261828d262aa5a55d2e887ec34e03
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [0.1.0] - 2021-12-28
8
+ ### Added
9
+ - An "init" command to create a basic resume YAML template
10
+ - A "usage" command to display the quick help
11
+ - An "export" command to convert the YAML resume to PDF or HTML
12
+ - Use a nice default HTML/PDF theme
13
+ - This CHANGELOG file
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Vito Tardia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # Resumer
2
+
3
+ Resumer is a Ruby tool that processes resumes written in machine-readable YAML format and produces a well-formatted version in either HTML or PDF.
4
+
5
+ It was initially inspired by [JSON Resume][jsonresume] but I though YAML would be a more human-readable format for this purpose.
6
+
7
+ Under the hood, Resumer uses the [commander gem][commander], and produces PDF files using [wkhtmltopdf][wkhtmltopdf], which must be already installed in the system.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'resumer'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ ```console
20
+ $ bundle install
21
+ ```
22
+
23
+ Or install it yourself as:
24
+
25
+ ```console
26
+ $ gem install resumer
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ Create a new YAML resume file:
32
+
33
+ ```console
34
+ $ resumer init [path/to/new/resume.yml]
35
+ ```
36
+
37
+ Edit the YAML file with your data, then export it:
38
+
39
+ ```console
40
+ $ resumer <sourceFile> [destFile/defaults to sourceFile.pdf/html]
41
+ ```
42
+
43
+ ## Contributing
44
+
45
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/resumer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
46
+
47
+ ## License
48
+
49
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
50
+
51
+ ## Code of Conduct
52
+
53
+ Everyone interacting in the Resumer project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/resumer/blob/master/CODE_OF_CONDUCT.md).
54
+
55
+ [jsonresume]: https://jsonresume.org/
56
+ [commander]: https://github.com/commander-rb/commander
57
+ [wkhtmltopdf]: http://wkhtmltopdf.org/
data/bin/resumer ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'resumer'
6
+ Resumer::CLI.new.run
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'commander'
4
+ require 'resumer/export'
5
+
6
+ module Resumer
7
+ module Command
8
+ # Export a YAML CV to an HTML or PDF file
9
+ class Export
10
+ def initialize(args = [], options = nil)
11
+ raise Error, 'No source file given, please provide a YAML source file' \
12
+ unless args.count.positive?
13
+
14
+ @export = Resumer::Export.new
15
+ @settings = parse_options(options)
16
+ @src, @dest = parse_args(args)
17
+ @settings[:format] = define_format(@dest) if options.format.nil?
18
+ @settings[:override] = (ask_override if File.exist? @dest) || false
19
+ end
20
+
21
+ def parse_args(args)
22
+ source = define_source(args.first)
23
+ destination = define_destination(args[1], source)
24
+ [source, destination]
25
+ end
26
+
27
+ def parse_options(options)
28
+ settings = config = {}
29
+ unless options.format.nil?
30
+ settings[:format] = options.format.downcase.to_sym
31
+ end
32
+
33
+ config = parse_custom_config(options.config) unless options.config.nil?
34
+
35
+ @export.defaults.merge(config, settings)
36
+ end
37
+
38
+ def parse_custom_config(config_file)
39
+ config_file_path = File.expand_path(config_file)
40
+ config = load(config_file_path)
41
+ return config if config[:theme].nil?
42
+
43
+ config[:theme] = parse_custom_theme(config[:theme], config_file_path)
44
+ config
45
+ end
46
+
47
+ def parse_custom_theme(path, base_path)
48
+ theme_path = File.expand_path(path, File.dirname(base_path))
49
+ return theme_path if File.exist?(theme_path)
50
+
51
+ raise ArgumentError, "Cannot find custom theme '#{theme_path}'"
52
+ end
53
+
54
+ def define_source(arg)
55
+ source = File.absolute_path arg
56
+ raise Error, "Source file does not exist: #{source}" \
57
+ unless File.exist? source
58
+
59
+ source
60
+ end
61
+
62
+ def define_destination(arg, source)
63
+ return File.absolute_path arg if arg
64
+
65
+ # Default to source path with export format extension
66
+ File.join(
67
+ File.absolute_path(File.dirname(source)),
68
+ File.basename(source, '.*')
69
+ ) + '.' + @settings[:format].to_s
70
+ end
71
+
72
+ def define_format(destination)
73
+ # File extension without leading dot
74
+ File.extname(destination)[/^\.(.*)/, 1].to_sym
75
+ end
76
+
77
+ def ask_override
78
+ agree('Destination exists, do you want to override? [y/N]', true) do |q|
79
+ q.responses[:not_valid] = 'Please enter "[y]es" or "[n]o".'
80
+ end
81
+ end
82
+
83
+ def load(file)
84
+ YAML.safe_load(File.read(file), [Symbol], symbolize_names: true)
85
+ rescue StandardError => e
86
+ raise Error, "Failed to read #{file}: #{e.message}"
87
+ end
88
+
89
+ # TODO: check the override+file-exists options and throw errors if
90
+ # false+true
91
+ def run
92
+ # Ensure source file is valid YAML/CV
93
+ data = load(@src)
94
+
95
+ # Ensure destination format is supported
96
+ unless @export.formats.include? @settings[:format]
97
+ raise Error, "Unsupported format: '#{@settings[:format].to_s.upcase}'"
98
+ end
99
+
100
+ say(
101
+ "Exporting #{@src} to #{@dest} in #{@settings[:format].to_s.upcase}" \
102
+ " format (override: #{@settings[:override]})"
103
+ )
104
+
105
+ @export.run(data, @dest, @settings)
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'commander'
4
+
5
+ module Resumer
6
+ module Command
7
+ # Initialize a resume YAML file
8
+ class Init < Commander::Command
9
+ def initialize
10
+ super(:init)
11
+ @summary = 'Initialize a resume.yml file'
12
+ @syntax = "#{Resumer::BIN} init [/path/to/resume.yml]"
13
+ @template = File.expand_path(
14
+ "#{File.dirname(__FILE__)}/../../../templates/default.yml"
15
+ )
16
+ end
17
+
18
+ def ask_override
19
+ agree('Destination exists, do you want to override? [y/n]', true) do |q|
20
+ q.responses[:not_valid] = 'Please enter "[y]es" or "[n]o".'
21
+ end
22
+ end
23
+
24
+ def parse_dest(path)
25
+ return File.join(Dir.pwd, 'resume.yml') if path.nil?
26
+
27
+ dest = File.expand_path(path)
28
+ return File.join(dest, 'resume.yml') if File.directory? dest
29
+
30
+ dest
31
+ end
32
+
33
+ def run(*args)
34
+ dest = parse_dest(args.first)
35
+ override = (ask_override if File.exist? dest) || false
36
+ return if File.exist?(dest) && !override
37
+
38
+ FileUtils.cp @template, dest
39
+ say "Your new resume is ready at '#{dest}'"
40
+ say 'Good luck!'
41
+ rescue Errno => e
42
+ say e.message
43
+ exit e.class::Errno
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'commander'
4
+
5
+ module Resumer
6
+ module Command
7
+ # Display usage info
8
+ class Usage < Commander::Command
9
+ def initialize
10
+ super(:usage)
11
+ @summary = 'Display usage info'
12
+ @syntax = "#{Resumer::BIN} <command> [options] <args>"
13
+ end
14
+
15
+ def run(*_args)
16
+ say("usage: #{@syntax}")
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'commander'
4
+
5
+ module Resumer
6
+ module Command
7
+ # Validate a YAML resume
8
+ class Validate < Commander::Command
9
+ def initialize
10
+ super(:validate)
11
+ @summary = 'Validate your YAML resume (not implemented)'
12
+ @syntax = "#{Resumer::BIN} validate [/path/to/resume.yml]"
13
+ end
14
+
15
+ def run(*_args)
16
+ say "[#{self.class.name}] Feature not yet implemented"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Resumer
4
+ class Error < StandardError; end
5
+ end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'ostruct'
5
+ require 'pdfkit'
6
+ require 'kramdown'
7
+
8
+ module Resumer
9
+ # Export a YAML CV to an HTML or PDF file
10
+ class Export
11
+ attr_reader :defaults
12
+ attr_reader :formats
13
+
14
+ def initialize
15
+ @formats = %i[html pdf]
16
+ @defaults = {
17
+ format: @formats.first,
18
+ theme: File.expand_path(
19
+ "#{File.dirname(__FILE__)}/../../themes/default"
20
+ ),
21
+ pdf: pdf_defaults
22
+ }
23
+ end
24
+
25
+ def pdf_defaults
26
+ {
27
+ page_size: 'A4',
28
+ margin_top: '1.5cm',
29
+ margin_bottom: '1.4cm',
30
+ margin_left: '1.5cm',
31
+ margin_right: '1.5cm',
32
+ print_media_type: true
33
+ }
34
+ end
35
+
36
+ def default_format
37
+ @defaults[:format]
38
+ end
39
+
40
+ # Transform the YAML object in an OpenStruct with symbol keys
41
+ def normalize(data)
42
+ return normalize_hash(data) if data.is_a? Hash
43
+
44
+ return normalize_array(data) if data.is_a? Array
45
+
46
+ data
47
+ end
48
+
49
+ def normalize_hash(data)
50
+ result = OpenStruct.new data
51
+ data.each do |k, v|
52
+ result[k] = normalize(v)
53
+ end
54
+ result
55
+ end
56
+
57
+ def normalize_array(data)
58
+ result = []
59
+ data.each do |i|
60
+ result.push normalize(i)
61
+ end
62
+ result
63
+ end
64
+
65
+ # Markdown helper for tempaltes
66
+ def markdown(text)
67
+ Kramdown::Document.new(text).to_html
68
+ end
69
+
70
+ def create_html(data, theme = @defaults[:theme], with_style = false)
71
+ # Load the theme
72
+ erb = ERB.new(
73
+ File.read(File.absolute_path("#{theme}/index.html"))
74
+ )
75
+ if with_style
76
+ style = File.read(
77
+ File.absolute_path("#{theme}/css/styles.css")
78
+ )
79
+ end
80
+ data = normalize(data)
81
+ # Generate the HTML
82
+ erb.result(binding)
83
+ end
84
+
85
+ def save_html(html, dest)
86
+ File.open(dest, 'w+') do |file|
87
+ file.puts html
88
+ end
89
+ end
90
+
91
+ def save_pdf(html, dest, theme = @defaults[:theme])
92
+ pdf_config
93
+ kit = PDFKit.new(html)
94
+
95
+ # Load generic styles
96
+ kit.stylesheets << "#{theme}/css/styles.css"
97
+
98
+ # Load PDF-specific styles
99
+ pdfstyles = "#{theme}/css/pdf.styles.css"
100
+ kit.stylesheets << pdfstyles if File.exist? pdfstyles
101
+
102
+ # Compile and save
103
+ kit.to_file(dest)
104
+ end
105
+
106
+ def run(data, dest, settings = @defaults)
107
+ raise Error, 'Invalid or empty source data' if data.nil?
108
+
109
+ html = create_html(data, settings[:theme], (settings[:format] == :html))
110
+ case settings[:format]
111
+ # Process HTML export
112
+ when :html
113
+ save_html(html, dest)
114
+ # Process PDF export
115
+ when :pdf
116
+ save_pdf(html, dest, settings[:theme])
117
+ else
118
+ raise Error, "Invalid format #{settings[:format].to_s.upcase}"
119
+ end
120
+ end
121
+
122
+ private
123
+
124
+ def pdf_config
125
+ PDFKit.configure do |config|
126
+ # config.wkhtmltopdf = '/path/to/wkhtmltopdf'
127
+ config.default_options = pdf_defaults
128
+ # Use only if your external hostname is unavailable on the server.
129
+ # config.root_url = "file://#{File.dirname('../')}/themes/default"
130
+ # config.protocol = 'file'
131
+ config.verbose = false
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Resumer
4
+ PKG = 'Resumer'
5
+ BIN = 'resumer'
6
+ VERSION = '0.1.0'
7
+ SUMMARY = ''
8
+ DESCRIPTION = 'Parse YAML CVs into HTML or PDF'
9
+ AUTHOR = ['Author', 'Vito Tardia <https://vito.tardia.me>'].freeze
10
+ end
data/lib/resumer.rb ADDED
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'resumer/error'
4
+ require 'resumer/info'
5
+ require 'commander'
6
+
7
+ # Require all the available commands
8
+ Dir.glob(File.expand_path('resumer/commands/*.rb', __dir__), &method(:require))
9
+
10
+ module Resumer
11
+ # Commander CLI interface
12
+ class CLI
13
+ include Commander::Methods
14
+
15
+ def initialize
16
+ program :name, Resumer::PKG
17
+ program :version, Resumer::VERSION
18
+ program :description, Resumer::DESCRIPTION
19
+ program :help_formatter, :compact
20
+ program :help, *Resumer::AUTHOR
21
+ end
22
+
23
+ # rubocop:disable Metrics/MethodLength
24
+ def add_export_command
25
+ command :export do |c|
26
+ c.option '-c', '--config FILE', 'Load a custom configuration file'
27
+ c.option '--format STRING', 'Destination format (HTML or PDF)'
28
+ c.summary = 'Export a YAML resume to HTML or PDF format'
29
+ c.syntax = "#{Resumer::BIN} export <source.yml> [destination.html|.pdf]"
30
+ c.action do |args, options|
31
+ Resumer::Command::Export.new(args, options).run
32
+ rescue StandardError => e
33
+ say "Error (#{e.class.name}): #{e.message}"
34
+ exit 1
35
+ end
36
+ end
37
+ end
38
+ # rubocop:enable Metrics/MethodLength
39
+
40
+ def run
41
+ default_command :usage
42
+ add_command(Resumer::Command::Usage.new)
43
+ add_command(Resumer::Command::Init.new)
44
+ add_command(Resumer::Command::Validate.new)
45
+ add_export_command
46
+ run!
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,86 @@
1
+ ---
2
+ basics:
3
+ name: Joe Bloggs
4
+ label: coder / nerd
5
+ picture: ''
6
+ email: me@example.com
7
+ phone: "+44 (0) 1234 567890"
8
+ website: http://mywebsite.com
9
+
10
+ summary: "I'm Joe. Lorem ipsum **dolor sit amet**, consectetur adipisicing elit,
11
+ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
12
+ Ut enim ad minim veniam, quis nostrud _exercitation ullamco_ laboris nisi ut
13
+ aliquip ex ea commodo consequat."
14
+
15
+ profiles:
16
+
17
+ - network: Your GitHub
18
+ username: jbloggs
19
+ url: https://github.com/jbloggs
20
+
21
+ - network: You on LinkedIn
22
+ username: joe
23
+ url: https://linkedin.com/in/joe
24
+
25
+ #...other profiles
26
+
27
+ work:
28
+
29
+ - company: Great Company 1, London - UK
30
+ position: Frontend Engineer
31
+ startDate: august 2019
32
+ summary: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
33
+ eiusmod tempor incididunt ut labore et dolore magna aliqua.
34
+
35
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
36
+ aliquip ex ea commodo consequat."
37
+
38
+ - company: Great Company 2, NYC - US
39
+ position: Great Well paid role
40
+ startDate: february 2017
41
+ endDate: august 2019
42
+ summary: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
43
+ eiusmod tempor incididunt ut labore et dolore magna aliqua.
44
+
45
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
46
+ aliquip ex ea commodo consequat."
47
+
48
+ - company: <Company>, <City> - <Country>
49
+ position: <Role>
50
+ website: http://somesite.com
51
+ startDate: <Month Year>
52
+ endDate: <Month Year>
53
+ summary: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
54
+ eiusmod tempor incididunt ut labore et dolore magna aliqua.
55
+
56
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
57
+ aliquip ex ea commodo consequat."
58
+
59
+ education:
60
+
61
+ - institution: <School One>, <City> - Country
62
+ area: <Topic or degree>
63
+ startDate: <Month Year>
64
+ endDate: <Month Year>
65
+
66
+ - institution: <School Two or Great Mentor>
67
+ studyType: <Freeform description of the experience>
68
+ startDate: <Month Year>
69
+ endDate: <Month Year>
70
+
71
+ # more education here
72
+
73
+ skills:
74
+
75
+ - name: "**Website design**, with standard compliant **HTML/CSS**"
76
+ keywords:
77
+ - HTML
78
+ - CSS
79
+ - Javascript
80
+
81
+ - name: "**User experience** (UX) and product design"
82
+ keywords:
83
+ - UI
84
+ - UX
85
+
86
+ - name: "**Email marketing** services and consulting"
@@ -0,0 +1,34 @@
1
+ /* csslint ignore:start */
2
+ header {
3
+ display: -webkit-box;
4
+
5
+ -webkit-box-pack: justify;
6
+ }
7
+
8
+ header .contact {
9
+ -webkit-box-flex: 1;
10
+ }
11
+
12
+ main {
13
+ overflow: hidden;
14
+ }
15
+
16
+ .experience {
17
+ float: left;
18
+ }
19
+
20
+ aside {
21
+ display: -webkit-box;
22
+ -webkit-box-flex: 1;
23
+ -webkit-box-orient: vertical;
24
+ float: right;
25
+ }
26
+
27
+ aside section {
28
+ -webkit-box-flex: 1;
29
+ }
30
+
31
+ .profiles {
32
+ clear: both;
33
+ }
34
+ /* csslint ignore:end */
@@ -0,0 +1,213 @@
1
+ /*
2
+ SCREEN 16px/1.4 => 22,4px baseline
3
+ PRINT 10pt/14pt => 14pt baseline
4
+ */
5
+
6
+ html {
7
+ background-color: #fff;
8
+ font-family: Calibri, 'Helvetica Neue', Arial, sans-serif;
9
+ font-size: 100%;
10
+ line-height: 1.4;
11
+ color: #333;
12
+ }
13
+
14
+ .page {
15
+ width: 100%;
16
+ margin: 0;
17
+ padding: 0;
18
+ }
19
+
20
+ a, a:link, a:hover, a:visited {
21
+ color: #000099;
22
+ }
23
+
24
+ p {
25
+ margin-top: .5em;
26
+ margin-bottom: .5em;
27
+ }
28
+
29
+ /** Typography **/
30
+ header .contact, .profiles, .license, .skills ul, .education ul {
31
+ font-size: 90%;
32
+ line-height: 1.55;
33
+ }
34
+
35
+ header {
36
+ display: flex;
37
+ justify-content: space-between;
38
+ border-top: .2em solid #AAAAAA;
39
+ }
40
+
41
+ header .author-name {
42
+ letter-spacing: .1rem;
43
+ margin: 0;
44
+ padding: 1em 0 .7em 0;
45
+ flex-grow: 1;
46
+ max-width: 32.88em;
47
+ }
48
+
49
+ header svg {
50
+ display: block;
51
+ margin-bottom: 0.54em; /* Total height 67,2px (22,4*3) */
52
+ }
53
+
54
+ header .contact {
55
+ flex:1;
56
+
57
+ flex-grow: 1;
58
+ max-width: 15.86em; /* 14.28em = 90% */
59
+
60
+ padding: 1em 0 0 0;
61
+ font-style: italic;
62
+ color: #444;
63
+ }
64
+
65
+ .contact ul {
66
+ margin: 0;
67
+ padding: 0;
68
+ list-style: none;
69
+ }
70
+
71
+ main {
72
+ display: flex;
73
+ flex-direction: row;
74
+ justify-content: space-between;
75
+ flex-wrap: wrap;
76
+ align-items: stretch;
77
+ align-content: space-between;
78
+ }
79
+
80
+ .experience {
81
+ flex-grow: 2;
82
+ max-width: 31.88em;
83
+ }
84
+
85
+ .summary {
86
+ line-height: 1.8;
87
+ margin: 1.6em 0;
88
+ font-size: 110%;
89
+ color: #666;
90
+ }
91
+
92
+ aside {
93
+ display: flex;
94
+ flex:1;
95
+ flex-grow: 1;
96
+ max-width: 14.28em;
97
+ flex-direction: column;
98
+ justify-content: flex-start;
99
+ align-items: flex-start;
100
+ }
101
+
102
+ aside section {
103
+ flex: 1;
104
+ }
105
+
106
+ aside ul {
107
+ padding: 0;
108
+ list-style: none;
109
+ }
110
+
111
+ aside li {
112
+ margin: .775em 0;
113
+ }
114
+
115
+ .experience .headline {
116
+ font-size: 160%;
117
+ font-weight: normal;
118
+ line-height: 1;
119
+ margin-top: .2em;
120
+ margin-bottom: .5em;
121
+ color: #444;
122
+ }
123
+
124
+ .experience .position {
125
+ font-size: 130%;
126
+ font-weight: normal;
127
+ line-height: 1.2;
128
+ margin-top: 0.82em;
129
+ margin-bottom: 0.33em;
130
+ color: #232323;
131
+ }
132
+
133
+ .experience .dates {
134
+ margin-top: 0;
135
+ margin-bottom: 0;
136
+ font-style: italic;
137
+ color: #444;
138
+ }
139
+
140
+ .experience dd {
141
+ margin: .5em 0;
142
+ color: #424242;
143
+ }
144
+
145
+ .skills .headline, .education .headline {
146
+ font-size: 110%;
147
+ font-weight: normal;
148
+ line-height: 1.25;
149
+ margin-top: .63em;
150
+ margin-bottom: .63em;
151
+ color: #232323;
152
+ border-bottom: .1em solid #515151;
153
+ }
154
+
155
+ .education .headline {
156
+ font-style: italic;
157
+ }
158
+
159
+ .skills ul, .education ul {
160
+ color: #424242;
161
+ }
162
+
163
+ .profiles {
164
+ color: #424242;
165
+ }
166
+
167
+ .profiles ul {
168
+ padding: 0 2em;
169
+ }
170
+
171
+ footer {
172
+ margin-top: 4em;
173
+ }
174
+
175
+ .license {
176
+ font-style: italic;
177
+ color: #444;
178
+ }
179
+
180
+ @media screen {
181
+ html {
182
+ background-color: #ddd;
183
+ }
184
+ .page {
185
+ background-color: #fff;
186
+ max-width: 48.57em;
187
+ margin: 1.4em auto;
188
+ padding: 5.014em 4.285em;
189
+ box-shadow: .2em .2em .5em #666;
190
+ }
191
+ }
192
+
193
+ @media print {
194
+ html {
195
+ font-size: 10pt;
196
+ }
197
+ header svg {
198
+ width: 5.2cm;
199
+ }
200
+ p {
201
+ orphans:3;
202
+ widows:2;
203
+ }
204
+
205
+ footer {
206
+ margin-top: 0;
207
+ margin-bottom: 0;
208
+ }
209
+
210
+ .experience {
211
+ max-width: 44em;
212
+ }
213
+ }
@@ -0,0 +1,89 @@
1
+ <!doctype html>
2
+
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="utf-8">
6
+
7
+ <title><%=data.basics.name%> | Resume</title>
8
+ <% if style then %><style><%="#{style}"%></style><% end %>
9
+
10
+ </head>
11
+
12
+ <body>
13
+ <div class="page">
14
+ <!-- Page Header: Curriculum Vitae -->
15
+ <header role="banner">
16
+ <h1 class="author-name"><%=data.basics.name%></h1>
17
+
18
+ <!-- Contact box -->
19
+ <div class="contact h-card">
20
+ <ul>
21
+ <li><strong>w</strong>: <span class="p-name u-url"><%=data.basics.website%></span></li>
22
+ <li><strong>@</strong>: <span class="u-email"><%=data.basics.email%></span></li>
23
+ <li><strong>m</strong>: <span class="u-phone"><%=data.basics.phone%></span></li>
24
+ </ul>
25
+ </div>
26
+ </header>
27
+
28
+ <main>
29
+ <% if data.basics.summary %>
30
+ <section id="summary" class="summary">
31
+ <%=markdown(data.basics.summary)%>
32
+ </section>
33
+ <% end %>
34
+
35
+ <section id="experience" class="experience">
36
+ <h1 class="headline">Professional Experience</h1>
37
+ <dl>
38
+ <%data.basics.work.each do |w| %>
39
+ <dt class="position"><%=(w.position) ? w.position + " /": "" %> <%=w.company%></dt>
40
+ <dt class="dates"><%=w.startDate%> - <%=w.endDate || "present"%></dt>
41
+ <dd><%=markdown(w.summary)%></dd>
42
+ <%end%>
43
+ </dl>
44
+ </section>
45
+
46
+ <aside>
47
+ <% if data.skills.count > 0 %>
48
+ <section id="skills" class="skills">
49
+ <h1 class="headline">Skills</h1>
50
+ <ul>
51
+ <% data.skills.each do |s| %>
52
+ <li><%=markdown(s.name)%></li>
53
+ <% end %>
54
+ </ul>
55
+ </section>
56
+ <% end %>
57
+
58
+ <% if data.education.count > 0 %>
59
+ <section id="education" class="education">
60
+ <h1 class="headline">Education</h1>
61
+ <ul>
62
+ <% data.education.each do |e| %>
63
+ <li>
64
+ <strong><%=e.institution%></strong><br/>
65
+ <%=e.area || e.studyType || ""%> <%=e.startDate || ""%> <%=e.endDate || ""%>
66
+ </li>
67
+ <% end %>
68
+ </ul>
69
+ </section>
70
+ <% end %>
71
+ </aside>
72
+ </main>
73
+
74
+ <% if data.basics.profiles.count > 0 %>
75
+ <section id="profiles" class="profiles">
76
+ <ul>
77
+ <% data.basics.profiles.each do |p| %>
78
+ <li><%=p.network%> <a href="<%=p.url%>"><%=p.url%></a></li>
79
+ <% end %>
80
+ </ul>
81
+ </section>
82
+ <% end %>
83
+
84
+ <footer>
85
+ <p><small class="license">This document is licensed using a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License.</small></p>
86
+ </footer>
87
+ </div>
88
+ </body>
89
+ </html>
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resumer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Vito Tardia
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-12-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: commander
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: kramdown
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pdfkit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.8'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '13.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '13.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ description: |
98
+ Write your resume once in machine-readable YAML format and export it
99
+ to HTML or PDF using custom themes.
100
+ email:
101
+ - vito@tardia.me
102
+ executables:
103
+ - resumer
104
+ extensions: []
105
+ extra_rdoc_files:
106
+ - README.md
107
+ - CHANGELOG.md
108
+ - LICENSE.txt
109
+ files:
110
+ - CHANGELOG.md
111
+ - LICENSE.txt
112
+ - README.md
113
+ - bin/resumer
114
+ - lib/resumer.rb
115
+ - lib/resumer/commands/export.rb
116
+ - lib/resumer/commands/init.rb
117
+ - lib/resumer/commands/usage.rb
118
+ - lib/resumer/commands/validate.rb
119
+ - lib/resumer/error.rb
120
+ - lib/resumer/export.rb
121
+ - lib/resumer/info.rb
122
+ - templates/default.yml
123
+ - themes/default/css/pdf.styles.css
124
+ - themes/default/css/styles.css
125
+ - themes/default/index.html
126
+ homepage: https://github.com/vtardia/resumer
127
+ licenses:
128
+ - MIT
129
+ metadata:
130
+ allowed_push_host: https://rubygems.org
131
+ homepage_uri: https://github.com/vtardia/resumer
132
+ source_code_uri: https://github.com/vtardia/resumer
133
+ changelog_uri: https://github.com/vtardia/resumer/blob/main/CHANGELOG.md
134
+ post_install_message:
135
+ rdoc_options:
136
+ - "--title"
137
+ - Resumer - beautiful CVs from YAML files
138
+ - "--main"
139
+ - README.md
140
+ - "--line-numbers"
141
+ - "--inline-source"
142
+ - "--quiet"
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - "~>"
148
+ - !ruby/object:Gem::Version
149
+ version: '2.7'
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubygems_version: 3.1.6
157
+ signing_key:
158
+ specification_version: 4
159
+ summary: Resumer creates beautiful CVs from YAML files
160
+ test_files: []