serenity-odt 0.2.1 → 0.3.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a7be056fd011f7a963e1b60902e82dd9e119c88915c5bf9304c1a2095f8a9f35
4
+ data.tar.gz: 6ac2a7386e6235325520d8e4f4a0f2eac72a4316b4bd9bb7de9bb263b4d79105
5
+ SHA512:
6
+ metadata.gz: b11f27eebe9a903ecb5524bccb07892c27380da92576cb319e4ec9137902359eec6430b0f0e74e501be5f8dd2e675bc4af45fa1a24133b9311388dcb07b0e105
7
+ data.tar.gz: f2a2d498e108ed501999492902de684a38c877cc778441db6835b882c61295a2ef5ff473a3152f04f1940ea18b553941e49fe6b0b42cfe65ed92535a78ea8d7e
data/Rakefile CHANGED
@@ -1,32 +1,8 @@
1
1
  require 'rake'
2
- require 'spec/rake/spectask'
3
- require 'rake/gempackagetask'
2
+ require 'rspec/core/rake_task'
4
3
 
5
4
  task :default => [:spec]
6
5
 
7
- Spec::Rake::SpecTask.new("spec")
6
+ RSpec::Core::RakeTask.new(:spec)
8
7
 
9
- spec = Gem::Specification.new do |s|
10
- s.name = %q{serenity-odt}
11
- s.version = "0.2.1"
12
-
13
- s.authors = ["Tomas Kramar"]
14
- s.description = <<-EOF
15
- Embedded ruby for OpenOffice Text Document (.odt) files. You provide an .odt template
16
- with ruby code in a special markup and the data, and Serenity generates the document.
17
- Very similar to .erb files.
18
- EOF
19
- s.email = %q{kramar.tomas@gmail.com}
20
- s.files = Dir.glob('lib/**/*.rb') + %w{README.md Rakefile LICENSE}
21
- s.has_rdoc = false
22
- s.require_paths = ["lib"]
23
- s.rubygems_version = %q{1.3.5}
24
- s.summary = %q{Embedded ruby for OpenOffice Text Document (.odt) files}
25
- s.test_files = Dir.glob('spec/**/*.rb') + Dir.glob('fixtures/*.odt')
26
- s.add_dependency('rubyzip', '>= 0.9.1')
27
- s.add_development_dependency('rspec', '>= 1.2.9')
28
- end
29
-
30
- Rake::GemPackageTask.new(spec) do |p|
31
- p.gem_spec = spec
32
- end
8
+ # Build the gem with: gem build serenity-odt.gemspec
@@ -1,18 +1,16 @@
1
1
  class String
2
2
  def escape_xml
3
- mgsub!([[/&/, '&amp;'], [/</, '&lt;'], [/>/, '&gt;']])
3
+ mgsub([[/&/, '&amp;'], [/</, '&lt;'], [/>/, '&gt;']])
4
4
  end
5
5
 
6
6
  def convert_newlines
7
- gsub!("\n", '<text:line-break/>')
8
- self
7
+ gsub("\n", '<text:line-break/>')
9
8
  end
10
9
 
11
- def mgsub!(key_value_pairs=[].freeze)
10
+ def mgsub(key_value_pairs=[].freeze)
12
11
  regexp_fragments = key_value_pairs.collect { |k,v| k }
13
- gsub!(Regexp.union(*regexp_fragments)) do |match|
12
+ gsub(Regexp.union(*regexp_fragments)) do |match|
14
13
  key_value_pairs.detect{|k,v| k =~ match}[1]
15
14
  end
16
- self
17
15
  end
18
16
  end
@@ -0,0 +1,32 @@
1
+ module Serenity
2
+ class ImagesProcessor
3
+ include Debug
4
+
5
+ IMAGE_DIR_NAME = "Pictures"
6
+
7
+ def initialize(xml_content, context)
8
+ @replacements = []
9
+ @images = eval('@images', context)
10
+ @xml_content = xml_content
11
+ end
12
+
13
+ def generate_replacements
14
+ require 'nokogiri'
15
+
16
+ if @images && @images.kind_of?(Hash)
17
+ xml_data = Nokogiri::XML(@xml_content)
18
+
19
+ @images.each do |image_name, replacement_path|
20
+ if node = xml_data.xpath("//draw:frame[@draw:name='#{image_name}']/draw:image").first
21
+ placeholder_path = node.attribute('href').value
22
+ odt_image_path = ::File.join(IMAGE_DIR_NAME, ::File.basename(placeholder_path))
23
+
24
+ @replacements << [odt_image_path, replacement_path]
25
+ end
26
+ end
27
+ end
28
+
29
+ @replacements
30
+ end
31
+ end
32
+ end
data/lib/serenity/line.rb CHANGED
@@ -44,7 +44,7 @@ module Serenity
44
44
  end
45
45
 
46
46
  def escape_code code
47
- code.mgsub! [[/&apos;/, "'"], [/&gt;/, '>'], [/&lt/, '<'], [/&quot;/, '"'], [/&amp;/, '&']]
47
+ code.mgsub [[/&apos;/, "'"], [/&gt;/, '>'], [/&lt/, '<'], [/&quot;/, '"'], [/&amp;/, '&']]
48
48
  end
49
49
  end
50
50
 
@@ -14,6 +14,7 @@ module Serenity
14
14
  end
15
15
 
16
16
  def evaluate context
17
+ @src = @src.force_encoding Encoding.default_external
17
18
  eval(@src, context)
18
19
  end
19
20
 
@@ -21,6 +22,9 @@ module Serenity
21
22
 
22
23
  def convert template
23
24
  src = "_buf = '';"
25
+ # `buffer`/`buffer_next` stay flat arrays of Line objects, so appending with `concat`
26
+ # keeps them flat in O(k). (The old `buffer << arr; buffer.flatten!` rescanned the whole
27
+ # buffer on every node, making `convert` O(n^2) in the number of template nodes.)
24
28
  buffer = []
25
29
  buffer_next = []
26
30
 
@@ -32,8 +36,7 @@ module Serenity
32
36
  elsif is_nonpair_tag? node
33
37
  next
34
38
  else
35
- buffer << buffer_next
36
- buffer.flatten!
39
+ buffer.concat(buffer_next)
37
40
  buffer_next = []
38
41
  end
39
42
  end
@@ -41,8 +44,7 @@ module Serenity
41
44
  if type == NodeType::CONTROL
42
45
  buffer_next = process_instruction(node)
43
46
  else
44
- buffer << process_instruction(node)
45
- buffer.flatten!
47
+ buffer.concat(process_instruction(node))
46
48
  end
47
49
  end
48
50
 
@@ -1,7 +1,15 @@
1
- require 'zip/zip'
1
+ begin
2
+ require 'zip' # rubyzip >= 1.0
3
+ rescue LoadError
4
+ require 'zip/zip' # rubyzip 0.9.x (legacy)
5
+ end
2
6
  require 'fileutils'
7
+ require 'tempfile'
3
8
 
4
9
  module Serenity
10
+ # rubyzip renamed Zip::ZipFile to Zip::File in 1.0; support both.
11
+ ZipFile = defined?(::Zip::File) ? ::Zip::File : ::Zip::ZipFile
12
+
5
13
  class Template
6
14
  attr_accessor :template
7
15
 
@@ -12,18 +20,178 @@ module Serenity
12
20
 
13
21
  def process context
14
22
  tmpfiles = []
15
- Zip::ZipFile.open(@template) do |zipfile|
23
+ ZipFile.open(@template) do |zipfile|
24
+ # Pre-read embedded object templates for inline processing
25
+ object_entries = zipfile.entries
26
+ .map(&:name)
27
+ .select { |name| name.match?(%r{\AObject \d+/(content|styles)\.xml\z}) }
28
+
29
+ object_templates = {}
30
+ object_entries.each { |name| object_templates[name] = zipfile.read(name) }
31
+
32
+ objects_by_dir = object_templates.keys.group_by { |name| name.split('/').first }
33
+
34
+ # Find highest existing object number for generating unique names
35
+ max_obj_num = zipfile.entries.map(&:name)
36
+ .grep(%r{\AObject (\d+)/}) { $1.to_i }.max || 0
37
+
38
+ # Pre-read all object-related files for copying to duplicates
39
+ object_all_files = {}
40
+ zipfile.entries.each do |entry|
41
+ name = entry.name
42
+ if name.match?(%r{\AObject \d+/}) || name.match?(%r{\AObjectReplacements/Object \d+\z})
43
+ object_all_files[name] = zipfile.read(name)
44
+ end
45
+ end
46
+
47
+ # Counters and results shared by processor lambdas
48
+ obj_counter = Hash.new(0)
49
+ obj_results = {}
50
+
51
+ # Build a processor lambda per object directory.
52
+ # Each call evaluates the embedded template with the current binding
53
+ # (capturing loop variables like `person`), saves/restores _buf to avoid
54
+ # clobbering the outer template buffer, and stores the result keyed by
55
+ # iteration number.
56
+ processors = {}
57
+ objects_by_dir.each do |obj_dir, obj_files|
58
+ processors[obj_dir] = lambda do |ctx|
59
+ buf_save = ctx.local_variable_get(:_buf)
60
+
61
+ obj_counter[obj_dir] += 1
62
+ n = obj_counter[obj_dir]
63
+
64
+ obj_files.each do |name|
65
+ file_part = name.split('/').last
66
+ obj_results["#{obj_dir}__#{n}/#{file_part}"] =
67
+ OdtEruby.new(XmlReader.new(object_templates[name])).evaluate(ctx)
68
+ end
69
+
70
+ ctx.local_variable_set(:_buf, buf_save)
71
+ end
72
+ end
73
+
16
74
  %w(content.xml styles.xml).each do |xml_file|
17
75
  content = zipfile.read(xml_file)
76
+
77
+ # Images replacement
78
+ images_replacements = ImagesProcessor.new(content, context).generate_replacements
79
+ images_replacements.each do |r|
80
+ zipfile.replace(r.first, r.last)
81
+ end
82
+
83
+ # Inject inline processing for embedded objects at their draw:object
84
+ # reference points so loop variables are in scope during evaluation
85
+ if xml_file == 'content.xml' && !processors.empty?
86
+ context.local_variable_set(:_serenity_processors, processors)
87
+
88
+ objects_by_dir.each do |obj_dir, _|
89
+ ref_pattern = /(<draw:object[^>]*?xlink:href="\.\/#{Regexp.escape(obj_dir)}"[^>]*?\/>.*?<\/draw:frame>)/m
90
+ content = content.sub(ref_pattern, "\\1{% _serenity_processors['#{obj_dir}'].call(binding) %}")
91
+ end
92
+ end
93
+
18
94
  odteruby = OdtEruby.new(XmlReader.new(content))
19
95
  out = odteruby.evaluate(context)
96
+ out.force_encoding Encoding.default_external
97
+
98
+ # Post-process content.xml: give each loop iteration its own object
99
+ if xml_file == 'content.xml'
100
+ objects_by_dir.each do |obj_dir, obj_files|
101
+ count = obj_counter[obj_dir]
102
+
103
+ # Rename each draw:frame's object references to a unique object dir
104
+ iteration = 0
105
+ out = out.gsub(/<draw:frame[^>]*>.*?<\/draw:frame>/m) do |frame|
106
+ if frame.include?("./#{obj_dir}")
107
+ iteration += 1
108
+ new_obj_dir = iteration == 1 ? obj_dir : "Object #{max_obj_num + iteration - 1}"
109
+ frame.gsub(obj_dir, new_obj_dir)
110
+ else
111
+ frame
112
+ end
113
+ end
114
+
115
+ # Write evaluated templates and supporting files for each iteration
116
+ (1..count).each do |n|
117
+ final_obj = n == 1 ? obj_dir : "Object #{max_obj_num + n - 1}"
118
+
119
+ # Write evaluated content.xml / styles.xml
120
+ obj_files.each do |name|
121
+ file_part = name.split('/').last
122
+ result = obj_results["#{obj_dir}__#{n}/#{file_part}"]
123
+ next unless result
124
+
125
+ result.force_encoding Encoding.default_external
126
+ tmpfiles << (file = Tempfile.new("serenity"))
127
+ file << result
128
+ file.close
129
+
130
+ final_name = "#{final_obj}/#{file_part}"
131
+ if zipfile.find_entry(final_name)
132
+ zipfile.replace(final_name, file.path)
133
+ else
134
+ zipfile.add(final_name, file.path)
135
+ end
136
+ end
137
+
138
+ # Copy non-template files (meta.xml, ObjectReplacements) for new objects
139
+ next if n == 1
140
+ object_all_files.each do |name, data|
141
+ next if object_templates.key?(name)
142
+
143
+ new_name = nil
144
+ if name.start_with?("#{obj_dir}/")
145
+ new_name = name.sub(obj_dir, final_obj)
146
+ elsif name == "ObjectReplacements/#{obj_dir}"
147
+ new_name = "ObjectReplacements/#{final_obj}"
148
+ end
149
+
150
+ if new_name
151
+ tmpfiles << (file = Tempfile.new("serenity"))
152
+ file.binmode
153
+ file << data
154
+ file.close
155
+ zipfile.add(new_name, file.path)
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
20
161
 
21
162
  tmpfiles << (file = Tempfile.new("serenity"))
22
163
  file << out
23
164
  file.close
24
-
25
165
  zipfile.replace(xml_file, file.path)
26
166
  end
167
+
168
+ # Update manifest.xml with entries for new object directories
169
+ if obj_counter.values.any? { |c| c > 1 }
170
+ manifest = zipfile.read('META-INF/manifest.xml')
171
+ new_entries = ""
172
+
173
+ objects_by_dir.each do |obj_dir, _|
174
+ count = obj_counter[obj_dir]
175
+ (2..count).each do |n|
176
+ new_obj = "Object #{max_obj_num + n - 1}"
177
+ new_entries << %( <manifest:file-entry manifest:media-type="application/vnd.oasis.opendocument.chart" manifest:full-path="#{new_obj}/"/>\n)
178
+ new_entries << %( <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="#{new_obj}/content.xml"/>\n)
179
+ new_entries << %( <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="#{new_obj}/styles.xml"/>\n)
180
+ if object_all_files["#{obj_dir}/meta.xml"]
181
+ new_entries << %( <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="#{new_obj}/meta.xml"/>\n)
182
+ end
183
+ if object_all_files["ObjectReplacements/#{obj_dir}"]
184
+ new_entries << %( <manifest:file-entry manifest:media-type="application/x-openoffice-gdimetafile;windows_formatname=&quot;GDIMetaFile&quot;" manifest:full-path="ObjectReplacements/#{new_obj}"/>\n)
185
+ end
186
+ end
187
+ end
188
+
189
+ manifest = manifest.sub("</manifest:manifest>", "#{new_entries}</manifest:manifest>")
190
+ tmpfiles << (file = Tempfile.new("serenity"))
191
+ file << manifest
192
+ file.close
193
+ zipfile.replace('META-INF/manifest.xml', file.path)
194
+ end
27
195
  end
28
196
  end
29
197
  end
@@ -2,7 +2,7 @@ module Serenity
2
2
  class XmlReader
3
3
 
4
4
  def initialize src
5
- @src = src
5
+ @src = src.force_encoding("UTF-8")
6
6
  end
7
7
 
8
8
  def each_node
data/lib/serenity.rb CHANGED
@@ -4,6 +4,7 @@ require 'serenity/debug'
4
4
  require 'serenity/node_type'
5
5
  require 'serenity/escape_xml'
6
6
  require 'serenity/odteruby'
7
+ require 'serenity/images_processor'
7
8
  require 'serenity/template'
8
9
  require 'serenity/xml_reader'
9
10
  require 'serenity/generator'
metadata CHANGED
@@ -1,133 +1,96 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: serenity-odt
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 2
8
- - 1
9
- version: 0.2.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
10
5
  platform: ruby
11
- authors:
12
- - Tomas Kramar
13
- autorequire:
6
+ authors:
7
+ - kremso
14
8
  bindir: bin
15
9
  cert_chain: []
16
-
17
- date: 2010-11-03 00:00:00 +01:00
18
- default_executable:
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
21
13
  name: rubyzip
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 0.9.1
19
+ type: :runtime
22
20
  prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
- none: false
25
- requirements:
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
26
23
  - - ">="
27
- - !ruby/object:Gem::Version
28
- segments:
29
- - 0
30
- - 9
31
- - 1
24
+ - !ruby/object:Gem::Version
32
25
  version: 0.9.1
26
+ - !ruby/object:Gem::Dependency
27
+ name: nokogiri
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '1.0'
33
33
  type: :runtime
34
- version_requirements: *id001
35
- - !ruby/object:Gem::Dependency
36
- name: rspec
37
34
  prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
39
- none: false
40
- requirements:
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '1.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rspec
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
41
44
  - - ">="
42
- - !ruby/object:Gem::Version
43
- segments:
44
- - 1
45
- - 2
46
- - 9
47
- version: 1.2.9
45
+ - !ruby/object:Gem::Version
46
+ version: '3.0'
48
47
  type: :development
49
- version_requirements: *id002
50
- description: " Embedded ruby for OpenOffice Text Document (.odt) files. You provide an .odt template\n with ruby code in a special markup and the data, and Serenity generates the document.\n Very similar to .erb files.\n"
51
- email: kramar.tomas@gmail.com
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '3.0'
54
+ description: Embedded ruby for OpenOffice/LibreOffice Text Document (.odt) files.
55
+ You provide an .odt template with ruby code in a special markup and the data, and
56
+ Serenity generates the document. Very similar to .erb files.
57
+ email: ''
52
58
  executables: []
53
-
54
59
  extensions: []
55
-
56
60
  extra_rdoc_files: []
57
-
58
- files:
61
+ files:
62
+ - LICENSE
63
+ - README.md
64
+ - Rakefile
65
+ - lib/serenity.rb
59
66
  - lib/serenity/debug.rb
60
67
  - lib/serenity/escape_xml.rb
61
68
  - lib/serenity/generator.rb
69
+ - lib/serenity/images_processor.rb
62
70
  - lib/serenity/line.rb
63
71
  - lib/serenity/node_type.rb
64
72
  - lib/serenity/odteruby.rb
65
73
  - lib/serenity/template.rb
66
74
  - lib/serenity/xml_reader.rb
67
- - lib/serenity.rb
68
- - README.md
69
- - Rakefile
70
- - LICENSE
71
- - spec/escape_xml_spec.rb
72
- - spec/generator_spec.rb
73
- - spec/odteruby_spec.rb
74
- - spec/spec_helper.rb
75
- - spec/support/matchers/be_a_document.rb
76
- - spec/support/matchers/contain_in.rb
77
- - spec/template_spec.rb
78
- - spec/xml_reader_spec.rb
79
- - fixtures/advanced.odt
80
- - fixtures/footer.odt
81
- - fixtures/header.odt
82
- - fixtures/loop.odt
83
- - fixtures/loop_table.odt
84
- - fixtures/table_rows.odt
85
- - fixtures/variables.odt
86
- has_rdoc: true
87
- homepage:
88
- licenses: []
89
-
90
- post_install_message:
75
+ homepage: https://github.com/kremso/serenity
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
91
79
  rdoc_options: []
92
-
93
- require_paths:
80
+ require_paths:
94
81
  - lib
95
- required_ruby_version: !ruby/object:Gem::Requirement
96
- none: false
97
- requirements:
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
98
84
  - - ">="
99
- - !ruby/object:Gem::Version
100
- segments:
101
- - 0
102
- version: "0"
103
- required_rubygems_version: !ruby/object:Gem::Requirement
104
- none: false
105
- requirements:
85
+ - !ruby/object:Gem::Version
86
+ version: 2.6.0
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
106
89
  - - ">="
107
- - !ruby/object:Gem::Version
108
- segments:
109
- - 0
110
- version: "0"
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
111
92
  requirements: []
112
-
113
- rubyforge_project:
114
- rubygems_version: 1.3.7
115
- signing_key:
116
- specification_version: 3
117
- summary: Embedded ruby for OpenOffice Text Document (.odt) files
118
- test_files:
119
- - spec/escape_xml_spec.rb
120
- - spec/generator_spec.rb
121
- - spec/odteruby_spec.rb
122
- - spec/spec_helper.rb
123
- - spec/support/matchers/be_a_document.rb
124
- - spec/support/matchers/contain_in.rb
125
- - spec/template_spec.rb
126
- - spec/xml_reader_spec.rb
127
- - fixtures/advanced.odt
128
- - fixtures/footer.odt
129
- - fixtures/header.odt
130
- - fixtures/loop.odt
131
- - fixtures/loop_table.odt
132
- - fixtures/table_rows.odt
133
- - fixtures/variables.odt
93
+ rubygems_version: 4.0.14
94
+ specification_version: 4
95
+ summary: Parse ODT file and substitutes placeholders like ERb.
96
+ test_files: []
Binary file
data/fixtures/footer.odt DELETED
Binary file
data/fixtures/header.odt DELETED
Binary file
data/fixtures/loop.odt DELETED
Binary file
Binary file
Binary file
Binary file
@@ -1,19 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- describe String do
4
- it 'should escape <' do
5
- '1 < 2'.escape_xml.should == '1 &lt; 2'
6
- end
7
-
8
- it 'should escape >' do
9
- '2 > 1'.escape_xml.should == '2 &gt; 1'
10
- end
11
-
12
- it 'should escape &' do
13
- '1 & 2'.escape_xml.should == '1 &amp; 2'
14
- end
15
-
16
- it 'should escape < > &' do
17
- '1 < 2 && 2 > 1'.escape_xml.should == '1 &lt; 2 &amp;&amp; 2 &gt; 1'
18
- end
19
- end
@@ -1,25 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- module Serenity
4
- describe Generator do
5
- after(:each) do
6
- FileUtils.rm(fixture('loop_output.odt'))
7
- end
8
-
9
- it 'should make context from instance variables and run the provided template' do
10
- class GeneratorClient
11
- include Serenity::Generator
12
-
13
- def generate_odt
14
- @crew = ['Mal', 'Inara', 'Wash', 'Zoe']
15
-
16
- render_odt fixture('loop.odt')
17
- end
18
- end
19
-
20
- client = GeneratorClient.new
21
- lambda { client.generate_odt }.should_not raise_error
22
- fixture('loop_output.odt').should be_a_document
23
- end
24
- end
25
- end
@@ -1,102 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- module Serenity
4
-
5
- describe OdtEruby do
6
- before(:each) do
7
- name = 'test_name'
8
- type = 'test_type'
9
- rows = []
10
- rows << Ship.new('test_name_1', 'test_type_1')
11
- rows << Ship.new('test_name_2', 'test_type_2')
12
- @context = binding
13
- end
14
-
15
- def squeeze text
16
- text.each_char.inject('') { |memo, line| memo += line.strip } unless text.nil?
17
- end
18
-
19
- def run_spec template, expected, context=@context
20
- content = OdtEruby.new(XmlReader.new template)
21
- result = content.evaluate context
22
-
23
- squeeze(result).should == squeeze(expected)
24
- end
25
-
26
- it 'should escape single quotes properly' do
27
- expected = template = "<text:p>It's a 'quote'</text:p>"
28
-
29
- run_spec template, expected
30
- end
31
-
32
- it 'should properly escape special XML characters ("<", ">", "&")' do
33
- template = "<text:p>{%= description %}</text:p>"
34
- description = 'This will only hold true if length < 1 && var == true or length > 1000'
35
- expected = "<text:p>This will only hold true if length &lt; 1 &amp;&amp; var == true or length &gt; 1000</text:p>"
36
-
37
- run_spec template, expected, binding
38
- end
39
-
40
- it 'should replace variables with values from context' do
41
- template = <<-EOF
42
- <text:p text:style-name="Text_1_body">{%= name %}</text:p>
43
- <text:p text:style-name="Text_1_body">{%= type %}</text:p>
44
- <text:p text:style-name="Text_1_body"/>
45
- EOF
46
-
47
- expected = <<-EOF
48
- <text:p text:style-name="Text_1_body">test_name</text:p>
49
- <text:p text:style-name="Text_1_body">test_type</text:p>
50
- <text:p text:style-name="Text_1_body"/>
51
- EOF
52
-
53
- run_spec template, expected
54
- end
55
-
56
- it 'should replace multiple variables on one line' do
57
- template = '<text:p text:style-name="Text_1_body">{%= type %} and {%= name %}</text:p>'
58
- expected = '<text:p text:style-name="Text_1_body">test_type and test_name</text:p>'
59
-
60
- run_spec template, expected
61
- end
62
-
63
- it 'should remove empty tags after a control structure processing' do
64
- template = <<-EOF
65
- <table:table style="Table_1">
66
- <table:row style="Table_1_A1">
67
- <table:cell style="Table_1_A1_cell">
68
- {% for row in rows do %}
69
- </table:cell>
70
- </table:row>
71
- <text:p text:style-name="Text_1_body">{%= row.name %}</text:p>
72
- <text:p text:style-name="Text_1_body">{%= row.type %}</text:p>
73
- <table:row style="Table_1_A1">
74
- <table:cell style="Table_1_A1_cell">
75
- {% end %}
76
- </table:cell>
77
- </table:row>
78
- </table:table>
79
- EOF
80
-
81
- expected = <<-EOF
82
- <table:table style="Table_1">
83
- <text:p text:style-name="Text_1_body">test_name_1</text:p>
84
- <text:p text:style-name="Text_1_body">test_type_1</text:p>
85
- <text:p text:style-name="Text_1_body">test_name_2</text:p>
86
- <text:p text:style-name="Text_1_body">test_type_2</text:p>
87
- </table:table>
88
- EOF
89
-
90
- run_spec template, expected
91
- end
92
-
93
- it 'should replace \n with soft newlines' do
94
- text_with_newline = "First line\nSecond line"
95
-
96
- template = '<text:p text:style-name="P2">{%= text_with_newline %}</text:p>'
97
- expected = '<text:p text:style-name="P2">First line <text:line-break/>Second line</text:p>'
98
-
99
- run_spec template, expected, binding
100
- end
101
- end
102
- end
data/spec/spec_helper.rb DELETED
@@ -1,12 +0,0 @@
1
- $:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
2
-
3
- require 'rubygems'
4
- require 'serenity'
5
- Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}
6
-
7
- Ship = Struct.new(:name, :type)
8
- Person = Struct.new(:name, :skill)
9
-
10
- def fixture name
11
- File.join(File.dirname(__FILE__), '..', 'fixtures', name)
12
- end
@@ -1,16 +0,0 @@
1
- module Serenity
2
- Spec::Matchers.define :be_a_document do
3
- match do |actual|
4
- File.exists? actual
5
- end
6
-
7
- failure_message_for_should do |actual|
8
- "expected that a file #{actual} would exist"
9
- end
10
-
11
- failure_message_for_should_not do |actual|
12
- "expected that a file #{actual} would not exist"
13
- end
14
-
15
- end
16
- end
@@ -1,20 +0,0 @@
1
- require "zip/zip"
2
-
3
- module Serenity
4
- Spec::Matchers.define :contain_in do |xml_file, expected|
5
-
6
- match do |actual|
7
- content = Zip::ZipFile.open(actual) { |zip_file| zip_file.read(xml_file) }
8
- content =~ Regexp.new(".*#{Regexp.escape(expected)}.*")
9
- end
10
-
11
- failure_message_for_should do |actual|
12
- "expected #{actual} to contain the text #{expected}"
13
- end
14
-
15
- failure_message_for_should_not do |actual|
16
- "expected #{actual} to not contain the text #{expected}"
17
- end
18
-
19
- end
20
- end
@@ -1,79 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
- require 'fileutils'
3
-
4
- module Serenity
5
-
6
- describe Template do
7
-
8
- after(:each) do
9
- FileUtils.rm(Dir['*.odt'])
10
- end
11
-
12
- it "should process a document with simple variable substitution" do
13
- @name = 'Malcolm Reynolds'
14
- @title = 'captain'
15
-
16
- template = Template.new(fixture('variables.odt'), 'output_variables.odt')
17
- template.process binding
18
-
19
- 'output_variables.odt'.should contain_in('content.xml', 'Malcolm Reynolds')
20
- 'output_variables.odt'.should contain_in('content.xml', 'captain')
21
- end
22
-
23
- it "should unroll a simple for loop" do
24
- @crew = %w{'River', 'Jayne', 'Wash'}
25
-
26
- template = Template.new(fixture('loop.odt'), 'output_loop.odt')
27
- template.process binding
28
- end
29
-
30
- it "should unroll an advanced loop with tables" do
31
- @ships = [Ship.new('Firefly', 'transport'), Ship.new('Colonial', 'battle')]
32
-
33
- template = Template.new(fixture('loop_table.odt'), 'output_loop_table.odt')
34
- template.process binding
35
-
36
- ['Firefly', 'transport', 'Colonial', 'battle'].each do |text|
37
- 'output_loop_table.odt'.should contain_in('content.xml', text)
38
- end
39
- end
40
-
41
- it "should process an advanced document" do
42
- @persons = [Person.new('Malcolm', 'captain'), Person.new('River', 'psychic'), Person.new('Jay', 'gunslinger')]
43
-
44
- template = Template.new(fixture('advanced.odt'), 'output_advanced.odt')
45
- template.process binding
46
-
47
- ['Malcolm', 'captain', 'River', 'psychic', 'Jay', 'gunslinger'].each do |text|
48
- 'output_advanced.odt'.should contain_in('content.xml', text)
49
- end
50
- end
51
-
52
- it "should loop and generate table rows" do
53
- @ships = [Ship.new('Firefly', 'transport'), Ship.new('Colonial', 'battle')]
54
-
55
- template = Template.new(fixture('table_rows.odt'), 'output_table_rows.odt')
56
- template.process binding
57
-
58
- ['Firefly', 'transport', 'Colonial', 'battle'].each do |text|
59
- 'output_table_rows.odt'.should contain_in('content.xml', text)
60
- end
61
- end
62
-
63
- it "should parse the header" do
64
- @title = 'captain'
65
-
66
- template = Template.new(fixture('header.odt'), 'output_header.odt')
67
- template.process(binding)
68
- 'output_header.odt'.should contain_in('styles.xml', 'captain')
69
- end
70
-
71
- it 'should parse the footer' do
72
- @title = 'captain'
73
-
74
- template = Template.new(fixture('footer.odt'), 'output_footer.odt')
75
- template.process(binding)
76
- 'output_footer.odt'.should contain_in('styles.xml', 'captain')
77
- end
78
- end
79
- end
@@ -1,41 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- module Serenity
4
-
5
- Node = Struct.new(:text, :type)
6
-
7
- describe XmlReader do
8
- it "should stream the xml, tag by tag" do
9
- xml = <<-EOF
10
- <?xml version="1.0" encoding="UTF-8"?><office:document-content><office:scripts/>
11
- <office:body><text:p style="Standard">This is a sentence</text:p><text:s>{%= yeah %}</text:s></office:body>
12
- {% for row in rows %}
13
- </office:document-content>
14
- EOF
15
-
16
- expected = [Node.new('<?xml version="1.0" encoding="UTF-8"?>', NodeType::TAG),
17
- Node.new('<office:document-content>', NodeType::TAG),
18
- Node.new('<office:scripts/>', NodeType::TAG),
19
- Node.new('<office:body>', NodeType::TAG),
20
- Node.new('<text:p style="Standard">', NodeType::TAG),
21
- Node.new('This is a sentence', NodeType::TEMPLATE),
22
- Node.new('</text:p>', NodeType::TAG),
23
- Node.new('<text:s>', NodeType::TAG),
24
- Node.new('{%= yeah %}', NodeType::TEMPLATE),
25
- Node.new('</text:s>', NodeType::TAG),
26
- Node.new('</office:body>', NodeType::TAG),
27
- Node.new('{% for row in rows %}', NodeType::CONTROL),
28
- Node.new('</office:document-content>', NodeType::TAG)]
29
-
30
- reader = XmlReader.new xml
31
-
32
- idx = 0
33
- reader.each_node do |node, type|
34
- expected[idx].text.should == node.strip
35
- expected[idx].type.should == type
36
- idx += 1
37
- end
38
- end
39
- end
40
- end
41
-