sablon 0.0.3 → 0.0.4

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: ac6584d3c9681b51c131bb10ae1f76fc0b1b5538
4
- data.tar.gz: 80475970e6f321ef36650bf593f4e36e08a29101
3
+ metadata.gz: b9bed4d04a0e610c47abed3aa044222d37c7f716
4
+ data.tar.gz: 0942a8f508bc8198b41d4bee926e0fbf7e4a4ec3
5
5
  SHA512:
6
- metadata.gz: 16e48b0a357ae6449daad1dbf7d26329e4616f103fa53c568ecb01bfd7b36a5830a70a773e83c4c2504c906bcfaddf9689926dbbe0b3d5f830edc8634d4c0a31
7
- data.tar.gz: d56a4d4fcbeec941f44e0dd1f3dc96731a383c808cf3fc571a6cad71a6b13a98f057eba4a190215debe8c02d2e16bf93c48384fe8a815a2f0c5f614c3cf817d7
6
+ metadata.gz: 5cd8914ff55c1178b946fcb9704759d579816d332e2da8b69b7a0da9d6792bfc9489c9cd1d05cd3c48a37ca5dc9f3d7e3dcbeeed04bdda6308dd4e0defed3d5d
7
+ data.tar.gz: 04ae3adf13a090fdc4adca34d1b038281cc5468444217f4fcccdaf3e651c170a7eaab25742ef5d003a4e4e1a0c7d13dda651d2e51555aec4ee2e6b6f6e822741
data/README.md CHANGED
@@ -3,10 +3,10 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/sablon.svg)](http://badge.fury.io/rb/sablon) [![Build Status](https://travis-ci.org/senny/sablon.svg?branch=master)](https://travis-ci.org/senny/sablon)
4
4
 
5
5
  Is a document template processor for Word `docx` files. It leverages Word's
6
- built-in formatting and layouting capabilities to make it easy to create your
7
- templates.
6
+ built-in formatting and layouting capabilities to make template creation easy
7
+ and efficient.
8
8
 
9
- *Note: Sablon is still in early development. If you encounter any issues along the way please report.*
9
+ *Note: Sablon is still in early development. Please report if you encounter any issues along the way.*
10
10
 
11
11
  ## Installation
12
12
 
@@ -51,6 +51,10 @@ It's also possible to call a method on a context object using:
51
51
  «=post.title»
52
52
  ```
53
53
 
54
+ NOTE: The dot operator can also be used to perform a hash lookup.
55
+ This means that it's not possible to call methods on a hash instance.
56
+ Sablon will always try to make a lookup instead.
57
+
54
58
 
55
59
  #### Conditionals
56
60
 
@@ -97,6 +101,17 @@ to repeat. Have a look at the
97
101
  It is possible to nest loops and conditionals.
98
102
 
99
103
 
104
+ ### Executable
105
+
106
+ The `sablon` executable can be used to process templates on the command-line.
107
+ The usage is as follows:
108
+
109
+ ```
110
+ cat <context path>.json | sablon <template path> <output path>
111
+ ```
112
+
113
+ Have a look at [this test](test/executable_test.rb) for an example.
114
+
100
115
  ### Examples
101
116
 
102
117
  There is a [sample template](test/fixtures/sablon_template.docx) in the
@@ -130,7 +145,7 @@ For more details, check out this [test case](test/sablon_test.rb).
130
145
 
131
146
  ## Inspiration
132
147
 
133
- The following projects address a similar goal and inspired the work on Sablon:
148
+ These projects address a similar goal and inspired the work on Sablon:
134
149
 
135
150
  * [ruby-docx-templater](https://github.com/jawspeak/ruby-docx-templater)
136
151
  * [docx_mailmerge](https://github.com/annaswims/docx_mailmerge)
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'sablon'
4
+ require 'json'
5
+
6
+ if ARGV.size != 2
7
+ puts <<-HELP
8
+ cat <context path> | sablon <template path> <output path>
9
+ HELP
10
+ exit
11
+ end
12
+
13
+ template_path = File.expand_path(ARGV[0])
14
+ output_path = File.expand_path(ARGV[1])
15
+ context_json = STDIN.readlines.join
16
+ context = JSON.parse(context_json)
17
+
18
+ template = Sablon.template(template_path)
19
+ template.render_to_file(output_path, context)
@@ -52,20 +52,24 @@ module Sablon
52
52
  end
53
53
  end
54
54
 
55
- class SimpleMethodCall < Struct.new(:receiver, :method)
55
+ class LookupOrMethodCall < Struct.new(:receiver_expr, :method)
56
56
  def evaluate(context)
57
- receiver.evaluate(context).public_send method
57
+ receiver = receiver_expr.evaluate(context)
58
+ case receiver
59
+ when Hash; receiver[method]
60
+ else; receiver.public_send method
61
+ end
58
62
  end
59
63
 
60
64
  def inspect
61
- "«#{receiver.name}.#{method}»"
65
+ "«#{receiver_expr.name}.#{method}»"
62
66
  end
63
67
  end
64
68
 
65
69
  def self.parse(expression)
66
70
  if expression.include?(".")
67
71
  parts = expression.split(".")
68
- SimpleMethodCall.new(Variable.new(parts.first), parts.last)
72
+ LookupOrMethodCall.new(Variable.new(parts.first), parts.last)
69
73
  else
70
74
  Variable.new(expression)
71
75
  end
@@ -2,7 +2,7 @@ module Sablon
2
2
  module Parser
3
3
  class MailMerge
4
4
  class MergeField
5
- KEY_PATTERN = /^\s*MERGEFIELD ([^ ]+)\s+\\\* MERGEFORMAT\s*$/
5
+ KEY_PATTERN = /^\s*MERGEFIELD\s+([^ ]+)\s+\\\*\s+MERGEFORMAT\s*$/
6
6
  def expression
7
7
  $1 if @raw_expression =~ KEY_PATTERN
8
8
  end
@@ -1,3 +1,3 @@
1
1
  module Sablon
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -0,0 +1,23 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "test_helper"
3
+
4
+ class ExecutableTest < Sablon::TestCase
5
+ include Sablon::Test::Assertions
6
+
7
+ def setup
8
+ super
9
+ @base_path = Pathname.new(File.expand_path("../", __FILE__))
10
+ @output_path = @base_path + "sandbox/shopping_list.docx"
11
+ end
12
+
13
+ def test_generate_document_from_template
14
+ template_path = @base_path + "fixtures/shopping_list_template.docx"
15
+ context_path = @base_path + "fixtures/shopping_list_context.json"
16
+
17
+ executable_path = @base_path + '../bin/sablon'
18
+
19
+ `cat #{context_path} | #{executable_path} #{template_path} #{@output_path}`
20
+
21
+ assert_docx_equal @base_path + "fixtures/shopping_list_sample.docx", @output_path
22
+ end
23
+ end
@@ -16,13 +16,26 @@ class VariableExpressionTest < Sablon::TestCase
16
16
  end
17
17
  end
18
18
 
19
- class SimpleMethodCallTest < Sablon::TestCase
20
- def test_calls_method_on_context_variable
19
+ class LookupOrMethodCallTest < Sablon::TestCase
20
+ def test_calls_method_on_object
21
21
  user = OpenStruct.new(first_name: "Jack")
22
22
  expr = Sablon::Expression.parse("user.first_name")
23
23
  assert_equal "Jack", expr.evaluate({"user" => user})
24
24
  end
25
25
 
26
+ def test_calls_perform_lookup_on_hash_with_string_keys
27
+ user = {"first_name" => "Jack"}
28
+ expr = Sablon::Expression.parse("user.first_name")
29
+ assert_equal "Jack", expr.evaluate({"user" => user})
30
+ end
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
+
26
39
  def test_inspect
27
40
  expr = Sablon::Expression.parse("user.first_name")
28
41
  assert_equal "«user.first_name»", expr.inspect
@@ -0,0 +1,8 @@
1
+ {
2
+ "title": "Shopping List",
3
+ "items": [
4
+ {"name": "Milk", "amount": "1L"},
5
+ {"name": "Sugar", "amount": "1kg"},
6
+ {"name": "Tomatoes", "amount": "500g"}
7
+ ]
8
+ }
@@ -36,57 +36,60 @@ module MailMergeParser
36
36
 
37
37
  def test_replace
38
38
  field.replace("Hello")
39
- assert_equal <<-body_xml.strip, body_xml
40
- <w:r w:rsidR=\"004B49F0\">
39
+ xml = <<-xml
40
+ <w:r w:rsidR=\"004B49F0\">
41
41
  <w:rPr><w:noProof/></w:rPr>
42
42
  <w:t>Hello</w:t>
43
43
  </w:r>
44
- body_xml
44
+ xml
45
+ assert_equal body_xml.strip, xml.strip
45
46
  end
46
47
 
47
48
  def test_replace_with_newlines
48
49
  field.replace("First\nSecond\n\nThird")
49
-
50
- assert_equal <<-body_xml.strip, body_xml
50
+ xml = <<-xml
51
51
  <w:r w:rsidR=\"004B49F0\">
52
52
  <w:rPr><w:noProof/></w:rPr>
53
53
  <w:t>First</w:t><w:br/><w:t>Second</w:t><w:br/><w:br/><w:t>Third</w:t>
54
54
  </w:r>
55
- body_xml
55
+ xml
56
+ assert_equal body_xml.strip, xml.strip
56
57
  end
57
58
 
58
59
  def test_replace_with_nil
59
60
  field.replace(nil)
60
-
61
- assert_equal <<-body_xml.strip, body_xml.gsub(/^\s+$/,'')
61
+ xml = <<-xml
62
62
  <w:r w:rsidR=\"004B49F0\">
63
63
  <w:rPr><w:noProof/></w:rPr>
64
64
 
65
65
  </w:r>
66
- body_xml
66
+ xml
67
+ assert_equal body_xml.gsub(/^\s+$/,'').strip, xml.strip
67
68
  end
68
69
 
69
70
  def test_replace_with_numeric
70
71
  field.replace(45)
71
-
72
- assert_equal <<-body_xml.strip, body_xml.gsub(/^\s+$/,'')
72
+ xml = <<-xml
73
73
  <w:r w:rsidR=\"004B49F0\">
74
74
  <w:rPr><w:noProof/></w:rPr>
75
75
  <w:t>45</w:t>
76
76
  </w:r>
77
- body_xml
77
+ xml
78
+ assert_equal body_xml.gsub(/^\s+$/,'').strip, xml.strip
78
79
  end
79
80
 
80
81
  private
82
+
81
83
  def xml
82
- wrap(<<-xml)
84
+ xml = <<-xml
83
85
  <w:fldSimple w:instr=" MERGEFIELD =first_name \\* MERGEFORMAT ">
84
86
  <w:r w:rsidR="004B49F0">
85
87
  <w:rPr><w:noProof/></w:rPr>
86
88
  <w:t>«=first_name»</w:t>
87
89
  </w:r>
88
90
  </w:fldSimple>
89
- xml
91
+ xml
92
+ wrap(xml)
90
93
  end
91
94
  end
92
95
 
@@ -99,7 +102,7 @@ body_xml
99
102
 
100
103
  def test_replace
101
104
  field.replace("Hello")
102
- assert_equal <<-body_xml.strip, body_xml
105
+ xml = <<-xml
103
106
  <w:r w:rsidR="004B49F0">
104
107
  <w:rPr>
105
108
  <w:b/>
@@ -107,13 +110,13 @@ body_xml
107
110
  </w:rPr>
108
111
  <w:t>Hello</w:t>
109
112
  </w:r>
110
- body_xml
113
+ xml
114
+ assert_equal body_xml.strip, xml.strip
111
115
  end
112
116
 
113
117
  def test_replace_with_newlines
114
118
  field.replace("First\nSecond\n\nThird")
115
-
116
- assert_equal <<-body_xml.strip, body_xml
119
+ xml = <<-xml
117
120
  <w:r w:rsidR="004B49F0">
118
121
  <w:rPr>
119
122
  <w:b/>
@@ -121,13 +124,14 @@ body_xml
121
124
  </w:rPr>
122
125
  <w:t>First</w:t><w:br/><w:t>Second</w:t><w:br/><w:br/><w:t>Third</w:t>
123
126
  </w:r>
124
- body_xml
127
+ xml
128
+ assert_equal body_xml.strip, xml.strip
125
129
  end
126
130
 
127
131
  def test_replace_with_nil
128
132
  field.replace(nil)
129
133
 
130
- assert_equal <<-body_xml.strip, body_xml.gsub(/^\s+$/,'')
134
+ xml = <<-xml
131
135
  <w:r w:rsidR="004B49F0">
132
136
  <w:rPr>
133
137
  <w:b/>
@@ -135,12 +139,14 @@ body_xml
135
139
  </w:rPr>
136
140
 
137
141
  </w:r>
138
- body_xml
142
+ xml
143
+ assert_equal body_xml.gsub(/^\s+$/,'').strip, xml.strip
139
144
  end
140
145
 
141
146
  private
147
+
142
148
  def xml
143
- wrap(<<-xml)
149
+ xml = <<-xml
144
150
  <w:r w:rsidR="00BE47B1" w:rsidRPr="00BE47B1">
145
151
  <w:rPr><w:b/></w:rPr>
146
152
  <w:fldChar w:fldCharType="begin"/>
@@ -164,7 +170,8 @@ body_xml
164
170
  <w:rPr><w:b/></w:rPr>
165
171
  <w:fldChar w:fldCharType="end"/>
166
172
  </w:r>
167
- xml
173
+ xml
174
+ wrap(xml)
168
175
  end
169
176
  end
170
177
 
@@ -176,8 +183,9 @@ body_xml
176
183
  end
177
184
 
178
185
  private
186
+
179
187
  def xml
180
- wrap(<<-xml)
188
+ xml = <<-xml
181
189
  <w:p w14:paraId="0CF428D7" w14:textId="77777777" w:rsidR="00043618" w:rsidRDefault="00043618" w:rsidP="00B960C2">
182
190
  <w:pPr>
183
191
  <w:pStyle w:val="Footer" />
@@ -218,7 +226,28 @@ body_xml
218
226
  <w:fldChar w:fldCharType="end" />
219
227
  </w:r>
220
228
  </w:p>
221
- xml
229
+ xml
230
+ wrap(xml)
231
+ end
232
+ end
233
+
234
+ class FieldWithWhitespaceTest < Sablon::TestCase
235
+ include SharedBehavior
236
+
237
+ def test_recognizes_expression
238
+ assert_equal ["=title"], fields.map(&:expression)
239
+ end
240
+
241
+ def xml
242
+ 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>
249
+ xml
250
+ wrap(xml)
222
251
  end
223
252
  end
224
253
  end
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.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yves Senn
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-17 00:00:00.000000000 Z
11
+ date: 2015-02-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -98,7 +98,8 @@ description: Sablon is a document template processor. At this time it works only
98
98
  docx and MailMerge fields.
99
99
  email:
100
100
  - yves.senn@gmail.com
101
- executables: []
101
+ executables:
102
+ - sablon
102
103
  extensions: []
103
104
  extra_rdoc_files: []
104
105
  files:
@@ -108,6 +109,7 @@ files:
108
109
  - LICENSE.txt
109
110
  - README.md
110
111
  - Rakefile
112
+ - bin/sablon
111
113
  - lib/sablon.rb
112
114
  - lib/sablon/operations.rb
113
115
  - lib/sablon/parser/mail_merge.rb
@@ -120,9 +122,13 @@ files:
120
122
  - misc/output.png
121
123
  - misc/template.png
122
124
  - sablon.gemspec
125
+ - test/executable_test.rb
123
126
  - test/expression_test.rb
124
127
  - test/fixtures/sablon_sample.docx
125
128
  - test/fixtures/sablon_template.docx
129
+ - test/fixtures/shopping_list_context.json
130
+ - test/fixtures/shopping_list_sample.docx
131
+ - test/fixtures/shopping_list_template.docx
126
132
  - test/fixtures/xml/complex_field.xml
127
133
  - test/fixtures/xml/conditional.xml
128
134
  - test/fixtures/xml/conditional_with_predicate.xml
@@ -163,14 +169,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
163
169
  version: '0'
164
170
  requirements: []
165
171
  rubyforge_project:
166
- rubygems_version: 2.2.2
172
+ rubygems_version: 2.4.5
167
173
  signing_key:
168
174
  specification_version: 4
169
175
  summary: docx tempalte processor
170
176
  test_files:
177
+ - test/executable_test.rb
171
178
  - test/expression_test.rb
172
179
  - test/fixtures/sablon_sample.docx
173
180
  - test/fixtures/sablon_template.docx
181
+ - test/fixtures/shopping_list_context.json
182
+ - test/fixtures/shopping_list_sample.docx
183
+ - test/fixtures/shopping_list_template.docx
174
184
  - test/fixtures/xml/complex_field.xml
175
185
  - test/fixtures/xml/conditional.xml
176
186
  - test/fixtures/xml/conditional_with_predicate.xml