docx_templater 0.0.7 → 0.1.1

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