├── test ├── sandbox │ └── .gitkeep ├── fixtures │ ├── cv_sample.docx │ ├── cv_template.docx │ ├── html_sample.docx │ ├── images │ │ ├── c3po.jpg │ │ ├── clone.jpg │ │ ├── r2d2.jpg │ │ ├── darth_vader.jpg │ │ └── svg_sample.svg │ ├── images_sample.docx │ ├── loops_sample.docx │ ├── recipe_sample.docx │ ├── images_template.docx │ ├── loops_template.docx │ ├── markdown_sample.docx │ ├── recipe_template.docx │ ├── insertion_template.docx │ ├── svg_images_sample.docx │ ├── conditionals_sample.docx │ ├── conditionals_template.docx │ ├── svg_images_template.docx │ ├── custom_field_handlers_sample.docx │ ├── insertion_template_no_styles.docx │ ├── custom_field_handlers_template.docx │ ├── xml │ │ ├── simple_field.xml │ │ ├── simple_fields.xml │ │ ├── conditional_with_predicate.xml │ │ ├── conditional_without_ending.xml │ │ ├── comment.xml │ │ ├── simple_field_with_styling.xml │ │ ├── conditional_inline.xml │ │ ├── conditional.xml │ │ ├── comment_block_and_comment_as_key.xml │ │ ├── corrupt_table.xml │ │ ├── complex_field.xml │ │ ├── edited_complex_field.xml │ │ ├── paragraph_loop_within_table_cell.xml │ │ ├── conditional_with_elsif_else_clauses.xml │ │ ├── conditional_inline_with_elsif_else_clauses.xml │ │ ├── test_ignore_complex_field_spanning_multiple_paragraphs.xml │ │ ├── loop_without_ending.xml │ │ ├── mock_document │ │ │ └── word │ │ │ │ └── document.xml │ │ ├── paragraph_loop.xml │ │ ├── complex_field_inline_conditional.xml │ │ ├── image.xml │ │ ├── loop_with_unique_ids.xml │ │ ├── table_multi_row_loop.xml │ │ └── table_row_loop.xml │ ├── recipe_context.json │ └── html │ │ └── html_test_content.html ├── support │ ├── xml_snippets.rb │ ├── html_snippets.rb │ └── document_xml_helper.rb ├── executable_test.rb ├── environment_test.rb ├── html_test.rb ├── expression_test.rb ├── test_helper.rb ├── processor │ └── section_properties_test.rb ├── html │ ├── ast_builder_test.rb │ ├── node_properties_test.rb │ └── ast_test.rb ├── context_test.rb ├── custom_field_handler_test.rb ├── configuration_test.rb ├── mail_merge_parser_test.rb ├── sablon_test.rb └── content_test.rb ├── lib ├── sablon │ ├── test.rb │ ├── version.rb │ ├── html │ │ ├── converter.rb │ │ ├── visitor.rb │ │ ├── node_properties.rb │ │ └── ast_builder.rb │ ├── environment.rb │ ├── document_object_model │ │ ├── file_handler.rb │ │ ├── content_types.rb │ │ ├── model.rb │ │ ├── numbering.rb │ │ └── relationships.rb │ ├── processor │ │ ├── section_properties.rb │ │ ├── document │ │ │ ├── operation_construction.rb │ │ │ ├── field_handlers.rb │ │ │ └── blocks.rb │ │ └── document.rb │ ├── context.rb │ ├── test │ │ └── assertions.rb │ ├── configuration │ │ └── html_tag.rb │ ├── parser │ │ └── mail_merge.rb │ ├── template.rb │ ├── operations.rb │ └── content.rb └── sablon.rb ├── misc ├── step_1.png ├── step_2.png ├── step_4.png ├── step_5.png ├── step_6.png ├── step_7.png ├── step_3_1.png ├── step_3_2.png ├── cv_sample.png ├── cv_template.png ├── step_3_3_a.png ├── step_3_3_b.png ├── image-example.png ├── recipe_sample.png ├── recipe_template.png └── TEMPLATE.md ├── gemfiles └── ci.gemfile ├── Gemfile ├── .gitignore ├── Rakefile ├── bin └── rake ├── Gemfile.lock ├── exe └── sablon ├── .github └── workflows │ └── ruby.yml ├── LICENSE.txt └── sablon.gemspec /test/sandbox/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/sablon/test.rb: -------------------------------------------------------------------------------- 1 | require "sablon/test/assertions" 2 | -------------------------------------------------------------------------------- /lib/sablon/version.rb: -------------------------------------------------------------------------------- 1 | module Sablon 2 | VERSION = "0.4.3" 3 | end 4 | -------------------------------------------------------------------------------- /misc/step_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/misc/step_1.png -------------------------------------------------------------------------------- /misc/step_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/misc/step_2.png -------------------------------------------------------------------------------- /misc/step_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/misc/step_4.png -------------------------------------------------------------------------------- /misc/step_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/misc/step_5.png -------------------------------------------------------------------------------- /misc/step_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/misc/step_6.png -------------------------------------------------------------------------------- /misc/step_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/misc/step_7.png -------------------------------------------------------------------------------- /gemfiles/ci.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec path: "../" 4 | -------------------------------------------------------------------------------- /misc/step_3_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/misc/step_3_1.png -------------------------------------------------------------------------------- /misc/step_3_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/misc/step_3_2.png -------------------------------------------------------------------------------- /misc/cv_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/misc/cv_sample.png -------------------------------------------------------------------------------- /misc/cv_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/misc/cv_template.png -------------------------------------------------------------------------------- /misc/step_3_3_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/misc/step_3_3_a.png -------------------------------------------------------------------------------- /misc/step_3_3_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/misc/step_3_3_b.png -------------------------------------------------------------------------------- /misc/image-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/misc/image-example.png -------------------------------------------------------------------------------- /misc/recipe_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/misc/recipe_sample.png -------------------------------------------------------------------------------- /misc/recipe_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/misc/recipe_template.png -------------------------------------------------------------------------------- /test/fixtures/cv_sample.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/cv_sample.docx -------------------------------------------------------------------------------- /test/fixtures/cv_template.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/cv_template.docx -------------------------------------------------------------------------------- /test/fixtures/html_sample.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/html_sample.docx -------------------------------------------------------------------------------- /test/fixtures/images/c3po.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/images/c3po.jpg -------------------------------------------------------------------------------- /test/fixtures/images/clone.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/images/clone.jpg -------------------------------------------------------------------------------- /test/fixtures/images/r2d2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/images/r2d2.jpg -------------------------------------------------------------------------------- /test/fixtures/images_sample.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/images_sample.docx -------------------------------------------------------------------------------- /test/fixtures/loops_sample.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/loops_sample.docx -------------------------------------------------------------------------------- /test/fixtures/recipe_sample.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/recipe_sample.docx -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in sablon.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /test/fixtures/images_template.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/images_template.docx -------------------------------------------------------------------------------- /test/fixtures/loops_template.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/loops_template.docx -------------------------------------------------------------------------------- /test/fixtures/markdown_sample.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/markdown_sample.docx -------------------------------------------------------------------------------- /test/fixtures/recipe_template.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/recipe_template.docx -------------------------------------------------------------------------------- /test/fixtures/images/darth_vader.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/images/darth_vader.jpg -------------------------------------------------------------------------------- /test/fixtures/insertion_template.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/insertion_template.docx -------------------------------------------------------------------------------- /test/fixtures/svg_images_sample.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/svg_images_sample.docx -------------------------------------------------------------------------------- /test/fixtures/conditionals_sample.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/conditionals_sample.docx -------------------------------------------------------------------------------- /test/fixtures/conditionals_template.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/conditionals_template.docx -------------------------------------------------------------------------------- /test/fixtures/svg_images_template.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/svg_images_template.docx -------------------------------------------------------------------------------- /test/fixtures/custom_field_handlers_sample.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/custom_field_handlers_sample.docx -------------------------------------------------------------------------------- /test/fixtures/insertion_template_no_styles.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/insertion_template_no_styles.docx -------------------------------------------------------------------------------- /test/fixtures/custom_field_handlers_template.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/senny/sablon/HEAD/test/fixtures/custom_field_handlers_template.docx -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | *.bundle 10 | *.so 11 | *.o 12 | *.a 13 | mkmf.log 14 | 15 | /test/sandbox/* 16 | !/test/sandbox/.gitkeep 17 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require 'rake/testtask' 3 | 4 | Rake::TestTask.new do |t| 5 | t.test_files = FileList['test/**/*_test.rb'] 6 | t.libs.push 'test' 7 | t.verbose = true 8 | end 9 | 10 | task default: :test 11 | -------------------------------------------------------------------------------- /test/support/xml_snippets.rb: -------------------------------------------------------------------------------- 1 | module XMLSnippets 2 | def snippet(name) 3 | File.read(File.expand_path("#{name}.xml", snippet_path)) 4 | end 5 | 6 | def snippet_path 7 | @snippet_path ||= File.expand_path("../../fixtures/xml", __FILE__) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/fixtures/images/svg_sample.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sample 5 | 6 | 7 | -------------------------------------------------------------------------------- /test/support/html_snippets.rb: -------------------------------------------------------------------------------- 1 | module HTMLSnippets 2 | def snippet(name) 3 | File.read(File.expand_path("#{name}.html", snippet_path)) 4 | end 5 | 6 | def snippet_path 7 | @snippet_path ||= File.expand_path("../../fixtures/html", __FILE__) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/fixtures/xml/simple_field.xml: -------------------------------------------------------------------------------- 1 | 2 | Hello! My Name is 3 | 4 | 5 | 6 | «=first_name» 7 | 8 | 9 | , nice to meet you. 10 | 11 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'rake' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('rake', 'rake') 17 | -------------------------------------------------------------------------------- /test/fixtures/xml/simple_fields.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | «=first_name» 6 | 7 | 8 | 9 | 10 | 11 | «=last_name» 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/fixtures/xml/conditional_with_predicate.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | «body:if(empty?)» 6 | 7 | 8 | 9 | 10 | some content 11 | 12 | 13 | 14 | 15 | 16 | «body:endIf» 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /lib/sablon/html/converter.rb: -------------------------------------------------------------------------------- 1 | require "sablon/html/ast" 2 | require "sablon/html/visitor" 3 | 4 | module Sablon 5 | class HTMLConverter 6 | def process(input, env) 7 | @env = env 8 | processed_ast(input).to_docx 9 | end 10 | 11 | def processed_ast(input) 12 | ast = build_ast(input) 13 | ast.accept LastNewlineRemoverVisitor.new 14 | ast 15 | end 16 | 17 | def build_ast(input) 18 | doc = Nokogiri::HTML.fragment(input) 19 | Root.new(@env, doc) 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/fixtures/xml/conditional_without_ending.xml: -------------------------------------------------------------------------------- 1 | Anthony 2 | 3 | 4 | 5 | 6 | «middle_name:if» 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | «=middle_name» 15 | 16 | 17 | 18 | Hall 19 | -------------------------------------------------------------------------------- /test/fixtures/xml/comment.xml: -------------------------------------------------------------------------------- 1 | Before 2 | 3 | 4 | 5 | 6 | «comment» 7 | 8 | 9 | 10 | 11 | 12 | Inside Comment! 13 | 14 | 15 | 16 | 17 | 18 | 19 | «endComment» 20 | 21 | 22 | 23 | After 24 | -------------------------------------------------------------------------------- /test/fixtures/xml/simple_field_with_styling.xml: -------------------------------------------------------------------------------- 1 | 2 | Generated by 3 | 4 | 5 | 6 | 7 | 8 | 9 | « 10 | 11 | 12 | 13 | =system_name 14 | 15 | 16 | 17 | » 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | sablon (0.4.3) 5 | nokogiri (>= 1.8.5) 6 | rubyzip (>= 1.3.0) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | minitest (5.25.5) 12 | mutex_m (0.3.0) 13 | nokogiri (1.18.9-arm64-darwin) 14 | racc (~> 1.4) 15 | ostruct (0.6.3) 16 | racc (1.8.1) 17 | rake (13.3.0) 18 | rexml (3.4.1) 19 | rubyzip (3.0.1) 20 | xml-simple (1.1.9) 21 | rexml 22 | 23 | PLATFORMS 24 | arm64-darwin 25 | 26 | DEPENDENCIES 27 | bundler (>= 1.6) 28 | minitest (~> 5.4) 29 | mutex_m 30 | ostruct 31 | rake (~> 13.0) 32 | sablon! 33 | xml-simple 34 | 35 | BUNDLED WITH 36 | 2.4.15 37 | -------------------------------------------------------------------------------- /test/fixtures/xml/conditional_inline.xml: -------------------------------------------------------------------------------- 1 | 2 | Anthony 3 | 4 | 5 | 6 | 7 | «middle_name:if» 8 | 9 | 10 | 11 | Michael 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | «middle_name:endIf» 20 | 21 | 22 | 23 | Hall 24 | 25 | 26 | -------------------------------------------------------------------------------- /exe/sablon: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'sablon' 4 | require 'json' 5 | 6 | if ARGV.size < 1 7 | puts <<-HELP 8 | cat | sablon