odf-report 0.6.1 → 0.7.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.
@@ -1,7 +1,6 @@
1
1
  module ODFReport
2
2
 
3
3
  class Report
4
- include Images
5
4
 
6
5
  def initialize(template_name = nil, io: nil)
7
6
 
@@ -10,10 +9,10 @@ class Report
10
9
  @texts = []
11
10
  @fields = []
12
11
  @tables = []
13
- @images = {}
14
- @image_names_replacements = {}
15
12
  @sections = []
16
13
 
14
+ @images = []
15
+
17
16
  yield(self) if block_given?
18
17
 
19
18
  end
@@ -46,8 +45,10 @@ class Report
46
45
  yield(sec)
47
46
  end
48
47
 
49
- def add_image(name, path)
50
- @images[name] = path
48
+ def add_image(image_name, value='')
49
+ opts = {:name => image_name, :value => value}
50
+ image = Image.new(opts)
51
+ @images << image
51
52
  end
52
53
 
53
54
  def generate(dest = nil)
@@ -56,29 +57,36 @@ class Report
56
57
 
57
58
  file.update_files do |doc|
58
59
 
59
- @sections.each { |s| s.replace!(doc) }
60
- @tables.each { |t| t.replace!(doc) }
60
+ @sections.each { |c| c.replace!(doc) }
61
+ @tables.each { |c| c.replace!(doc) }
61
62
 
62
- @texts.each { |t| t.replace!(doc) }
63
- @fields.each { |f| f.replace!(doc) }
63
+ @texts.each { |c| c.replace!(doc) }
64
+ @fields.each { |c| c.replace!(doc) }
64
65
 
65
- find_image_name_matches(doc)
66
- avoid_duplicate_image_names(doc)
66
+ @images.each { |c| c.replace!(doc) }
67
67
 
68
68
  end
69
69
 
70
- include_image_files(file)
70
+ all_images.each { |i| Image.include_image_file(file, i) }
71
+
72
+ file.update_manifest do |content|
73
+ all_images.each { |i| Image.include_manifest_entry(content, i) }
74
+ end
71
75
 
72
76
  end
73
77
 
74
78
  if dest
75
- ::File.open(dest, "wb") {|f| f.write(@template.data) }
79
+ File.open(dest, "wb") { |f| f.write(@template.data) }
76
80
  else
77
81
  @template.data
78
82
  end
79
83
 
80
84
  end
81
85
 
86
+ def all_images
87
+ @all_images ||= (@images.map(&:files) + @sections.map(&:all_images) + @tables.map(&:all_images)).flatten.uniq
88
+ end
89
+
82
90
  end
83
91
 
84
92
  end
@@ -1,39 +1,21 @@
1
1
  module ODFReport
2
+ class Section < Nestable
2
3
 
3
- class Section
4
- include Nested
4
+ def replace!(doc)
5
5
 
6
- def initialize(opts)
7
- @name = opts[:name]
8
- @collection_field = opts[:collection_field]
9
- @collection = opts[:collection]
6
+ return unless find_section_node(doc)
10
7
 
11
- @fields = []
12
- @texts = []
13
- @tables = []
14
- @sections = []
8
+ @data_source.each do |record|
15
9
 
16
- end
17
-
18
- def replace!(doc, row = nil)
19
-
20
- return unless @section_node = find_section_node(doc)
21
-
22
- @collection = get_collection_from_item(row, @collection_field) if row
23
-
24
- @collection.each do |data_item|
25
-
26
- new_section = get_section_node
27
-
28
- @tables.each { |t| t.replace!(new_section, data_item) }
10
+ new_section = deep_clone(@section_node)
29
11
 
30
- @sections.each { |s| s.replace!(new_section, data_item) }
12
+ @tables.each { |t| t.set_source(record).replace!(new_section) }
13
+ @sections.each { |s| s.set_source(record).replace!(new_section) }
14
+ @texts.each { |t| t.set_source(record).replace!(new_section) }
15
+ @fields.each { |f| f.set_source(record).replace!(new_section) }
16
+ @images.each { |i| i.set_source(record).replace!(new_section) }
31
17
 
32
- @texts.each { |t| t.replace!(new_section, data_item) }
33
-
34
- @fields.each { |f| f.replace!(new_section, data_item) }
35
-
36
- @section_node.before(new_section)
18
+ @section_node.before(new_section.to_xml)
37
19
 
38
20
  end
39
21
 
@@ -44,23 +26,14 @@ module ODFReport
44
26
  private
45
27
 
46
28
  def find_section_node(doc)
47
-
48
- sections = doc.xpath(".//text:section[@text:name='#{@name}']")
49
-
50
- sections.empty? ? nil : sections.first
51
-
29
+ @section_node = doc.at_css("text|section[@text|name='#{@name}']")
52
30
  end
53
31
 
54
- def get_section_node
55
- node = @section_node.dup
56
-
57
- name = node.get_attribute('text:name').to_s
58
- @idx ||=0; @idx +=1
59
- node.set_attribute('text:name', "#{name}_#{@idx}")
32
+ def deep_clone(node)
33
+ Nokogiri::XML(wrap_with_ns(node)).at("text|section")
34
+ .tap { |n| n.attribute('name').content = SecureRandom.uuid }
60
35
 
61
- node
62
36
  end
63
37
 
64
38
  end
65
-
66
39
  end
@@ -1,88 +1,83 @@
1
1
  module ODFReport
2
+ class Table < Nestable
2
3
 
3
- class Table
4
- include Nested
4
+ def initialize(opts)
5
+ super(opts)
5
6
 
6
- def initialize(opts)
7
- @name = opts[:name]
8
- @collection_field = opts[:collection_field]
9
- @collection = opts[:collection]
7
+ @template_rows = []
8
+ @header = opts[:header] || false
9
+ @skip_if_empty = opts[:skip_if_empty] || false
10
+ end
10
11
 
11
- @fields = []
12
- @texts = []
13
- @tables = []
12
+ def replace!(doc)
13
+ return unless table = find_table_node(doc)
14
14
 
15
- @template_rows = []
16
- @header = opts[:header] || false
17
- @skip_if_empty = opts[:skip_if_empty] || false
18
- end
15
+ @template_rows = table.xpath("table:table-row")
19
16
 
20
- def replace!(doc, row = nil)
17
+ @header = table.xpath("table:table-header-rows").empty? ? @header : false
21
18
 
22
- return unless table = find_table_node(doc)
19
+ if @skip_if_empty && @data_source.empty?
20
+ table.remove
21
+ return
22
+ end
23
23
 
24
- @template_rows = table.xpath("table:table-row")
24
+ @data_source.each do |record|
25
25
 
26
- @header = table.xpath("table:table-header-rows").empty? ? @header : false
26
+ new_node = get_next_row
27
27
 
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) }
28
33
 
29
- @collection = get_collection_from_item(row, @collection_field) if row
34
+ table.add_child(new_node.to_xml)
30
35
 
31
- if @skip_if_empty && @collection.empty?
32
- table.remove
33
- return
34
- end
36
+ end
35
37
 
36
- @collection.each do |data_item|
38
+ @template_rows.each_with_index do |r, i|
39
+ r.remove if (get_start_node..template_length) === i
40
+ end
37
41
 
38
- new_node = get_next_row
42
+ end # replace
39
43
 
40
- @tables.each { |t| t.replace!(new_node, data_item) }
44
+ private
41
45
 
42
- @texts.each { |t| t.replace!(new_node, data_item) }
46
+ def get_next_row
47
+ if @template_rows.size == 1
43
48
 
44
- @fields.each { |f| f.replace!(new_node, data_item) }
49
+ ret = @template_rows.first
45
50
 
46
- table.add_child(new_node)
51
+ else
52
+ @row_cursor = get_start_node unless defined?(@row_cursor)
47
53
 
48
- end
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
60
+ end
49
61
 
50
- @template_rows.each_with_index do |r, i|
51
- r.remove if (get_start_node..template_length) === i
62
+ return deep_clone(ret)
63
+ # return ret.dup
52
64
  end
53
65
 
54
- end # replace
55
-
56
- private
57
-
58
- def get_next_row
59
- @row_cursor = get_start_node unless defined?(@row_cursor)
60
-
61
- ret = @template_rows[@row_cursor]
62
- if @template_rows.size == @row_cursor + 1
63
- @row_cursor = get_start_node
64
- else
65
- @row_cursor += 1
66
+ def get_start_node
67
+ @header ? 1 : 0
66
68
  end
67
- return ret.dup
68
- end
69
-
70
- def get_start_node
71
- @header ? 1 : 0
72
- end
73
-
74
- def template_length
75
- @tl ||= @template_rows.size
76
- end
77
69
 
78
- def find_table_node(doc)
70
+ def template_length
71
+ @tl ||= @template_rows.size
72
+ end
79
73
 
80
- 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
81
77
 
82
- tables.empty? ? nil : tables.first
78
+ def deep_clone(node)
79
+ Nokogiri::XML(wrap_with_ns(node)).at("table|table-row")
80
+ end
83
81
 
84
82
  end
85
-
86
- end
87
-
88
83
  end
@@ -2,6 +2,7 @@ module ODFReport
2
2
  class Template
3
3
 
4
4
  CONTENT_FILES = ['content.xml', 'styles.xml']
5
+ MANIFEST_FILE = "META-INF/manifest.xml"
5
6
 
6
7
  attr_accessor :output_stream
7
8
 
@@ -34,18 +35,37 @@ module ODFReport
34
35
  process_entry(data, &block)
35
36
  end
36
37
 
37
- @output_stream.put_next_entry(entry.name)
38
- @output_stream.write data
38
+ update_file(entry.name, data)
39
39
 
40
40
  end
41
41
  end
42
42
 
43
43
  end
44
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
+
45
60
  def data
46
61
  @buffer.string
47
62
  end
48
63
 
64
+ def update_file(name, data)
65
+ @output_stream.put_next_entry(name)
66
+ @output_stream.write data
67
+ end
68
+
49
69
  private
50
70
 
51
71
  def get_template_entries
@@ -59,7 +79,7 @@ module ODFReport
59
79
  end
60
80
 
61
81
  def process_entry(entry)
62
- doc = Nokogiri::XML(entry)
82
+ doc = Nokogiri::XML(entry, &:noblanks)
63
83
  yield doc
64
84
  entry.replace(doc.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML))
65
85
  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)
@@ -1,3 +1,3 @@
1
1
  module ODFReport
2
- VERSION = "0.6.1"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
25
25
  s.add_development_dependency "launchy"
26
26
 
27
27
  s.add_runtime_dependency('rubyzip', ">= 1.3.0")
28
- s.add_runtime_dependency('nokogiri', ">= 1.5.0")
28
+ s.add_runtime_dependency('nokogiri', ">= 1.10.0")
29
+ s.add_runtime_dependency('mime-types')
29
30
 
30
31
  end
@@ -7,7 +7,7 @@ RSpec.describe "Fields" do
7
7
 
8
8
  @itens_01 = Item.get_list(3)
9
9
 
10
- report = ODFReport::Report.new("spec/specs.odt") do |r|
10
+ report = ODFReport::Report.new("spec/templates/specs.odt") do |r|
11
11
 
12
12
  r.add_field(:field_01, @field_01)
13
13
  r.add_field(:field_02, @field_02)
@@ -65,7 +65,7 @@ RSpec.describe "Fields" do
65
65
 
66
66
  @itens_01.each_with_index do |item, idx|
67
67
 
68
- section = @data.xml.xpath(".//text:section[@text:name='SECTION_01_#{idx+1}']").to_s
68
+ section = @data.xml.at_xpath(".//text:section[#{idx+1}]").to_s
69
69
 
70
70
  expect(section).to match(item.id.to_s)
71
71
  expect(section).to match(item.name)
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,80 @@
1
+ RSpec.describe "Images" do
2
+
3
+ before(:context) do
4
+
5
+ @list = []
6
+ @list << OpenStruct.new({ name: "IMG - [1, 1]", path: 'spec/images/image_1.jpg', path2: 'spec/images/image_1.jpg' })
7
+ @list << OpenStruct.new({ name: "IMG - [2, 1]", path: 'spec/images/image_2.jpg', path2: 'spec/images/image_1.jpg' })
8
+ @list << OpenStruct.new({ name: "IMG - [3, 2]", path: 'spec/images/image_3.jpg', path2: 'spec/images/image_2.jpg' })
9
+ @list << OpenStruct.new({ name: "IMG - [1, 3]", path: 'spec/images/image_1.jpg', path2: 'spec/images/image_3.jpg' })
10
+ @list << OpenStruct.new({ name: "IMG - [2, 2]", path: 'spec/images/image_2.jpg', path2: 'spec/images/image_2.jpg' })
11
+
12
+
13
+ report = ODFReport::Report.new("spec/templates/images.odt") do |r|
14
+
15
+ r.add_image("IMAGE_01", 'spec/images/rails.png')
16
+ r.add_image("IMAGE_02", 'spec/images/piriapolis.jpg')
17
+
18
+ r.add_table('IMAGE_TABLE', @list) do |t|
19
+ t.add_column(:image_name, :name)
20
+ t.add_image('IMAGE_IN_TABLE_01', :path)
21
+ t.add_image('IMAGE_IN_TABLE_02', :path2)
22
+ end
23
+
24
+ r.add_section('SECTION', @list) do |t|
25
+ t.add_field(:image_name, :name)
26
+ t.add_image('IMAGE_IN_SECTION_01', :path2)
27
+ t.add_image('IMAGE_IN_SECTION_02', :path)
28
+ end
29
+
30
+ end
31
+
32
+ report.generate("spec/result/images.odt")
33
+
34
+ @data = Inspector.new("spec/result/images.odt")
35
+
36
+ end
37
+
38
+
39
+ it "simple image replacement" do
40
+
41
+ images = @data.xml.xpath("//draw:image")
42
+
43
+ expect(images[0].attribute('href').value).to eq "Pictures/rails.png"
44
+ expect(images[1].attribute('href').value).to eq "Pictures/piriapolis.jpg"
45
+
46
+ end
47
+
48
+ it "table columns replacement" do
49
+
50
+ table = @data.xml.at_xpath(".//table:table[@table:name='IMAGE_TABLE']")
51
+
52
+ @list.each_with_index do |item, idx|
53
+
54
+ row = table.xpath(".//table:table-row[#{idx+1}]")
55
+
56
+ images = row.xpath(".//draw:image")
57
+
58
+ expect(File.basename(images[0].attribute('href').value)).to eq File.basename(item.path)
59
+ expect(File.basename(images[1].attribute('href').value)).to eq File.basename(item.path2)
60
+
61
+ end
62
+
63
+ end
64
+
65
+ it "section fields replacement" do
66
+
67
+ @list.each_with_index do |item, idx|
68
+
69
+ section = @data.xml.at_xpath(".//text:section[#{idx+1}]")
70
+
71
+ images = section.xpath(".//draw:image")
72
+
73
+ expect(File.basename(images[0].attribute('href').value)).to eq File.basename(item.path)
74
+ expect(File.basename(images[1].attribute('href').value)).to eq File.basename(item.path2)
75
+
76
+ end
77
+
78
+ end
79
+
80
+ end