kamisaku 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/Gemfile.lock +1 -1
- data/README.md +28 -64
- data/examples/birthday_invitation/dino/invitation.pdf +0 -0
- data/examples/resume/chromatic/example.pdf +0 -0
- data/examples/resume/gradient/example.pdf +0 -0
- data/examples/resume/meridian/example.pdf +0 -0
- data/examples/resume/paper/example.pdf +0 -0
- data/examples/resume/prism/example.pdf +0 -0
- data/examples/resume/sleek/example.pdf +0 -0
- data/examples/resume/zenith/example.pdf +0 -0
- data/lib/kamisaku/arg_parser.rb +4 -0
- data/lib/kamisaku/cli_runner.rb +2 -2
- data/lib/kamisaku/content_validators/base_content_validator.rb +14 -0
- data/lib/kamisaku/content_validators/birthday_invitation_content_validator.rb +218 -0
- data/lib/kamisaku/{content_validator.rb → content_validators/resume_content_validator.rb} +28 -11
- data/lib/kamisaku/html_builder.rb +4 -3
- data/lib/kamisaku/pdf.rb +17 -12
- data/lib/kamisaku/template_helpers.rb +0 -2
- data/lib/kamisaku/version.rb +1 -1
- data/lib/kamisaku.rb +4 -2
- data/lib/schema/birthday_invitation/example.yml +45 -0
- data/lib/schema/birthday_invitation/schema.yml +40 -0
- data/lib/schema/resume/example.yml +274 -0
- data/lib/schema/resume/schema.yml +112 -0
- data/lib/templates/birthday_invitation/dino/template.html.erb +486 -0
- data/lib/templates/resume/chromatic/template.html.erb +275 -0
- data/lib/templates/resume/gradient/template.html.erb +793 -0
- data/lib/templates/resume/meridian/template.html.erb +535 -0
- data/lib/templates/resume/paper/template.html.erb +525 -0
- data/lib/templates/resume/prism/template.html.erb +818 -0
- data/lib/templates/{sleek → resume/sleek}/template.html.erb +1 -1
- data/lib/templates/resume/zenith/template.html.erb +546 -0
- data/scripts/rebuild_examples.rb +45 -0
- metadata +25 -9
- data/examples/paper/john_doe.pdf +0 -0
- data/examples/paper/john_doe.yml +0 -157
- data/examples/sleek/john_doe.pdf +0 -0
- data/examples/sleek/john_doe.yml +0 -157
- data/lib/templates/paper/template.html.erb +0 -420
- data/template.yml +0 -64
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0307528fa9400ae092638a768482af0a290b9f7e65f0ca8fcccfa501493eeb92'
|
|
4
|
+
data.tar.gz: bec81b49d0f9a635ad59dcfde08f718a5f8ab5471fa5d9c23eaf37dd2a3789e5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0738241ce36d4fb6e43439a35e39614ac935e922247cfb91e87dc3e236b6897f105feee6d827e3341f19286344935e7c37ea19e4705b6c7eb5187c6758049c6f'
|
|
7
|
+
data.tar.gz: 0525b51569873f87984eadd9c779b3ac0254250c541e3fbf6cb37e0f14a746ac3866e2024911f94727f6af40f58afd0758ef6c5faddb915534328e638fa0b27f
|
data/CHANGELOG.md
CHANGED
|
@@ -13,6 +13,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
13
13
|
|
|
14
14
|
### Removed
|
|
15
15
|
|
|
16
|
+
## [0.4.0] - 2025.06.09
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- Added new `birthday_invitation` schema type for Birthday Invitation PDF generations
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Updated the CLI tool to accept `category` to generate specific PDF
|
|
23
|
+
|
|
24
|
+
## [0.3.4] - 2025.06.06
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- Add new templates `chromatic`, `gradient`, `meridian`, `prism`, and `zenith`
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- Update paper template
|
|
31
|
+
|
|
32
|
+
|
|
16
33
|
## [0.3.3] - 2025.06.01
|
|
17
34
|
|
|
18
35
|
### Changed
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 📃 Kamisaku
|
|
2
2
|
|
|
3
|
-
*Build
|
|
3
|
+
*Build resume PDF from a yaml text file.*
|
|
4
4
|
|
|
5
5
|
🚀 See it in action at [https://kamisaku.sinaru.com/](https://kamisaku.sinaru.com/?utm_source=github).
|
|
6
6
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
See [examples](/examples) directory for sample generated PDF files based of [templates](/lib/templates).
|
|
12
12
|
|
|
13
13
|
## Templates
|
|
14
|
-
For a list of templates availble for
|
|
14
|
+
For a list of templates availble for PDF generation, check the [examples](/examples) directory where each directory name is a template name.
|
|
15
15
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
@@ -42,64 +42,9 @@ $ gem install kamisaku
|
|
|
42
42
|
|
|
43
43
|
## Usage
|
|
44
44
|
|
|
45
|
-
First we need to have a `yaml` file or a string with the
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
version: 1
|
|
49
|
-
profile:
|
|
50
|
-
name: # Your name
|
|
51
|
-
title: # Your current job title
|
|
52
|
-
about: # Some sleek details about your experience
|
|
53
|
-
|
|
54
|
-
contact:
|
|
55
|
-
github: # Github username
|
|
56
|
-
mobile: # Mobile number
|
|
57
|
-
email: # email address
|
|
58
|
-
linkedin: # Linkedin username
|
|
59
|
-
location:
|
|
60
|
-
country: # country name
|
|
61
|
-
city: # city name
|
|
62
|
-
|
|
63
|
-
skills:
|
|
64
|
-
- area: # specific skill area you are specialized in
|
|
65
|
-
items:
|
|
66
|
-
- # sub item such as a technology you have the skill in under the specialized area
|
|
67
|
-
|
|
68
|
-
experiences:
|
|
69
|
-
- title: # job title
|
|
70
|
-
organisation: # Name of the place you worked
|
|
71
|
-
location:
|
|
72
|
-
city: # city name
|
|
73
|
-
country: # country name
|
|
74
|
-
from:
|
|
75
|
-
month: # month number that you started
|
|
76
|
-
year: # year number that you started
|
|
77
|
-
# if following is not given, it is assumed you are still working
|
|
78
|
-
to:
|
|
79
|
-
month: # month number that you stopped
|
|
80
|
-
year: # year number that you stopped
|
|
81
|
-
skills:
|
|
82
|
-
- # a short name for a specialized skilled you gained
|
|
83
|
-
achievements:
|
|
84
|
-
- # Things you have achieved or did
|
|
85
|
-
|
|
86
|
-
education:
|
|
87
|
-
- institute: # name of the place you studied
|
|
88
|
-
location:
|
|
89
|
-
city: # city name
|
|
90
|
-
country: # country name
|
|
91
|
-
qualification: # name of the degree/diploma qualification
|
|
92
|
-
field: # name of the field such as Computer Science
|
|
93
|
-
from:
|
|
94
|
-
month: # month number that you started
|
|
95
|
-
year: # year number that you started
|
|
96
|
-
# if following is not given, it is assumed you are still studying
|
|
97
|
-
to:
|
|
98
|
-
month: # month number that you stopped
|
|
99
|
-
year: # year number that you stopped
|
|
100
|
-
achievements:
|
|
101
|
-
- # Things you have achieved or did
|
|
102
|
-
```
|
|
45
|
+
First we need to have a `yaml` file or a string with the correct schema data structure for the type of the PDF you are generating.
|
|
46
|
+
|
|
47
|
+
For the list of schemas, check `lib/schema` folder. E.g. For resume it is `lib/schema/resume/schema.yml`.
|
|
103
48
|
|
|
104
49
|
## Generating PDF
|
|
105
50
|
|
|
@@ -108,13 +53,14 @@ education:
|
|
|
108
53
|
Once you have the YAML text file, feed it into the `bin/console` and specify the output location.
|
|
109
54
|
|
|
110
55
|
```bash
|
|
111
|
-
bin/console -c examples/
|
|
56
|
+
bin/console -c examples/john_doe.yml -o examples/paper/john_doe.pdf -k resume -t paper
|
|
112
57
|
```
|
|
113
58
|
|
|
114
59
|
#### Bash options
|
|
115
60
|
|
|
116
61
|
- `-c` the YAML file
|
|
117
62
|
- `-o` output location for the PDF file including the name
|
|
63
|
+
- `-k` the type of document you want to generate
|
|
118
64
|
- `-t` template to use
|
|
119
65
|
|
|
120
66
|
### Using `PDF` class
|
|
@@ -125,9 +71,9 @@ yaml_str = "..."
|
|
|
125
71
|
# Create a hash
|
|
126
72
|
content_hash = Kamisaku::Helpers.yaml_str_to_content_hash(yaml_str)
|
|
127
73
|
# Validate the hash is correct. If there is any issue, it will raise a ` Kamisaku::Error ` exception.
|
|
128
|
-
Kamisaku::
|
|
74
|
+
Kamisaku::BaseContentValidator.new(content_hash:).validate!
|
|
129
75
|
# create a pdf instance
|
|
130
|
-
pdf = Kamisaku::PDF.new(content_hash:, template: "paper")
|
|
76
|
+
pdf = Kamisaku::PDF.new(content_hash:, category: "resume", template: "paper")
|
|
131
77
|
# create the PDF at given path
|
|
132
78
|
pdf.write_to('/path/to/generated_file.pdf')
|
|
133
79
|
```
|
|
@@ -136,7 +82,25 @@ pdf.write_to('/path/to/generated_file.pdf')
|
|
|
136
82
|
|
|
137
83
|
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.
|
|
138
84
|
|
|
139
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
85
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
86
|
+
|
|
87
|
+
### Create or Update a template
|
|
88
|
+
|
|
89
|
+
To a new template, create a folder with template name at `lib/templates`. Additionally add the template name to `Kamisaku::TemplateHelpers::TEMPLATES` list.
|
|
90
|
+
|
|
91
|
+
Inside this folder, create `template.html.erb`.
|
|
92
|
+
|
|
93
|
+
The template will be exposed to a Ruby hash variable called `data`. This data represent the data in the YAML file as a hash.
|
|
94
|
+
So you can use the Ruby hash methods to access and render the values.
|
|
95
|
+
|
|
96
|
+
In addition, the template helper methods defined at `lib/kamisaku/template_helpers.rb` will also be available to use directly.
|
|
97
|
+
|
|
98
|
+
To test and build the template, you can run `scripts/rebuild_examples.rb -t <name of new template>`. This will create the PDF using the `lib/schema/example.yml`
|
|
99
|
+
and place it in `examples/` folder.
|
|
100
|
+
|
|
101
|
+
### Releasing a new gem version
|
|
102
|
+
|
|
103
|
+
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).
|
|
140
104
|
|
|
141
105
|
## Contributing
|
|
142
106
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/kamisaku/arg_parser.rb
CHANGED
|
@@ -20,6 +20,10 @@ module Kamisaku
|
|
|
20
20
|
options[:pdf_file] = pdf_file
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
parser.on("-k", "--category CATEGORY", "Provide the TEMPLATE name") do |category|
|
|
24
|
+
options[:category] = category
|
|
25
|
+
end
|
|
26
|
+
|
|
23
27
|
parser.on("-t", "--template TEMPLATE", "Provide the TEMPLATE name") do |template|
|
|
24
28
|
options[:template] = template
|
|
25
29
|
end
|
data/lib/kamisaku/cli_runner.rb
CHANGED
|
@@ -6,9 +6,9 @@ module Kamisaku
|
|
|
6
6
|
raise Kamisaku::Error.new "YAML file does not exist" unless File.exist?(yaml_file)
|
|
7
7
|
|
|
8
8
|
yaml_string = File.read(yaml_file)
|
|
9
|
-
pdf = PDF.new(content_hash: Helpers.yaml_str_to_content_hash(yaml_string), template: options[:template])
|
|
9
|
+
pdf = PDF.new(content_hash: Helpers.yaml_str_to_content_hash(yaml_string), category: options[:category], template: options[:template])
|
|
10
10
|
pdf.write_to(options[:pdf_file])
|
|
11
|
-
pdf.
|
|
11
|
+
pdf.write_to_html_file(options[:html_output]) if options[:html_output]
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
14
|
end
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
module Kamisaku
|
|
2
|
+
class BirthdayInvitationContentValidator < BaseContentValidator
|
|
3
|
+
TEMPLATES = %(
|
|
4
|
+
dino
|
|
5
|
+
).freeze
|
|
6
|
+
|
|
7
|
+
def validate!
|
|
8
|
+
validate_party_details
|
|
9
|
+
validate_venue
|
|
10
|
+
validate_contact_info
|
|
11
|
+
validate_rsvp
|
|
12
|
+
validate_special_instructions if data[:special_instructions]
|
|
13
|
+
validate_activities if data[:activities]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def validate_party_details
|
|
19
|
+
raise Error, "Missing party_details" unless data[:party_details]
|
|
20
|
+
raise Error, "party_details must be a hash" unless data[:party_details].is_a?(Hash)
|
|
21
|
+
|
|
22
|
+
required_fields = %i[person_name age date start_time]
|
|
23
|
+
allowed_fields = %i[person_name age date start_time end_time theme]
|
|
24
|
+
party_fields = data[:party_details].keys
|
|
25
|
+
|
|
26
|
+
unless party_fields.all? { |field| allowed_fields.include?(field) }
|
|
27
|
+
raise Error, "party_details contains invalid fields"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
required_fields.each do |field|
|
|
31
|
+
unless party_fields.include?(field)
|
|
32
|
+
raise Error, "party_details missing required field '#{field}'"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
validate_string_field(data[:party_details][:person_name], "party_details", "person_name")
|
|
37
|
+
validate_integer_field(data[:party_details][:age], "party_details", "age")
|
|
38
|
+
validate_date(data[:party_details][:date], "party_details")
|
|
39
|
+
validate_string_field(data[:party_details][:start_time], "party_details", "start_time")
|
|
40
|
+
|
|
41
|
+
if data[:party_details][:end_time]
|
|
42
|
+
validate_string_field(data[:party_details][:end_time], "party_details", "end_time")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if data[:party_details][:theme]
|
|
46
|
+
validate_string_field(data[:party_details][:theme], "party_details", "theme")
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def validate_venue
|
|
51
|
+
raise Error, "Missing venue" unless data[:venue]
|
|
52
|
+
raise Error, "venue must be a hash" unless data[:venue].is_a?(Hash)
|
|
53
|
+
|
|
54
|
+
required_fields = %i[address]
|
|
55
|
+
allowed_fields = %i[name address additional_instructions]
|
|
56
|
+
venue_fields = data[:venue].keys
|
|
57
|
+
|
|
58
|
+
unless venue_fields.all? { |field| allowed_fields.include?(field) }
|
|
59
|
+
raise Error, "venue contains invalid fields"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
required_fields.each do |field|
|
|
63
|
+
unless venue_fields.include?(field)
|
|
64
|
+
raise Error, "venue missing required field '#{field}'"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
validate_string_field(data[:venue][:address], "venue", "address")
|
|
69
|
+
|
|
70
|
+
if data[:venue][:name]
|
|
71
|
+
validate_string_field(data[:venue][:name], "venue", "name")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
if data[:venue][:additional_instructions]
|
|
75
|
+
validate_string_field(data[:venue][:additional_instructions], "venue", "additional_instructions")
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def validate_contact_info
|
|
80
|
+
raise Error, "Missing contact_info" unless data[:contact_info]
|
|
81
|
+
raise Error, "contact_info must be a hash" unless data[:contact_info].is_a?(Hash)
|
|
82
|
+
|
|
83
|
+
required_fields = %i[host_name phone]
|
|
84
|
+
allowed_fields = %i[host_name phone email]
|
|
85
|
+
contact_fields = data[:contact_info].keys
|
|
86
|
+
|
|
87
|
+
unless contact_fields.all? { |field| allowed_fields.include?(field) }
|
|
88
|
+
raise Error, "contact_info contains invalid fields"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
required_fields.each do |field|
|
|
92
|
+
unless contact_fields.include?(field)
|
|
93
|
+
raise Error, "contact_info missing required field '#{field}'"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
validate_string_field(data[:contact_info][:host_name], "contact_info", "host_name")
|
|
98
|
+
validate_string_field(data[:contact_info][:phone], "contact_info", "phone")
|
|
99
|
+
|
|
100
|
+
if data[:contact_info][:email]
|
|
101
|
+
validate_string_field(data[:contact_info][:email], "contact_info", "email")
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def validate_rsvp
|
|
106
|
+
raise Error, "Missing rsvp" unless data[:rsvp]
|
|
107
|
+
raise Error, "rsvp must be a hash" unless data[:rsvp].is_a?(Hash)
|
|
108
|
+
|
|
109
|
+
required_fields = %i[deadline method contact]
|
|
110
|
+
rsvp_fields = data[:rsvp].keys
|
|
111
|
+
|
|
112
|
+
unless rsvp_fields.all? { |field| required_fields.include?(field) }
|
|
113
|
+
raise Error, "rsvp contains invalid fields"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
required_fields.each do |field|
|
|
117
|
+
unless rsvp_fields.include?(field)
|
|
118
|
+
raise Error, "rsvp missing required field '#{field}'"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
validate_date(data[:rsvp][:deadline], "rsvp")
|
|
123
|
+
validate_string_field(data[:rsvp][:method], "rsvp", "method")
|
|
124
|
+
validate_string_field(data[:rsvp][:contact], "rsvp", "contact")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def validate_special_instructions
|
|
128
|
+
raise Error, "special_instructions must be a hash" unless data[:special_instructions].is_a?(Hash)
|
|
129
|
+
|
|
130
|
+
allowed_fields = %i[what_to_bring dress_code food_allergies_note gift_preferences parking_info weather_backup]
|
|
131
|
+
special_fields = data[:special_instructions].keys
|
|
132
|
+
|
|
133
|
+
unless special_fields.all? { |field| allowed_fields.include?(field) }
|
|
134
|
+
raise Error, "special_instructions contains invalid fields"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
data[:special_instructions].each do |field, value|
|
|
138
|
+
validate_string_field(value, "special_instructions", field.to_s)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def validate_activities
|
|
143
|
+
raise Error, "activities must be a hash" unless data[:activities].is_a?(Hash)
|
|
144
|
+
|
|
145
|
+
allowed_fields = %i[main_activities entertainment]
|
|
146
|
+
activities_fields = data[:activities].keys
|
|
147
|
+
|
|
148
|
+
unless activities_fields.all? { |field| allowed_fields.include?(field) }
|
|
149
|
+
raise Error, "activities contains invalid fields"
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
if data[:activities][:main_activities]
|
|
153
|
+
validate_array_of_strings(data[:activities][:main_activities], "activities", "main_activities")
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
if data[:activities][:entertainment]
|
|
157
|
+
validate_string_field(data[:activities][:entertainment], "activities", "entertainment")
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def validate_date(date, section)
|
|
162
|
+
raise Error, "#{section}: date must be a hash" unless date.is_a?(Hash)
|
|
163
|
+
|
|
164
|
+
required_fields = %i[day month year]
|
|
165
|
+
date_fields = date.keys
|
|
166
|
+
|
|
167
|
+
unless date_fields.all? { |field| required_fields.include?(field) }
|
|
168
|
+
raise Error, "#{section}: date contains invalid fields"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
required_fields.each do |field|
|
|
172
|
+
unless date_fields.include?(field)
|
|
173
|
+
raise Error, "#{section}: date missing required field '#{field}'"
|
|
174
|
+
end
|
|
175
|
+
validate_integer_field(date[field], section, "date.#{field}")
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
validate_day_range(date[:day], section)
|
|
179
|
+
validate_month_range(date[:month], section)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def validate_string_field(value, section, field)
|
|
183
|
+
unless value.is_a?(String)
|
|
184
|
+
raise Error, "#{section}: field '#{field}' must be a string"
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def validate_integer_field(value, section, field)
|
|
189
|
+
unless value.is_a?(Integer)
|
|
190
|
+
raise Error, "#{section}: field '#{field}' must be an integer"
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def validate_array_of_strings(value, section, field)
|
|
195
|
+
unless value.is_a?(Array)
|
|
196
|
+
raise Error, "#{section}: field '#{field}' must be an array"
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
value.each do |item|
|
|
200
|
+
unless item.is_a?(String)
|
|
201
|
+
raise Error, "#{section}: each item in '#{field}' must be a string"
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def validate_day_range(day, section)
|
|
207
|
+
unless day.between?(1, 31)
|
|
208
|
+
raise Error, "#{section}: day must be between 1-31"
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def validate_month_range(month, section)
|
|
213
|
+
unless month.between?(1, 12)
|
|
214
|
+
raise Error, "#{section}: month must be between 1-12"
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
module Kamisaku
|
|
2
|
-
class
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
class ResumeContentValidator < Kamisaku::BaseContentValidator
|
|
3
|
+
TEMPLATES = %w[
|
|
4
|
+
paper
|
|
5
|
+
sleek
|
|
6
|
+
zenith
|
|
7
|
+
meridian
|
|
8
|
+
prism
|
|
9
|
+
gradient
|
|
10
|
+
chromatic
|
|
11
|
+
].freeze
|
|
9
12
|
|
|
10
13
|
def validate!
|
|
11
14
|
validate_version
|
|
@@ -25,14 +28,15 @@ module Kamisaku
|
|
|
25
28
|
raise Error, "Missing profile" unless data[:profile]
|
|
26
29
|
raise Error, "Profile must be a hash" unless data[:profile].is_a?(Hash)
|
|
27
30
|
|
|
28
|
-
allowed_fields = %i[name title about]
|
|
31
|
+
allowed_fields = %i[name title about photo_url]
|
|
32
|
+
required_fields = %i[name title about]
|
|
29
33
|
profile_fields = data[:profile].keys
|
|
30
34
|
|
|
31
35
|
unless profile_fields.all? { |field| allowed_fields.include?(field) }
|
|
32
36
|
raise Error, "Profile contains invalid fields"
|
|
33
37
|
end
|
|
34
38
|
|
|
35
|
-
unless
|
|
39
|
+
unless required_fields.all? { |field| profile_fields.include?(field) }
|
|
36
40
|
raise Error, "Profile must contain exactly the fields: #{allowed_fields.join(", ")}"
|
|
37
41
|
end
|
|
38
42
|
|
|
@@ -41,6 +45,19 @@ module Kamisaku
|
|
|
41
45
|
raise Error, "Profile field '#{field}' must be a string"
|
|
42
46
|
end
|
|
43
47
|
end
|
|
48
|
+
|
|
49
|
+
if data[:profile][:photo_url]
|
|
50
|
+
validate_photo_url(data[:profile][:photo_url])
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def validate_photo_url(photo_url)
|
|
55
|
+
raise Error, "Profile field '#{field}' must be a string" unless photo_url.is_a?(String)
|
|
56
|
+
|
|
57
|
+
valid_url_regex = /\Ahttps?:\/\/.+\.(jpg|jpeg)\z/i
|
|
58
|
+
unless valid_url_regex.match?(photo_url)
|
|
59
|
+
raise Error, "Invalid photo_url. It must be an HTTP/HTTPS URL ending with .jpg or .jpeg"
|
|
60
|
+
end
|
|
44
61
|
end
|
|
45
62
|
|
|
46
63
|
def validate_contact
|
|
@@ -87,7 +104,7 @@ module Kamisaku
|
|
|
87
104
|
data[:skills].each do |skill|
|
|
88
105
|
raise Error, "Each skill must be a hash" unless skill.is_a?(Hash)
|
|
89
106
|
|
|
90
|
-
allowed_fields = %i[
|
|
107
|
+
allowed_fields = %i[name items]
|
|
91
108
|
skill_fields = skill.keys
|
|
92
109
|
|
|
93
110
|
unless skill_fields.all? { |field| allowed_fields.include?(field) }
|
|
@@ -98,7 +115,7 @@ module Kamisaku
|
|
|
98
115
|
raise Error, "Skills section: Skill missing required field '#{field}'" unless skill_fields.include?(field)
|
|
99
116
|
end
|
|
100
117
|
|
|
101
|
-
raise Error, "Skills section: Skill field '
|
|
118
|
+
raise Error, "Skills section: Skill field 'name' must be a string" unless skill[:name].is_a?(String)
|
|
102
119
|
raise Error, "Skills section: Skill field 'items' must be an array" unless skill[:items].is_a?(Array)
|
|
103
120
|
|
|
104
121
|
skill[:items].each do |item|
|
|
@@ -2,13 +2,14 @@ require "erb"
|
|
|
2
2
|
|
|
3
3
|
module Kamisaku
|
|
4
4
|
class HtmlBuilder
|
|
5
|
-
attr_reader :content_hash, :template
|
|
5
|
+
attr_reader :content_hash, :category, :template
|
|
6
6
|
alias_method :data, :content_hash
|
|
7
7
|
|
|
8
8
|
include TemplateHelpers
|
|
9
9
|
|
|
10
|
-
def initialize(content_hash, template)
|
|
10
|
+
def initialize(content_hash, category, template)
|
|
11
11
|
@content_hash = content_hash
|
|
12
|
+
@category = category
|
|
12
13
|
@template = template
|
|
13
14
|
end
|
|
14
15
|
|
|
@@ -20,7 +21,7 @@ module Kamisaku
|
|
|
20
21
|
private
|
|
21
22
|
|
|
22
23
|
def template_html
|
|
23
|
-
path = File.join(File.dirname(__FILE__), "/../templates/#{template}/template.html.erb")
|
|
24
|
+
path = File.join(File.dirname(__FILE__), "/../templates/#{category}/#{template}/template.html.erb")
|
|
24
25
|
File.read(path)
|
|
25
26
|
end
|
|
26
27
|
end
|
data/lib/kamisaku/pdf.rb
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
|
+
require "tempfile"
|
|
2
|
+
|
|
1
3
|
module Kamisaku
|
|
2
4
|
class PDF
|
|
3
|
-
attr_reader :content_hash, :template
|
|
5
|
+
attr_reader :content_hash, :category, :template
|
|
6
|
+
|
|
7
|
+
CONTENT_VALIDATOR_MAP = {
|
|
8
|
+
resume: ResumeContentValidator,
|
|
9
|
+
birthday_invitation: BirthdayInvitationContentValidator
|
|
10
|
+
}
|
|
4
11
|
|
|
5
|
-
def initialize(content_hash:, template:
|
|
12
|
+
def initialize(content_hash:, category:, template:)
|
|
6
13
|
@content_hash = content_hash
|
|
7
|
-
@
|
|
8
|
-
|
|
14
|
+
@category = category
|
|
15
|
+
@template = template
|
|
9
16
|
raise Error, "Invalid template name '#{template}'" unless template.is_a?(String)
|
|
10
|
-
|
|
17
|
+
validator_klass = CONTENT_VALIDATOR_MAP[category.to_sym]
|
|
18
|
+
raise Error, "Invalid template name '#{category}'" unless validator_klass
|
|
19
|
+
validator_klass.new(content_hash:).validate!
|
|
20
|
+
raise Error, "Invalid template name '#{template}'" unless validator_klass::TEMPLATES.include?(template)
|
|
11
21
|
end
|
|
12
22
|
|
|
13
23
|
def write_to(pdf_location)
|
|
@@ -17,7 +27,7 @@ module Kamisaku
|
|
|
17
27
|
end
|
|
18
28
|
end
|
|
19
29
|
|
|
20
|
-
def
|
|
30
|
+
def write_to_html_file(html_location)
|
|
21
31
|
html_file { |file_path| FileUtils.cp(file_path, html_location) }
|
|
22
32
|
end
|
|
23
33
|
|
|
@@ -42,13 +52,8 @@ module Kamisaku
|
|
|
42
52
|
def html
|
|
43
53
|
return @html if defined? @html
|
|
44
54
|
|
|
45
|
-
builder = HtmlBuilder.new(content_hash, template)
|
|
55
|
+
builder = HtmlBuilder.new(content_hash, category, template)
|
|
46
56
|
@html = builder.html
|
|
47
57
|
end
|
|
48
|
-
|
|
49
|
-
def template_html
|
|
50
|
-
path = File.join(File.dirname(__FILE__), "/../templates/#{template}/template.html.erb")
|
|
51
|
-
File.read(path)
|
|
52
|
-
end
|
|
53
58
|
end
|
|
54
59
|
end
|
data/lib/kamisaku/version.rb
CHANGED
data/lib/kamisaku.rb
CHANGED
|
@@ -2,9 +2,11 @@ require_relative "kamisaku/version"
|
|
|
2
2
|
require_relative "kamisaku/errors"
|
|
3
3
|
require_relative "kamisaku/helpers"
|
|
4
4
|
require_relative "kamisaku/arg_parser"
|
|
5
|
-
require_relative "kamisaku/pdf"
|
|
6
5
|
require_relative "kamisaku/chrome_runner"
|
|
7
6
|
require_relative "kamisaku/cli_runner"
|
|
8
7
|
require_relative "kamisaku/template_helpers"
|
|
9
8
|
require_relative "kamisaku/html_builder"
|
|
10
|
-
require_relative "kamisaku/
|
|
9
|
+
require_relative "kamisaku/content_validators/base_content_validator"
|
|
10
|
+
require_relative "kamisaku/content_validators/resume_content_validator"
|
|
11
|
+
require_relative "kamisaku/content_validators/birthday_invitation_content_validator"
|
|
12
|
+
require_relative "kamisaku/pdf"
|