curriculum-generator 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.ruby-version +1 -0
  4. data/CGen.gemspec +35 -0
  5. data/Gemfile +5 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +62 -0
  8. data/Rakefile +3 -0
  9. data/bin/cgen.rb +86 -0
  10. data/lib/CGen.rb +27 -0
  11. data/lib/cgen/compiler.rb +96 -0
  12. data/lib/cgen/curriculum.rb +57 -0
  13. data/lib/cgen/data_loader/yaml_data_loader.rb +31 -0
  14. data/lib/cgen/data_loader.rb +6 -0
  15. data/lib/cgen/generator/basic_generator.rb +37 -0
  16. data/lib/cgen/generator/cv_column.rb +32 -0
  17. data/lib/cgen/generator/cv_double_item.rb +42 -0
  18. data/lib/cgen/generator/cv_entry.rb +33 -0
  19. data/lib/cgen/generator/cv_item.rb +29 -0
  20. data/lib/cgen/generator/cv_item_with_comment.rb +32 -0
  21. data/lib/cgen/generator/cv_list_double_item.rb +31 -0
  22. data/lib/cgen/generator/cv_list_item.rb +23 -0
  23. data/lib/cgen/generator/list.rb +25 -0
  24. data/lib/cgen/generator/macro_substitution.rb +11 -0
  25. data/lib/cgen/generator/specific/education.rb +95 -0
  26. data/lib/cgen/generator/specific/self_assessment.rb +61 -0
  27. data/lib/cgen/generator/specific/work_experience.rb +81 -0
  28. data/lib/cgen/generator.rb +10 -0
  29. data/lib/cgen/util/latex_to_pdf.rb +61 -0
  30. data/lib/cgen/util/logging.rb +27 -0
  31. data/lib/cgen/util/shell_command.rb +48 -0
  32. data/lib/cgen/util.rb +6 -0
  33. data/lib/cgen/version.rb +6 -0
  34. data/static/bundled_templates/moderncv/.gitkeep +0 -0
  35. data/static/bundled_templates/moderncv/deps.yml +15 -0
  36. data/static/bundled_templates/moderncv/main.tex +208 -0
  37. data/static/bundled_templates/moderncv/publications.bib +40 -0
  38. data/static/bundled_templates/moderncv/resources/AuthorPhoto.png +0 -0
  39. metadata +194 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9d188f9f8afdc19e86cada3e6707bf0cbd0d7f89
4
+ data.tar.gz: e82273c0742e3da2644db4c48508499c2de0d6a2
5
+ SHA512:
6
+ metadata.gz: 965ded86275d134b62ab68dda1b54333d607ac28ac7cd7acf91f435e5c750b6381cd3a58da277894ae725f779273d5b4f27c200ce0ab4c493d7666d9396d8f1a
7
+ data.tar.gz: 4059c63ef2bc1e468bf0f9b633396a0903e0019a1b0d0c925690d904ed197f07855988049669bdf21ae8bba9f8ecc003be02a11e6fb535fea82fc3d0fba954aa
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ /.idea
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.1
data/CGen.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'cgen/version'
6
+
7
+
8
+ Gem::Specification.new do |spec|
9
+
10
+ spec.name = 'curriculum-generator'
11
+ spec.version = CGen::VERSION
12
+ spec.authors = ['Alessandro Molari']
13
+ spec.email = ['molari.alessandro@gmail.com']
14
+ spec.summary = %q{Curriculum Vitae generator}
15
+ spec.homepage = 'http://github.com/alem0lars/cgen'
16
+ spec.license = 'Apache 2'
17
+
18
+ spec.files = `git ls-files -z`.split("\x0")
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec)/})
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.6'
24
+ spec.add_development_dependency 'rake'
25
+
26
+ spec.add_runtime_dependency 'hash-deep-merge'
27
+ spec.add_runtime_dependency 'monadic'
28
+
29
+ spec.add_runtime_dependency 'awesome_print'
30
+ spec.add_runtime_dependency 'colorize'
31
+ spec.add_runtime_dependency 'highline'
32
+
33
+ spec.add_runtime_dependency 'erubis'
34
+
35
+ end
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+
4
+ # Specify your gem's dependencies in CGen.gemspec
5
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Alessandro Molari
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # CGen
2
+
3
+ CGen is the next **localized Curriculum Vitae** editor, both for hackers and end-users.
4
+
5
+ *Please note that this is still in early development stages. If you are an end-user you may want to check this project out in a couple of weeks.*
6
+
7
+ ## Installation
8
+
9
+ Via homebrew
10
+
11
+ $ brew install cgen
12
+
13
+ Via rubygems
14
+
15
+ $ gem install curriculum-generator
16
+
17
+
18
+ ## Usage
19
+
20
+ CGen provides two editing modes:
21
+ 1. **Hacking Mode**
22
+ 2. **Normal Mode**
23
+
24
+ ### Hacking Mode
25
+
26
+ This is where CGen shrines, by allowing users to **just provide the data** and let the system do the rest. This system comes with *extensibility* in mind, by providing different levels of customization:
27
+ 1. **Data-level**: The first level is the most commonly used. The system automatically pulls all of your defined data. This means that there isn't anything wired-in, no names, no values, anything. Data is just data and you can use it as you want !
28
+ 2. **Templates-level**: For basic customizations you can write your own (LaTeX) **templates**, or modify an existing one.
29
+ 3. **Generators-level**: For more advanced customizations, you can write your own (Ruby) **generators**.
30
+
31
+ ### Normal Mode
32
+
33
+ This is primarly for non-hackers. In this mode the curriculum is edited by using a GUI.
34
+
35
+ *This mode is still in development: not released yet*.
36
+
37
+ ## Contributing
38
+
39
+ ### Project management
40
+
41
+ #### Scrum & Trello
42
+
43
+ The project follows the **Scrum Methodology**, using Trello as the main tool.
44
+
45
+ The board is available under: [CGen Trello Board](https://trello.com/b/8er5R7dK/cgen)
46
+
47
+ #### Report bugs / Request enhancements
48
+
49
+ Create a new card in the *Product Backlog* list:
50
+
51
+ * With the *red* label for bug reporting
52
+ * With the *blue* label to request an enhancement
53
+
54
+ ### Fork-Pull pattern
55
+
56
+ The project contribution is commonly done by using the so-much-famous *fork-pull pattern*:
57
+
58
+ 1. Fork it ( https://github.com/[my-github-username]/cgen/fork )
59
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
60
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
61
+ 4. Push to the branch (`git push origin my-new-feature`)
62
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake'
3
+ require 'rake/clean'
data/bin/cgen.rb ADDED
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ require 'cgen'
5
+
6
+
7
+ # == Setup functions ===================================================================================================
8
+
9
+ def setup_opts
10
+
11
+ $tmp_pth = Pathname.new Dir.mktmpdir
12
+ $tex_out_pth = Pathname.new Dir.mktmpdir
13
+ $log_dir = $tmp_pth.join('log')
14
+
15
+ $data_pth = Pathname.new File.expand_path(ask("What's the path for the directory containing the data ? ".magenta))
16
+ $out_pth = Pathname.new File.expand_path(ask("What's the destination path ? ".magenta))
17
+ $main_tex_file_name = ask('Main TeX file name ? '.magenta) { |q| q.default = 'main.tex' }
18
+ $resources_dir_name = ask("What's the name for the directory containing the resources (relative to the directory that contains the template) ? ".magenta) { |q| q.default = 'resources' }
19
+ $template_dir_pth = Pathname.new(File.expand_path(
20
+ ask("What's the path of the template (leave empty for the default template) ? ".magenta) { |q|
21
+ q.default = File.join(File.dirname(File.dirname(__FILE__)), 'static', 'bundled_templates', 'moderncv')
22
+ }))
23
+ $template_deps_file_pth = Pathname.new(File.expand_path(
24
+ ask("What's the path for the YAML file containing the dependencies ? ".magenta) { |q|
25
+ q.default = $template_dir_pth.join('deps.yml').to_s
26
+ }))
27
+
28
+ # Ensure that the languages are correctly setup, i.e. if they aren't given use all of the available languages
29
+ $langs ||= Dir.glob($data_pth.join('*')).collect do |lang_data_pth|
30
+ File.basename(lang_data_pth).to_sym
31
+ end
32
+
33
+ end
34
+
35
+ # Setup the files / directories
36
+ def setup_filesystem
37
+ FileUtils.mkdir_p($out_pth)
38
+ FileUtils.mkdir_p($log_dir)
39
+ end
40
+
41
+
42
+ # == Utilities =========================================================================================================
43
+
44
+ def create_log_file(name)
45
+ $log_dir.join("log_#{DateTime.now.strftime('%Y.%m.%d')}_#{name}.txt")
46
+ end
47
+
48
+
49
+ # == CGen entry point ==================================================================================================
50
+
51
+ puts '> Starting Curriculum'.green
52
+
53
+ setup_opts
54
+ setup_filesystem
55
+
56
+ $curriculum = CGen::Curriculum.new(
57
+ CGen::DataLoader::YamlDataLoader.new, # TODO: Let the user choose the data loader
58
+ CGen::Compiler.new($tex_out_pth),
59
+ $data_pth,
60
+ $template_dir_pth,
61
+ $langs,
62
+ File.directory?($data_pth.join('en')) ? :en : nil)
63
+
64
+ unless $curriculum.validate_deps($template_deps_file_pth)
65
+ CGen::Util::Logging.log(:fatal_error, msg: 'The dependencies are not satisfied')
66
+ end
67
+
68
+ $curriculum.compile($langs)
69
+
70
+ puts '>> Generating PDFs'.green
71
+
72
+ $langs.each do |lang|
73
+ puts '>> Generating PDF for language '.cyan + lang.to_s.light_black
74
+
75
+ input_dir = $tex_out_pth.join(lang.to_s)
76
+ out_pth = $out_pth.join(lang.to_s)
77
+ FileUtils.mkdir_p(out_pth)
78
+
79
+ latex_to_pdf = CGen::Util::LatexToPdf.new($main_tex_file_name,
80
+ input_dir,
81
+ input_dir.join($resources_dir_name).join('*').to_s,
82
+ out_pth,
83
+ create_log_file('latex_to_pdf'))
84
+ latex_to_pdf.generate
85
+
86
+ end
data/lib/CGen.rb ADDED
@@ -0,0 +1,27 @@
1
+ # The common module for all of the CGen classes
2
+ module CGen; end
3
+
4
+
5
+ # ==> Setup RubyGems and Bundler
6
+ require 'rubygems'
7
+ require 'bundler/setup'
8
+
9
+ # ==> Require the core dependencies
10
+ require 'tmpdir'
11
+ require 'pathname'
12
+
13
+ # ==> Require all of the external dependencies
14
+ require 'hash_deep_merge'
15
+ require 'monadic'
16
+ require 'awesome_print'
17
+ require 'colorize'
18
+ require 'highline/import'
19
+ require 'erubis'
20
+
21
+ # ==> Require the project stuff
22
+ require 'cgen/version'
23
+ require 'cgen/util'
24
+ require 'cgen/compiler'
25
+ require 'cgen/curriculum'
26
+ require 'cgen/data_loader'
27
+ require 'cgen/generator'
@@ -0,0 +1,96 @@
1
+ class CGen::Compiler
2
+
3
+ def initialize(tex_out_pth)
4
+ @tex_out_pth = tex_out_pth
5
+ @generator_regex = /^generate\s*\(\s*(?<generator>[^,()]+)\s*,\s*(?<param>[^,()]+)\s*\)\s*$/i
6
+ @line_regex = /(?<re>\<=(?:(?<ctnt>(?>[^<>=]+)|\g<re>)+)\=\>)/
7
+ end
8
+
9
+ def validate_deps(deps_file_pth)
10
+ puts '>> Ensuring that the dependencies are satisfied'
11
+
12
+ # ==> Load the data from the YAML file
13
+ data = {}
14
+ File.open(deps_file_pth.to_s, 'r') { |deps_file| data.merge!(YAML::load(deps_file)) }
15
+
16
+ # ==> Validate commands are available on the system
17
+ data.has_key?('cmds') && data['cmds'].respond_to?(:all?) && data['cmds'].all? do |cmd|
18
+ CGen::Util::ShellCommand.exist?(cmd) ? true : puts(">> Command #{cmd} not found".red)
19
+ end
20
+
21
+ # ==> Validate that the required fonts are available
22
+ data.has_key?('pkgs') && data['pkgs'].respond_to?(:all?) && data['pkgs'].all? do |pkg|
23
+ `tlmgr list --only-installed | grep "i #{pkg}:"`.strip.length > 0 ? true : puts(">> Package #{pkg} not found".red)
24
+ end
25
+
26
+ # Outputs the manual dependencies
27
+ data.has_key?('manual_deps') && data['manual_deps'].respond_to?(:each) do |manual_dep|
28
+ puts '>> Please ensure that the manual dependency is installed:'.yellow + manual_dep.to_s.light_black
29
+ end
30
+ end
31
+
32
+ def compile(data, template_pth, lang)
33
+ puts '>> Compiling the language '.cyan + ":#{lang}".light_black
34
+ puts ' against the template '.cyan + template_pth.to_s.light_black
35
+ puts ' into the output directory '.cyan + @tex_out_pth.to_s.light_black
36
+
37
+ Dir.glob(template_pth.join('**').join('*')) do |file_pth|
38
+ if File.file?(file_pth)
39
+ rel_file_pth = file_pth.to_s.gsub(/#{template_pth}[\/]?/, '')
40
+ out_file_pth = @tex_out_pth.join(lang.to_s).join(rel_file_pth)
41
+
42
+ if File.extname(file_pth) == '.tex'
43
+ out_lines = []
44
+
45
+ File.open(file_pth, 'r').each do |line|
46
+ out_lines << line.gsub(@line_regex) do |_|
47
+ req_str = $2.strip.dup
48
+
49
+ md = @generator_regex.match(req_str)
50
+ if md
51
+ # Generate command
52
+ handle_generate(md[:generator].to_sym, md[:param], data, lang)
53
+ else
54
+ # Macro substitution
55
+ handle_generate(:macro_substitution, req_str, data, lang)
56
+ end
57
+ end
58
+ end
59
+
60
+ FileUtils.mkdir_p(out_file_pth.dirname)
61
+ File.open(out_file_pth, 'w') do |tex_out_file|
62
+ tex_out_file.write(out_lines.join(''))
63
+ end
64
+ else
65
+ FileUtils.mkdir_p(out_file_pth.dirname)
66
+ FileUtils.cp_r(file_pth, out_file_pth)
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ def handle_generate(generator, param, data, lang)
73
+ generators = {
74
+ # Generic
75
+ cvitem: CGen::Generator::CvItem,
76
+ cventry: CGen::Generator::CvEntry,
77
+ cvitemwithcomment: CGen::Generator::CvItemWithComment,
78
+ cvdoubleitem: CGen::Generator::CvDoubleItem,
79
+ cvlistitem: CGen::Generator::CvListItem,
80
+ cvlistdoubleitem: CGen::Generator::CvListDoubleItem,
81
+ cvcolumn: CGen::Generator::CvColumn,
82
+ list: CGen::Generator::List,
83
+ # Specific
84
+ work_experience: CGen::Generator::WorkExperience,
85
+ education: CGen::Generator::Education,
86
+ self_assessment: CGen::Generator::SelfAssessment,
87
+ # Macro
88
+ macro_substitution: CGen::Generator::MacroSubstitution
89
+ }
90
+
91
+ generators.has_key?(generator) ?
92
+ generators[generator].new(param, data, lang).generate :
93
+ raise("Invalid generator: #{generator}. Expected one of: #{generators}")
94
+ end
95
+
96
+ end
@@ -0,0 +1,57 @@
1
+ class CGen::Curriculum
2
+
3
+ attr_accessor :langs, :master_lang, :data_loader, :compiler, :data_pth
4
+
5
+ def initialize(data_loader, compiler, data_pth, template_pth, langs=[], master_lang=nil)
6
+ # Preconditions
7
+ raise 'Invalid langs. It cannot be empty' if langs.empty?
8
+
9
+ @data_loader = data_loader
10
+ @compiler = compiler
11
+ @langs = {}
12
+ # if the master language is not provided, then defaults to the first provided language
13
+ @master_lang = master_lang.nil? ? langs[0] : master_lang
14
+
15
+ @data_pth = data_pth
16
+ @template_pth = template_pth
17
+
18
+ puts '> Picking up the available languages'.green
19
+
20
+ langs.each do |lang|
21
+ lang_data_pth = @data_pth.join(lang.to_s)
22
+
23
+ inst = self
24
+ Either.chain do
25
+ bind -> { lang_data_pth.directory? }
26
+ bind -> {
27
+ lang_data = inst.data_loader.load_data(inst.data_pth, lang.to_sym, inst.master_lang.to_sym)
28
+ lang_data.is_a?(Hash) ? Success(lang_data) : Failure('lang data')
29
+ }
30
+ bind ->(lang_data) {
31
+ inst.langs[lang.to_sym] = { data: lang_data }
32
+ }
33
+ end
34
+ end
35
+ end
36
+
37
+ def validate_deps(template_deps_file_pth)
38
+ @compiler.validate_deps template_deps_file_pth
39
+ end
40
+
41
+ # Compile the curriculum for the provided languages
42
+ def compile(langs)
43
+ puts "> Compiling the curriculum for the languages: #{langs}".green
44
+
45
+ if langs.respond_to?(:each)
46
+ langs.each do |lang|
47
+ lang = lang.to_sym
48
+ @compiler.compile(@langs[lang][:data], @template_pth, lang)
49
+ end
50
+ elsif @langs.include? langs
51
+ @compiler.compile(@langs[lang][:data], @template_pth, langs)
52
+ else
53
+ raise 'Invalid lang'
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,31 @@
1
+ require 'yaml'
2
+
3
+
4
+ class CGen::DataLoader::YamlDataLoader
5
+
6
+ # Load the localized data from the directory `data_dir_pth`, following the convention that the localized data for
7
+ # a language are in a subdirectory of `data_dir_pth` named with the same name of the language.
8
+ # The target language name (which is also the subdirectory name) is `trgt_lang`, which fallbacks to the `master_lang`
9
+ def load_data(data_dir_pth, trgt_lang, master_lang)
10
+ CGen::Util::Logging.log(:loading_curriculum_data, trgt_lang: trgt_lang, master_lang: master_lang)
11
+
12
+ trgt_lang_data_dir_pth = data_dir_pth.join(trgt_lang.to_s)
13
+ master_lang_data_dir_pth = data_dir_pth.join(master_lang.to_s)
14
+
15
+ master_data = load_recursive_from_pth(trgt_lang_data_dir_pth)
16
+ trgt_data = load_recursive_from_pth(master_lang_data_dir_pth)
17
+
18
+ trgt_data.deep_merge(master_data) # return
19
+ end
20
+
21
+ # Load all of the YAML file starting from the given `base_dir_pth` and merges all of the data into an `Hash` and
22
+ # returns it
23
+ def load_recursive_from_pth(base_dir_pth)
24
+ data = {}
25
+ Dir.glob(base_dir_pth.join('**').join('*.yml')) do |yml_file_pth|
26
+ File.open(yml_file_pth, 'r') { |yml_file| data.merge!(YAML::load(yml_file)) }
27
+ end
28
+ data # return
29
+ end
30
+
31
+ end
@@ -0,0 +1,6 @@
1
+ # All of the classes under cgen/data_loader should be namespaced with this module
2
+ module CGen::DataLoader; end
3
+
4
+
5
+ # Require all of the data loaders
6
+ Dir.glob(File.join(File.dirname(__FILE__), 'data_loader/*.rb')) { |mod| require mod }
@@ -0,0 +1,37 @@
1
+ # Abstract class for a generator. All generators should inherit from this class
2
+ class CGen::Generator::BasicGenerator
3
+
4
+ attr_accessor(:param)
5
+ attr_accessor(:data)
6
+ attr_accessor(:lang)
7
+
8
+ def initialize(param, data, lang)
9
+ @param = param
10
+ @data = data
11
+ @lang = lang
12
+ end
13
+
14
+ def generate
15
+ raise 'Abstract class'
16
+ end
17
+
18
+ def get_value(keys_str)
19
+ keys = keys_str.split('.').reverse
20
+ if keys.empty?
21
+ '' # return
22
+ else
23
+ data_tmp = @data.dup
24
+ until keys.empty?
25
+ key = keys.pop
26
+ data_tmp = data_tmp[key]
27
+ end
28
+ data_tmp # return
29
+ end
30
+ end
31
+
32
+ def evaluate(input, context)
33
+ eruby = Erubis::Eruby.new(input)
34
+ eruby.evaluate(context) # return
35
+ end
36
+
37
+ end
@@ -0,0 +1,32 @@
1
+ class CGen::Generator::CvColumn < CGen::Generator::BasicGenerator
2
+
3
+ def initialize(param, data, lang)
4
+ super(param, data, lang)
5
+ end
6
+
7
+ def generate
8
+ value = get_value(param)
9
+ unless value.is_a?(Array)
10
+ value = Array[value]
11
+ end
12
+ ("\\begin{cvcolumns}" + (value.collect do |elem|
13
+ instance = self
14
+ result = Either.chain do
15
+ bind -> { elem.is_a?(Hash) }
16
+ bind -> { elem.has_key?('item_0') && elem.has_key?('item_1') }
17
+ bind -> {
18
+ instance.get_cv_list_double_item(elem['item_0'], elem['item_1'])
19
+ }
20
+ end
21
+ result.success? ? result.fetch : ''
22
+ end.join("\n")) +
23
+ "\\end{cvcolumns")
24
+ end
25
+
26
+ protected
27
+
28
+ def get_cv_list_double_item(item_0, item_1)
29
+ "\\cvcolumn{#{item_0}}{#{item_1}}"
30
+ end
31
+
32
+ end
@@ -0,0 +1,42 @@
1
+ class CGen::Generator::CvDoubleItem < CGen::Generator::BasicGenerator
2
+
3
+ def initialize(param, data, lang)
4
+ super(param, data, lang)
5
+ end
6
+
7
+ def generate
8
+ value = get_value(param)
9
+ unless value.is_a?(Array)
10
+ value = Array[value]
11
+ end
12
+ value.collect do |elem|
13
+ instance = self
14
+ result = Either.chain do
15
+ bind -> { elem.is_a?(Hash) }
16
+ bind -> {
17
+ elem.has_key?('item_0') &&
18
+ elem['item_0'].has_key?('title') &&
19
+ elem['item_0'].has_key?('content')
20
+ }
21
+ bind -> {
22
+ elem.has_key?('item_1') &&
23
+ elem['item_1'].has_key?('title') &&
24
+ elem['item_1'].has_key?('content')
25
+ }
26
+ bind -> {
27
+ instance.get_cv_double_item(elem['item_0'], elem['item_1'])
28
+ }
29
+ end
30
+ result.success? ? result.fetch : ''
31
+ end.join("\n")
32
+ end
33
+
34
+ protected
35
+
36
+ def get_cv_double_item(item_0, item_1)
37
+ "\\cvdoubleitem" +
38
+ "{#{item_0['title']}}{#{item_0['content']}}" +
39
+ "{#{item_1['title']}}{#{item_1['content']}}"
40
+ end
41
+
42
+ end
@@ -0,0 +1,33 @@
1
+ class CGen::Generator::CvEntry < CGen::Generator::BasicGenerator
2
+
3
+ def initialize(param, data, lang)
4
+ super(param, data, lang)
5
+ end
6
+
7
+ def generate
8
+ value = get_value(param)
9
+ unless value.is_a?(Array)
10
+ value = Array[value]
11
+ end
12
+ instance = self
13
+ result = Either.chain do
14
+ bind -> { value.is_a?(Array) }
15
+ bind -> { instance.get_cv_entry(value) }
16
+ end
17
+ result.success? ? result.fetch : ''
18
+ end
19
+
20
+ protected
21
+
22
+ def get_cv_entry(context)
23
+ result = "\\cventry"
24
+ context.each do |elem|
25
+ result += "{#{elem}}"
26
+ end
27
+ (6 - context.size).times do
28
+ result += '{}'
29
+ end
30
+ result # return
31
+ end
32
+
33
+ end
@@ -0,0 +1,29 @@
1
+ class CGen::Generator::CvItem < CGen::Generator::BasicGenerator
2
+
3
+ def initialize(param, data, lang)
4
+ super(param, data, lang)
5
+ end
6
+
7
+ def generate
8
+ value = get_value(param)
9
+ unless value.is_a?(Array)
10
+ value = Array[value]
11
+ end
12
+ value.collect do |elem|
13
+ instance = self
14
+ result = Either.chain do
15
+ bind -> { elem.is_a?(Hash) }
16
+ bind -> { elem.has_key?('title') && elem.has_key?('content') }
17
+ bind -> { instance.get_cv_item(elem['title'], elem['content']) }
18
+ end
19
+ result.success? ? result.fetch : ''
20
+ end.join("\n")
21
+ end
22
+
23
+ protected
24
+
25
+ def get_cv_item(title, content)
26
+ "\\cvitem{#{title}}{#{content}}"
27
+ end
28
+
29
+ end