sablon 0.0.8 → 0.0.9

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: a74f19959e3d2566b25c322d1d9365001d95a085
4
- data.tar.gz: be248f49337bedb4f7747f6d11b26e721a215514
3
+ metadata.gz: 249d5af8b120f806c35a208e2804915c575b23e7
4
+ data.tar.gz: e7a006f1fcd0bbd7202d25681578c4fb327ed49b
5
5
  SHA512:
6
- metadata.gz: 7e77454cdb18b5a10a2a57b205c6fe837bd8e01e0d22ded78ba43a1db5f2717aa987f62a0cc016c02a5a4f1ba322f6424bd3ff45ef80e459ebded2dd6ca32762
7
- data.tar.gz: 301bc31e4ddc13ae6b224b04ddb43fc4ba09db01b2c080c14bc6fbc126adc3bfd59e0f975d137de179d422ad36975ef67698a1da454595e0f383dee61b8274ad
6
+ metadata.gz: 39bc20fe127fbd6d8bc2737af0e5c1e559182815ce94798ac5c488c66029829148665befdafee6fec0dab3c1b9a241ec6326600dedd6518b8eb5e697e10ed2d3
7
+ data.tar.gz: ceb09efa9b30aa2a6ac69ef199f2f8f8452db76b9272c5940c9c10b437459bbd4fbff370d62adbba5a3bacda4eb8f79aa0d1abf2856176ef2e33a73e02f4ee89
data/README.md CHANGED
@@ -61,6 +61,44 @@ This works for chained method calls and nested hash lookup as well:
61
61
  «=buyer.address.street»
62
62
  ```
63
63
 
64
+ ##### WordProcessingML
65
+
66
+ Generally Sablon tries to reuse the formatting defined in the template. However,
67
+ there are situations where more fine grained control is needed. Imagine you need
68
+ to insert a body of text containing different formats. If you can't decide the
69
+ format ahead of processing time (in the template) you can insert
70
+ [WordProcessingML](http://en.wikipedia.org/wiki/Microsoft_Office_XML_formats)
71
+ directly.
72
+
73
+ The template can use a simple insertion operation like so:
74
+
75
+ ```
76
+ «=long_description»
77
+ ```
78
+
79
+ The thing that changes is the context passed when processing the template:
80
+
81
+ ```ruby
82
+ word_processing_ml = <<-XML
83
+ <w:p>
84
+ <w:r w:rsidRPr="00B97C39">
85
+ <w:rPr>
86
+ <w:b />
87
+ </w:rPr>
88
+ <w:t>this is bold text</w:t>
89
+ </w:r>
90
+ </w:p>
91
+ XML
92
+ context = {
93
+ long_description: Sablon.word_ml(word_processing_ml)
94
+ }
95
+ template.render_to_file File.expand_path("~/Desktop/output.docx"), context
96
+ ```
97
+
98
+ **IMPORTANT:** This feature is very much *experimental*. Currently, this only
99
+ works if the insertion is the only thing inside the template paragraph. Other
100
+ content is discarded!
101
+
64
102
  #### Conditionals
65
103
 
66
104
  Sablon can render parts of the template conditonally based on the value of a
data/lib/sablon.rb CHANGED
@@ -1,9 +1,12 @@
1
1
  require "sablon/version"
2
+ require "sablon/context"
2
3
  require "sablon/template"
3
4
  require "sablon/processor"
4
5
  require "sablon/processor/section_properties"
5
6
  require "sablon/parser/mail_merge"
6
7
  require "sablon/operations"
8
+ require "sablon/content"
9
+
7
10
  require 'zip'
8
11
  require 'nokogiri'
9
12
 
@@ -14,4 +17,12 @@ module Sablon
14
17
  def self.template(path)
15
18
  Template.new(path)
16
19
  end
20
+
21
+ def self.word_ml(xml)
22
+ Sablon::Content::WordML.new(xml)
23
+ end
24
+
25
+ def self.string(object)
26
+ Sablon::Content::String.new(object.to_s)
27
+ end
17
28
  end
@@ -0,0 +1,30 @@
1
+ module Sablon
2
+ module Content
3
+ class String < Struct.new(:string)
4
+ include Sablon::Content
5
+
6
+ def append_to(paragraph, display_node)
7
+ string.scan(/[^\n]+|\n/).reverse.each do |part|
8
+ if part == "\n"
9
+ display_node.add_next_sibling Nokogiri::XML::Node.new "w:br", display_node.document
10
+ else
11
+ text_part = display_node.dup
12
+ text_part.content = part
13
+ display_node.add_next_sibling text_part
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ class WordML < Struct.new(:xml)
20
+ include Sablon::Content
21
+
22
+ def append_to(paragraph, display_node)
23
+ Nokogiri::XML.fragment(xml).children.reverse.each do |child|
24
+ paragraph.add_next_sibling child
25
+ end
26
+ paragraph.remove
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+ module Sablon
2
+ module Context
3
+ def self.transform(hash)
4
+ transform_hash(hash)
5
+ end
6
+
7
+ def self.transform_hash(hash)
8
+ Hash[hash.map{|k,v| transform_pair(k.to_s, v) }]
9
+ end
10
+
11
+ def self.transform_pair(key, value)
12
+ if key =~ /\Awordml:(.+)\z/
13
+ [$1, Sablon.word_ml(value)]
14
+ else
15
+ transform_standard_key(key, value)
16
+ end
17
+ end
18
+
19
+ def self.transform_standard_key(key, value)
20
+ case value
21
+ when Hash
22
+ [key, transform_hash(value)]
23
+ else
24
+ [key, value]
25
+ end
26
+ end
27
+ end
28
+ end
@@ -3,7 +3,16 @@ module Sablon
3
3
  module Statement
4
4
  class Insertion < Struct.new(:expr, :field)
5
5
  def evaluate(context)
6
- field.replace(expr.evaluate(context))
6
+ field.replace(wrap_content(expr.evaluate(context)))
7
+ end
8
+
9
+ private
10
+ def wrap_content(value)
11
+ if value.is_a?(Sablon::Content)
12
+ value
13
+ else
14
+ Sablon.string(value)
15
+ end
7
16
  end
8
17
  end
9
18
 
@@ -8,17 +8,10 @@ module Sablon
8
8
  end
9
9
 
10
10
  private
11
- def replace_field_display(node, text)
11
+ def replace_field_display(node, content)
12
+ paragraph = node.ancestors(".//w:p").first
12
13
  display_node = node.search(".//w:t").first
13
- text.to_s.scan(/[^\n]+|\n/).reverse.each do |part|
14
- if part == "\n"
15
- display_node.add_next_sibling Nokogiri::XML::Node.new "w:br", display_node.document
16
- else
17
- text_part = display_node.dup
18
- text_part.content = part
19
- display_node.add_next_sibling text_part
20
- end
21
- end
14
+ content.append_to(paragraph, display_node)
22
15
  display_node.remove
23
16
  end
24
17
  end
@@ -29,8 +22,8 @@ module Sablon
29
22
  @raw_expression = @nodes.flat_map {|n| n.search(".//w:instrText").map(&:content) }.join
30
23
  end
31
24
 
32
- def replace(value)
33
- replace_field_display(pattern_node, value)
25
+ def replace(content)
26
+ replace_field_display(pattern_node, content)
34
27
  (@nodes - [pattern_node]).each(&:remove)
35
28
  end
36
29
 
@@ -54,8 +47,8 @@ module Sablon
54
47
  @raw_expression = @node["w:instr"]
55
48
  end
56
49
 
57
- def replace(value)
58
- replace_field_display(@node, value)
50
+ def replace(content)
51
+ replace_field_display(@node, content)
59
52
  @node.replace(@node.children)
60
53
  end
61
54
 
@@ -3,8 +3,7 @@ module Sablon
3
3
  class Processor
4
4
  def self.process(xml_node, context, properties = {})
5
5
  processor = new(parser)
6
- stringified_context = Hash[context.map {|k, v| [k.to_s, v] }]
7
- processor.manipulate xml_node, stringified_context
6
+ processor.manipulate xml_node, Sablon::Context.transform(context)
8
7
  processor.write_properties xml_node, properties if properties.any?
9
8
  xml_node
10
9
  end
@@ -6,7 +6,7 @@ module Sablon
6
6
 
7
7
  # Same as +render_to_string+ but writes the processed template to +output_path+.
8
8
  def render_to_file(output_path, context, properties = {})
9
- File.open(output_path, 'w') do |f|
9
+ File.open(output_path, 'wb') do |f|
10
10
  f.write render_to_string(context, properties)
11
11
  end
12
12
  end
@@ -24,14 +24,23 @@ module Sablon
24
24
  out.put_next_entry(entry_name)
25
25
  content = entry.get_input_stream.read
26
26
  if entry_name == 'word/document.xml'
27
- out.write(Processor.process(Nokogiri::XML(content), context, properties).to_xml)
27
+ out.write(process(content, context, properties))
28
28
  elsif entry_name =~ /word\/header\d*\.xml/ || entry_name =~ /word\/footer\d*\.xml/
29
- out.write(Processor.process(Nokogiri::XML(content), context).to_xml)
29
+ out.write(process(content, context))
30
30
  else
31
31
  out.write(content)
32
32
  end
33
33
  end
34
34
  end
35
35
  end
36
+
37
+ # process the sablon xml template with the given +context+.
38
+ #
39
+ # IMPORTANT: Open Office does not ignore whitespace around tags.
40
+ # We need to render the xml without indent and whitespace.
41
+ def process(content, context, *args)
42
+ document = Nokogiri::XML(content)
43
+ Processor.process(document, context, *args).to_xml(indent: 0, save_with: 0)
44
+ end
36
45
  end
37
46
  end
@@ -1,3 +1,3 @@
1
1
  module Sablon
2
- VERSION = "0.0.8"
2
+ VERSION = "0.0.9"
3
3
  end
@@ -0,0 +1,92 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "test_helper"
3
+
4
+ module ContentTestSetup
5
+ def setup
6
+ super
7
+ @template_text = '<w:p><span>template</span></w:p><w:p>AFTER</w:p>'
8
+ @document = Nokogiri::XML.fragment(@template_text)
9
+ @paragraph = @document.children.first
10
+ @node = @document.css("span").first
11
+ end
12
+
13
+ private
14
+ def assert_xml_equal(expected, document)
15
+ assert_equal expected, document.to_xml(indent: 0, save_with: 0)
16
+ end
17
+ end
18
+
19
+ class ContentStringTest < Sablon::TestCase
20
+ include ContentTestSetup
21
+
22
+ def test_single_line_string
23
+ Sablon.string("a normal string").append_to @paragraph, @node
24
+
25
+ output = <<-XML.strip
26
+ <w:p><span>template</span><span>a normal string</span></w:p><w:p>AFTER</w:p>
27
+ XML
28
+ assert_xml_equal output, @document
29
+ end
30
+
31
+ def test_numeric_string
32
+ Sablon.string(42).append_to @paragraph, @node
33
+
34
+ output = <<-XML.strip
35
+ <w:p><span>template</span><span>42</span></w:p><w:p>AFTER</w:p>
36
+ XML
37
+ assert_xml_equal output, @document
38
+ end
39
+
40
+ def test_string_with_newlines
41
+ Sablon.string("a\nmultiline\n\nstring").append_to @paragraph, @node
42
+
43
+ output = <<-XML.strip.gsub("\n", "")
44
+ <w:p>
45
+ <span>template</span>
46
+ <span>a</span>
47
+ <w:br/>
48
+ <span>multiline</span>
49
+ <w:br/>
50
+ <w:br/>
51
+ <span>string</span>
52
+ </w:p>
53
+ <w:p>AFTER</w:p>
54
+ XML
55
+
56
+ assert_xml_equal output, @document
57
+ end
58
+
59
+ def test_blank_string
60
+ Sablon.string("").append_to @paragraph, @node
61
+
62
+ assert_xml_equal @template_text, @document
63
+ end
64
+ end
65
+
66
+ class ContentWordMLTest < Sablon::TestCase
67
+ include ContentTestSetup
68
+
69
+ def test_blank_word_ml
70
+ Sablon.word_ml("").append_to @paragraph, @node
71
+
72
+ assert_xml_equal "<w:p>AFTER</w:p>", @document
73
+ end
74
+
75
+ def test_inserts_word_ml_into_the_document
76
+ @word_ml = '<w:p><w:r><w:t xml:space="preserve">a </w:t></w:r></w:p>'
77
+ Sablon.word_ml(@word_ml).append_to @paragraph, @node
78
+
79
+ output = <<-XML.strip.gsub("\n", "")
80
+ <w:p>
81
+ <w:r><w:t xml:space=\"preserve\">a </w:t></w:r>
82
+ </w:p>
83
+ <w:p>AFTER</w:p>
84
+ XML
85
+
86
+ assert_xml_equal output, @document
87
+ end
88
+
89
+ def test_inserting_word_ml_multiple_times_into_same_paragraph
90
+ skip "Content::WordML currently removes the paragraph..."
91
+ end
92
+ end
@@ -0,0 +1,15 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "test_helper"
3
+
4
+ class ContextTest < Sablon::TestCase
5
+ def test_converts_symbol_keys_to_string_keys
6
+ transformed = Sablon::Context.transform({a: 1, b: {c: 2, "d" => 3}})
7
+ assert_equal({"a"=>1, "b"=>{"c" =>2, "d"=>3}}, transformed)
8
+ end
9
+
10
+ def test_recognizes_wordml_keys
11
+ transformed = Sablon::Context.transform({"wordml:mykey" => "<w:p><w:p>", "otherkey" => "<nope>"})
12
+ assert_equal({ "mykey"=>Sablon.word_ml("<w:p><w:p>"),
13
+ "otherkey"=>"<nope>"}, transformed)
14
+ end
15
+ end
@@ -10,9 +10,7 @@ class ExecutableTest < Sablon::TestCase
10
10
  @template_path = @base_path + "fixtures/shopping_list_template.docx"
11
11
  @context_path = @base_path + "fixtures/shopping_list_context.json"
12
12
  @executable_path = @base_path + '../bin/sablon'
13
- end
14
13
 
15
- def teardown
16
14
  @output_path.delete if @output_path.exist?
17
15
  end
18
16
 
@@ -29,13 +29,6 @@ class LookupOrMethodCallTest < Sablon::TestCase
29
29
  assert_equal "Jack", expr.evaluate("user" => user)
30
30
  end
31
31
 
32
- def test_calls_perform_lookup_on_hash_with_symbol_keys
33
- skip
34
- user = {first_name: "Jack"}
35
- expr = Sablon::Expression.parse("user.first_name")
36
- assert_equal "Jack", expr.evaluate("user" => user)
37
- end
38
-
39
32
  def test_inspect
40
33
  expr = Sablon::Expression.parse("user.first_name")
41
34
  assert_equal "«user.first_name»", expr.inspect
Binary file
Binary file
@@ -4,7 +4,8 @@
4
4
  "author": {
5
5
  "first_name": "John",
6
6
  "last_name": "Doe"
7
- }
7
+ },
8
+ "wordml:description": "<w:p><w:r><w:t xml:space=\"preserve\">A list of items to buy at the next </w:t></w:r><w:r><w:rPr><w:b /></w:rPr><w:t>Shop</w:t></w:r><w:r><w:t xml:space=\"preserve\">.</w:t></w:r></w:p>"
8
9
  },
9
10
  "items": [
10
11
  {"name": "Milk", "amount": "1L"},
@@ -0,0 +1 @@
1
+ <w:p><w:r><w:t xml:space="preserve">I </w:t></w:r><w:r><w:rPr><w:i /></w:rPr><w:t>am</w:t></w:r><w:r><w:t xml:space="preserve"> </w:t></w:r></w:p><w:p><w:r><w:t xml:space="preserve">a </w:t></w:r><w:r><w:rPr><w:b /></w:rPr><w:t>Software Developer</w:t></w:r></w:p>
@@ -1,22 +1,24 @@
1
- <w:r><w:t xml:space="preserve">Hello! My Name is </w:t></w:r>
2
- <w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
3
- <w:rPr><w:b/></w:rPr>
4
- <w:fldChar w:fldCharType="begin"/>
5
- </w:r>
6
- <w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
7
- <w:rPr><w:b/></w:rPr>
8
- <w:instrText xml:space="preserve"> MERGEFIELD =last_name \* MERGEFORMAT </w:instrText>
9
- </w:r>
10
- <w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
11
- <w:rPr><w:b/></w:rPr>
12
- <w:fldChar w:fldCharType="separate"/>
13
- </w:r>
14
- <w:r w:rsidR="004B49F0">
15
- <w:rPr><w:b/><w:noProof/></w:rPr>
16
- <w:t>«=last_name»</w:t>
17
- </w:r>
18
- <w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
19
- <w:rPr><w:b/></w:rPr>
20
- <w:fldChar w:fldCharType="end"/>
21
- </w:r>
22
- <w:r w:rsidR="00BE47B1"><w:t xml:space="preserve">, nice to meet you.</w:t></w:r>
1
+ <w:p>
2
+ <w:r><w:t xml:space="preserve">Hello! My Name is </w:t></w:r>
3
+ <w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
4
+ <w:rPr><w:b/></w:rPr>
5
+ <w:fldChar w:fldCharType="begin"/>
6
+ </w:r>
7
+ <w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
8
+ <w:rPr><w:b/></w:rPr>
9
+ <w:instrText xml:space="preserve"> MERGEFIELD =last_name \* MERGEFORMAT </w:instrText>
10
+ </w:r>
11
+ <w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
12
+ <w:rPr><w:b/></w:rPr>
13
+ <w:fldChar w:fldCharType="separate"/>
14
+ </w:r>
15
+ <w:r w:rsidR="004B49F0">
16
+ <w:rPr><w:b/><w:noProof/></w:rPr>
17
+ <w:t>«=last_name»</w:t>
18
+ </w:r>
19
+ <w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
20
+ <w:rPr><w:b/></w:rPr>
21
+ <w:fldChar w:fldCharType="end"/>
22
+ </w:r>
23
+ <w:r w:rsidR="00BE47B1"><w:t xml:space="preserve">, nice to meet you.</w:t></w:r>
24
+ </w:p>
@@ -1,26 +1,28 @@
1
- <w:r>
2
- <w:t xml:space="preserve">Hello! My Name is </w:t>
3
- </w:r>
4
- <w:r w:rsidR="003C4780">
5
- <w:fldChar w:fldCharType="begin" />
6
- </w:r>
7
- <w:r w:rsidR="003C4780">
8
- <w:instrText xml:space="preserve"> MERGEFIELD </w:instrText>
9
- </w:r>
10
- <w:r w:rsidR="003A4504">
11
- <w:instrText>=</w:instrText>
12
- </w:r>
13
- <w:r w:rsidR="003C4780">
14
- <w:instrText xml:space="preserve">first_name \* MERGEFORMAT </w:instrText>
15
- </w:r>
16
- <w:r w:rsidR="003C4780">
17
- <w:fldChar w:fldCharType="separate" />
18
- </w:r>
19
- <w:r w:rsidR="00441382">
20
- <w:rPr><w:noProof /></w:rPr>
21
- <w:t>«=person.first_name»</w:t>
22
- </w:r>
23
- <w:r w:rsidR="003C4780">
24
- <w:fldChar w:fldCharType="end" />
25
- </w:r>
26
- <w:r w:rsidR="00BE47B1"><w:t xml:space="preserve">, nice to meet you.</w:t></w:r>
1
+ <w:p>
2
+ <w:r>
3
+ <w:t xml:space="preserve">Hello! My Name is </w:t>
4
+ </w:r>
5
+ <w:r w:rsidR="003C4780">
6
+ <w:fldChar w:fldCharType="begin" />
7
+ </w:r>
8
+ <w:r w:rsidR="003C4780">
9
+ <w:instrText xml:space="preserve"> MERGEFIELD </w:instrText>
10
+ </w:r>
11
+ <w:r w:rsidR="003A4504">
12
+ <w:instrText>=</w:instrText>
13
+ </w:r>
14
+ <w:r w:rsidR="003C4780">
15
+ <w:instrText xml:space="preserve">first_name \* MERGEFORMAT </w:instrText>
16
+ </w:r>
17
+ <w:r w:rsidR="003C4780">
18
+ <w:fldChar w:fldCharType="separate" />
19
+ </w:r>
20
+ <w:r w:rsidR="00441382">
21
+ <w:rPr><w:noProof /></w:rPr>
22
+ <w:t>«=person.first_name»</w:t>
23
+ </w:r>
24
+ <w:r w:rsidR="003C4780">
25
+ <w:fldChar w:fldCharType="end" />
26
+ </w:r>
27
+ <w:r w:rsidR="00BE47B1"><w:t xml:space="preserve">, nice to meet you.</w:t></w:r>
28
+ </w:p>
@@ -1,8 +1,10 @@
1
- <w:r><w:t xml:space="preserve">Hello! My Name is </w:t></w:r>
2
- <w:fldSimple w:instr=" MERGEFIELD =first_name \* MERGEFORMAT ">
3
- <w:r w:rsidR="004B49F0">
4
- <w:rPr><w:noProof/></w:rPr>
5
- <w:t>«=first_name»</w:t>
6
- </w:r>
7
- </w:fldSimple>
8
- <w:r w:rsidR="00BE47B1"><w:t xml:space="preserve">, nice to meet you.</w:t></w:r>
1
+ <w:p>
2
+ <w:r><w:t xml:space="preserve">Hello! My Name is </w:t></w:r>
3
+ <w:fldSimple w:instr=" MERGEFIELD =first_name \* MERGEFORMAT ">
4
+ <w:r w:rsidR="004B49F0">
5
+ <w:rPr><w:noProof/></w:rPr>
6
+ <w:t>«=first_name»</w:t>
7
+ </w:r>
8
+ </w:fldSimple>
9
+ <w:r w:rsidR="00BE47B1"><w:t xml:space="preserve">, nice to meet you.</w:t></w:r>
10
+ </w:p>
@@ -1,12 +1,14 @@
1
- <w:fldSimple w:instr=" MERGEFIELD =first_name \* MERGEFORMAT ">
2
- <w:r w:rsidR="004B49F0">
3
- <w:rPr><w:noProof/></w:rPr>
4
- <w:t>«=first_name»</w:t>
5
- </w:r>
6
- </w:fldSimple>
7
- <w:fldSimple w:instr=" MERGEFIELD =last_name \* MERGEFORMAT ">
8
- <w:r w:rsidR="004B49F0">
9
- <w:rPr><w:noProof/></w:rPr>
10
- <w:t>«=last_name»</w:t>
11
- </w:r>
12
- </w:fldSimple>
1
+ <w:p>
2
+ <w:fldSimple w:instr=" MERGEFIELD =first_name \* MERGEFORMAT ">
3
+ <w:r w:rsidR="004B49F0">
4
+ <w:rPr><w:noProof/></w:rPr>
5
+ <w:t>«=first_name»</w:t>
6
+ </w:r>
7
+ </w:fldSimple>
8
+ <w:fldSimple w:instr=" MERGEFIELD =last_name \* MERGEFORMAT ">
9
+ <w:r w:rsidR="004B49F0">
10
+ <w:rPr><w:noProof/></w:rPr>
11
+ <w:t>«=last_name»</w:t>
12
+ </w:r>
13
+ </w:fldSimple>
14
+ </w:p>
@@ -19,7 +19,7 @@ module MailMergeParser
19
19
  end
20
20
 
21
21
  def body_xml
22
- document.search(".//w:body").children.map(&:to_xml).join.strip
22
+ document.search(".//w:body").children.map(&:to_xml).map(&:strip).join
23
23
  end
24
24
 
25
25
  def document
@@ -35,59 +35,28 @@ module MailMergeParser
35
35
  end
36
36
 
37
37
  def test_replace
38
- field.replace("Hello")
39
- xml = <<-xml
40
- <w:r w:rsidR=\"004B49F0\">
41
- <w:rPr><w:noProof/></w:rPr>
42
- <w:t>Hello</w:t>
43
- </w:r>
44
- xml
45
- assert_equal body_xml.strip, xml.strip
46
- end
47
-
48
- def test_replace_with_newlines
49
- field.replace("First\nSecond\n\nThird")
50
- xml = <<-xml
51
- <w:r w:rsidR=\"004B49F0\">
52
- <w:rPr><w:noProof/></w:rPr>
53
- <w:t>First</w:t><w:br/><w:t>Second</w:t><w:br/><w:br/><w:t>Third</w:t>
54
- </w:r>
55
- xml
56
- assert_equal body_xml.strip, xml.strip
57
- end
58
-
59
- def test_replace_with_nil
60
- field.replace(nil)
61
- xml = <<-xml
38
+ field.replace(Sablon.string("Hello"))
39
+ xml = <<-xml.strip
40
+ <w:p>
62
41
  <w:r w:rsidR=\"004B49F0\">
63
- <w:rPr><w:noProof/></w:rPr>
64
-
65
- </w:r>
66
- xml
67
- assert_equal body_xml.gsub(/^\s+$/,'').strip, xml.strip
68
- end
69
-
70
- def test_replace_with_numeric
71
- field.replace(45)
72
- xml = <<-xml
73
- <w:r w:rsidR=\"004B49F0\">
74
- <w:rPr><w:noProof/></w:rPr>
75
- <w:t>45</w:t>
76
- </w:r>
42
+ <w:rPr><w:noProof/></w:rPr>
43
+ <w:t>Hello</w:t>
44
+ </w:r>
45
+ </w:p>
77
46
  xml
78
- assert_equal body_xml.gsub(/^\s+$/,'').strip, xml.strip
47
+ assert_equal xml, body_xml
79
48
  end
80
49
 
81
50
  private
82
51
 
83
52
  def xml
84
- xml = <<-xml
85
- <w:fldSimple w:instr=" MERGEFIELD =first_name \\* MERGEFORMAT ">
86
- <w:r w:rsidR="004B49F0">
87
- <w:rPr><w:noProof/></w:rPr>
88
- <w:t>«=first_name»</w:t>
89
- </w:r>
90
- </w:fldSimple>
53
+ xml = <<-xml.strip
54
+ <w:p><w:fldSimple w:instr=" MERGEFIELD =first_name \\* MERGEFORMAT ">
55
+ <w:r w:rsidR="004B49F0">
56
+ <w:rPr><w:noProof/></w:rPr>
57
+ <w:t>«=first_name»</w:t>
58
+ </w:r>
59
+ </w:fldSimple></w:p>
91
60
  xml
92
61
  wrap(xml)
93
62
  end
@@ -101,75 +70,50 @@ xml
101
70
  end
102
71
 
103
72
  def test_replace
104
- field.replace("Hello")
105
- xml = <<-xml
106
- <w:r w:rsidR="004B49F0">
107
- <w:rPr>
108
- <w:b/>
109
- <w:noProof/>
110
- </w:rPr>
111
- <w:t>Hello</w:t>
112
- </w:r>
113
- xml
114
- assert_equal body_xml.strip, xml.strip
115
- end
73
+ field.replace(Sablon.string("Hello"))
74
+ xml = <<-xml.strip
75
+ <w:p>
116
76
 
117
- def test_replace_with_newlines
118
- field.replace("First\nSecond\n\nThird")
119
- xml = <<-xml
120
- <w:r w:rsidR="004B49F0">
121
- <w:rPr>
122
- <w:b/>
123
- <w:noProof/>
124
- </w:rPr>
125
- <w:t>First</w:t><w:br/><w:t>Second</w:t><w:br/><w:br/><w:t>Third</w:t>
126
- </w:r>
127
- xml
128
- assert_equal body_xml.strip, xml.strip
129
- end
130
-
131
- def test_replace_with_nil
132
- field.replace(nil)
133
77
 
134
- xml = <<-xml
135
78
  <w:r w:rsidR="004B49F0">
136
- <w:rPr>
137
- <w:b/>
138
- <w:noProof/>
139
- </w:rPr>
140
-
79
+ <w:rPr>
80
+ <w:b/>
81
+ <w:noProof/>
82
+ </w:rPr>
83
+ <w:t>Hello</w:t>
141
84
  </w:r>
85
+ </w:p>
142
86
  xml
143
- assert_equal body_xml.gsub(/^\s+$/,'').strip, xml.strip
87
+ assert_equal body_xml.strip, xml.strip
144
88
  end
145
89
 
146
90
  private
147
91
 
148
92
  def xml
149
- xml = <<-xml
150
- <w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
151
- <w:rPr><w:b/></w:rPr>
152
- <w:fldChar w:fldCharType="begin"/>
93
+ xml = <<-xml.strip
94
+ <w:p><w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
95
+ <w:rPr><w:b/></w:rPr>
96
+ <w:fldChar w:fldCharType="begin"/>
153
97
  </w:r>
154
98
  <w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
155
- <w:rPr><w:b/></w:rPr>
156
- <w:instrText xml:space="preserve"> MERGEFIELD =last_name \\* MERGEFORMAT </w:instrText>
99
+ <w:rPr><w:b/></w:rPr>
100
+ <w:instrText xml:space="preserve"> MERGEFIELD =last_name \\* MERGEFORMAT </w:instrText>
157
101
  </w:r>
158
102
  <w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
159
- <w:rPr><w:b/></w:rPr>
160
- <w:fldChar w:fldCharType="separate"/>
103
+ <w:rPr><w:b/></w:rPr>
104
+ <w:fldChar w:fldCharType="separate"/>
161
105
  </w:r>
162
106
  <w:r w:rsidR="004B49F0">
163
- <w:rPr>
164
- <w:b/>
165
- <w:noProof/>
166
- </w:rPr>
167
- <w:t>«=last_name»</w:t>
107
+ <w:rPr>
108
+ <w:b/>
109
+ <w:noProof/>
110
+ </w:rPr>
111
+ <w:t>«=last_name»</w:t>
168
112
  </w:r>
169
113
  <w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
170
- <w:rPr><w:b/></w:rPr>
171
- <w:fldChar w:fldCharType="end"/>
172
- </w:r>
114
+ <w:rPr><w:b/></w:rPr>
115
+ <w:fldChar w:fldCharType="end"/>
116
+ </w:r></w:p>
173
117
  xml
174
118
  wrap(xml)
175
119
  end
@@ -240,12 +184,12 @@ xml
240
184
 
241
185
  def xml
242
186
  xml = <<-xml
243
- <w:fldSimple w:instr=" MERGEFIELD =title \\* MERGEFORMAT ">
244
- <w:r w:rsidR="004B49F0">
245
- <w:rPr><w:noProof/></w:rPr>
246
- <w:t>«=title»</w:t>
247
- </w:r>
248
- </w:fldSimple>
187
+ <w:p><w:fldSimple w:instr=" MERGEFIELD =title \\* MERGEFORMAT ">
188
+ <w:r w:rsidR="004B49F0">
189
+ <w:rPr><w:noProof/></w:rPr>
190
+ <w:t>«=title»</w:t>
191
+ </w:r>
192
+ </w:fldSimple></w:p>
249
193
  xml
250
194
  wrap(xml)
251
195
  end
@@ -17,12 +17,14 @@ class ProcessorTest < Sablon::TestCase
17
17
 
18
18
  assert_equal "Hello! My Name is Jack , nice to meet you.", text(result)
19
19
  assert_xml_equal <<-document, result
20
+ <w:p>
20
21
  <w:r><w:t xml:space="preserve">Hello! My Name is </w:t></w:r>
21
22
  <w:r w:rsidR="004B49F0">
22
23
  <w:rPr><w:noProof/></w:rPr>
23
24
  <w:t>Jack</w:t>
24
25
  </w:r>
25
26
  <w:r w:rsidR="00BE47B1"><w:t xml:space="preserve">, nice to meet you.</w:t></w:r>
27
+ </w:p>
26
28
  document
27
29
  end
28
30
 
@@ -36,12 +38,14 @@ class ProcessorTest < Sablon::TestCase
36
38
 
37
39
  assert_equal "Hello! My Name is Zane , nice to meet you.", text(result)
38
40
  assert_xml_equal <<-document, result
41
+ <w:p>
39
42
  <w:r><w:t xml:space="preserve">Hello! My Name is </w:t></w:r>
40
43
  <w:r w:rsidR="004B49F0">
41
44
  <w:rPr><w:b/><w:noProof/></w:rPr>
42
45
  <w:t>Zane</w:t>
43
46
  </w:r>
44
47
  <w:r w:rsidR="00BE47B1"><w:t xml:space="preserve">, nice to meet you.</w:t></w:r>
48
+ </w:p>
45
49
  document
46
50
  end
47
51
 
@@ -50,12 +54,14 @@ class ProcessorTest < Sablon::TestCase
50
54
 
51
55
  assert_equal "Hello! My Name is Daniel , nice to meet you.", text(result)
52
56
  assert_xml_equal <<-document, result
57
+ <w:p>
53
58
  <w:r><w:t xml:space="preserve">Hello! My Name is </w:t></w:r>
54
59
  <w:r w:rsidR="00441382">
55
60
  <w:rPr><w:noProof/></w:rPr>
56
61
  <w:t>Daniel</w:t>
57
62
  </w:r>
58
63
  <w:r w:rsidR="00BE47B1"><w:t xml:space="preserve">, nice to meet you.</w:t></w:r>
64
+ </w:p>
59
65
  document
60
66
  end
61
67
 
data/test/sablon_test.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require "test_helper"
3
+ require "support/xml_snippets"
3
4
 
4
5
  class SablonTest < Sablon::TestCase
5
6
  include Sablon::Test::Assertions
7
+ include XMLSnippets
6
8
 
7
9
  def setup
8
10
  super
@@ -21,6 +23,7 @@ class SablonTest < Sablon::TestCase
21
23
  author: "Yves Senn",
22
24
  title: "Letter of application",
23
25
  person: person,
26
+ about_me: Sablon.word_ml(snippet("about_me_snippet").strip),
24
27
  items: [item.new("1.", "Ruby", "★" * 5), item.new("2.", "Java", "★" * 1), item.new("3.", "Python", "★" * 3)],
25
28
  career: [position.new("1999 - 2006", "Junior Java Engineer", "Lorem ipsum dolor\nsit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."),
26
29
  position.new("2006 - 2013", "Senior Ruby Developer", "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo."),
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sablon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yves Senn
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-17 00:00:00.000000000 Z
11
+ date: 2015-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -111,6 +111,8 @@ files:
111
111
  - Rakefile
112
112
  - bin/sablon
113
113
  - lib/sablon.rb
114
+ - lib/sablon/content.rb
115
+ - lib/sablon/context.rb
114
116
  - lib/sablon/operations.rb
115
117
  - lib/sablon/parser/mail_merge.rb
116
118
  - lib/sablon/processor.rb
@@ -122,6 +124,8 @@ files:
122
124
  - misc/output.png
123
125
  - misc/template.png
124
126
  - sablon.gemspec
127
+ - test/content_test.rb
128
+ - test/context_test.rb
125
129
  - test/executable_test.rb
126
130
  - test/expression_test.rb
127
131
  - test/fixtures/sablon_sample.docx
@@ -129,6 +133,7 @@ files:
129
133
  - test/fixtures/shopping_list_context.json
130
134
  - test/fixtures/shopping_list_sample.docx
131
135
  - test/fixtures/shopping_list_template.docx
136
+ - test/fixtures/xml/about_me_snippet.xml
132
137
  - test/fixtures/xml/complex_field.xml
133
138
  - test/fixtures/xml/conditional.xml
134
139
  - test/fixtures/xml/conditional_with_predicate.xml
@@ -175,6 +180,8 @@ signing_key:
175
180
  specification_version: 4
176
181
  summary: docx tempalte processor
177
182
  test_files:
183
+ - test/content_test.rb
184
+ - test/context_test.rb
178
185
  - test/executable_test.rb
179
186
  - test/expression_test.rb
180
187
  - test/fixtures/sablon_sample.docx
@@ -182,6 +189,7 @@ test_files:
182
189
  - test/fixtures/shopping_list_context.json
183
190
  - test/fixtures/shopping_list_sample.docx
184
191
  - test/fixtures/shopping_list_template.docx
192
+ - test/fixtures/xml/about_me_snippet.xml
185
193
  - test/fixtures/xml/complex_field.xml
186
194
  - test/fixtures/xml/conditional.xml
187
195
  - test/fixtures/xml/conditional_with_predicate.xml