odf-report 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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