Skip to content

Commit

Permalink
Merge pull request #2 from sinaru/parse-sample-file
Browse files Browse the repository at this point in the history
Parse sample file
  • Loading branch information
sinaru authored Nov 24, 2023
2 parents 0e7f724 + 7de93c6 commit 9bd49a5
Show file tree
Hide file tree
Showing 17 changed files with 330 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
ruby:
- '3.1.2'
- '3.1.3'

steps:
- uses: actions/checkout@v2
Expand Down
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.1.3
5 changes: 4 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

source "https://rubygems.org"

ruby "3.1.2"
ruby "3.1.3"

# Specify your gem's dependencies in cv_builder.gemspec
gemspec
Expand All @@ -14,3 +14,6 @@ gem "minitest", "~> 5.0"
gem "rubocop", "~> 1.57", ">= 1.57.2"
gem "rubocop-rake", "~> 0.6.0"
gem "rubocop-minitest", "~> 0.33.0"

gem "prawn"
gem "pdfkit"
11 changes: 10 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ GEM
parser (3.2.2.4)
ast (~> 2.4.1)
racc
pdf-core (0.9.0)
pdfkit (0.8.7.3)
prawn (2.4.0)
pdf-core (~> 0.9.0)
ttfunk (~> 1.7)
racc (1.7.3)
rainbow (3.1.1)
rake (13.0.6)
Expand All @@ -37,21 +42,25 @@ GEM
rubocop-rake (0.6.0)
rubocop (~> 1.0)
ruby-progressbar (1.13.0)
ttfunk (1.7.0)
unicode-display_width (2.5.0)

PLATFORMS
arm64-darwin-22
x86_64-linux

DEPENDENCIES
cv_builder!
minitest (~> 5.0)
pdfkit
prawn
rake (~> 13.0)
rubocop (~> 1.57, >= 1.57.2)
rubocop-minitest (~> 0.33.0)
rubocop-rake (~> 0.6.0)

RUBY VERSION
ruby 3.1.2p20
ruby 3.1.3p185

BUNDLED WITH
2.3.7
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

*Build a beautiful CV from a text file.*

Uses `wkhtmltopdf` to generate the PDF.

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/cv_builder`. To experiment with that code, run `bin/console` for an interactive prompt.

TODO: Delete this and the text above, and describe your gem
Expand All @@ -24,7 +26,25 @@ Or install it yourself as:

## Usage

TODO: Write usage instructions here
First we need to have a yaml file with the CV data. The yaml file support following sections.

```yaml
version: 1
prorfile:
name: <string>
title: <string>
about: <string>
contact:
github: <username>
mobile: <mobile number digits>
email: <email address>
linkedin: <linkedin username>
location:
country: <country name or code>
city: <city name>
skills:
-
```
## Development
Expand Down
4 changes: 3 additions & 1 deletion bin/console
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ require "cv_builder"
# Pry.start

require "irb"
IRB.start(__FILE__)

builder = CvBuilder::Builder.new
builder.run
30 changes: 30 additions & 0 deletions examples/basic_cv.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
version: 1
profile:
name: Sinaru Gunawardena
title: Senior Software Engineer
about: "Versatile software developer with more than 6 years experience
in the field of web application development. Consistent learner, focus
on improving skills, collect and share new knowledge while contributing
the best to the organization. A team player with collaboration experience
in Scrum and Kanban teams."
contact:
github: sinaru
mobile: +353 838315349
website: sinaru.com
email: [email protected]
linkedin: sinaru
location:
country: IRE
city: Dublin
skills:
- area: Development
items:
- Ruby
- Ruby on Rails
- ES6 JavaScript
- Vue.js 2
- React
- area: Database
items:
- PostgreSQL
- MongoDB
9 changes: 9 additions & 0 deletions lib/arg_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

module CvBuilder
class ArgParser
def initialize(args)
puts args
end
end
end
16 changes: 15 additions & 1 deletion lib/cv_builder.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
# frozen_string_literal: true

require_relative "cv_builder/version"
require_relative "cv_builder/cv_data"
require_relative "cv_builder/cv_data_sections/skill"
require_relative "cv_builder/meta_file_parser"
require_relative "cv_builder/cv_generator"

module CvBuilder
class Error < StandardError; end
# Your code goes here...

class Builder
def run
example_yaml = File.join(File.dirname(__FILE__), "/../examples/basic_cv.yml")
parser = MetaFileParser.new(example_yaml)
cv_data = parser.parse!

output_location = File.join(File.dirname(__FILE__), "/../tmp/basic_cv.pdf")
CvGenerator.new(cv_data).generate(output_location)
end
end
end
30 changes: 30 additions & 0 deletions lib/cv_builder/cv_data.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

module CvBuilder
class CvData
def initialize(hash)
@hash = hash
end

def get_bindings
binding
end

def dig(*path)
if path.first.instance_of?(CvDataSection::Skill)
return path.first.dig(*path[1..])
end

# TODO: if experiences, return CvData::Experiences
path_s = path.map(&:to_s)
case path_s.first
when "skills"
@hash[path_s.first].each do |skill_hash|
yield CvDataSection::Skill.new(skill_hash)
end
else
@hash.dig(*path_s)
end
end
end
end
20 changes: 20 additions & 0 deletions lib/cv_builder/cv_data_sections/skill.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

module CvDataSection
class Skill
def initialize(hash)
@hash = hash
end

def get_bindings
binding
end

def dig(*path)
path_s = path.map(&:to_s)
data = @hash.dig(*path_s)
return [] if data.nil? && path.first == :items
data
end
end
end
37 changes: 37 additions & 0 deletions lib/cv_builder/cv_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

require "erb"
require "pdfkit"

module CvBuilder
class CvGenerator
def initialize(cv_data)
@cv_data = cv_data
@template = "basic"
end

## Generate CV PDF file using PDFKit. In future if an alternative approach is needed (e.g. using Prawn gem),
# consider refactoring code to follow strategy pattern.
def generate(output_location)
pdf = PDFKit.new(cv_html, page_size: "A4")
pdf.stylesheets = template_stylesheets
pdf.to_file(output_location)
end

private

def cv_html
rhtml = ERB.new(template_html)
rhtml.result(@cv_data.get_bindings)
end

def template_html
path = File.join(File.dirname(__FILE__), "/templates/#{@template}/index.html.erb")
File.read(path)
end

def template_stylesheets
[File.join(File.dirname(__FILE__), "/templates/#{@template}/styles/index.css")]
end
end
end
26 changes: 26 additions & 0 deletions lib/cv_builder/meta_file_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

require "yaml"

module CvBuilder
class MetaFileParser
attr_reader :location

def initialize(location)
@location = location
end

def validate!
# TODO: Check if file contains correct fields
end

def parse!
validate!
CvData.new(yaml)
end

def yaml
@yaml ||= YAML.load_file(location)
end
end
end
Binary file added lib/cv_builder/templates/basic/Cambria-Font.ttf
Binary file not shown.
61 changes: 61 additions & 0 deletions lib/cv_builder/templates/basic/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<html>
<head>
</head>
<body>
<div class="profile-section">
<div class="flex">
<div class="profile">
<div class="name"> <%= dig :profile, :name %></div>
<div class="title"> <%= dig :profile, :title %></div>
</div>
<div class="flex contact">
<div>
<div>
<span class="icon"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 496 512"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg></span>
<span><a href="https://github.com/<%= dig :contact, :github %>">github.com/<%= dig :contact, :github %></a></span>
</div>

<div>
<span class="icon"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 448 512"><path d="M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3zM135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5-38.5 38.5zm282.1 243h-66.4V312c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9V416h-66.4V202.2h63.7v29.2h.9c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9V416z"/></svg></span>
<span><a href="https://linkedin.com/in/<%= dig :contact, :linkedin %>">linkedin.com/in/<%= dig :contact, :linkedin %></a></span>
</div>

<div>
<span class="icon"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512"><path d="M352 256c0 22.2-1.2 43.6-3.3 64H163.3c-2.2-20.4-3.3-41.8-3.3-64s1.2-43.6 3.3-64H348.7c2.2 20.4 3.3 41.8 3.3 64zm28.8-64H503.9c5.3 20.5 8.1 41.9 8.1 64s-2.8 43.5-8.1 64H380.8c2.1-20.6 3.2-42 3.2-64s-1.1-43.4-3.2-64zm112.6-32H376.7c-10-63.9-29.8-117.4-55.3-151.6c78.3 20.7 142 77.5 171.9 151.6zm-149.1 0H167.7c6.1-36.4 15.5-68.6 27-94.7c10.5-23.6 22.2-40.7 33.5-51.5C239.4 3.2 248.7 0 256 0s16.6 3.2 27.8 13.8c11.3 10.8 23 27.9 33.5 51.5c11.6 26 20.9 58.2 27 94.7zm-209 0H18.6C48.6 85.9 112.2 29.1 190.6 8.4C165.1 42.6 145.3 96.1 135.3 160zM8.1 192H131.2c-2.1 20.6-3.2 42-3.2 64s1.1 43.4 3.2 64H8.1C2.8 299.5 0 278.1 0 256s2.8-43.5 8.1-64zM194.7 446.6c-11.6-26-20.9-58.2-27-94.6H344.3c-6.1 36.4-15.5 68.6-27 94.6c-10.5 23.6-22.2 40.7-33.5 51.5C272.6 508.8 263.3 512 256 512s-16.6-3.2-27.8-13.8c-11.3-10.8-23-27.9-33.5-51.5zM135.3 352c10 63.9 29.8 117.4 55.3 151.6C112.2 482.9 48.6 426.1 18.6 352H135.3zm358.1 0c-30 74.1-93.6 130.9-171.9 151.6c25.5-34.2 45.2-87.7 55.3-151.6H493.4z"/></svg></span>
<span><a href="https://<%= dig :contact, :website %>"><%= dig :contact, :website %></a></span>
</div>
</div>

<div>
<div>
<span class="icon"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 320 512"><path d="M0 64C0 28.7 28.7 0 64 0H256c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64zm64 96v64c0 17.7 14.3 32 32 32H224c17.7 0 32-14.3 32-32V160c0-17.7-14.3-32-32-32H96c-17.7 0-32 14.3-32 32zM80 352a24 24 0 1 0 0-48 24 24 0 1 0 0 48zm24 56a24 24 0 1 0 -48 0 24 24 0 1 0 48 0zm56-56a24 24 0 1 0 0-48 24 24 0 1 0 0 48zm24 56a24 24 0 1 0 -48 0 24 24 0 1 0 48 0zm56-56a24 24 0 1 0 0-48 24 24 0 1 0 0 48zm24 56a24 24 0 1 0 -48 0 24 24 0 1 0 48 0zM128 48c-8.8 0-16 7.2-16 16s7.2 16 16 16h64c8.8 0 16-7.2 16-16s-7.2-16-16-16H128z"/></svg></span>
<span><a href="tel:<%= dig :contact, :mobile %>"><%= dig :contact, :mobile %></a></span>
</div>
<div>
<span class="icon"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512"><path d="M48 64C21.5 64 0 85.5 0 112c0 15.1 7.1 29.3 19.2 38.4L236.8 313.6c11.4 8.5 27 8.5 38.4 0L492.8 150.4c12.1-9.1 19.2-23.3 19.2-38.4c0-26.5-21.5-48-48-48H48zM0 176V384c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V176L294.4 339.2c-22.8 17.1-54 17.1-76.8 0L0 176z"/></svg></span>
<span><%= dig :contact, :email %></span>
</div>
<div>
<span class="icon"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 384 512"><path d="M215.7 499.2C267 435 384 279.4 384 192C384 86 298 0 192 0S0 86 0 192c0 87.4 117 243 168.3 307.2c12.3 15.3 35.1 15.3 47.4 0zM192 128a64 64 0 1 1 0 128 64 64 0 1 1 0-128z"/></svg></span>
<span><%= dig :contact, :location, :city %>, <%= dig :contact, :location, :country %></span>
</div>
</div>

</div>
</div>
<div class="about">
<p><%= dig :profile, :about %></p>
</div>
</div>
<hr>

<div>
<% dig :skills do |skill| %>
<p><%= dig skill, :area %></p>
<% dig(skill, :items).each do |item| %>
* <%= item %>
<% end %>
<% end %>
</div>
</body>
</html>
Loading

0 comments on commit 9bd49a5

Please sign in to comment.