docx_templater 0.0.7 → 0.1.1

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.
@@ -0,0 +1,25 @@
1
+ module Docx
2
+ class ArgumentCombiner
3
+ attr_reader :attributes
4
+ def initialize(*args)
5
+ @attributes = {}
6
+ args.flatten!
7
+ # Prefixes the model name or custom prefix. Makes it so we don't having naming clashes when used with records from multiple m
8
+ args.each do |arg|
9
+ if arg.is_a?(Hash) && arg.has_key?(:data) && arg.has_key?(:prefix)
10
+ template_attributes = (arg[:data].respond_to?(:template_attributes) && :template_attributes) || :attributes
11
+ arg[:data].send(template_attributes).each_key do |key|
12
+ @attributes["#{arg[:prefix]}_#{key.to_s}".to_sym] = arg[:data].send(template_attributes)[key]
13
+ end
14
+ elsif arg.is_a?(Hash)
15
+ @attributes.merge!(arg)
16
+ else
17
+ template_attributes = (arg.respond_to?(:template_attributes) && :template_attributes) || :attributes
18
+ arg.send(template_attributes).each_key do |key|
19
+ @attributes["#{arg.class.name.underscore}_#{key.to_s}".to_sym] = arg.send(template_attributes)[key]
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ require 'rexml/document'
2
+ require 'docx/placeholder_observer'
3
+
4
+ module Docx
5
+ class DocumentReplacer
6
+
7
+ attr_reader :doc, :observer
8
+
9
+ def initialize(str, data_provider)
10
+ @doc = REXML::Document.new(str)
11
+ @observer = Docx::PlaceholderObserver.new(data_provider)
12
+ walk_node(@doc.root)
13
+ @observer.end_of_document
14
+ end
15
+
16
+ def replaced
17
+ @doc.to_s
18
+ end
19
+
20
+ private
21
+
22
+ def walk_node(node)
23
+ if node.is_a?(REXML::Element)
24
+ node.children.each do |n|
25
+ walk_node(n)
26
+ end
27
+ elsif node.is_a?(REXML::Text)
28
+ observer.next_node(node)
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,38 @@
1
+ module Docx
2
+ class NodesToFix
3
+ attr_accessor :node_list, :current_node, :value
4
+ def initialize
5
+ forget
6
+ end
7
+
8
+ def forget
9
+ @current_node = nil
10
+ @node_list = []
11
+ @value = ''
12
+ end
13
+
14
+ def remember(node, index)
15
+ new_node = current_node.nil? || current_node != node
16
+ if new_node
17
+ @current_node = node
18
+ @node_list << {:node => node, :range => index..index}
19
+ else
20
+ @node_list.last[:range] = (node_list.last[:range].min)..index
21
+ end
22
+ end
23
+
24
+ def fix
25
+ @node_list.each do |obj|
26
+ node = obj[:node]
27
+ range = obj[:range]
28
+ new_val = node.value
29
+ new_val[range] = value.to_s || ''
30
+ node.value = new_val
31
+ if new_val =~ /^\s+/ && node.parent
32
+ node.parent.add_attribute('xml:space', 'preserve')
33
+ end
34
+ self.value = nil
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,79 @@
1
+ require 'rexml/document'
2
+ require 'docx/nodes_to_fix'
3
+ module Docx
4
+ class PlaceholderObserver
5
+ attr_reader :data_provider
6
+ def initialize(data_provider)
7
+ @data_provider = data_provider
8
+ @buffer = ''
9
+ @state = :waiting_for_opening
10
+ @nodes_to_fix = NodesToFix.new
11
+ @fixes_to_make = []
12
+ end
13
+
14
+ def next_node(node)
15
+ node.value.split(//).each_with_index do |c,index|
16
+ next_char(node,index,c)
17
+ end
18
+ end
19
+
20
+ def end_of_document
21
+ make_fixes
22
+ end
23
+
24
+ private
25
+
26
+ attr_accessor :state, :buffer, :nodes_to_fix
27
+
28
+ def next_char(node, index, c)
29
+ send(state, node, index, c)
30
+ end
31
+
32
+ def waiting_for_opening(node, index, c)
33
+ if c == '|'
34
+ add_char_to_buffer(node,index,c)
35
+ if buffer == '||'
36
+ self.state = :capturing_placeholder
37
+ end
38
+ else
39
+ truncate_buffer
40
+ end
41
+ end
42
+
43
+ def capturing_placeholder(node, index, c)
44
+ add_char_to_buffer(node,index,c)
45
+ if buffer[-2..-1] == '||'
46
+ key = buffer[2..-3]
47
+ if data_provider.has_key?(key.to_sym)
48
+ new_value = data_provider[key.to_sym]
49
+ save_fix_for_later(new_value)
50
+ end
51
+ self.state = :waiting_for_opening
52
+ truncate_buffer
53
+ end
54
+ end
55
+
56
+ def add_char_to_buffer(node, index, c)
57
+ @nodes_to_fix.remember(node,index)
58
+ @buffer << c
59
+ end
60
+
61
+ def truncate_buffer
62
+ @buffer = ''
63
+ @nodes_to_fix.forget
64
+ end
65
+
66
+ def save_fix_for_later(val)
67
+ @nodes_to_fix.value = val
68
+ @fixes_to_make << @nodes_to_fix
69
+ @nodes_to_fix = NodesToFix.new
70
+ end
71
+
72
+ def make_fixes
73
+ @fixes_to_make.reverse.each do |nodes_to_fix|
74
+ nodes_to_fix.fix
75
+ end
76
+ @fixes_to_make = []
77
+ end
78
+ end
79
+ end
@@ -1,5 +1,7 @@
1
1
  require 'zip/zipfilesystem'
2
2
  require 'htmlentities'
3
+ require 'docx/argument_combiner'
4
+ require 'docx/document_replacer'
3
5
 
4
6
  # Use .docx as reusable templates
5
7
  #
@@ -32,53 +34,20 @@ class DocxTemplater
32
34
  end
33
35
 
34
36
  def generate_tags_for(*args)
35
- attributes = {}
36
- args.flatten!
37
- # Prefixes the model name or custom prefix. Makes it so we don't having naming clashes when used with records from multiple m
38
- args.each do |arg|
39
- if arg.is_a?(Hash) && arg.has_key?(:data) && arg.has_key?(:prefix)
40
- template_attributes = (arg[:data].respond_to?(:template_attributes) && :template_attributes) || :attributes
41
- arg[:data].send(template_attributes).each_key do |key|
42
- attributes["#{arg[:prefix]}_#{key.to_s}".to_sym] = arg[:data].send(template_attributes)[key]
43
- end
44
- elsif arg.is_a?(Hash)
45
- attributes.merge!(arg)
46
- else
47
- template_attributes = (arg.respond_to?(:template_attributes) && :template_attributes) || :attributes
48
- arg.send(template_attributes).each_key do |key|
49
- attributes["#{arg.class.name.underscore}_#{key.to_s}".to_sym] = arg.send(template_attributes)[key]
50
- end
51
- end
52
- end
53
- attributes
37
+ Docx::ArgumentCombiner.new(*args).attributes
54
38
  end
55
39
 
56
- private
57
- def all_tags_regex
58
- /\|\|\<*.+?\>*\|\|/
59
- end
60
-
61
- def malformed_tag_regex
62
- /(?<=>)\w{3,}(?=<)/
63
- end
64
-
65
- def well_formed_tag_regex
66
- /(?<=\|\|)\w{3,}(?=\|\|)/
67
- end
68
-
69
- def just_label_regex
70
- /(?<=>)(\w{3,})/
71
- end
72
-
73
40
  def entry_requires_replacement?(entry)
74
41
  entry.ftype != :directory && entry.name =~ /document|header|footer/
75
42
  end
76
43
 
77
44
  def get_entry_content(entry, data_provider)
45
+ file_string = entry.get_input_stream.read
78
46
  if entry_requires_replacement?(entry)
79
- replace_entry_content(entry.get_input_stream.read, data_provider)
47
+ replacer = Docx::DocumentReplacer.new(file_string, data_provider)
48
+ replacer.replaced
80
49
  else
81
- entry.get_input_stream.read
50
+ file_string
82
51
  end
83
52
  end
84
53
 
@@ -86,29 +55,4 @@ class DocxTemplater
86
55
  output.put_next_entry(entry.name)
87
56
  output.write get_entry_content(entry, data_provider) if entry.ftype != :directory
88
57
  end
89
-
90
- def replace_entry_content(str, data_provider)
91
- possible_tags = str.scan(all_tags_regex)
92
- # Loops through what looks like are tags. Anything with ||name|| even if they are not in the available tags list
93
- possible_tags.each do |tag|
94
- #extracts just the tag name
95
- tag_name = malformed_tag_regex.match(tag)
96
- tag_name ||= well_formed_tag_regex.match(tag)
97
- tag_name ||= ''
98
- # This will handle instances where someone edits just part of a tag and Word wraps that part in more XML
99
- words = tag.scan(just_label_regex).flatten!
100
- if words.respond_to?(:size) && words.size > 1
101
- #Then the tag was split by word
102
- tag_name = words.join('')
103
- end
104
- tag_name = tag_name.to_s.to_sym
105
- # if in the available tag list, replace with the new value
106
- if data_provider.has_key?(tag_name)
107
- encoder = HTMLEntities.new
108
- content = encoder.encode("#{data_provider[tag_name]}")
109
- str.gsub!(tag, content)
110
- end
111
- end
112
- str
113
- end
114
58
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docx_templater
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -52,6 +52,10 @@ extensions: []
52
52
  extra_rdoc_files: []
53
53
  files:
54
54
  - lib/docx_templater.rb
55
+ - lib/docx/argument_combiner.rb
56
+ - lib/docx/document_replacer.rb
57
+ - lib/docx/nodes_to_fix.rb
58
+ - lib/docx/placeholder_observer.rb
55
59
  homepage: http://rubygems.org/gems/docx_templater
56
60
  licenses: []
57
61
  post_install_message:
@@ -72,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
76
  version: '0'
73
77
  requirements: []
74
78
  rubyforge_project:
75
- rubygems_version: 1.8.24
79
+ rubygems_version: 1.8.23
76
80
  signing_key:
77
81
  specification_version: 3
78
82
  summary: Uses a .docx as a template and replaces 'tags' within || with other content