odf-report 0.5.1 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +5 -13
  2. data/.github/workflows/gem-push.yml +40 -0
  3. data/.gitignore +2 -0
  4. data/.rspec +4 -0
  5. data/CHANGELOG.md +56 -0
  6. data/README.md +220 -0
  7. data/Rakefile +8 -0
  8. data/bin/odt-extract +10 -0
  9. data/bin/odt-viewer +18 -0
  10. data/lib/odf-report.rb +11 -9
  11. data/lib/odf-report/data_source.rb +65 -0
  12. data/lib/odf-report/field.rb +35 -36
  13. data/lib/odf-report/image.rb +57 -0
  14. data/lib/odf-report/nestable.rb +65 -0
  15. data/lib/odf-report/parser/default.rb +5 -4
  16. data/lib/odf-report/report.rb +29 -57
  17. data/lib/odf-report/section.rb +17 -80
  18. data/lib/odf-report/table.rb +52 -81
  19. data/lib/odf-report/template.rb +88 -0
  20. data/lib/odf-report/text.rb +2 -4
  21. data/lib/odf-report/version.rb +1 -1
  22. data/odf-report.gemspec +7 -4
  23. data/spec/fields_spec.rb +77 -0
  24. data/spec/images/image_1.jpg +0 -0
  25. data/spec/images/image_2.jpg +0 -0
  26. data/spec/images/image_3.jpg +0 -0
  27. data/{test → spec/images}/piriapolis.jpg +0 -0
  28. data/spec/images/placeholder.jpg +0 -0
  29. data/{test → spec/images}/rails.png +0 -0
  30. data/spec/images_spec.rb +159 -0
  31. data/spec/sections_spec.rb +51 -0
  32. data/spec/spec_helper.rb +43 -0
  33. data/spec/tables_spec.rb +39 -0
  34. data/spec/template_spec.rb +45 -0
  35. data/spec/templates/images.odt +0 -0
  36. data/spec/templates/specs.odt +0 -0
  37. data/test/fields_inside_text_test.rb +38 -0
  38. data/test/images_test.rb +32 -0
  39. data/test/nested_tables_test.rb +43 -0
  40. data/test/sections_test.rb +44 -0
  41. data/test/sub_sections_test.rb +58 -0
  42. data/test/table_headers_test.rb +41 -0
  43. data/test/tables_test.rb +67 -0
  44. data/test/templates/images/image_1.jpg +0 -0
  45. data/test/templates/images/image_2.jpg +0 -0
  46. data/test/templates/images/image_3.jpg +0 -0
  47. data/test/templates/images/placeholder.jpg +0 -0
  48. data/test/templates/images/placeholder.png +0 -0
  49. data/test/templates/piriapolis.jpg +0 -0
  50. data/test/templates/rails.png +0 -0
  51. data/test/templates/test_images.odt +0 -0
  52. data/test/templates/test_sub_sections.odt +0 -0
  53. data/test/templates/test_text.odt +0 -0
  54. data/test/test.rb +262 -0
  55. data/test/text_test.rb +56 -0
  56. metadata +151 -46
  57. data/README.textile +0 -225
  58. data/lib/odf-report/fields.rb +0 -40
  59. data/lib/odf-report/file.rb +0 -50
  60. data/lib/odf-report/images.rb +0 -44
  61. data/lib/odf-report/nested.rb +0 -34
  62. data/test/test_fields_inside_text.rb +0 -37
  63. data/test/test_nested_tables.rb +0 -39
  64. data/test/test_sections.rb +0 -39
  65. data/test/test_sub_sections.rb +0 -57
  66. data/test/test_table_headers.rb +0 -39
  67. data/test/test_tables.rb +0 -62
  68. data/test/test_text.rb +0 -48
@@ -1,112 +1,83 @@
1
1
  module ODFReport
2
+ class Table < Nestable
2
3
 
3
- class Table
4
- include Fields, Nested
4
+ def initialize(opts)
5
+ super(opts)
5
6
 
6
- attr_accessor :fields, :rows, :name, :collection_field, :data, :header, :parent, :tables
7
-
8
- def initialize(opts)
9
- @name = opts[:name]
10
- @collection_field = opts[:collection_field]
11
- @collection = opts[:collection]
12
- @parent = opts[:parent]
7
+ @template_rows = []
8
+ @header = opts[:header] || false
9
+ @skip_if_empty = opts[:skip_if_empty] || false
10
+ end
13
11
 
14
- @fields = []
15
- @tables = []
12
+ def replace!(doc)
13
+ return unless table = find_table_node(doc)
16
14
 
17
- @template_rows = []
18
- @header = opts[:header] || false
19
- @skip_if_empty = opts[:skip_if_empty] || false
20
- end
15
+ @template_rows = table.xpath("table:table-row")
21
16
 
22
- def add_column(name, data_field=nil, &block)
23
- opts = {:name => name, :data_field => data_field}
24
- field = Field.new(opts, &block)
25
- @fields << field
17
+ @header = table.xpath("table:table-header-rows").empty? ? @header : false
26
18
 
27
- end
28
-
29
- def add_table(table_name, collection_field, opts={}, &block)
30
- opts.merge!(:name => table_name, :collection_field => collection_field, :parent => self)
31
- tab = Table.new(opts)
32
- @tables << tab
19
+ if @skip_if_empty && @data_source.empty?
20
+ table.remove
21
+ return
22
+ end
33
23
 
34
- yield(tab)
35
- end
24
+ @data_source.each do |record|
36
25
 
37
- def populate!(row)
38
- @collection = get_collection_from_item(row, @collection_field) if row
39
- end
26
+ new_node = get_next_row
40
27
 
41
- def replace!(doc, row = nil)
28
+ @tables.each { |n| n.set_source(record).replace!(new_node) }
29
+ @sections.each { |n| n.set_source(record).replace!(new_node) }
30
+ @texts.each { |n| n.set_source(record).replace!(new_node) }
31
+ @fields.each { |n| n.set_source(record).replace!(new_node) }
32
+ @images.each { |n| n.set_source(record).replace!(new_node) }
42
33
 
43
- return unless table = find_table_node(doc)
34
+ table.add_child(new_node.to_xml)
44
35
 
45
- populate!(row)
36
+ end
46
37
 
47
- if (@skip_if_empty || !@header) && @collection.empty?
48
- table.remove
49
- return
50
- end
38
+ @template_rows.each_with_index do |r, i|
39
+ r.remove if (get_start_node..template_length) === i
40
+ end
51
41
 
52
- @template_rows = table.xpath("table:table-row")
42
+ end # replace
53
43
 
54
- @header = table.xpath("table:table-header-rows").empty? ? @header : false
44
+ private
55
45
 
56
- @collection.each do |data_item|
46
+ def get_next_row
47
+ if @template_rows.size == 1
57
48
 
58
- new_node = get_next_row
49
+ ret = @template_rows.first
59
50
 
60
- replace_fields!(new_node, data_item)
51
+ else
52
+ @row_cursor = get_start_node unless defined?(@row_cursor)
61
53
 
62
- @tables.each do |t|
63
- t.replace!(new_node, data_item)
54
+ ret = @template_rows[@row_cursor]
55
+ if @template_rows.size == @row_cursor + 1
56
+ @row_cursor = get_start_node
57
+ else
58
+ @row_cursor += 1
59
+ end
64
60
  end
65
61
 
66
- table.add_child(new_node)
67
-
62
+ return deep_clone(ret)
63
+ # return ret.dup
68
64
  end
69
65
 
70
- @template_rows.each_with_index do |r, i|
71
- r.remove if (get_start_node..template_length) === i
66
+ def get_start_node
67
+ @header ? 1 : 0
72
68
  end
73
69
 
74
- end # replace
75
-
76
- private
77
-
78
- def get_next_row
79
- @row_cursor = get_start_node unless defined?(@row_cursor)
80
-
81
- ret = @template_rows[@row_cursor]
82
- if @template_rows.size == @row_cursor + 1
83
- @row_cursor = get_start_node
84
- else
85
- @row_cursor += 1
70
+ def template_length
71
+ @tl ||= @template_rows.size
86
72
  end
87
- return ret.dup
88
- end
89
-
90
- def get_start_node
91
- @header ? 1 : 0
92
- end
93
-
94
- def reset
95
- @row_cursor = get_start_node
96
- end
97
-
98
- def template_length
99
- @tl ||= @template_rows.size
100
- end
101
-
102
- def find_table_node(doc)
103
73
 
104
- tables = doc.xpath(".//table:table[@table:name='#{@name}']")
74
+ def find_table_node(doc)
75
+ doc.at_css("table|table[@table|name='#{@name}']")
76
+ end
105
77
 
106
- tables.empty? ? nil : tables.first
78
+ def deep_clone(node)
79
+ Nokogiri::XML(wrap_with_ns(node)).at("table|table-row")
80
+ end
107
81
 
108
82
  end
109
-
110
- end
111
-
112
83
  end
@@ -0,0 +1,88 @@
1
+ module ODFReport
2
+ class Template
3
+
4
+ CONTENT_FILES = ['content.xml', 'styles.xml']
5
+ MANIFEST_FILE = "META-INF/manifest.xml"
6
+
7
+ attr_accessor :output_stream
8
+
9
+ def initialize(template = nil, io: nil)
10
+ raise "You must provide either a filename or an io: string" unless template || io
11
+ raise "Template [#{template}] not found." unless template.nil? || ::File.exist?(template)
12
+
13
+ @template = template
14
+ @io = io
15
+ end
16
+
17
+ def update_content
18
+ @buffer = Zip::OutputStream.write_buffer do |out|
19
+ @output_stream = out
20
+ yield self
21
+ end
22
+ end
23
+
24
+ def update_files(&block)
25
+
26
+ get_template_entries.each do |entry|
27
+
28
+ next if entry.directory?
29
+
30
+ entry.get_input_stream do |is|
31
+
32
+ data = is.sysread
33
+
34
+ if CONTENT_FILES.include?(entry.name)
35
+ process_entry(data, &block)
36
+ end
37
+
38
+ update_file(entry.name, data)
39
+
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ def update_manifest(&block)
46
+ entry = get_template_entries.find_entry(MANIFEST_FILE)
47
+
48
+ entry.get_input_stream do |is|
49
+
50
+ data = is.sysread
51
+
52
+ process_entry(data, &block)
53
+
54
+ update_file(MANIFEST_FILE, data)
55
+
56
+ end
57
+
58
+ end
59
+
60
+ def data
61
+ @buffer.string
62
+ end
63
+
64
+ def update_file(name, data)
65
+ @output_stream.put_next_entry(name)
66
+ @output_stream.write data
67
+ end
68
+
69
+ private
70
+
71
+ def get_template_entries
72
+
73
+ if @template
74
+ Zip::File.open(@template)
75
+ else
76
+ Zip::File.open_buffer(@io.force_encoding("ASCII-8BIT"))
77
+ end
78
+
79
+ end
80
+
81
+ def process_entry(entry)
82
+ doc = Nokogiri::XML(entry, &:noblanks)
83
+ yield doc
84
+ entry.replace(doc.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML))
85
+ end
86
+
87
+ end
88
+ end
@@ -8,9 +8,7 @@ module ODFReport
8
8
 
9
9
  return unless node = find_text_node(doc)
10
10
 
11
- text_value = get_value(data_item)
12
-
13
- @parser = Parser::Default.new(text_value, node)
11
+ @parser = Parser::Default.new(@data_source.value, node)
14
12
 
15
13
  @parser.paragraphs.each do |p|
16
14
  node.before(p)
@@ -40,4 +38,4 @@ module ODFReport
40
38
 
41
39
  end
42
40
 
43
- end
41
+ end
@@ -1,3 +1,3 @@
1
1
  module ODFReport
2
- VERSION = "0.5.1"
2
+ VERSION = "0.7.1"
3
3
  end
@@ -9,7 +9,6 @@ Gem::Specification.new do |s|
9
9
  s.authors = ["Sandro Duarte"]
10
10
  s.description = %q{Generates ODF files, given a template (.odt) and data, replacing tags}
11
11
  s.email = %q{sandrods@gmail.com}
12
- s.has_rdoc = false
13
12
  s.homepage = %q{http://sandrods.github.com/odf-report/}
14
13
  s.rubygems_version = %q{1.3.7}
15
14
  s.summary = %q{Generates ODF files, given a template (.odt) and data, replacing tags}
@@ -19,10 +18,14 @@ Gem::Specification.new do |s|
19
18
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
20
19
  s.require_paths = ["lib"]
21
20
 
22
- s.add_development_dependency "bundler", "~> 1.6"
21
+ s.add_development_dependency "bundler"
23
22
  s.add_development_dependency "rake"
23
+ s.add_development_dependency "rspec", "~> 3.0.0"
24
+ s.add_development_dependency "faker"
25
+ s.add_development_dependency "launchy"
24
26
 
25
- s.add_runtime_dependency('rubyzip', "~> 1.1.0")
26
- s.add_runtime_dependency('nokogiri', ">= 1.5.0")
27
+ s.add_runtime_dependency('rubyzip', ">= 1.3.0")
28
+ s.add_runtime_dependency('nokogiri', ">= 1.10.0")
29
+ s.add_runtime_dependency('mime-types')
27
30
 
28
31
  end
@@ -0,0 +1,77 @@
1
+ RSpec.describe "Fields" do
2
+
3
+ before(:context) do
4
+
5
+ @field_01 = Faker::Company.name
6
+ @field_02 = Faker::Name.name
7
+
8
+ @itens_01 = Item.get_list(3)
9
+
10
+ report = ODFReport::Report.new("spec/templates/specs.odt") do |r|
11
+
12
+ r.add_field(:field_01, @field_01)
13
+ r.add_field(:field_02, @field_02)
14
+
15
+ r.add_table('TABLE_01', @itens_01) do |t|
16
+ t.add_column(:column_01, :id)
17
+ t.add_column(:column_02, :name)
18
+ end
19
+
20
+ r.add_section('SECTION_01', @itens_01) do |t|
21
+ t.add_field(:s01_field_01, :id)
22
+ t.add_field(:s01_field_02, :name)
23
+ end
24
+
25
+ end
26
+
27
+ report.generate("spec/result/specs.odt")
28
+
29
+ @data = Inspector.new("spec/result/specs.odt")
30
+
31
+ end
32
+
33
+
34
+ it "simple fields replacement" do
35
+
36
+ expect(@data.text).not_to match(/\[FIELD_01\]/)
37
+ expect(@data.text).not_to match(/\[FIELD_02\]/)
38
+
39
+ expect(@data.text).to match @field_01
40
+ expect(@data.text).to match @field_02
41
+
42
+ end
43
+
44
+ it "table columns replacement" do
45
+
46
+ table = @data.xml.xpath(".//table:table[@table:name='TABLE_01']").to_s
47
+
48
+ @itens_01.each do |item|
49
+
50
+ expect(table).not_to match(/\[COLUMN_01\]/)
51
+ expect(table).not_to match(/\[COLUMN_02\]/)
52
+ expect(table).to match(/\[COLUMN_03\]/)
53
+
54
+ expect(table).to match(item.id.to_s)
55
+ expect(table).to match(item.name)
56
+
57
+ end
58
+
59
+ end
60
+
61
+ it "section fields replacement" do
62
+
63
+ expect(@data.text).not_to match(/\[S01_FIELD_01\]/)
64
+ expect(@data.text).not_to match(/\[S01_FIELD_02\]/)
65
+
66
+ @itens_01.each_with_index do |item, idx|
67
+
68
+ section = @data.xml.at_xpath(".//text:section[#{idx+1}]").to_s
69
+
70
+ expect(section).to match(item.id.to_s)
71
+ expect(section).to match(item.name)
72
+
73
+ end
74
+
75
+ end
76
+
77
+ end
Binary file
Binary file
Binary file
File without changes
File without changes
@@ -0,0 +1,159 @@
1
+ RSpec.describe "Images" do
2
+
3
+ context('Adding Images') do
4
+
5
+ before(:context) do
6
+
7
+ @list = []
8
+ @list << OpenStruct.new({ name: "IMG - [1, 1]", path: 'spec/images/image_1.jpg', path2: 'spec/images/image_1.jpg' })
9
+ @list << OpenStruct.new({ name: "IMG - [2, 1]", path: 'spec/images/image_2.jpg', path2: 'spec/images/image_1.jpg' })
10
+ @list << OpenStruct.new({ name: "IMG - [3, 2]", path: 'spec/images/image_3.jpg', path2: 'spec/images/image_2.jpg' })
11
+ @list << OpenStruct.new({ name: "IMG - [1, 3]", path: 'spec/images/image_1.jpg', path2: 'spec/images/image_3.jpg' })
12
+ @list << OpenStruct.new({ name: "IMG - [2, 2]", path: 'spec/images/image_2.jpg', path2: 'spec/images/image_2.jpg' })
13
+
14
+
15
+ report = ODFReport::Report.new("spec/templates/images.odt") do |r|
16
+
17
+ r.add_image("IMAGE_01", 'spec/images/rails.png')
18
+ r.add_image("IMAGE_02", 'spec/images/piriapolis.jpg')
19
+
20
+ r.add_table('IMAGE_TABLE', @list) do |t|
21
+ t.add_column(:image_name, :name)
22
+ t.add_image('IMAGE_IN_TABLE_01', :path)
23
+ t.add_image('IMAGE_IN_TABLE_02', :path2)
24
+ end
25
+
26
+ r.add_section('SECTION', @list) do |t|
27
+ t.add_field(:image_name, :name)
28
+ t.add_image('IMAGE_IN_SECTION_01', :path2)
29
+ t.add_image('IMAGE_IN_SECTION_02', :path)
30
+ end
31
+
32
+ end
33
+
34
+ report.generate("spec/result/images.odt")
35
+
36
+ @data = Inspector.new("spec/result/images.odt")
37
+
38
+ end
39
+
40
+
41
+ it "simple image replacement" do
42
+
43
+ images = @data.xml.xpath("//draw:image")
44
+
45
+ expect(images[0].attribute('href').value).to eq "Pictures/rails.png"
46
+ expect(images[1].attribute('href').value).to eq "Pictures/piriapolis.jpg"
47
+
48
+ end
49
+
50
+ it "table columns replacement" do
51
+
52
+ table = @data.xml.at_xpath(".//table:table[@table:name='IMAGE_TABLE']")
53
+
54
+ @list.each_with_index do |item, idx|
55
+
56
+ row = table.xpath(".//table:table-row[#{idx+1}]")
57
+
58
+ images = row.xpath(".//draw:image")
59
+
60
+ expect(File.basename(images[0].attribute('href').value)).to eq File.basename(item.path)
61
+ expect(File.basename(images[1].attribute('href').value)).to eq File.basename(item.path2)
62
+
63
+ end
64
+
65
+ end
66
+
67
+ it "section fields replacement" do
68
+
69
+ @list.each_with_index do |item, idx|
70
+
71
+ section = @data.xml.at_xpath(".//text:section[#{idx+1}]")
72
+
73
+ images = section.xpath(".//draw:image")
74
+
75
+ expect(File.basename(images[0].attribute('href').value)).to eq File.basename(item.path)
76
+ expect(File.basename(images[1].attribute('href').value)).to eq File.basename(item.path2)
77
+
78
+ end
79
+
80
+ end
81
+ end
82
+
83
+ context "Removing Images" do
84
+
85
+ before(:context) do
86
+
87
+ @list = []
88
+ @list << OpenStruct.new({ name: "IMG - both ok", path: 'spec/images/image_1.jpg', path2: 'spec/images/image_1.jpg' })
89
+ @list << OpenStruct.new({ name: "IMG - 1 ok", path: 'spec/images/image_2.jpg', path2: nil })
90
+ @list << OpenStruct.new({ name: "IMG - 2 ok", path: nil, path2: 'spec/images/image_3.jpg' })
91
+ # @list << OpenStruct.new({ name: "IMG - 2 invalid", path: nil, path2: 'spec/images/invalid.jpg' })
92
+
93
+ report = ODFReport::Report.new("spec/templates/images.odt") do |r|
94
+
95
+ # r.add_image("IMAGE_01")
96
+ r.add_image("IMAGE_02", nil)
97
+
98
+ r.add_table('IMAGE_TABLE', @list) do |t|
99
+ t.add_column(:image_name, :name)
100
+ t.add_image('IMAGE_IN_TABLE_01', :path)
101
+ t.add_image('IMAGE_IN_TABLE_02', :path2)
102
+ end
103
+
104
+ r.add_section('SECTION', @list) do |t|
105
+ t.add_field(:image_name, :name)
106
+ t.add_image('IMAGE_IN_SECTION_01', :path2)
107
+ t.add_image('IMAGE_IN_SECTION_02', :path)
108
+ end
109
+
110
+ end
111
+
112
+ report.generate("spec/result/images.odt")
113
+
114
+ @data = Inspector.new("spec/result/images.odt")
115
+
116
+ end
117
+
118
+ it "removes nil images in report" do
119
+ expect(@data.xml.at_css("draw|frame[@draw|name='IMAGE_01']")).to be
120
+ expect(@data.xml.at_css("draw|frame[@draw|name='IMAGE_02']")).to be_nil
121
+ end
122
+
123
+ it "removes nil images in tables" do
124
+
125
+ table = @data.xml.at_xpath(".//table:table[@table:name='IMAGE_TABLE']")
126
+
127
+ images = table.xpath(".//table:table-row[1]//draw:image")
128
+ expect(File.basename(images[0].attribute('href').value)).to eq File.basename(@list[0].path)
129
+ expect(File.basename(images[1].attribute('href').value)).to eq File.basename(@list[0].path2)
130
+
131
+ images = table.xpath(".//table:table-row[2]//draw:image")
132
+ expect(File.basename(images[0].attribute('href').value)).to eq File.basename(@list[1].path)
133
+ expect(images[1]).to be_nil
134
+
135
+ images = table.xpath(".//table:table-row[3]//draw:image")
136
+ expect(File.basename(images[0].attribute('href').value)).to eq File.basename(@list[2].path2)
137
+ expect(images[1]).to be_nil
138
+
139
+ end
140
+
141
+ it "removes nil images in sections " do
142
+
143
+ images = @data.xml.xpath(".//text:section[1]//draw:image")
144
+ expect(File.basename(images[0].attribute('href').value)).to eq File.basename(@list[0].path)
145
+ expect(File.basename(images[1].attribute('href').value)).to eq File.basename(@list[0].path2)
146
+
147
+ images = @data.xml.xpath(".//text:section[2]//draw:image")
148
+ expect(File.basename(images[0].attribute('href').value)).to eq File.basename(@list[1].path)
149
+ expect(images[1]).to be_nil
150
+
151
+ images = @data.xml.xpath(".//text:section[3]//draw:image")
152
+ expect(File.basename(images[0].attribute('href').value)).to eq File.basename(@list[2].path2)
153
+ expect(images[1]).to be_nil
154
+
155
+ end
156
+
157
+ end
158
+
159
+ end