md_resume 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.standard.yml +3 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +124 -0
- data/LICENSE.txt +21 -0
- data/README.md +67 -0
- data/Rakefile +14 -0
- data/assets/defaults.css +167 -0
- data/assets/reload.js +24 -0
- data/assets/sample-resume.md +64 -0
- data/exe/md_resume +34 -0
- data/lib/local-server.rb +97 -0
- data/lib/md_resume/version.rb +5 -0
- data/lib/parser.rb +198 -0
- data/lib/resume_generator.rb +233 -0
- data/md_resume.gemspec +42 -0
- data/resume.png +0 -0
- data/sig/md_resume.rbs +4 -0
- metadata +115 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5e17377e32b45ab6b2127c36392a12d26ee7b0b3b0b6f078b3fdf07e4f9b2663
|
4
|
+
data.tar.gz: 946d865aae51c40a864f70112be7d04797b40292fd35c2f2deaf01c672d3b30d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d7abc200a53f58087cdcb9bebc2a4f08ac9a6890a78f4c87b0ee132d4f6f354350bbb0e16c183f7db71d55aef067cc09d89465e12aaed8a25eb9a2e600c08543
|
7
|
+
data.tar.gz: c13a6605ec135d342b46bdf91eba7c8307be5e1f2c60cb32cabab4740a23f578dae9406534f7280647044f07262abf874c1ecec727c305d086ef84219b7b904a
|
data/.standard.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
md_resume (0.1.0)
|
5
|
+
foreman (~> 0.87)
|
6
|
+
guard (~> 2.18)
|
7
|
+
guard-livereload (~> 2.5)
|
8
|
+
guard-process (~> 1.0)
|
9
|
+
guard-shell (~> 0.7)
|
10
|
+
kramdown (~> 2.4)
|
11
|
+
webrick (~> 1.8)
|
12
|
+
|
13
|
+
GEM
|
14
|
+
remote: https://rubygems.org/
|
15
|
+
specs:
|
16
|
+
ast (2.4.2)
|
17
|
+
coderay (1.1.3)
|
18
|
+
em-websocket (0.5.3)
|
19
|
+
eventmachine (>= 0.12.9)
|
20
|
+
http_parser.rb (~> 0)
|
21
|
+
eventmachine (1.2.7)
|
22
|
+
ffi (1.16.3)
|
23
|
+
foreman (0.87.2)
|
24
|
+
formatador (1.1.0)
|
25
|
+
guard (2.18.1)
|
26
|
+
formatador (>= 0.2.4)
|
27
|
+
listen (>= 2.7, < 4.0)
|
28
|
+
lumberjack (>= 1.0.12, < 2.0)
|
29
|
+
nenv (~> 0.1)
|
30
|
+
notiffany (~> 0.0)
|
31
|
+
pry (>= 0.13.0)
|
32
|
+
shellany (~> 0.0)
|
33
|
+
thor (>= 0.18.1)
|
34
|
+
guard-compat (1.2.1)
|
35
|
+
guard-livereload (2.5.2)
|
36
|
+
em-websocket (~> 0.5)
|
37
|
+
guard (~> 2.8)
|
38
|
+
guard-compat (~> 1.0)
|
39
|
+
multi_json (~> 1.8)
|
40
|
+
guard-process (1.2.1)
|
41
|
+
guard-compat (~> 1.2, >= 1.2.1)
|
42
|
+
spoon (~> 0.0.1)
|
43
|
+
guard-shell (0.7.2)
|
44
|
+
guard (>= 2.0.0)
|
45
|
+
guard-compat (~> 1.0)
|
46
|
+
http_parser.rb (0.8.0)
|
47
|
+
json (2.7.1)
|
48
|
+
kramdown (2.4.0)
|
49
|
+
rexml
|
50
|
+
language_server-protocol (3.17.0.3)
|
51
|
+
lint_roller (1.1.0)
|
52
|
+
listen (3.8.0)
|
53
|
+
rb-fsevent (~> 0.10, >= 0.10.3)
|
54
|
+
rb-inotify (~> 0.9, >= 0.9.10)
|
55
|
+
lumberjack (1.2.10)
|
56
|
+
method_source (1.0.0)
|
57
|
+
minitest (5.20.0)
|
58
|
+
multi_json (1.15.0)
|
59
|
+
nenv (0.3.0)
|
60
|
+
notiffany (0.1.3)
|
61
|
+
nenv (~> 0.1)
|
62
|
+
shellany (~> 0.0)
|
63
|
+
parallel (1.24.0)
|
64
|
+
parser (3.2.2.4)
|
65
|
+
ast (~> 2.4.1)
|
66
|
+
racc
|
67
|
+
pry (0.14.2)
|
68
|
+
coderay (~> 1.1)
|
69
|
+
method_source (~> 1.0)
|
70
|
+
racc (1.7.3)
|
71
|
+
rainbow (3.1.1)
|
72
|
+
rake (13.1.0)
|
73
|
+
rb-fsevent (0.11.2)
|
74
|
+
rb-inotify (0.10.1)
|
75
|
+
ffi (~> 1.0)
|
76
|
+
regexp_parser (2.8.3)
|
77
|
+
rexml (3.2.6)
|
78
|
+
rubocop (1.59.0)
|
79
|
+
json (~> 2.3)
|
80
|
+
language_server-protocol (>= 3.17.0)
|
81
|
+
parallel (~> 1.10)
|
82
|
+
parser (>= 3.2.2.4)
|
83
|
+
rainbow (>= 2.2.2, < 4.0)
|
84
|
+
regexp_parser (>= 1.8, < 3.0)
|
85
|
+
rexml (>= 3.2.5, < 4.0)
|
86
|
+
rubocop-ast (>= 1.30.0, < 2.0)
|
87
|
+
ruby-progressbar (~> 1.7)
|
88
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
89
|
+
rubocop-ast (1.30.0)
|
90
|
+
parser (>= 3.2.1.0)
|
91
|
+
rubocop-performance (1.20.1)
|
92
|
+
rubocop (>= 1.48.1, < 2.0)
|
93
|
+
rubocop-ast (>= 1.30.0, < 2.0)
|
94
|
+
ruby-progressbar (1.13.0)
|
95
|
+
shellany (0.0.1)
|
96
|
+
spoon (0.0.6)
|
97
|
+
ffi
|
98
|
+
standard (1.33.0)
|
99
|
+
language_server-protocol (~> 3.17.0.2)
|
100
|
+
lint_roller (~> 1.0)
|
101
|
+
rubocop (~> 1.59.0)
|
102
|
+
standard-custom (~> 1.0.0)
|
103
|
+
standard-performance (~> 1.3)
|
104
|
+
standard-custom (1.0.2)
|
105
|
+
lint_roller (~> 1.0)
|
106
|
+
rubocop (~> 1.50)
|
107
|
+
standard-performance (1.3.0)
|
108
|
+
lint_roller (~> 1.1)
|
109
|
+
rubocop-performance (~> 1.20.1)
|
110
|
+
thor (1.3.0)
|
111
|
+
unicode-display_width (2.5.0)
|
112
|
+
webrick (1.8.1)
|
113
|
+
|
114
|
+
PLATFORMS
|
115
|
+
x86_64-linux
|
116
|
+
|
117
|
+
DEPENDENCIES
|
118
|
+
md_resume!
|
119
|
+
minitest (~> 5.0)
|
120
|
+
rake (~> 13.0)
|
121
|
+
standard (~> 1.3)
|
122
|
+
|
123
|
+
BUNDLED WITH
|
124
|
+
2.4.22
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2024 YuriBocharov
|
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,67 @@
|
|
1
|
+
# md_resume
|
2
|
+
|
3
|
+
https://github.com/elasticspoon/md_resume/assets/14540596/d5034437-2842-4974-bffb-dbeacc742030
|
4
|
+
|
5
|
+
Write your resume in [Markdown](https://raw.githubusercontent.com/mikepqr/resume.md/main/resume.md), style it with [CSS](resume.css), output to [HTML](resume.html) and [PDF](resume.pdf). Open a your resume in the browser and watch it update live with changes made to the markdown.
|
6
|
+
|
7
|
+
## Prerequisites
|
8
|
+
|
9
|
+
- Ruby ≥ 3.0
|
10
|
+
- Optional, required for PDF output: Google Chrome or Chromium
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
```bash
|
15
|
+
gem install md_resume
|
16
|
+
```
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
```
|
21
|
+
Usage: md_resume command filename [options…]
|
22
|
+
|
23
|
+
Commands:
|
24
|
+
serve Start a local server to preview your resume
|
25
|
+
build Build your resume in html and pdf formats.
|
26
|
+
generate Generate a template with given name (defaults to markdown)
|
27
|
+
|
28
|
+
Specific options:
|
29
|
+
--chrome-path=PATH Path to Chrome executable
|
30
|
+
--no-pdf Do not write pdf output
|
31
|
+
--no-html Do not write html output
|
32
|
+
-p, --pdf-path=PATH Path of pdf output
|
33
|
+
-h, --html-path=PATH Path of html output
|
34
|
+
--css-path=PATH Path of css inputs.
|
35
|
+
--server-port=PORT Specify the localhost port number for the server
|
36
|
+
--serve-only
|
37
|
+
-v, --[no-]verbose Run verbosely
|
38
|
+
|
39
|
+
Common options:
|
40
|
+
--help Show this message
|
41
|
+
```
|
42
|
+
|
43
|
+
## Customization
|
44
|
+
|
45
|
+
You can generate the default style sheet with `md_resume generate-css FILENAME`. The default style is extremely generic, which is perhaps what you want in a resume,
|
46
|
+
but CSS gives you a lot of flexibility. See, e.g. [The Tech Resume Inside-Out](https://www.thetechinterview.com/) for good advice about what a resume should look like (and what it should say).
|
47
|
+
|
48
|
+
Change the appearance of the PDF version (without affecting the HTML version) by adding rules under the `@media print` CSS selector.
|
49
|
+
Change the margins and paper size of the PDF version by editing the [`@page` CSS rule](https://developer.mozilla.org/en-US/docs/Web/CSS/%40page/size).
|
50
|
+
|
51
|
+
## Note
|
52
|
+
|
53
|
+
The idea for the project is based off of https://github.com/mikepqr/resume.md. I could not get python to play nice so I rewrote it in Ruby and added features.
|
54
|
+
|
55
|
+
## Development
|
56
|
+
|
57
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
58
|
+
|
59
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
60
|
+
|
61
|
+
## Contributing
|
62
|
+
|
63
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/elasticspoon/md_resume.
|
64
|
+
|
65
|
+
## License
|
66
|
+
|
67
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rake/testtask"
|
5
|
+
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.libs << "lib"
|
9
|
+
t.test_files = FileList["test/**/test_*.rb"]
|
10
|
+
end
|
11
|
+
|
12
|
+
require "standard/rake"
|
13
|
+
|
14
|
+
task default: %i[test standard]
|
data/assets/defaults.css
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
body {
|
2
|
+
color: #000000;
|
3
|
+
background: #eeeeee;
|
4
|
+
font-size: 1.1em;
|
5
|
+
font-family: "Roboto", "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
6
|
+
line-height: 1.2;
|
7
|
+
margin: 40px 0;
|
8
|
+
}
|
9
|
+
#resume {
|
10
|
+
margin: 0 auto;
|
11
|
+
max-width: 800px;
|
12
|
+
padding: 40px 60px;
|
13
|
+
background: #ffffff;
|
14
|
+
border: 1px solid #cccccc;
|
15
|
+
box-shadow: 2px 2px 4px #aaaaaa;
|
16
|
+
-webkit-box-shadow: 2px 2px 4px #aaaaaa;
|
17
|
+
}
|
18
|
+
h1,
|
19
|
+
h2 {
|
20
|
+
font-family: "Archivo Black", sans-serif;
|
21
|
+
line-height: 1;
|
22
|
+
}
|
23
|
+
h1 {
|
24
|
+
text-transform: uppercase;
|
25
|
+
margin-top: 0;
|
26
|
+
margin-bottom: 0;
|
27
|
+
margin-left: auto;
|
28
|
+
margin-right: auto;
|
29
|
+
width: fit-content;
|
30
|
+
padding: 0;
|
31
|
+
color: #090079;
|
32
|
+
}
|
33
|
+
h2 {
|
34
|
+
border-bottom: 1px solid;
|
35
|
+
border-color: linear-gradient(to right, #fc5c7d, #6a82fb);
|
36
|
+
text-transform: uppercase;
|
37
|
+
font-size: 130%;
|
38
|
+
margin: 1em 0 0 0;
|
39
|
+
padding: 0 0 1px 0;
|
40
|
+
color: #015aa8;
|
41
|
+
}
|
42
|
+
h3 {
|
43
|
+
font-size: 100%;
|
44
|
+
margin: 0.8em 0 0.3em 0;
|
45
|
+
padding: 0;
|
46
|
+
display: flex;
|
47
|
+
justify-content: space-between;
|
48
|
+
}
|
49
|
+
p {
|
50
|
+
margin: 0 0 0.5em 0;
|
51
|
+
padding: 0;
|
52
|
+
}
|
53
|
+
ul {
|
54
|
+
padding: 0;
|
55
|
+
margin: 0 1.5em;
|
56
|
+
}
|
57
|
+
/* ul immediately after h1 = contact list */
|
58
|
+
h1 + ul {
|
59
|
+
display: flex;
|
60
|
+
align-items: center;
|
61
|
+
justify-content: center;
|
62
|
+
margin: 0;
|
63
|
+
padding: 0;
|
64
|
+
}
|
65
|
+
h1 + ul > li {
|
66
|
+
display: inline-flex;
|
67
|
+
white-space: pre;
|
68
|
+
list-style-type: none;
|
69
|
+
& span {
|
70
|
+
margin-block: auto;
|
71
|
+
}
|
72
|
+
}
|
73
|
+
h1 + ul > li:after {
|
74
|
+
content: " \2022 ";
|
75
|
+
margin-block: auto;
|
76
|
+
}
|
77
|
+
h1 + ul > li:last-child:after {
|
78
|
+
content: "";
|
79
|
+
}
|
80
|
+
/* p immediately after contact list = summary */
|
81
|
+
h1 + ul + p {
|
82
|
+
margin: 1em 0;
|
83
|
+
}
|
84
|
+
img {
|
85
|
+
height: 20px;
|
86
|
+
}
|
87
|
+
.img_link {
|
88
|
+
display: inline-flex;
|
89
|
+
}
|
90
|
+
#skills {
|
91
|
+
margin-bottom: 8px;
|
92
|
+
}
|
93
|
+
|
94
|
+
@media print {
|
95
|
+
body {
|
96
|
+
font-size: 10pt;
|
97
|
+
margin: 0;
|
98
|
+
padding: 0;
|
99
|
+
background: none;
|
100
|
+
}
|
101
|
+
#resume {
|
102
|
+
margin: 0;
|
103
|
+
padding: 0;
|
104
|
+
border: 0px;
|
105
|
+
background: none;
|
106
|
+
box-shadow: none;
|
107
|
+
-webkit-box-shadow: none;
|
108
|
+
}
|
109
|
+
/* Do not underline abbr tags in PDF */
|
110
|
+
abbr {
|
111
|
+
text-decoration: none;
|
112
|
+
font-variant: none;
|
113
|
+
}
|
114
|
+
/* Make links black in PDF */
|
115
|
+
/* Move this outside the print block to apply this in HTML too */
|
116
|
+
a,
|
117
|
+
a:link,
|
118
|
+
a:visited,
|
119
|
+
a:hover {
|
120
|
+
color: #000000;
|
121
|
+
text-decoration: underline;
|
122
|
+
}
|
123
|
+
}
|
124
|
+
@page {
|
125
|
+
/* Change margins and paper size of PDF */
|
126
|
+
/* https://developer.mozilla.org/en-US/docs/Web/CSS/@page */
|
127
|
+
size: letter;
|
128
|
+
margin: 0.5in 0.8in;
|
129
|
+
}
|
130
|
+
@media screen and (max-width: 800px) {
|
131
|
+
body {
|
132
|
+
font-size: 16pt;
|
133
|
+
margin: 0;
|
134
|
+
padding: 0;
|
135
|
+
background: #ffffff !important;
|
136
|
+
}
|
137
|
+
#resume {
|
138
|
+
margin: 0;
|
139
|
+
padding: 1em;
|
140
|
+
border: 0px;
|
141
|
+
background: none;
|
142
|
+
box-shadow: none;
|
143
|
+
-webkit-box-shadow: none;
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
@media screen {
|
148
|
+
h1 {
|
149
|
+
background-color: #fc5c7d;
|
150
|
+
background-image: linear-gradient(to right, #fc5c7d, #6a82fb);
|
151
|
+
background-size: 100%;
|
152
|
+
-webkit-background-clip: text;
|
153
|
+
-webkit-text-fill-color: transparent;
|
154
|
+
-moz-background-clip: text;
|
155
|
+
-moz-text-fill-color: transparent;
|
156
|
+
}
|
157
|
+
|
158
|
+
h2 {
|
159
|
+
background-color: #314755;
|
160
|
+
background-image: linear-gradient(45deg, #003850, #154bdf 20%);
|
161
|
+
background-size: 100%;
|
162
|
+
-webkit-background-clip: text;
|
163
|
+
-webkit-text-fill-color: transparent;
|
164
|
+
-moz-background-clip: text;
|
165
|
+
-moz-text-fill-color: transparent;
|
166
|
+
}
|
167
|
+
}
|
data/assets/reload.js
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
function reload() {
|
2
|
+
fetch("/")
|
3
|
+
.then((response) => {
|
4
|
+
return response.text();
|
5
|
+
})
|
6
|
+
.then((html) => {
|
7
|
+
document.body.innerHTML = html;
|
8
|
+
console.log("Reloaded");
|
9
|
+
});
|
10
|
+
}
|
11
|
+
|
12
|
+
function pollServer() {
|
13
|
+
fetch("http://localhost:12345/")
|
14
|
+
.then(() => {
|
15
|
+
reload();
|
16
|
+
pollServer();
|
17
|
+
})
|
18
|
+
.catch((err) => {
|
19
|
+
console.log(err);
|
20
|
+
console.log("Server not ready. Reload the page with the server running.");
|
21
|
+
});
|
22
|
+
}
|
23
|
+
|
24
|
+
pollServer();
|
@@ -0,0 +1,64 @@
|
|
1
|
+
<!-- The (first) h1 will be used as the <title> of the HTML page -->
|
2
|
+
|
3
|
+
# Richard Hendricks
|
4
|
+
|
5
|
+
<!-- The unordered list immediately after the h1 will be formatted on a single
|
6
|
+
line. It is intended to be used for contact details -->
|
7
|
+
|
8
|
+
- <richard.hendriks@mail.com>
|
9
|
+
- (912) 555-4321
|
10
|
+
- [richardhendricks.example.com](http://richardhendricks.example.com)
|
11
|
+
- San Francisco, CA
|
12
|
+
|
13
|
+
<!-- The paragraph after the h1 and ul and before the first h2 is optional. It
|
14
|
+
is intended to be used for a short summary. -->
|
15
|
+
|
16
|
+
CEO and Software Engineer with knowledge of applied information theory,
|
17
|
+
including optimizing lossless compression schema of both the length-limited and
|
18
|
+
adaptive variants.
|
19
|
+
|
20
|
+
## Experience
|
21
|
+
|
22
|
+
<!-- You have to wrap the "left" and "right" half of these headings in spans by
|
23
|
+
hand -->
|
24
|
+
|
25
|
+
### <span>CEO/President, Pied Piper</span> <span>Dec 2013 -- Dec 2014</span>
|
26
|
+
|
27
|
+
Pied Piper is a multi-platform technology based on a proprietary universal
|
28
|
+
compression algorithm that has consistently fielded high Weisman Scores™ that
|
29
|
+
are not merely competitive, but approach the theoretical limit of lossless
|
30
|
+
compression.
|
31
|
+
|
32
|
+
- Build an algorithm for artist to detect if their music was violating
|
33
|
+
copyright infringement laws
|
34
|
+
- Successfully won Techcrunch Disrupt
|
35
|
+
- Optimized an algorithm that holds the current world record for Weisman Scores
|
36
|
+
|
37
|
+
### <span>Teacher, CoderDojo</span> <span>July 2013 -- Dec 2013</span>
|
38
|
+
|
39
|
+
Global movement of free coding clubs for young people.
|
40
|
+
|
41
|
+
- Awarded 'Teacher of the Month'
|
42
|
+
|
43
|
+
## Projects
|
44
|
+
|
45
|
+
### <span>Miss Direction</span> <span>Aug 2016</span>
|
46
|
+
|
47
|
+
A mapping engine that misguides you:
|
48
|
+
|
49
|
+
- Won award at AIHacks 2016
|
50
|
+
- Built by all women team of newbie programmers
|
51
|
+
- Using modern technologies such as GoogleMaps, Chrome Extension and Javascript
|
52
|
+
|
53
|
+
## Education
|
54
|
+
|
55
|
+
### <span>University of Oklahoma, BA Information Technology</span> <span>2011 -- 2014</span>
|
56
|
+
|
57
|
+
- GPA 4.0
|
58
|
+
- DB1101 - Basic SQL
|
59
|
+
- CS2011 - Java Introduction
|
60
|
+
|
61
|
+
## Skills
|
62
|
+
|
63
|
+
- Web development: HTML, CSS, JavaScript
|
64
|
+
- Compression: Mpeg, MP4, GIF
|
data/exe/md_resume
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'resume_generator'
|
4
|
+
require 'parser'
|
5
|
+
require 'local-server'
|
6
|
+
|
7
|
+
command = ARGV.shift
|
8
|
+
resume = ARGV.shift
|
9
|
+
|
10
|
+
parser = Parser.new
|
11
|
+
opts = parser.parse(command, ARGV)
|
12
|
+
|
13
|
+
if resume.nil?
|
14
|
+
puts parser.args
|
15
|
+
exit
|
16
|
+
end
|
17
|
+
|
18
|
+
resume = File.expand_path(resume)
|
19
|
+
opts.input = resume
|
20
|
+
|
21
|
+
case command
|
22
|
+
when 'serve'
|
23
|
+
generator = ResumeGenerator.new(opts)
|
24
|
+
server = Server.new(generator, opts)
|
25
|
+
server.start
|
26
|
+
when 'build'
|
27
|
+
generator = ResumeGenerator.new(opts)
|
28
|
+
generator.write
|
29
|
+
when 'generate'
|
30
|
+
generator = ResumeGenerator.new(opts)
|
31
|
+
generator.generate_template
|
32
|
+
else
|
33
|
+
puts parser.args
|
34
|
+
end
|
data/lib/local-server.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'webrick'
|
2
|
+
require 'socket'
|
3
|
+
require 'filewatcher'
|
4
|
+
|
5
|
+
class Server
|
6
|
+
attr_reader :opts, :generator
|
7
|
+
|
8
|
+
def initialize(generator, opts)
|
9
|
+
@opts = opts
|
10
|
+
@generator = generator
|
11
|
+
@needs_reload = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
start_file_watcher
|
16
|
+
start_reload_server
|
17
|
+
open_browser
|
18
|
+
start_local_server
|
19
|
+
ensure
|
20
|
+
clean_build_dir
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def filewatcher
|
26
|
+
puts "watching #{opts.input} and #{opts.css_path}" if opts.verbose
|
27
|
+
@filewatcher ||= Filewatcher.new([opts.input, opts.css_path])
|
28
|
+
end
|
29
|
+
|
30
|
+
def open_browser
|
31
|
+
link = "http://localhost:#{opts.port}"
|
32
|
+
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
33
|
+
system "start #{link}"
|
34
|
+
elsif RbConfig::CONFIG['host_os'] =~ /darwin/
|
35
|
+
system "open #{link}"
|
36
|
+
elsif RbConfig::CONFIG['host_os'] =~ /linux|bsd/
|
37
|
+
system "xdg-open #{link}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def start_file_watcher
|
42
|
+
generator.write
|
43
|
+
@thread = Thread.new(filewatcher) do |fw|
|
44
|
+
fw.watch do |change|
|
45
|
+
puts "Change detected: #{change}" if opts.verbose
|
46
|
+
generator.write
|
47
|
+
@needs_reload = true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def start_reload_server
|
53
|
+
puts 'Starting reload server on port 12345'
|
54
|
+
@reload_thread = Thread.new do
|
55
|
+
reload_server
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def reload_server
|
60
|
+
server = TCPServer.new('localhost', 12_345)
|
61
|
+
|
62
|
+
loop do
|
63
|
+
socket = server.accept
|
64
|
+
request = socket.gets
|
65
|
+
warn request
|
66
|
+
sleep 0.5 until @needs_reload
|
67
|
+
@needs_reload = false
|
68
|
+
socket.print(headers)
|
69
|
+
socket.close
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def start_local_server
|
74
|
+
puts "Starting local server on port #{opts.port}"
|
75
|
+
root = opts.html_path
|
76
|
+
server = WEBrick::HTTPServer.new(Port: opts.port, DocumentRoot: root)
|
77
|
+
|
78
|
+
trap 'INT' do server.shutdown end
|
79
|
+
|
80
|
+
server.start
|
81
|
+
end
|
82
|
+
|
83
|
+
def headers
|
84
|
+
headers = [
|
85
|
+
'HTTP/1.1 200 OK',
|
86
|
+
'Content-Type: text/html',
|
87
|
+
"Access-Control-Allow-Origin: http://localhost:#{opts.port}"
|
88
|
+
]
|
89
|
+
@headers ||= "#{headers.join("\r\n")}\r\n\r\n"
|
90
|
+
end
|
91
|
+
|
92
|
+
def clean_build_dir
|
93
|
+
tmp_dir = opts.html_path.dirname
|
94
|
+
FileUtils.rm_rf(tmp_dir)
|
95
|
+
puts "Could not delete #{tmp_dir}" if File.directory?(tmp_dir)
|
96
|
+
end
|
97
|
+
end
|
data/lib/parser.rb
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
class Parser
|
5
|
+
class ScriptOptions
|
6
|
+
attr_accessor :chrome_path, :html, :pdf, :css_path, :pdf_path, :html_path, :verbose, :input,
|
7
|
+
:serve_only, :port, :open_browser, :generate_md, :generate_css
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
self.chrome_path = nil
|
11
|
+
self.html = true
|
12
|
+
self.pdf = true
|
13
|
+
self.open_browser = true
|
14
|
+
self.css_path = default_css_path
|
15
|
+
self.pdf_path = Pathname.new('resume.pdf').expand_path
|
16
|
+
self.html_path = Pathname.new('resume.html').expand_path
|
17
|
+
self.verbose = false
|
18
|
+
self.serve_only = false
|
19
|
+
self.input = nil
|
20
|
+
self.port = 3000
|
21
|
+
self.generate_md = true
|
22
|
+
self.generate_css = false
|
23
|
+
end
|
24
|
+
|
25
|
+
def define_options(parser)
|
26
|
+
parser.banner = 'Usage: md_resume command filename [options...]'
|
27
|
+
parser.separator ''
|
28
|
+
parser.separator 'Commands:'
|
29
|
+
parser.separator "serve\t\t\tStart a local server to preview your resume"
|
30
|
+
parser.separator "build\t\t\tBuild your resume in html and pdf formats."
|
31
|
+
parser.separator "generate\t\tGenerate a resume template with the given filename"
|
32
|
+
parser.separator ''
|
33
|
+
parser.separator 'Specific options:'
|
34
|
+
|
35
|
+
# add additional options
|
36
|
+
specify_chrome_path_option(parser)
|
37
|
+
boolean_pdf_option(parser)
|
38
|
+
boolean_html_option(parser)
|
39
|
+
specify_output_pdf_option(parser)
|
40
|
+
specify_output_html_option(parser)
|
41
|
+
specify_input_css_option(parser)
|
42
|
+
specify_server_port_option(parser)
|
43
|
+
# TODO: remove this?
|
44
|
+
boolean_serve_only_option(parser)
|
45
|
+
boolean_open_browser_option(parser)
|
46
|
+
boolean_verbosity_option(parser)
|
47
|
+
boolean_generate_md_option(parser)
|
48
|
+
boolean_generate_css_option(parser)
|
49
|
+
parser.separator ''
|
50
|
+
parser.separator 'Common options:'
|
51
|
+
# No argument, shows at tail. This will print an options summary.
|
52
|
+
# Try it and see!
|
53
|
+
parser.on_tail('-h', '--help', 'Show this message') do
|
54
|
+
puts parser
|
55
|
+
exit
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def specify_chrome_path_option(parser)
|
60
|
+
# Specifies an optional option argument
|
61
|
+
parser.on('--chrome-path=PATH', 'Path to Chrome executable') do |path|
|
62
|
+
full_path = Pathname.new(path).expand_path
|
63
|
+
self.chrome_path = full_path
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def specify_output_html_option(parser)
|
68
|
+
parser.on('-h PATH', '--html-path=PATH', 'Path of html output') do |path|
|
69
|
+
input_dir = Pathname.new(path).expand_path
|
70
|
+
self.html_path = input_dir
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def specify_output_pdf_option(parser)
|
75
|
+
parser.on('-p PATH', '--pdf-path=PATH', 'Path of pdf output') do |path|
|
76
|
+
input_dir = Pathname.new(path).expand_path
|
77
|
+
self.pdf_path = input_dir
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def specify_input_css_option(parser)
|
82
|
+
parser.on('--css-path=PATH', 'Path of css inputs.') do |path|
|
83
|
+
input_dir = Pathname.new(path).expand_path
|
84
|
+
self.css_path = input_dir
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def specify_server_port_option(parser)
|
89
|
+
parser.on('--server-port=PORT', 'Specify the localhost port number for the server') do |port|
|
90
|
+
port = port.to_i
|
91
|
+
raise OptionParser::InvalidArgument unless (1..65_535).cover?(port)
|
92
|
+
|
93
|
+
self.port = port
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def boolean_verbosity_option(parser)
|
98
|
+
parser.on('-v', '--[no-]verbose', 'Run verbosely') do |v|
|
99
|
+
self.verbose = v
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def boolean_pdf_option(parser)
|
104
|
+
# Boolean switch.
|
105
|
+
parser.on('--no-pdf', 'Do [not] write pdf output') do |v|
|
106
|
+
self.pdf = v
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def boolean_html_option(parser)
|
111
|
+
# Boolean switch.
|
112
|
+
parser.on('--[no-]html', 'Do [not] write html output') do |v|
|
113
|
+
self.html = v
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def boolean_serve_only_option(parser)
|
118
|
+
# Boolean switch.
|
119
|
+
parser.on('--serve-only') do |v|
|
120
|
+
self.serve_only = v
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def boolean_open_browser_option(parser)
|
125
|
+
# Boolean switch.
|
126
|
+
parser.on('--no-open', 'Do not automatically open browser when starting server') do |v|
|
127
|
+
self.open_browser = v
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def boolean_generate_md_option(parser)
|
132
|
+
parser.on('--no-generate-md', 'Do not generate markdown template.') do |v|
|
133
|
+
self.generate_md = v
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def boolean_generate_css_option(parser)
|
138
|
+
parser.on('--[no-]generate-css', 'Generate CSS template.') do |v|
|
139
|
+
self.generate_css = v
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def default_css_path
|
146
|
+
curr = Pathname.new(__FILE__).dirname
|
147
|
+
relative = Pathname.new('../assets/defaults.css')
|
148
|
+
File.expand_path(relative, curr)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
#
|
153
|
+
# Return a structure describing the options.
|
154
|
+
#
|
155
|
+
def parse(command, args)
|
156
|
+
@options = ScriptOptions.new
|
157
|
+
@args = OptionParser.new do |parser|
|
158
|
+
@options.define_options(parser)
|
159
|
+
parser.parse!(args)
|
160
|
+
set_command_defaults(command)
|
161
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
|
162
|
+
puts e
|
163
|
+
puts
|
164
|
+
puts parser
|
165
|
+
exit
|
166
|
+
end
|
167
|
+
@options
|
168
|
+
end
|
169
|
+
|
170
|
+
def set_command_defaults(command)
|
171
|
+
case command
|
172
|
+
when 'serve'
|
173
|
+
serve_defaults
|
174
|
+
when 'build'
|
175
|
+
build_defaults
|
176
|
+
when 'generate'
|
177
|
+
generate_defaults
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def serve_defaults
|
182
|
+
options.pdf = false
|
183
|
+
options.html = true
|
184
|
+
options.html_path = internal_tmp_dir.join('resume.html')
|
185
|
+
end
|
186
|
+
|
187
|
+
def build_defaults(parser); end
|
188
|
+
|
189
|
+
def generate_defaults(parser); end
|
190
|
+
|
191
|
+
attr_reader :parser, :options, :args
|
192
|
+
|
193
|
+
private
|
194
|
+
|
195
|
+
def internal_tmp_dir
|
196
|
+
Pathname.new('../../tmp').expand_path(__FILE__)
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
require 'kramdown'
|
2
|
+
require 'base64'
|
3
|
+
require 'open3'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
class ResumeGenerator
|
8
|
+
attr_reader :opts
|
9
|
+
|
10
|
+
ValueError = Class.new(StandardError)
|
11
|
+
|
12
|
+
POSTAMBLE = <<~POSTAMBLE.freeze
|
13
|
+
</div>
|
14
|
+
</body>
|
15
|
+
<script>
|
16
|
+
#{File.read(File.expand_path('../assets/reload.js', __dir__))}
|
17
|
+
</script>
|
18
|
+
</html>
|
19
|
+
POSTAMBLE
|
20
|
+
|
21
|
+
CHROME_GUESSES_MACOS = [
|
22
|
+
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
23
|
+
'/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
|
24
|
+
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
|
25
|
+
].freeze
|
26
|
+
|
27
|
+
LINUX_CHROME_DIRS = [
|
28
|
+
'/usr/local/sbin',
|
29
|
+
'/usr/local/bin',
|
30
|
+
'/usr/sbin',
|
31
|
+
'/usr/bin',
|
32
|
+
'/sbin',
|
33
|
+
'/bin',
|
34
|
+
'/opt/google/chrome',
|
35
|
+
"/etc/profiles/per-user/#{ENV['USER']}/bin" # NixOS
|
36
|
+
].freeze
|
37
|
+
|
38
|
+
LINUX_CHROME_EXECUTABLES = %w[google-chrome chrome chromium chromium-browser].freeze
|
39
|
+
|
40
|
+
def initialize(opts)
|
41
|
+
@opts = opts
|
42
|
+
@opts.chrome_path = guess_chrome_path if @opts.pdf && @opts.chrome_path.nil?
|
43
|
+
end
|
44
|
+
|
45
|
+
def write
|
46
|
+
valid_input?
|
47
|
+
write_html
|
48
|
+
write_pdf
|
49
|
+
end
|
50
|
+
|
51
|
+
def valid_input?
|
52
|
+
puts "Input file: #{@opts.input}"
|
53
|
+
return if File.exist?(@opts.input)
|
54
|
+
|
55
|
+
puts "Resume not found at #{@opts.input}"
|
56
|
+
exit
|
57
|
+
end
|
58
|
+
|
59
|
+
def generate_template
|
60
|
+
curr = Pathname.new(__FILE__).dirname
|
61
|
+
if @opts.generate_md
|
62
|
+
relative = Pathname.new('../assets/sample-resume.md')
|
63
|
+
template = File.expand_path(relative, curr)
|
64
|
+
FileUtils.copy_file(template, "#{@opts.input}.md")
|
65
|
+
end
|
66
|
+
|
67
|
+
return unless @opts.generate_css
|
68
|
+
|
69
|
+
relative = Pathname.new('../assets/defaults.css')
|
70
|
+
template = File.expand_path(relative, curr)
|
71
|
+
FileUtils.copy_file(template, "#{@opts.input}.css")
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def chrome_guesses_linux
|
77
|
+
LINUX_CHROME_DIRS.product(LINUX_CHROME_EXECUTABLES).map do |dir, exe|
|
78
|
+
"#{dir}/#{exe}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def chrome_guesses_windows
|
83
|
+
[
|
84
|
+
# Windows 10
|
85
|
+
File.expand_path('Google/Chrome/Application/chrome.exe', ENV.fetch('ProgramFiles(x86)', nil)),
|
86
|
+
File.expand_path('Google/Chrome/Application/chrome.exe', ENV.fetch('ProgramFiles(x86)', nil)),
|
87
|
+
File.expand_path('Google/Chrome/Application/chrome.exe', ENV.fetch('ProgramFiles(x86)', nil)),
|
88
|
+
# Windows 7
|
89
|
+
'C:/Program Files (x86)/Google/Chrome/Application/chrome.exe',
|
90
|
+
'C:/Program Files/Google/Chrome/Application/chrome.exe',
|
91
|
+
# Vista
|
92
|
+
'C:/Users/UserName/AppData/Local/Google/Chrome',
|
93
|
+
# XP
|
94
|
+
'C:/Documents and Settings/UserName/Local Settings/Application Data/Google/Chrome'
|
95
|
+
]
|
96
|
+
end
|
97
|
+
|
98
|
+
def guess_chrome_path
|
99
|
+
guesses = case RUBY_PLATFORM
|
100
|
+
when /darwin/
|
101
|
+
CHROME_GUESSES_MACOS
|
102
|
+
when /cygwin|mswin|mingw|bccwin|wince|emx/
|
103
|
+
chrome_guesses_windows
|
104
|
+
else
|
105
|
+
chrome_guesses_linux
|
106
|
+
end
|
107
|
+
guesses.each do |path|
|
108
|
+
if File.exist?(path)
|
109
|
+
puts "Guessed Chrome path: #{path}" if opts.verbose
|
110
|
+
return path
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
raise ValueError,
|
115
|
+
"Cannot guess Chrome path on platform #{RUBY_PLATFORM}.
|
116
|
+
Please set --chrome_path= manually."
|
117
|
+
end
|
118
|
+
|
119
|
+
def to_html
|
120
|
+
markdown = File.read(opts.input)
|
121
|
+
make_html(markdown, css_file: opts.css_path)
|
122
|
+
end
|
123
|
+
|
124
|
+
def to_pdf
|
125
|
+
make_pdf(to_html)
|
126
|
+
end
|
127
|
+
|
128
|
+
def make_pdf(html)
|
129
|
+
html64 = Base64.encode64(html.encode('utf-8')).chomp
|
130
|
+
prefix = opts.pdf_path.basename
|
131
|
+
tmp_dir = Pathname.new('../tmp').expand_path(__FILE__)
|
132
|
+
FileUtils.mkdir(tmp_dir) unless File.directory?(tmp_dir)
|
133
|
+
options = pdf_opts_string(tmp_dir)
|
134
|
+
create_output_dir(opts.pdf_path)
|
135
|
+
|
136
|
+
begin
|
137
|
+
cmd = "#{opts.chrome_path} #{options} --print-to-pdf=#{opts.pdf_path} 'data:text/html;base64,#{html64}'"
|
138
|
+
_stdout, stderr, status = Open3.capture3(cmd)
|
139
|
+
raise stderr unless status.success?
|
140
|
+
|
141
|
+
puts "Wrote #{prefix}" if opts.verbose
|
142
|
+
rescue StandardError => e
|
143
|
+
puts e.message
|
144
|
+
ensure
|
145
|
+
FileUtils.rm_rf(tmp_dir)
|
146
|
+
puts "Could not delete #{tmp_dir}" if File.directory?(tmp_dir)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def pdf_opts_string(tmp_dir)
|
151
|
+
options = [
|
152
|
+
'--no-sandbox',
|
153
|
+
'--headless',
|
154
|
+
'--no-pdf-header-footer',
|
155
|
+
'--enable-logging=stderr',
|
156
|
+
'--log-level=2',
|
157
|
+
'--in-process-gpu',
|
158
|
+
'--disable-gpu'
|
159
|
+
]
|
160
|
+
options.push("--crash-dumps-dir=#{tmp_dir}")
|
161
|
+
options.push("--user-data-dir=#{tmp_dir}")
|
162
|
+
options.join(' ')
|
163
|
+
end
|
164
|
+
|
165
|
+
def preamable(title, css)
|
166
|
+
<<~PREAMBLE
|
167
|
+
<html lang="en">
|
168
|
+
<head>
|
169
|
+
<meta charset="UTF-8">
|
170
|
+
<meta property="time_built" content="#{Time.now.iso8601}">
|
171
|
+
<title>#{title}</title>
|
172
|
+
<style>
|
173
|
+
#{css}
|
174
|
+
</style>
|
175
|
+
</head>
|
176
|
+
<body>
|
177
|
+
<div id="resume">
|
178
|
+
PREAMBLE
|
179
|
+
end
|
180
|
+
|
181
|
+
def title(markdown)
|
182
|
+
# Return the contents of the first markdown heading in md, which we
|
183
|
+
# assume to be the title of the document.
|
184
|
+
markdown.each_line do |line|
|
185
|
+
return Regexp.last_match(1) if line =~ /^#([^#]*)$/
|
186
|
+
end
|
187
|
+
|
188
|
+
raise ValueError,
|
189
|
+
'Cannot find any lines that look like markdown h1 headings to use as the title'
|
190
|
+
end
|
191
|
+
|
192
|
+
def reload_script
|
193
|
+
File.read(File.expand_path('assets/reload.js', __dir__))
|
194
|
+
end
|
195
|
+
|
196
|
+
def make_html(markdown, css_file: nil)
|
197
|
+
title = title(markdown)
|
198
|
+
html = Kramdown::Document.new(markdown).to_html
|
199
|
+
begin
|
200
|
+
css = File.read(css_file.to_s)
|
201
|
+
rescue Errno::ENOENT
|
202
|
+
warn "Cannot find CSS file #{css_file}"
|
203
|
+
warn 'Output will not be styled'
|
204
|
+
css = ''
|
205
|
+
end
|
206
|
+
preamable(title, css) + html + POSTAMBLE
|
207
|
+
end
|
208
|
+
|
209
|
+
def write_html
|
210
|
+
puts "Writing HTML to #{opts.html_path}"
|
211
|
+
File.write(opts.html_path, to_html, mode: 'w') if opts.html
|
212
|
+
rescue Errno::ENOENT => e
|
213
|
+
raise e unless e.message =~ /No such file or directory/
|
214
|
+
|
215
|
+
create_output_dir(opts.html_path)
|
216
|
+
retry
|
217
|
+
end
|
218
|
+
|
219
|
+
def write_pdf
|
220
|
+
to_pdf if opts.pdf
|
221
|
+
end
|
222
|
+
|
223
|
+
def create_output_dir(path)
|
224
|
+
FileUtils.mkdir_p(File.dirname(path))
|
225
|
+
end
|
226
|
+
|
227
|
+
def set_server_opts
|
228
|
+
opts.html_path = Pathname.new(Dir.pwd).join('tmp/resume.html')
|
229
|
+
opts.html = true
|
230
|
+
opts.pdf = false
|
231
|
+
opts.verbose = true
|
232
|
+
end
|
233
|
+
end
|
data/md_resume.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/md_resume/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'md_resume'
|
7
|
+
spec.version = MdResume::VERSION
|
8
|
+
spec.authors = ['YuriBocharov']
|
9
|
+
spec.email = ['quesadillaman@gmail.com']
|
10
|
+
|
11
|
+
spec.summary = 'Ruby gem for creating resume from markdown file.'
|
12
|
+
spec.description = 'Write a resume in markdown, style it with CSS, distribute it as either HTML or PDF. Now with even faster feedback cycles, make changes and preview them immediately!</br></br><code>md-resume</code> is a resume generator written in Ruby styled with CSS. Instead of stopping at exclusively generation of a resume in HTML or PDF format, the project goes further in letting you perfect your resume. Running <code>resume.rb</code> in development mode will spin up a local server that will watch your changes to the resume styles and content. The server will live update an HTML preview of your resume letting you move quickly with changes and updates. The project uses <code>kramdown</code> to translate markdown to HTML, bringing with it additional inline markdown customization options.'
|
13
|
+
spec.homepage = 'https://github.com/elasticspoon/markdown-resume'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
spec.required_ruby_version = '>= 2.6.0'
|
16
|
+
|
17
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
18
|
+
|
19
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
20
|
+
spec.metadata['source_code_uri'] = 'https://github.com/elasticspoon/markdown-resume'
|
21
|
+
# spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
22
|
+
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
+
spec.files = Dir.chdir(__dir__) do
|
26
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
(File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
spec.bindir = 'exe'
|
31
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ['lib']
|
33
|
+
|
34
|
+
# Uncomment to register a new dependency of your gem
|
35
|
+
# spec.add_dependency "example-gem", "~> 1.0"
|
36
|
+
spec.add_dependency 'filewatcher', '~> 2.1'
|
37
|
+
spec.add_dependency 'kramdown', '~> 2.4'
|
38
|
+
spec.add_dependency 'webrick', '~> 1.8'
|
39
|
+
|
40
|
+
# For more information and examples about making a new gem, check out our
|
41
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
42
|
+
end
|
data/resume.png
ADDED
Binary file
|
data/sig/md_resume.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: md_resume
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- YuriBocharov
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-01-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: filewatcher
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.1'
|
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.4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: webrick
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.8'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.8'
|
55
|
+
description: Write a resume in markdown, style it with CSS, distribute it as either
|
56
|
+
HTML or PDF. Now with even faster feedback cycles, make changes and preview them
|
57
|
+
immediately!</br></br><code>md-resume</code> is a resume generator written in Ruby
|
58
|
+
styled with CSS. Instead of stopping at exclusively generation of a resume in HTML
|
59
|
+
or PDF format, the project goes further in letting you perfect your resume. Running
|
60
|
+
<code>resume.rb</code> in development mode will spin up a local server that will
|
61
|
+
watch your changes to the resume styles and content. The server will live update
|
62
|
+
an HTML preview of your resume letting you move quickly with changes and updates.
|
63
|
+
The project uses <code>kramdown</code> to translate markdown to HTML, bringing with
|
64
|
+
it additional inline markdown customization options.
|
65
|
+
email:
|
66
|
+
- quesadillaman@gmail.com
|
67
|
+
executables:
|
68
|
+
- md_resume
|
69
|
+
extensions: []
|
70
|
+
extra_rdoc_files: []
|
71
|
+
files:
|
72
|
+
- ".standard.yml"
|
73
|
+
- CHANGELOG.md
|
74
|
+
- Gemfile
|
75
|
+
- Gemfile.lock
|
76
|
+
- LICENSE.txt
|
77
|
+
- README.md
|
78
|
+
- Rakefile
|
79
|
+
- assets/defaults.css
|
80
|
+
- assets/reload.js
|
81
|
+
- assets/sample-resume.md
|
82
|
+
- exe/md_resume
|
83
|
+
- lib/local-server.rb
|
84
|
+
- lib/md_resume/version.rb
|
85
|
+
- lib/parser.rb
|
86
|
+
- lib/resume_generator.rb
|
87
|
+
- md_resume.gemspec
|
88
|
+
- resume.png
|
89
|
+
- sig/md_resume.rbs
|
90
|
+
homepage: https://github.com/elasticspoon/markdown-resume
|
91
|
+
licenses:
|
92
|
+
- MIT
|
93
|
+
metadata:
|
94
|
+
homepage_uri: https://github.com/elasticspoon/markdown-resume
|
95
|
+
source_code_uri: https://github.com/elasticspoon/markdown-resume
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 2.6.0
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
requirements: []
|
111
|
+
rubygems_version: 3.4.10
|
112
|
+
signing_key:
|
113
|
+
specification_version: 4
|
114
|
+
summary: Ruby gem for creating resume from markdown file.
|
115
|
+
test_files: []
|