resume-stylist 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 31ad8a2c22c71fb69b8e476894f3d9e38555f321
4
- data.tar.gz: 1424894eccf7f0b3eff561023b742906940bbcd8
3
+ metadata.gz: 2298b819f3fe882758cdc3db17a55c2ac89496c0
4
+ data.tar.gz: 047b6d97566a5373defcc80209bca8efbde744e0
5
5
  SHA512:
6
- metadata.gz: 785c11a423a56927fd6291bd174213171abada37ae7423e5d643d5aa15493ec3593e5b59b4b37105cacb5a04da3b5ab3fce9c331599f07430036e3deeba0aee1
7
- data.tar.gz: 07433278538c665d14e132e8ce34ac46f5acb780220312785139c05a28c5ed5eab517cf312f23058590820efcbc97201430f6005e9b9db26d277e0a94d206156
6
+ metadata.gz: 9fa53127f95fb8cde42b0f56a6b29eeb2e23809b46ddd9f911ef7580d6504fa8b6cd87da507419cea93df4ab7ee04d5a9964e8589bbf215eb154d32ef348d7ed
7
+ data.tar.gz: d1da79c170544948681f7c05c69ab4fd8c8e229bb0e498148a3da57e91c59b6d9622e393a6b966bb49dd36f157e0f6eb8930249b3b458d5280a1c51881ed54e7
data/README.md CHANGED
@@ -1,39 +1,83 @@
1
1
  # Resume Stylist
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/resume-stylist`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ **Note**: Currently in very early development.
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ Small framework for making CV/resume themes using [Liquid](https://github.com/Shopify/liquid/wiki/liquid-for-designers) and [Sass](http://sass-lang.com/).
6
6
 
7
7
  ## Installation
8
8
 
9
- Add this line to your application's Gemfile:
9
+ ~~~sh
10
+ $ gem install resume-stylist
11
+ ~~~
10
12
 
11
- ```ruby
12
- gem 'resume-stylist'
13
- ```
13
+ Also, since this uses PDFKit, you'll need to make sure you have `wkhtmltopdf` installed. On Arch it's as simple as running `sudo pacman -S wkhtmltopdf`, but consult their documentation for recommendations regarding other platforms: https://github.com/pdfkit/pdfkit#wkhtmltopdf
14
14
 
15
- And then execute:
15
+ ## Features
16
16
 
17
- $ bundle
17
+ Here are some cool features:
18
18
 
19
- Or install it yourself as:
20
-
21
- $ gem install resume-stylist
19
+ - Uses Liquid as the templating language
20
+ - Support for inline Sass and SCSS by using `<style type="text/sass">` and `<style type="text/scss">`
21
+ - Support for multiple Resume formats (JSON and XML currently), but you can BYOF (Bring Your Own Format)!
22
+ - Export to PDF
22
23
 
23
24
  ## Usage
24
25
 
25
- TODO: Write usage instructions here
26
+ ### Tool usage
26
27
 
27
- ## Development
28
+ Create a new theme and a JSON resume template:
28
29
 
29
- 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. Run `bundle exec resume-stylist` to use the gem in this directory, ignoring other installed copies of this gem.
30
+ ~~~sh
31
+ $ resume-stylist --new-theme fancy.html.liquid --new-resume john_doe.json
32
+ ~~~
30
33
 
31
- 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 tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
34
+ Build your theme as a PDF:
32
35
 
33
- ## Contributing
36
+ ~~~sh
37
+ $ resume-stylist build john_doe.json john_doe.pdf
38
+ ~~~
39
+
40
+ ~~~sh
41
+ $ resume-stylist build john_doe -I json john_doe.pdf
42
+ ~~~
43
+
44
+ Run `resume-stylist --help` for more options!
45
+
46
+ ### Library usage
47
+
48
+ #### Registering a custom resume format
49
+
50
+ ~~~rb
51
+ module MyResumeFormat
52
+ def self.handles?(resume_format)
53
+ # Handle
54
+ resume_format.to_s =~ /(myformat|myf)/i
55
+ end
34
56
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/resume-stylist. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
57
+ def load!(input)
58
+ # input is the resume data
59
+ data = My::Parser.parse(input)
60
+ @data = data.to_h
61
+ end
62
+ end
63
+
64
+ ResumeStylist::Resume.register_handler MyResumeFormat
65
+ ~~~
66
+
67
+ **NOTE**: The theme expects `@data` to be a `Hash` with `String` keys.
68
+
69
+ #### Creating a resume (HTML) programmatically:
70
+
71
+ ~~~rb
72
+ resume = ResumeStylist::Resume.new(resume_source, :myformat)
73
+ theme = ResumeStylist::Theme.new(theme_source)
74
+
75
+ html = theme.render(resume.data)
76
+ ~~~
77
+
78
+ ## Contributing
36
79
 
80
+ Bug reports and pull requests are welcome on GitHub at https://github.com/omninonsense/resume-stylist. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
37
81
 
38
82
  ## License
39
83
 
@@ -1,20 +1,522 @@
1
+ ---
2
+ theme:
3
+ author:
4
+ name: Nino Miletich
5
+ url: https://github.com/omninonsense
6
+ ---
1
7
  <!DOCTYPE html>
2
8
  <html>
3
9
  <head>
4
10
  <meta charset="utf-8">
5
- <title>{{ name }}'s Resume</title>
6
-
11
+ <title>{{ basics.name }}'s Resume</title>
12
+ <meta name="description" content="{{ basics.summary }}">
13
+ <meta name="generator" content="{{ _generator.name }}/{{ _generator.version }}">
14
+ <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,400i,800&subset=latin-ext" rel="stylesheet">
15
+ <link href="https://fonts.googleapis.com/css?family=Passion+One" rel="stylesheet">
7
16
  <!-- or type="text/sass" -->
8
17
  <style type="text/scss">
9
- .resume {
18
+ {% normalize_css inline %}
19
+
20
+ $max_width: 1200px;
21
+ #resume {
22
+ font-family: 'Open Sans', sans-serif;
23
+ color: #222;
24
+ margin: 3ex 1em;
25
+ @media screen and (min-width: $max_width){
26
+ width: 900px;
27
+ margin: 3ex auto;
28
+ }
29
+ }
30
+
31
+ small {
32
+ display: block;
33
+ }
34
+
35
+ #basics {
36
+ .picture {
37
+ $size: 128px;
38
+ display: block;
39
+ width: $size;
40
+ height: $size;
41
+ border-radius: 50%;
42
+ margin: auto;
43
+ box-shadow: 0 0 0 5px #aaa;
44
+ border: 4px solid #fff;
45
+ }
46
+
10
47
  h1 {
11
- weight: bold;
48
+ text-align: center;
49
+ font-weight: 300;
50
+
51
+ small {
52
+ color: #aaa;
53
+ font-weight: 100;
54
+ }
55
+ }
56
+
57
+ ul.contact {
58
+ text-align: center;
59
+ list-style-type: none;
60
+ padding: 0;
61
+ margin-top: 0;
62
+
63
+ li {
64
+ display: inline-block;
65
+ margin: 0 .5em;
66
+ }
67
+ }
68
+
69
+ ul.profiles {
70
+ list-style-type: none;
71
+ padding: 0;
72
+ text-align: center;
73
+
74
+ li {
75
+ display: inline-block;
76
+ margin: 0 1em;
77
+ }
78
+
79
+ .network-logo {
80
+ width: 22px;
81
+ }
82
+
83
+ a.profile {
84
+ text-decoration: none;
85
+ }
86
+
87
+ .network-logo {
88
+ vertical-align: middle;
89
+ margin-bottom: 3px;
90
+ }
91
+
92
+ .profile-facebook { color: #3C5A99; }
93
+ .profile-github { color: #000000; }
94
+ .profile-twitter { color: #66757f; }
95
+
96
+ .profile-username::before {
97
+ content: "@";
98
+ font-size: 125%;
99
+ position: relative;
100
+ bottom: -1px;
101
+ }
102
+
103
+ } /* .profiles */
104
+ } /* #basics */
105
+
106
+ /* WKHTML doesn't support flexbox properly */
107
+
108
+ .category {
109
+ /*display: flex;*/
110
+ display: table;
111
+ margin-top: 3ex;
112
+ page-break-before: auto;
113
+ page-break-inside: avoid;
114
+ page-break-after: auto;
115
+
116
+ & > div {
117
+ display: table-cell;
118
+ }
119
+
120
+ .title {
121
+ /*flex: 0 0 160px;*/
122
+ width: 160px;
123
+ text-align: right;
124
+ border-right: 1px solid #ccc;
125
+ padding: 1em;
126
+
127
+ h2 { font-weight: 100; }
128
+ }
129
+
130
+ ul.no-list {
131
+ padding: 0;
132
+ list-style-type: none;
133
+
134
+ & > li {
135
+ margin: 2em;
136
+ }
137
+
138
+ ul {
139
+ list-style-type: square;
140
+ li { margin-top: 0.75ex; }
141
+ }
142
+ }
143
+
144
+ .timespan { color: #999; }
145
+
146
+ .content {
147
+ h3 {
148
+ font-weight: 400;
149
+ margin-top: 0;
150
+ a { color: inherit; }
151
+ .company, .organisation { font-style: italic; }
152
+ }
153
+ }
154
+ } /* .category */
155
+
156
+ #skills, #interests {
157
+ h3 {
158
+ margin-bottom: 0;
159
+ margin-top: 1em;
160
+ }
161
+ }
162
+
163
+ #languages ul { margin: 2em; }
164
+
165
+ .courses, .keywords, .languages {
166
+ list-style-type: none;
167
+ padding: 0;
168
+
169
+ li {
170
+ margin: 0.75ex 0.25em;
171
+ display: inline-block;
172
+ background-color: #eee;
173
+ padding: .5ex .5em;
174
+ border-radius: 10%;
175
+ }
176
+ }
177
+
178
+
179
+ blockquote
180
+ {
181
+ border-left: 5px solid #ccc;
182
+ background-color: #eee;
183
+ margin: 0;
184
+ width: 100%;
185
+ padding: 1em;
186
+ position: relative;
187
+
188
+ p {
189
+ margin: 0;
190
+ margin-left: 1em;
191
+ }
192
+
193
+ cite {
194
+ display: block;
195
+ text-align: right;
196
+ &::before {
197
+ content: "— ";
198
+ }
199
+ }
200
+
201
+ &::before {
202
+ font-family: "Passion One", cursive;
203
+ display: block;
204
+ font-size: 500%;
205
+ color: #ccc;
206
+ height: 15px;
207
+ width: 60px;
208
+ float: left;
209
+ content: "“";
210
+ position: relative;
211
+ left: 20px;
212
+ top: -17px;
12
213
  }
13
214
  }
215
+
216
+ .flair {
217
+ color: #ccc;
218
+ text-align: right;
219
+
220
+ a {
221
+ color: #99b9d4;
222
+ }
223
+ }
224
+
14
225
  </style>
15
226
  </head>
16
- <body class="resume">
17
- <h1>{{ name }}'s Resume</h1>
18
- <!-- todo -->
227
+ <body id="resume">
228
+ <section id="basics">
229
+ {% unless basics.picture == "" %}
230
+ <div class="group">
231
+ <img class="picture" src="{{ basics.picture }}" alt="Picture of {{ basics.name }}" />
232
+ </div>
233
+ {% endunless %}
234
+ <div class="group">
235
+ <h1>{{ basics.name }}<small>{{ basics.label }}</small></h1>
236
+ <ul class="contact">
237
+ <li><a href="mailto:{{ basics.email }}" class="contact-info email">{{ basics.email }}</a></li>
238
+ <li><span class="contact-info phone">{{ basics.phone }}</span></li>
239
+ </ul>
240
+ <ul class="profiles">
241
+ {% unless basics.profiles == empty %}
242
+ {% for profile in basics.profiles %}
243
+ <li>
244
+ <a class="profile profile-{{ profile.network | downcase }}" href="{{ profile.url }}">
245
+
246
+ {% case profile.network %}
247
+ {% when "Facebook" %}
248
+ <!-- The below SVG was taken from Wikipedia. -->
249
+ <svg version="1.1" id="Facebook" class="network-logo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewbox="0 0 266.893 266.895" viewBox="0 0 266.893 266.895" xml:space="preserve">
250
+ <path id="Blue_1_" fill="#3C5A99" d="M248.082,262.307c7.854,0,14.223-6.369,14.223-14.225V18.812 c0-7.857-6.368-14.224-14.223-14.224H18.812c-7.857,0-14.224,6.367-14.224,14.224v229.27c0,7.855,6.366,14.225,14.224,14.225 H248.082z"/>
251
+ <path id="f" fill="#FFFFFF" d="M182.409,262.307v-99.803h33.499l5.016-38.895h-38.515V98.777c0-11.261,3.127-18.935,19.275-18.935 l20.596-0.009V45.045c-3.562-0.474-15.788-1.533-30.012-1.533c-29.695,0-50.025,18.126-50.025,51.413v28.684h-33.585v38.895h33.585 v99.803H182.409z"/>
252
+ </svg>
253
+ {% when "GitHub" %}
254
+ <!-- The below SVG was taken from Wikipedia. -->
255
+ <svg version="1.1" id="GitHub" class="network-logo" viewbox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" xml:space="preserve">
256
+ <path d="M512 0C229.25 0 0 229.25 0 512c0 226.25 146.688 418.125 350.156 485.812 25.594 4.688 34.938-11.125 34.938-24.625 0-12.188-0.469-52.562-0.719-95.312C242 908.812 211.906 817.5 211.906 817.5c-23.312-59.125-56.844-74.875-56.844-74.875-46.531-31.75 3.53-31.125 3.53-31.125 51.406 3.562 78.47 52.75 78.47 52.75 45.688 78.25 119.875 55.625 149 42.5 4.654-33 17.904-55.625 32.5-68.375C304.906 725.438 185.344 681.5 185.344 485.312c0-55.938 19.969-101.562 52.656-137.406-5.219-13-22.844-65.094 5.062-135.562 0 0 42.938-13.75 140.812 52.5 40.812-11.406 84.594-17.031 128.125-17.219 43.5 0.188 87.312 5.875 128.188 17.281 97.688-66.312 140.688-52.5 140.688-52.5 28 70.531 10.375 122.562 5.125 135.5 32.812 35.844 52.625 81.469 52.625 137.406 0 196.688-119.75 240-233.812 252.688 18.438 15.875 34.75 47 34.75 94.75 0 68.438-0.688 123.625-0.688 140.5 0 13.625 9.312 29.562 35.25 24.562C877.438 930 1024 738.125 1024 512 1024 229.25 794.75 0 512 0z" />
257
+ </svg>
258
+ {% when "Twitter" %}
259
+ <!-- The below SVG was taken from Wikipedia. -->
260
+ <svg id="Twitter" class="network-logo" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewbox="0 0 182.26 148.12" version="1.1" xmlns:cc="http://creativecommons.org/ns#" viewBox="0 0 182.66667 150.66667" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata id="metadata8"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata><defs id="defs6"><clipPath id="clipPath20" clipPathUnits="userSpaceOnUse"><path id="path18" d="m0 10.012h1366.9v1110.9h-1366.9z"/></clipPath></defs><g id="g10" transform="matrix(1.3333 0 0 -1.3333 0 150.67)"><g id="g12" transform="scale(.1)"><g id="g14"><g id="g16" clip-path="url(#clipPath20)"><path id="path22" d="m1366.9 989.39c-50.27-22.309-104.33-37.387-161.05-44.18 57.89 34.723 102.34 89.679 123.28 155.15-54.18-32.15-114.18-55.47-178.09-68.04-51.13 54.49-124.02 88.55-204.68 88.55-154.89 0-280.43-125.55-280.43-280.43 0-21.988 2.457-43.398 7.258-63.91-233.08 11.68-439.72 123.36-578.04 293.01-24.141-41.4-37.969-89.567-37.969-140.97 0-97.308 49.489-183.13 124.76-233.44-45.969 1.437-89.218 14.058-127.03 35.078-0.043-1.18-0.043-2.348-0.043-3.52 0-135.9 96.68-249.22 224.96-275-23.512-6.41-48.281-9.8-73.86-9.8-18.089 0-35.628 1.711-52.781 5 35.711-111.41 139.26-192.5 262-194.77-96.058-75.23-216.96-120.04-348.36-120.04-22.621 0-44.961 1.332-66.918 3.91 124.16-79.568 271.55-125.98 429.94-125.98 515.82 0 797.86 427.31 797.86 797.93 0 12.153-0.28 24.223-0.79 36.25 54.77 39.571 102.31 88.95 139.93 145.2" fill="#55ACEE"/></g></g></g></g></svg>
261
+ {% endcase %}
262
+
263
+ <span class="profile-username">{{ profile.username }}</span>
264
+ </a>
265
+ </li>
266
+
267
+
268
+ {% endfor %}
269
+ {% endunless %}
270
+ </ul>
271
+ </div>
272
+ </section>
273
+
274
+ {% unless work == empty %}
275
+ <section id="work" class="category">
276
+ <div class="title">
277
+ <h2>Work Experience</h2>
278
+ </div>
279
+ <div class="content">
280
+ <ul class="no-list">
281
+ {% for experience in work %}
282
+ <li>
283
+ <div class="meta">
284
+ <span class="timespan">
285
+ <time datetime="{{experience.startdate}}">{{ experience.startdate | date: "%Y" }}</time>
286
+ &ndash;
287
+ {% if experience.enddate == nil %}
288
+ current
289
+ {% else %}
290
+ <time datetime="{{ experience.enddate }}">{{ experience.enddate | date: "%Y" }}</time>
291
+ {% endif %}
292
+ </span>
293
+ <h3>{{ experience.position }} at <span class="company">{% if experience.website == "" %}{{ experience.company }}{% else %}<a href="{{experience.website}}">{{ experience.company }}</a>{% endif %}</span></h3>
294
+ </div>
295
+ <p>{{ experience.summary }}</p>
296
+ {% unless experience.highlights == empty %}
297
+ <ul class="highlights">
298
+ {% for highlight in experience.highlights %}
299
+ <li>{{ highlight }}</li>
300
+ {% endfor %}
301
+ </ul>
302
+ {%endunless%}
303
+ </li>
304
+ {% endfor %}
305
+ </ul>
306
+ </div>
307
+ </section>
308
+ {% endunless %}
309
+
310
+ {% unless volunteer == empty %}
311
+ <section id="volunteer" class="category">
312
+ <div class="title">
313
+ <h2>Volunteering</h2>
314
+ </div>
315
+ <div class="content">
316
+ <ul class="no-list">
317
+ {% for experience in volunteer %}
318
+ <li>
319
+ <div class="meta">
320
+ <span class="timespan">
321
+ <time datetime="{{experience.startdate}}">{{ experience.startdate | date: "%Y" }}</time>
322
+ &ndash;
323
+ {% if experience.enddate == nil %}
324
+ current
325
+ {% else %}
326
+ <time datetime="{{ experience.enddate }}">{{ experience.enddate | date: "%Y" }}</time>
327
+ {% endif %}
328
+ </span>
329
+ <h3>{{ experience.position }} at <span class="organisation">{% if experience.website == "" %}{{ experience.organization }}{% else %}<a href="{{experience.website}}">{{ experience.organization }}</a>{% endif %}</span></h3>
330
+ </div>
331
+ <p>{{ experience.summary }}</p>
332
+ {% unless experience.highlights == empty %}
333
+ <ul class="highlights">
334
+ {% for highlight in experience.highlights %}
335
+ <li>{{ highlight }}</li>
336
+ {% endfor %}
337
+ </ul>
338
+ {%endunless%}
339
+ </li>
340
+ {% endfor %}
341
+ </ul>
342
+ </div>
343
+ </section>
344
+ {% endunless %}
345
+
346
+ {% unless education == empty %}
347
+ <section id="education" class="category">
348
+ <div class="title">
349
+ <h2>Education</h2>
350
+ </div>
351
+ <div class="content">
352
+ <ul class="no-list">
353
+ {% for edu in education %}
354
+ <li>
355
+ <span class="timespan">
356
+ <time datetime="{{edu.startdate}}">{{ edu.startdate | date: "%Y" }}</time>
357
+ &ndash;
358
+ {% if edu.enddate == nil %}
359
+ current
360
+ {% else %}
361
+ <time datetime="{{ edu.enddate }}">{{ edu.enddate | date: "%Y" }}</time>
362
+ {% endif %}
363
+ </span>
364
+ <h3>{{ edu.area }}
365
+ <small>
366
+ {{ edu.institution }}
367
+ </small>
368
+ </h3>
369
+ {% unless edu.gpa == empty %} <p><b>GPA: </b> {{ edu.gpa }}</p> {% endunless %}
370
+ {% unless edu.courses == empty %}
371
+ <ul class="courses">
372
+ {% for course in edu.courses %}
373
+ <li>{{ course }}</li>
374
+ {% endfor %}
375
+ </ul>
376
+ {%endunless%}
377
+ </li>
378
+ {% endfor %}
379
+ </ul>
380
+ </div>
381
+ </section>
382
+ {% endunless %}
383
+
384
+ {% unless skills == empty %}
385
+ <section id="skills" class="category">
386
+ <div class="title">
387
+ <h2>Skills</h2>
388
+ </div>
389
+ <div class="content">
390
+ <ul class="no-list">
391
+ {% for skill in skills %}
392
+ <li>
393
+ <h3>{{ skill.name }}<small>{{ skill.level }}</small></h3>
394
+ {% unless skill.keywords == empty %}
395
+ <ul class="keywords">
396
+ {% for keyword in skill.keywords %}
397
+ <li>{{ keyword }}</li>
398
+ {% endfor %}
399
+ </ul>
400
+ {%endunless%}
401
+ </li>
402
+ {% endfor %}
403
+ </ul>
404
+ </div>
405
+ </section>
406
+ {% endunless %}
407
+
408
+ {% unless awards == empty %}
409
+ <section id="awards" class="category">
410
+ <div class="title">
411
+ <h2>Awards</h2>
412
+ </div>
413
+ <div class="content">
414
+ <ul class="no-list">
415
+ {% for award in awards %}
416
+ <li>
417
+ <div class="meta">
418
+ <span class="timespan"><time datetime="{{ award.date }}">{{ award.date | ordinal_date }}</time></span>
419
+ <h3>{{ award.title }}<small>Awarded by <span class="awarder">{{ award.awarder }}</span></small></h3>
420
+ </div>
421
+ <p class="summary">{{ award.summary }}</p>
422
+ </li>
423
+ {% endfor %}
424
+ </ul>
425
+ </div>
426
+ </section>
427
+ {% endunless %}
428
+
429
+ {% unless publications == empty %}
430
+ <section id="publications" class="category">
431
+ <div class="title">
432
+ <h2>Publications</h2>
433
+ </div>
434
+ <div class="content">
435
+ <ul class="no-list">
436
+ {% for pub in publications %}
437
+ <li>
438
+ <div class="meta">
439
+ <span class="timespan"><time datetime="{{ pub.releasedate }}">{{ pub.releasedate | ordinal_date }}</time></span>
440
+ <h3>
441
+ {% if pub.website == empty %}
442
+ {{ pub.name }}
443
+ {% else %}
444
+ <a href="{{ pub.website }}">{{ pub.name }}</a>
445
+ {% endif %}
446
+ <small>Published by <span class="publisher">{{ pub.publisher }}</span></small>
447
+ </h3>
448
+ </div>
449
+ <p class="summary">{{ pub.summary }}</p>
450
+ </li>
451
+ {% endfor %}
452
+ </ul>
453
+ </div>
454
+ </section>
455
+ {% endunless %}
456
+
457
+ {% unless interests == empty %}
458
+ <section id="interests" class="category">
459
+ <div class="title">
460
+ <h2>Interests</h2>
461
+ </div>
462
+ <div class="content">
463
+ <ul class="no-list">
464
+ {% for interest in interests %}
465
+ <li>
466
+ <h3>{{ interest.name }}</h3>
467
+ {% unless interest.keywords == empty %}
468
+ <ul class="keywords">
469
+ {% for keyword in interest.keywords %}
470
+ <li>{{ keyword }}</li>
471
+ {% endfor %}
472
+ </ul>
473
+ {%endunless%}
474
+ </li>
475
+ {% endfor %}
476
+ </ul>
477
+ </div>
478
+ </section>
479
+ {% endunless %}
480
+
481
+ {% unless references == empty %}
482
+ <section id="references" class="category">
483
+ <div class="title">
484
+ <h2>References</h2>
485
+ </div>
486
+ <div class="content">
487
+ <ul class="no-list">
488
+ {% for reference in references %}
489
+ <li>
490
+ <blockquote>
491
+ <div class="fancy-quote"></div>
492
+ <p>{{ reference.reference }}</p>
493
+ <cite>{{ reference.name }}</cite>
494
+ </blockquote>
495
+ </li>
496
+ {% endfor %}
497
+ </ul>
498
+ </div>
499
+ </section>
500
+ {% endunless %}
501
+
502
+ {% unless languages == empty %}
503
+ <section id="languages" class="category">
504
+ <div class="title">
505
+ <h2>Languages</h2>
506
+ </div>
507
+ <div class="content">
508
+ <ul class="languages">
509
+ {% for language in languages %}
510
+ <li>{{ language.name }}<small>{{ language.level }}</small></li>
511
+ {% endfor %}
512
+ </ul>
513
+ </div>
514
+ </section>
515
+ {% endunless %}
516
+
517
+ <div class="flair">
518
+ CV generated using <a href="https://github.com/omninonsense/resume-stylist">{{ _generator.name }} v{{ _generator.version }}</a>, resume theme by <a href="{{ frontmatter.theme.author.url }}">{{ frontmatter.theme.author.name }}</a>.
519
+ </div>
520
+
19
521
  </body>
20
522
  </html>
@@ -11,31 +11,41 @@ module ResumeStylist
11
11
  return date
12
12
  end
13
13
 
14
+ def self.downcase_keys_helper(h)
15
+ case h
16
+ when Hash
17
+ h = h.dup
18
+ h.keys.each do |key|
19
+ new_key = key.downcase
20
+ val = h.delete(key)
21
+ h[new_key] = downcase_keys_helper(val)
22
+ end
23
+ when Array
24
+ return h.map {|e| downcase_keys_helper(e) }
25
+ end
26
+
27
+ return h
28
+ end
29
+
14
30
  def self.handles?(resume_format)
15
31
  resume_format.to_s.downcase == "json"
16
32
  end
17
33
 
18
34
  def load!(input)
19
- data = Yajl::Parser.parse(input, symbolize_keys: false)
20
- # data = Yajl::Parser.parse(input, symbolize_keys: true)
21
-
22
- # Copy the data data from the "basics" field into top level
23
- # and remove the basics group from the hash
24
- data["basics"].each_pair {|k, v| data[k] = v }
25
- data.delete "basics"
26
-
27
- @data = data
35
+ data = Yajl::Parser.parse(input)
28
36
 
29
37
  # Fix dates, since they're just strings now
30
- [ @data["work"], @data["volunteer"], @data["education"] ].each do |set|
38
+ [ data["work"], data["volunteer"], data["education"] ].each do |set|
31
39
  set.each do |e|
32
40
  e["startDate"] = JSONResume.date_helper(e["startDate"])
33
41
  e["endDate"] = JSONResume.date_helper(e["endDate"])
34
42
  end
35
43
  end
36
44
 
37
- @data["publications"].each{|e| e["releaseDate"] = JSONResume.date_helper(e["releaseDate"]) }
38
- @data["awards"].each{|e| e["date"] = JSONResume.date_helper(e["date"]) }
45
+ data["publications"].each {|e| e["releaseDate"] = JSONResume.date_helper(e["releaseDate"]) }
46
+ data["awards"].each {|e| e["date"] = JSONResume.date_helper(e["date"]) }
47
+
48
+ @data = JSONResume.downcase_keys_helper(data)
39
49
  end
40
50
  end
41
51
  end
@@ -14,13 +14,15 @@ module ResumeStylist
14
14
 
15
15
  def initialize(input, resume_format)
16
16
  @data = {
17
- "name" => "",
18
- "label" => "",
19
- "picture" => "",
20
- "email" => "",
21
- "phone" => "",
22
- "website" => "",
23
- "summary" => "",
17
+ "basics" => {
18
+ "name" => nil,
19
+ "label" => nil,
20
+ "picture" => nil,
21
+ "email" => nil,
22
+ "phone" => nil,
23
+ "website" => nil,
24
+ "summary" => nil
25
+ },
24
26
 
25
27
  "location" => {
26
28
  "address" => nil,
@@ -30,7 +32,7 @@ module ResumeStylist
30
32
  "region" => nil
31
33
  },
32
34
 
33
- "profiles" => [], # { network_name, username, url }
35
+ "profiles" => [], # { network, username, url }
34
36
  "work" => [], # { organisation, position, website, summary, highlights => [], startDate, endDate }
35
37
  "volunteer" => [], # { organisation, position, website, summary, highlights => [], startDate, endDate }
36
38
  "education" => [], # { institution, area, studyType, grade, courses => [], startDate, endDate }
@@ -0,0 +1,31 @@
1
+ module ResumeStylist
2
+ class NormalizeCSS < Liquid::Tag
3
+
4
+ NormalizeCSS_URI = URI("https://necolas.github.io/normalize.css/latest/normalize.css")
5
+
6
+ def initialize(tag_name, tokens, liq)
7
+ if tokens.include? "inline"
8
+ req = Net::HTTP::Get.new(NormalizeCSS_URI.request_uri)
9
+
10
+ http = Net::HTTP.new(NormalizeCSS_URI.host, NormalizeCSS_URI.port)
11
+ http.use_ssl = (NormalizeCSS_URI.scheme == "https")
12
+
13
+ response = http.request(req)
14
+
15
+ if response.code == "200"
16
+ @content = response.body
17
+ else
18
+ @content = "/*! ERROR: Request for `#{NormalizeCSS_URI}` returned #{response.code}! Please report this bug at https://github.com/omninonsense/resume-stylist/issues/new */"
19
+ end
20
+ else
21
+ @content = %Q{<link rel="stylesheet" href="#{NormalizeCSS_URI}" media="screen">}
22
+ end
23
+ end
24
+
25
+ def render(context)
26
+ @content
27
+ end
28
+ end
29
+ end
30
+
31
+ Liquid::Template.register_tag('normalize_css', ResumeStylist::NormalizeCSS)
@@ -0,0 +1,46 @@
1
+ module ResumeStylist
2
+ module OrdinalDateFilter
3
+ # safe true
4
+
5
+ # # For languages with more diverse ordinals (if such even exist)
6
+ # Ordinals = %w{
7
+ # st nd rd th th th th th th th
8
+ # th th th th th th th th th th
9
+ # st nd rd th th th th th th th
10
+ # st
11
+ # }
12
+
13
+ # For English
14
+ Ordinals = Hash.new do |h, k|
15
+ if (11..13).cover? k
16
+ h[k] = "th"
17
+ elsif 1 == (k % 10)
18
+ h[k] = "st"
19
+ elsif 2 == (k % 10)
20
+ h[k] = "nd"
21
+ elsif 3 == (k % 10)
22
+ h[k] = "rd"
23
+ else
24
+ h[k] = "th"
25
+ end
26
+ end
27
+
28
+ def ordinal_date(input)
29
+ _day = input.mday
30
+ _ordinal = Ordinals[_day]
31
+ _month = Date::MONTHNAMES[input.month]
32
+ _year = input.year
33
+
34
+ day = %Q|<span class="day">#{_day}</span>|
35
+ ordinal = %Q|<span class="ordinal">#{_ordinal}</span>|
36
+ month = %Q|<span class="month">#{_month}</span>|
37
+ year = %Q|<span class="year">#{_year}</span>|
38
+
39
+ html = "#{day}#{ordinal} #{month} #{year}"
40
+
41
+ return html
42
+ end
43
+ end
44
+ end
45
+
46
+ Liquid::Template.register_filter(ResumeStylist::OrdinalDateFilter)
@@ -13,7 +13,17 @@ module ResumeStylist
13
13
  end
14
14
 
15
15
  def render(resume_data)
16
- @resume = @template.render(resume_data)
16
+ meta_data = {
17
+ "frontmatter" => @frontmatter,
18
+
19
+ "_generator" => {
20
+ "name" => "resume-stylist",
21
+ "version" => ResumeStylist::VERSION
22
+ }
23
+
24
+ }
25
+ ctx = resume_data.merge(meta_data)
26
+ @resume = @template.render(ctx)
17
27
  post_process!
18
28
 
19
29
  @resume
@@ -47,3 +57,6 @@ module ResumeStylist
47
57
  end
48
58
  end
49
59
  end
60
+
61
+ require "resume-stylist/theme/normalize_css"
62
+ require "resume-stylist/theme/ordinal_date"
@@ -1,3 +1,3 @@
1
1
  module ResumeStylist
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -3,6 +3,9 @@ require "oga"
3
3
  require "liquid"
4
4
  require "sass"
5
5
  require "date"
6
+ require 'net/http'
7
+ require 'uri'
8
+ require 'English'
6
9
 
7
10
  require "resume-stylist/version"
8
11
  require "resume-stylist/resume"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resume-stylist
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nino Miletich
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-04-16 00:00:00.000000000 Z
11
+ date: 2016-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -137,8 +137,6 @@ files:
137
137
  - LICENSE.txt
138
138
  - README.md
139
139
  - Rakefile
140
- - bin/console
141
- - bin/setup
142
140
  - data/resume-stylist/templates/resume.json
143
141
  - data/resume-stylist/templates/theme.html.liquid
144
142
  - exe/resume-stylist
@@ -147,6 +145,8 @@ files:
147
145
  - lib/resume-stylist/resume/json.rb
148
146
  - lib/resume-stylist/resume/xml.rb
149
147
  - lib/resume-stylist/theme.rb
148
+ - lib/resume-stylist/theme/normalize_css.rb
149
+ - lib/resume-stylist/theme/ordinal_date.rb
150
150
  - lib/resume-stylist/version.rb
151
151
  - resume-stylist.gemspec
152
152
  homepage: https://github.com/omninonsense/resume-stylist
data/bin/console DELETED
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "resume-stylist"
5
-
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start
data/bin/setup DELETED
@@ -1,7 +0,0 @@
1
- #!/bin/bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
-
5
- bundle install
6
-
7
- # Do any other automated setup that you need to do here