goldendocx 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.ruby-version +1 -0
  4. data/CHANGELOG.md +8 -2
  5. data/Gemfile.lock +3 -1
  6. data/README.md +10 -2
  7. data/Rakefile +14 -3
  8. data/demo/tables/create_embed_image_table.rb +3 -3
  9. data/demo/templates/generate_codes.rb +10 -0
  10. data/demo/templates/xml_to_class.rb +97 -0
  11. data/demo/texts/append_plain_text.rb +1 -1
  12. data/demo/texts/append_styled_text.rb +1 -2
  13. data/demo/texts/create_text.rb +14 -0
  14. data/lib/extensions/active_support_extensions.rb +16 -0
  15. data/lib/extensions/nokogiri_extensions.rb +41 -0
  16. data/lib/extensions/ox_extensions.rb +35 -0
  17. data/lib/extensions/xml_serialize_extensions.rb +45 -0
  18. data/lib/goldendocx/charts/properties.rb +1 -1
  19. data/lib/goldendocx/charts/series.rb +2 -2
  20. data/lib/goldendocx/charts.rb +1 -1
  21. data/lib/goldendocx/components/bar_chart.rb +2 -2
  22. data/lib/goldendocx/components/chart.rb +6 -6
  23. data/lib/goldendocx/components/column_chart.rb +2 -2
  24. data/lib/goldendocx/components/doughnut_chart.rb +2 -2
  25. data/lib/goldendocx/components/line_chart.rb +2 -2
  26. data/lib/goldendocx/components/properties/font_property.rb +19 -0
  27. data/lib/goldendocx/components/properties/language_property.rb +18 -0
  28. data/lib/goldendocx/components/properties/run_property.rb +2 -0
  29. data/lib/goldendocx/components/properties.rb +1 -1
  30. data/lib/goldendocx/components/table.rb +2 -2
  31. data/lib/goldendocx/components.rb +1 -1
  32. data/lib/goldendocx/content_types/default.rb +2 -6
  33. data/lib/goldendocx/content_types/override.rb +2 -6
  34. data/lib/goldendocx/document.rb +9 -28
  35. data/lib/goldendocx/documents/body.rb +23 -14
  36. data/lib/goldendocx/documents/document.rb +2 -4
  37. data/lib/goldendocx/documents/latent_styles.rb +12 -0
  38. data/lib/goldendocx/documents/properties/default_style_property.rb +17 -0
  39. data/lib/goldendocx/documents/properties/page_margin_property.rb +37 -0
  40. data/lib/goldendocx/documents/properties/page_size_property.rb +17 -0
  41. data/lib/goldendocx/documents/properties/paragraph_default_style_property.rb +16 -0
  42. data/lib/goldendocx/documents/properties/run_default_style_property.rb +22 -0
  43. data/lib/goldendocx/documents/properties/section_property.rb +17 -0
  44. data/lib/goldendocx/documents/properties/style_name_property.rb +16 -0
  45. data/lib/goldendocx/documents/properties.rb +12 -0
  46. data/lib/goldendocx/documents/settings.rb +23 -0
  47. data/lib/goldendocx/documents/style.rb +10 -10
  48. data/lib/goldendocx/documents/styles.rb +9 -33
  49. data/lib/goldendocx/documents.rb +1 -1
  50. data/lib/goldendocx/docx.rb +55 -12
  51. data/lib/goldendocx/element.rb +33 -140
  52. data/lib/goldendocx/has_associations.rb +54 -0
  53. data/lib/goldendocx/has_attributes.rb +67 -0
  54. data/lib/goldendocx/has_children.rb +116 -0
  55. data/lib/goldendocx/images/picture.rb +1 -1
  56. data/lib/goldendocx/images/properties.rb +1 -1
  57. data/lib/goldendocx/images.rb +1 -1
  58. data/lib/goldendocx/{documents → models}/relationship.rb +1 -1
  59. data/lib/goldendocx/models/relationships.rb +31 -0
  60. data/lib/goldendocx/models.rb +10 -0
  61. data/lib/goldendocx/parts/app.rb +46 -0
  62. data/lib/goldendocx/parts/content_types.rb +16 -30
  63. data/lib/goldendocx/parts/core.rb +46 -0
  64. data/lib/goldendocx/parts/documents.rb +27 -14
  65. data/lib/goldendocx/parts/media.rb +1 -1
  66. data/lib/goldendocx/parts/properties/created_at_property.rb +17 -0
  67. data/lib/goldendocx/parts/properties/creator_property.rb +16 -0
  68. data/lib/goldendocx/parts/properties/revision_property.rb +16 -0
  69. data/lib/goldendocx/parts/properties/updated_at_property.rb +17 -0
  70. data/lib/goldendocx/parts/properties/updater_property.rb +16 -0
  71. data/lib/goldendocx/parts/properties.rb +12 -0
  72. data/lib/goldendocx/parts.rb +1 -1
  73. data/lib/goldendocx/tables/header_cell.rb +1 -1
  74. data/lib/goldendocx/tables/properties.rb +1 -1
  75. data/lib/goldendocx/tables/row.rb +1 -1
  76. data/lib/goldendocx/tables.rb +1 -1
  77. data/lib/goldendocx/version.rb +1 -1
  78. data/lib/goldendocx/xml_serializers/nokogiri.rb +32 -24
  79. data/lib/goldendocx/xml_serializers/ox.rb +15 -25
  80. data/lib/goldendocx.rb +14 -2
  81. metadata +54 -9
  82. data/lib/goldendocx/documents/element.rb +0 -23
  83. data/lib/goldendocx/documents/relationships.rb +0 -39
  84. data/lib/goldendocx/documents/unparsed_style.rb +0 -17
@@ -1,19 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_support/core_ext/string/inflections'
4
-
5
- ActiveSupport::Inflector.inflections do |inflect|
6
- inflect.uncountable 'extents', 'image_data', 'data'
7
- inflect.irregular 'axis', 'axes'
8
-
9
- inflect.uncountable 'values' # TODO: Find better names
10
- end
3
+ require 'goldendocx/has_attributes'
4
+ require 'goldendocx/has_children'
11
5
 
12
6
  module Goldendocx
13
7
  module Element
14
- def self.included(base)
15
- base.extend(ClassMethods)
16
- end
8
+ extend ActiveSupport::Concern
9
+ include Goldendocx::HasAttributes
10
+ include Goldendocx::HasChildren
17
11
 
18
12
  module ClassMethods
19
13
  def tag(*args)
@@ -26,124 +20,36 @@ module Goldendocx
26
20
  @namespace
27
21
  end
28
22
 
29
- # alias_name: nil
30
- # readonly: false
31
- # default: nil
32
- # namespace: nil
33
- # setter: nil
34
- def attribute(name, **options)
35
- named = name.to_s
36
- attributes[named] = {
37
- alias_name: options[:alias_name],
38
- default: options[:default],
39
- namespace: options[:namespace],
40
- method: options[:method]
41
- }.compact
42
-
43
- readonly = options[:readonly] || false
44
- if readonly
45
- attr_reader named
46
- elsif options[:method]
47
- attr_writer named
48
- else
49
- attr_accessor named
50
- end
51
- end
52
-
53
- def attributes
54
- @attributes ||= {}
55
- end
56
-
57
- def create_children_getter(name)
58
- options = children[name]
59
- class_name = options[:class_name]
60
- multiple = options[:multiple]
61
- auto_build = options[:auto_build]
62
-
63
- define_method name do
64
- return instance_variable_get("@#{name}") if instance_variable_defined?("@#{name}")
65
-
66
- default_value = if multiple
67
- []
68
- else
69
- auto_build ? Kernel.const_get(class_name).new : nil
70
- end
71
- instance_variable_set("@#{name}", default_value)
72
- end
23
+ def tag_name
24
+ @tag_name ||= [namespace, tag].compact.join(':')
73
25
  end
74
26
 
75
- def create_children_setter(name)
76
- options = children[name]
77
- class_name = options[:class_name]
78
-
79
- define_method "#{name}=" do |value|
80
- value = value.to_s if value && class_name == 'String'
81
- instance_variable_set("@#{name}", value)
82
- end
27
+ def parse(xml_string)
28
+ root_node = Goldendocx.xml_serializer.parse(xml_string).root
29
+ read_from(root_node)
83
30
  end
84
31
 
85
- def create_children_builder(name)
86
- options = children[name]
87
- class_name = options[:class_name]
88
- multiple = options[:multiple]
89
-
90
- define_method "build_#{name.to_s.singularize}" do |**attributes|
91
- child = Kernel.const_get(class_name).new
92
- attributes.each { |key, value| child.send("#{key}=", value) if child.respond_to?("#{key}=") }
93
- multiple ? send(name) << child : instance_variable_set("@#{name}", child)
94
- child
95
- end
32
+ def adapt?(xml_node)
33
+ tag_name == xml_node.tag_name
96
34
  end
97
35
 
98
- def embeds_one(name, class_name:, auto_build: false)
99
- warning_naming_suggestion(name, name.to_s.singularize)
100
-
101
- children[name] = { class_name: class_name, multiple: false, auto_build: auto_build }
102
- create_children_getter(name)
103
- create_children_setter(name)
104
- create_children_builder(name)
105
- end
106
-
107
- def embeds_many(name, class_name:)
108
- warning_naming_suggestion(name, name.to_s.pluralize)
109
-
110
- children[name] = { class_name: class_name, multiple: true, auto_build: false }
111
- create_children_getter(name)
112
- create_children_builder(name)
113
- end
36
+ def read_from(xml_node)
37
+ return unless adapt?(xml_node)
114
38
 
115
- def children
116
- @children ||= {}
39
+ instance = new
40
+ instance.read_attributes(xml_node)
41
+ instance.read_children(xml_node)
42
+ instance
117
43
  end
118
44
 
119
45
  def concerning_ancestors
120
46
  ancestors.filter { |ancestor| ancestor.include?(Goldendocx::Element) }
121
47
  end
122
-
123
- private
124
-
125
- # :nocov:
126
- def warning_naming_suggestion(name, suggestion_name)
127
- return if suggestion_name == name.to_s
128
-
129
- location = caller.find { |c| c.include?('goldendocx/') && !c.include?('goldendocx/element.rb') }
130
- warn "warning: [embeds_one] `#{name}` better be singular `#{suggestion_name}` at #{location}"
131
- end
132
- # :nocov:
133
- end
134
-
135
- def attributes
136
- self.class.attributes.each_with_object({}) do |(name, options), result|
137
- value = public_send(options[:method] || name) || options[:default]
138
- next if value.nil?
139
-
140
- key = [options[:namespace], options[:alias_name] || name].compact.join(':')
141
- result[key] = value
142
- end
143
48
  end
144
49
 
145
- def assign_attributes(**attributes)
146
- attributes.each { |key, value| send("#{key}=", value) if respond_to?("#{key}=") }
50
+ def initialize(attributes = nil)
51
+ attributes ||= {}
52
+ assign_attributes(**attributes)
147
53
  end
148
54
 
149
55
  def tag
@@ -154,39 +60,26 @@ module Goldendocx
154
60
  self.class.concerning_ancestors.find { |ancestor| ancestor.namespace.present? }&.namespace
155
61
  end
156
62
 
157
- def root_tag
158
- @root_tag ||= [namespace, tag].compact.join(':')
63
+ def tag_name
64
+ @tag_name ||= [namespace, tag].compact.join(':')
159
65
  end
160
66
 
161
- def siblings
162
- return [] unless self.class.superclass.include?(Goldendocx::Element)
163
-
164
- self.class.superclass.children.keys.flat_map { |name| send(name) }
67
+ def to_element(**context, &)
68
+ Goldendocx.xml_serializer.build_element(tag_name, **context) { |xml| build_element(xml, &) }
165
69
  end
166
70
 
167
- def children
168
- self.class.children.keys.flat_map do |name|
169
- send(name)
170
- end.concat(siblings).compact
71
+ def to_xml(&)
72
+ Goldendocx.xml_serializer.build_xml(tag_name) { |xml| build_element(xml, &) }
171
73
  end
172
74
 
173
- def to_element(**context)
174
- Goldendocx.xml_serializer.build_element(root_tag, **context) do |xml|
175
- attributes.each { |name, value| xml[name] = value }
176
- children.each { |child| xml << child }
75
+ def build_element(xml)
76
+ attributes.each { |name, value| xml[name] = value }
77
+ unparsed_attributes.each { |name, value| xml[name] = value }
177
78
 
178
- yield(xml) if block_given?
179
- end
180
- end
79
+ children.each { |child| xml << child }
80
+ unparsed_children.each { |child| xml << child }
181
81
 
182
- def to_xml
183
- Goldendocx.xml_serializer.build_xml(root_tag) do |xml|
184
- attributes.each { |name, value| xml[name] = value }
185
-
186
- yield(xml) if block_given?
187
-
188
- children.each { |child| xml << child }
189
- end
82
+ yield(xml) if block_given?
190
83
  end
191
84
  end
192
85
  end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Goldendocx
4
+ module HasAssociations
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :associations, default: {}
9
+ class_attribute :relationships_xml_path
10
+
11
+ delegate :add_relationship, to: :relationships
12
+ end
13
+
14
+ Options = Data.define(:class_name, :isolate)
15
+
16
+ class_methods do
17
+ def relationships_at(xml_path)
18
+ self.relationships_xml_path = xml_path
19
+ end
20
+
21
+ def associate(name, class_name:, isolate: false)
22
+ named = name.to_s
23
+ associations[named] = Options.new(class_name:, isolate:)
24
+
25
+ define_method named do
26
+ return instance_variable_get("@#{name}") if instance_variable_defined?("@#{name}")
27
+
28
+ new_instance = class_name.constantize.new
29
+ instance_variable_set("@#{name}", new_instance)
30
+ end
31
+ end
32
+ end
33
+
34
+ def read_associations(docx_file)
35
+ associations.each do |association, options|
36
+ association_class = options.class_name.constantize
37
+ association_document_xml = docx_file.read(association_class::XML_PATH)
38
+ instance_variable_set("@#{association}", association_class.parse(association_document_xml))
39
+ end
40
+ end
41
+
42
+ def read_relationships(docx_file)
43
+ @relationships = Goldendocx::Models::Relationships.parse(docx_file.read(relationships_xml_path))
44
+ end
45
+
46
+ def write_relationships(zos)
47
+ relationships.write_to(zos, relationships_xml_path)
48
+ end
49
+
50
+ def relationships
51
+ @relationships ||= Goldendocx::Models::Relationships.new
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Goldendocx
4
+ module HasAttributes
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :attributes, instance_accessor: false, default: {}
9
+
10
+ def unparsed_attributes
11
+ @unparsed_attributes ||= {}
12
+ end
13
+ end
14
+
15
+ class_methods do
16
+ # alias_name: nil
17
+ # readonly: false
18
+ # default: nil
19
+ # namespace: nil
20
+ # setter: nil
21
+ def attribute(name, **options)
22
+ named = name.to_s
23
+ attributes[named] = {
24
+ alias_name: options[:alias_name],
25
+ default: options[:default],
26
+ namespace: options[:namespace],
27
+ method: options[:method]
28
+ }.compact
29
+
30
+ readonly = options[:readonly] || false
31
+ if readonly
32
+ attr_reader named
33
+ elsif options[:method]
34
+ attr_writer named
35
+ else
36
+ attr_accessor named
37
+ end
38
+ end
39
+ end
40
+
41
+ def attributes
42
+ self.class.attributes.each_with_object({}) do |(name, options), result|
43
+ value = public_send(options[:method] || name) || options[:default]
44
+ next if value.nil?
45
+
46
+ key = [options[:namespace], options[:alias_name] || name].compact.join(':')
47
+ result[key] = value
48
+ end
49
+ end
50
+
51
+ def read_attributes(node)
52
+ node_attributes = node.attributes_hash
53
+
54
+ attributes = self.class.attributes.each_with_object({}) do |(name, options), result|
55
+ attribute_tag = [options[:namespace], (options[:alias_name] || name)].compact.join(':')
56
+ result[name] = node_attributes.delete(attribute_tag)
57
+ end
58
+ assign_attributes(**attributes)
59
+
60
+ unparsed_attributes.update(node_attributes)
61
+ end
62
+
63
+ def assign_attributes(**attributes)
64
+ attributes.each { |key, value| send("#{key}=", value) if respond_to?("#{key}=") }
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'goldendocx/has_attributes'
4
+
5
+ module Goldendocx
6
+ module HasChildren
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ class_attribute :children, instance_accessor: false, default: {}
11
+
12
+ def unparsed_children
13
+ @unparsed_children ||= []
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+ def embeds_one(name, class_name:, auto_build: false)
19
+ warning_naming_suggestion(__method__, name, name.to_s.singularize)
20
+
21
+ options = { class_name:, multiple: false, auto_build: }
22
+ self.children = children.merge(name => options)
23
+
24
+ create_children_getter(name)
25
+ create_children_setter(name)
26
+ create_children_builder(name)
27
+ end
28
+
29
+ def embeds_many(name, class_name:, uniqueness: false)
30
+ warning_naming_suggestion(__method__, name, name.to_s.pluralize)
31
+
32
+ options = { class_name:, multiple: true, uniqueness: }
33
+ self.children = children.merge(name => options)
34
+
35
+ create_children_getter(name)
36
+ create_children_setter(name)
37
+ create_children_builder(name)
38
+ end
39
+
40
+ def default_value(name)
41
+ options = children[name]
42
+ return [] if options[:multiple]
43
+
44
+ options[:class_name].constantize.new if options[:auto_build]
45
+ end
46
+
47
+ private
48
+
49
+ def create_children_getter(name)
50
+ define_method name do
51
+ return instance_variable_get("@#{name}") if instance_variable_defined?("@#{name}")
52
+
53
+ instance_variable_set("@#{name}", self.class.default_value(name))
54
+ end
55
+ end
56
+
57
+ def create_children_setter(name)
58
+ define_method("#{name}=") { |value| instance_variable_set("@#{name}", value) }
59
+ end
60
+
61
+ def create_children_appender(name)
62
+ options = children[name]
63
+
64
+ define_method "append_#{name.to_s.singularize}" do |child|
65
+ return instance_variable_set("@#{name}", child) unless options[:multiple]
66
+
67
+ children = send(name)
68
+ children << child unless options[:uniqueness] && children.any?(child)
69
+ child
70
+ end
71
+ end
72
+
73
+ def create_children_builder(name)
74
+ options = children[name]
75
+ create_children_appender(name)
76
+
77
+ define_method "build_#{name.to_s.singularize}" do |**attributes|
78
+ child = options[:class_name].constantize.new
79
+ attributes.each { |key, value| child.send("#{key}=", value) if child.respond_to?("#{key}=") }
80
+ send("append_#{name.to_s.singularize}", child)
81
+ end
82
+ end
83
+
84
+ # :nocov:
85
+ def warning_naming_suggestion(method, name, suggestion_name)
86
+ return if suggestion_name == name.to_s
87
+
88
+ location = caller.find { |c| c.include?('goldendocx/') && !c.include?('goldendocx/element.rb') }
89
+ warn "warning: [#{method}] `#{name}` better be `#{suggestion_name}` at #{location}"
90
+ end
91
+ # :nocov:
92
+ end
93
+
94
+ def children
95
+ self.class.children.keys.flat_map { |name| send(name) }.compact
96
+ end
97
+
98
+ def read_children(xml_node)
99
+ xml_node.children.each do |child_node|
100
+ read_child(child_node)
101
+ end
102
+ end
103
+
104
+ def read_child(child_node)
105
+ name, options = self.class.children.find do |_, opts|
106
+ opts[:class_name].constantize.adapt?(child_node)
107
+ end
108
+ if name.present?
109
+ child = options[:class_name].constantize.read_from(child_node)
110
+ send("append_#{name.to_s.singularize}", child)
111
+ else
112
+ unparsed_children << child_node
113
+ end
114
+ end
115
+ end
116
+ end
@@ -31,7 +31,7 @@ module Goldendocx
31
31
  return unless relationship_id
32
32
 
33
33
  non_visual_picture.non_visual_drawing.assign_attributes(
34
- relationship_id: relationship_id,
34
+ relationship_id:,
35
35
  name: "#{relationship_id}.png"
36
36
  )
37
37
  picture_fill.blip.relationship_id = relationship_id
@@ -7,6 +7,6 @@ module Goldendocx
7
7
  end
8
8
  end
9
9
 
10
- Dir.glob(File.join(File.dirname(__FILE__), 'properties', '*.rb').to_s).sort.each do |file|
10
+ Dir.glob(File.join(File.dirname(__FILE__), 'properties', '*.rb').to_s).each do |file|
11
11
  require file
12
12
  end
@@ -7,6 +7,6 @@ end
7
7
 
8
8
  require 'goldendocx/images/properties'
9
9
 
10
- Dir.glob(File.join(File.dirname(__FILE__), 'images', '*.rb').to_s).sort.each do |file|
10
+ Dir.glob(File.join(File.dirname(__FILE__), 'images', '*.rb').to_s).each do |file|
11
11
  require file
12
12
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Goldendocx
4
- module Documents
4
+ module Models
5
5
  class Relationship
6
6
  include Goldendocx::Element
7
7
 
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Goldendocx
4
+ module Models
5
+ class Relationships
6
+ include Goldendocx::Document
7
+
8
+ NAMESPACE = 'http://schemas.openxmlformats.org/package/2006/relationships'
9
+
10
+ tag :Relationships
11
+ attribute :xmlns, default: NAMESPACE, readonly: true
12
+
13
+ embeds_many :relationships, class_name: 'Goldendocx::Models::Relationship'
14
+
15
+ def size
16
+ relationships.size
17
+ end
18
+
19
+ def write_to(zos, xml_path)
20
+ zos.put_next_entry xml_path
21
+ zos.write to_document_xml
22
+ end
23
+
24
+ def add_relationship(type, target)
25
+ relationship_id = "rId#{relationships.size + 1}"
26
+ build_relationship(id: relationship_id, type:, target:)
27
+ relationship_id
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Goldendocx
4
+ module Models
5
+ end
6
+ end
7
+
8
+ Dir.glob(File.join(File.dirname(__FILE__), 'models', '*.rb').to_s).each do |file|
9
+ require file
10
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Goldendocx
4
+ module Parts
5
+ class App
6
+ include Goldendocx::Document
7
+
8
+ TYPE = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties'
9
+ XML_PATH = 'docProps/app.xml'
10
+ NAMESPACE = 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties'
11
+ CONTENT_TYPE = 'application/vnd.openxmlformats-officedocument.extended-properties+xml'
12
+
13
+ tag :Properties
14
+ attribute :xmlns, default: NAMESPACE, readonly: true
15
+
16
+ class << self
17
+ def read_from(app_document)
18
+ new_instance = new
19
+
20
+ app_document.children.map do |node|
21
+ new_instance.properties[node.name.to_sym] = node.text
22
+ end
23
+
24
+ new_instance
25
+ end
26
+ end
27
+
28
+ def write_to(zos)
29
+ zos.put_next_entry XML_PATH
30
+ zos.write to_document_xml
31
+ end
32
+
33
+ def to_document_xml
34
+ super do |xml|
35
+ properties.each do |name, value|
36
+ xml << Goldendocx.xml_serializer.build_element(name).tap { |app| app << value }
37
+ end
38
+ end
39
+ end
40
+
41
+ def properties
42
+ @properties ||= { Application: "Goldendocx_#{Goldendocx::VERSION}" }
43
+ end
44
+ end
45
+ end
46
+ end
@@ -11,52 +11,38 @@ module Goldendocx
11
11
  XML_PATH = '[Content_Types].xml'
12
12
  NAMESPACE = 'http://schemas.openxmlformats.org/package/2006/content-types'
13
13
 
14
- attr_reader :defaults, :overrides
14
+ REQUIRED_DEFAULTS = {
15
+ rels: 'application/vnd.openxmlformats-package.relationships+xml',
16
+ xml: 'application/xml'
17
+ }.with_indifferent_access.freeze
15
18
 
16
19
  tag :Types
17
20
  attribute :xmlns, default: NAMESPACE, readonly: true
18
21
 
19
- class << self
20
- def read_from(docx_file)
21
- content_types = Goldendocx::Parts::ContentTypes.new
22
- content_types.read_defaults(docx_file)
23
- content_types.read_overrides(docx_file)
24
- content_types
25
- end
26
- end
22
+ embeds_many :defaults, class_name: 'Goldendocx::ContentTypes::Default', uniqueness: true
23
+ embeds_many :overrides, class_name: 'Goldendocx::ContentTypes::Override', uniqueness: true
27
24
 
28
25
  def initialize
29
- @defaults = []
30
- @overrides = []
31
- end
32
-
33
- def read_defaults(docx_file)
34
- @defaults = Goldendocx.xml_serializer.parse(docx_file.read(XML_PATH), %w[Types Default]).map do |node|
35
- Goldendocx::ContentTypes::Default.new(node[:Extension], node[:ContentType])
26
+ REQUIRED_DEFAULTS.map do |extension, content_type|
27
+ build_defaults(extension:, content_type:)
36
28
  end
37
29
  end
38
30
 
39
- def read_overrides(docx_file)
40
- @overrides = Goldendocx.xml_serializer.parse(docx_file.read(XML_PATH), %w[Types Override]).map do |node|
41
- Goldendocx::ContentTypes::Override.new(node[:PartName], node[:ContentType])
42
- end
31
+ def write_to(zos)
32
+ zos.put_next_entry XML_PATH
33
+ zos.write to_document_xml
43
34
  end
44
35
 
45
36
  def add_default(extension, content_type)
46
- new_default = Goldendocx::ContentTypes::Default.new(extension, content_type)
47
- defaults << new_default if defaults.none?(new_default)
37
+ return if defaults.any? { |default| extension == default.extension && content_type == default.content_type }
38
+
39
+ build_defaults(extension:, content_type:)
48
40
  end
49
41
 
50
42
  def add_override(part_name, content_type)
51
- new_override = Goldendocx::ContentTypes::Override.new(part_name, content_type)
52
- overrides << new_override if overrides.none?(new_override)
53
- end
43
+ return if overrides.any? { |override| part_name == override.part_name && content_type == override.content_type }
54
44
 
55
- def to_document_xml
56
- super do |xml|
57
- defaults.each { |default| xml << default }
58
- overrides.each { |override| xml << override }
59
- end
45
+ build_override(part_name:, content_type:)
60
46
  end
61
47
  end
62
48
  end