resumer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []