goldendocx 0.2.2 → 0.2.3
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/CHANGELOG.md +8 -2
- data/Gemfile.lock +3 -1
- data/README.md +10 -2
- data/Rakefile +14 -3
- data/demo/templates/generate_codes.rb +10 -0
- data/demo/templates/xml_to_class.rb +91 -0
- data/demo/texts/append_plain_text.rb +1 -1
- data/demo/texts/append_styled_text.rb +1 -2
- data/demo/texts/create_text.rb +14 -0
- data/lib/extensions/active_support_extensions.rb +16 -0
- data/lib/extensions/nokogiri_extensions.rb +41 -0
- data/lib/extensions/ox_extensions.rb +35 -0
- data/lib/extensions/xml_serialize_extensions.rb +45 -0
- data/lib/goldendocx/components/properties/font_property.rb +19 -0
- data/lib/goldendocx/components/properties/language_property.rb +18 -0
- data/lib/goldendocx/components/properties/run_property.rb +2 -0
- data/lib/goldendocx/content_types/default.rb +2 -6
- data/lib/goldendocx/content_types/override.rb +2 -6
- data/lib/goldendocx/document.rb +9 -28
- data/lib/goldendocx/documents/body.rb +20 -11
- data/lib/goldendocx/documents/document.rb +2 -4
- data/lib/goldendocx/documents/latent_styles.rb +12 -0
- data/lib/goldendocx/documents/properties/default_style_property.rb +17 -0
- data/lib/goldendocx/documents/properties/page_margin_property.rb +37 -0
- data/lib/goldendocx/documents/properties/page_size_property.rb +17 -0
- data/lib/goldendocx/documents/properties/paragraph_default_style_property.rb +16 -0
- data/lib/goldendocx/documents/properties/run_default_style_property.rb +22 -0
- data/lib/goldendocx/documents/properties/section_property.rb +17 -0
- data/lib/goldendocx/documents/properties/style_name_property.rb +16 -0
- data/lib/goldendocx/documents/properties.rb +12 -0
- data/lib/goldendocx/documents/settings.rb +23 -0
- data/lib/goldendocx/documents/style.rb +10 -10
- data/lib/goldendocx/documents/styles.rb +9 -33
- data/lib/goldendocx/docx.rb +55 -12
- data/lib/goldendocx/element.rb +33 -140
- data/lib/goldendocx/has_associations.rb +52 -0
- data/lib/goldendocx/has_attributes.rb +67 -0
- data/lib/goldendocx/has_children.rb +116 -0
- data/lib/goldendocx/{documents → models}/relationship.rb +1 -1
- data/lib/goldendocx/{documents → models}/relationships.rb +4 -12
- data/lib/goldendocx/models.rb +10 -0
- data/lib/goldendocx/parts/app.rb +46 -0
- data/lib/goldendocx/parts/content_types.rb +16 -30
- data/lib/goldendocx/parts/core.rb +46 -0
- data/lib/goldendocx/parts/documents.rb +26 -13
- data/lib/goldendocx/parts/properties/created_at_property.rb +17 -0
- data/lib/goldendocx/parts/properties/creator_property.rb +16 -0
- data/lib/goldendocx/parts/properties/revision_property.rb +16 -0
- data/lib/goldendocx/parts/properties/updated_at_property.rb +17 -0
- data/lib/goldendocx/parts/properties/updater_property.rb +16 -0
- data/lib/goldendocx/parts/properties.rb +12 -0
- data/lib/goldendocx/tables/row.rb +1 -1
- data/lib/goldendocx/version.rb +1 -1
- data/lib/goldendocx/xml_serializers/nokogiri.rb +30 -22
- data/lib/goldendocx/xml_serializers/ox.rb +10 -20
- data/lib/goldendocx.rb +14 -2
- metadata +51 -7
- data/lib/goldendocx/documents/element.rb +0 -23
- data/lib/goldendocx/documents/unparsed_style.rb +0 -17
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Goldendocx
|
4
|
+
module Documents
|
5
|
+
module Properties
|
6
|
+
class PageMarginProperty
|
7
|
+
include Goldendocx::Element
|
8
|
+
|
9
|
+
namespace :w
|
10
|
+
tag :pgMar
|
11
|
+
|
12
|
+
attribute :top, namespace: :w, default: 1440
|
13
|
+
attribute :bottom, namespace: :w, default: 1440
|
14
|
+
attribute :left, namespace: :w, default: 1800
|
15
|
+
attribute :right, namespace: :w, default: 1800
|
16
|
+
attribute :header, namespace: :w, default: 851
|
17
|
+
attribute :footer, namespace: :w, default: 992
|
18
|
+
attribute :gutter, namespace: :w, default: 0
|
19
|
+
|
20
|
+
# Follow html margin style setting
|
21
|
+
def margin=(*args)
|
22
|
+
args = Array(*args)
|
23
|
+
raise StandardError("wrong number of arguments (given #{args.length}, expected 1..4)") unless (1..4).cover?(args.length)
|
24
|
+
|
25
|
+
margins =
|
26
|
+
case args.length
|
27
|
+
when 1 then [args.first] * 4
|
28
|
+
when 2 then [args.first, args.last, args.first, args.last]
|
29
|
+
when 3 then [args.first, args.second, args.last, args.second]
|
30
|
+
else args
|
31
|
+
end
|
32
|
+
assign_attributes(**%i[top right bottom left].zip(margins).to_h)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Goldendocx
|
4
|
+
module Documents
|
5
|
+
module Properties
|
6
|
+
class PageSizeProperty
|
7
|
+
include Goldendocx::Element
|
8
|
+
|
9
|
+
namespace :w
|
10
|
+
tag :pgSz
|
11
|
+
|
12
|
+
attribute :width, alias_name: :w, namespace: :w, default: 11906
|
13
|
+
attribute :height, alias_name: :h, namespace: :w, default: 16838
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Goldendocx
|
4
|
+
module Documents
|
5
|
+
module Properties
|
6
|
+
class ParagraphDefaultStyleProperty
|
7
|
+
include Goldendocx::Element
|
8
|
+
|
9
|
+
namespace :w
|
10
|
+
tag :pPrDefault
|
11
|
+
|
12
|
+
embeds_one :property, class_name: 'Goldendocx::Components::Properties::Property'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Goldendocx
|
4
|
+
module Documents
|
5
|
+
module Properties
|
6
|
+
class RunDefaultStyleProperty
|
7
|
+
include Goldendocx::Element
|
8
|
+
|
9
|
+
namespace :w
|
10
|
+
tag :rPrDefault
|
11
|
+
|
12
|
+
embeds_one :property, class_name: 'Goldendocx::Components::Properties::RunProperty'
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
build_property
|
16
|
+
property.build_language
|
17
|
+
property.build_font
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Goldendocx
|
4
|
+
module Documents
|
5
|
+
module Properties
|
6
|
+
class SectionProperty
|
7
|
+
include Goldendocx::Element
|
8
|
+
|
9
|
+
namespace :w
|
10
|
+
tag :sectPr
|
11
|
+
|
12
|
+
embeds_one :size, class_name: 'Goldendocx::Documents::Properties::PageSizeProperty', auto_build: true
|
13
|
+
embeds_one :margin, class_name: 'Goldendocx::Documents::Properties::PageMarginProperty', auto_build: true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Goldendocx
|
4
|
+
module Documents
|
5
|
+
module Properties
|
6
|
+
class StyleNameProperty
|
7
|
+
include Goldendocx::Element
|
8
|
+
|
9
|
+
namespace :w
|
10
|
+
tag :name
|
11
|
+
|
12
|
+
attribute :name, alias_name: :val, namespace: :w
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Goldendocx
|
4
|
+
module Documents
|
5
|
+
class Settings
|
6
|
+
include Goldendocx::Document
|
7
|
+
|
8
|
+
XML_PATH = 'word/settings.xml'
|
9
|
+
TYPE = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings'
|
10
|
+
CONTENT_TYPE = 'application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml'
|
11
|
+
|
12
|
+
namespace :w
|
13
|
+
tag :settings
|
14
|
+
concern_namespaces :mc, :o, :r, :m, :v, :w10, :w, :w14, :w15, :w16cid, :w16se, :sl
|
15
|
+
ignore_namespaces :w14, :w15, :w16se, :w16cid
|
16
|
+
|
17
|
+
def write_to(zos)
|
18
|
+
zos.put_next_entry XML_PATH
|
19
|
+
zos.write to_document_xml
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -3,18 +3,18 @@
|
|
3
3
|
module Goldendocx
|
4
4
|
module Documents
|
5
5
|
class Style
|
6
|
-
|
6
|
+
include Goldendocx::Element
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
8
|
+
namespace :w
|
9
|
+
tag :style
|
10
|
+
|
11
|
+
attribute :id, alias_name: :styleId, namespace: :w
|
12
|
+
attribute :type, namespace: :w
|
13
|
+
|
14
|
+
embeds_one :style_name, class_name: 'Goldendocx::Documents::Properties::StyleNameProperty', auto_build: true
|
15
15
|
|
16
|
-
def
|
17
|
-
|
16
|
+
def name
|
17
|
+
style_name.name
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -6,42 +6,22 @@ module Goldendocx
|
|
6
6
|
include Goldendocx::Document
|
7
7
|
|
8
8
|
XML_PATH = 'word/styles.xml'
|
9
|
+
TYPE = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles'
|
10
|
+
CONTENT_TYPE = 'application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml'
|
9
11
|
|
10
12
|
tag :styles
|
11
13
|
namespace :w
|
12
14
|
concern_namespaces :mc, :r, :w, :w14, :w15
|
13
15
|
ignore_namespaces :w14, :w15
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
@styles = []
|
19
|
-
end
|
17
|
+
embeds_one :defaults, class_name: 'Goldendocx::Documents::Properties::DefaultStyleProperty', auto_build: true
|
18
|
+
embeds_one :latentStyles, class_name: 'Goldendocx::Documents::LatentStyles'
|
19
|
+
embeds_many :styles, class_name: 'Goldendocx::Documents::Style'
|
20
20
|
|
21
21
|
def size
|
22
22
|
styles.size
|
23
23
|
end
|
24
24
|
|
25
|
-
def read_from(docx_file)
|
26
|
-
@styles = Goldendocx.xml_serializer.parse(docx_file.read(XML_PATH), %w[w:styles w:style]).map do |node|
|
27
|
-
Goldendocx::Documents::Style.new(node)
|
28
|
-
end
|
29
|
-
@doc_defaults = Goldendocx.xml_serializer.parse(docx_file.read(XML_PATH), %w[w:styles w:docDefaults]).map do |node|
|
30
|
-
Goldendocx::Documents::UnparsedStyle.new(node)
|
31
|
-
end
|
32
|
-
@latent_styles = Goldendocx.xml_serializer.parse(docx_file.read(XML_PATH), %w[w:styles w:latentStyles]).map do |node|
|
33
|
-
Goldendocx::Documents::UnparsedStyle.new(node)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def to_document_xml
|
38
|
-
super do |xml|
|
39
|
-
@doc_defaults&.each { |element| xml << element }
|
40
|
-
@latent_styles&.each { |element| xml << element }
|
41
|
-
styles.each { |style| xml << style }
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
25
|
def write_to(zos)
|
46
26
|
zos.put_next_entry XML_PATH
|
47
27
|
zos.write to_document_xml
|
@@ -60,14 +40,10 @@ module Goldendocx
|
|
60
40
|
end
|
61
41
|
|
62
42
|
def add_style(fragment)
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
style = Goldendocx.xml_serializer.parse(fragment)
|
68
|
-
style['w:styleId'] = style_id if style['w:styleId'].nil? || style['w:styleId'].empty?
|
69
|
-
@styles << Goldendocx::Documents::Style.new(style)
|
70
|
-
style_id
|
43
|
+
style = Goldendocx::Documents::Style.parse(fragment)
|
44
|
+
style.id = (styles.size + 1).to_s # Rearrange id to prevent duplicates
|
45
|
+
styles << style
|
46
|
+
style.id
|
71
47
|
end
|
72
48
|
end
|
73
49
|
end
|
data/lib/goldendocx/docx.rb
CHANGED
@@ -1,32 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'goldendocx/
|
4
|
-
require 'goldendocx/element'
|
5
|
-
require 'goldendocx/document'
|
6
|
-
|
3
|
+
require 'goldendocx/models'
|
7
4
|
require 'goldendocx/components'
|
8
5
|
require 'goldendocx/documents'
|
9
6
|
require 'goldendocx/parts'
|
10
7
|
|
11
8
|
module Goldendocx
|
12
9
|
class Docx
|
13
|
-
|
10
|
+
include Goldendocx::HasAssociations
|
11
|
+
|
12
|
+
attr_reader :unstructured_entries, :documents
|
13
|
+
|
14
|
+
RELATIONSHIPS_XML_PATH = '_rels/.rels'
|
14
15
|
|
15
16
|
STRUCTURED_ENTRIES = [
|
17
|
+
RELATIONSHIPS_XML_PATH,
|
18
|
+
|
16
19
|
Goldendocx::Parts::ContentTypes::XML_PATH,
|
20
|
+
Goldendocx::Parts::App::XML_PATH,
|
21
|
+
Goldendocx::Parts::Core::XML_PATH,
|
22
|
+
|
23
|
+
Goldendocx::Parts::Documents::RELATIONSHIPS_XML_PATH,
|
17
24
|
|
18
|
-
Goldendocx::Documents::
|
19
|
-
Goldendocx::Documents::
|
25
|
+
Goldendocx::Documents::Document::XML_PATH,
|
26
|
+
Goldendocx::Documents::Settings::XML_PATH,
|
20
27
|
Goldendocx::Documents::Styles::XML_PATH
|
21
28
|
].freeze
|
22
29
|
|
23
|
-
|
30
|
+
relationships_at RELATIONSHIPS_XML_PATH
|
31
|
+
associate :app, class_name: 'Goldendocx::Parts::App'
|
32
|
+
associate :core, class_name: 'Goldendocx::Parts::Core'
|
33
|
+
associate :content_types, class_name: 'Goldendocx::Parts::ContentTypes', isolate: true
|
34
|
+
|
35
|
+
def initialize(file_path = nil)
|
36
|
+
file_path.present? ? read_from(file_path) : build_default
|
37
|
+
end
|
38
|
+
|
39
|
+
def read_from(file_path)
|
24
40
|
docx_file = Zip::File.new(file_path)
|
41
|
+
|
42
|
+
read_relationships(docx_file)
|
43
|
+
read_associations(docx_file)
|
44
|
+
|
25
45
|
@documents = Goldendocx::Parts::Documents.read_from(docx_file)
|
26
|
-
|
46
|
+
|
27
47
|
@unstructured_entries = docx_file.entries.filter_map do |entry|
|
28
48
|
UnStructuredEntry.new(entry) unless STRUCTURED_ENTRIES.include?(entry.name)
|
29
49
|
end
|
50
|
+
|
51
|
+
self
|
30
52
|
end
|
31
53
|
|
32
54
|
def write_to(new_path)
|
@@ -65,6 +87,25 @@ module Goldendocx
|
|
65
87
|
|
66
88
|
private
|
67
89
|
|
90
|
+
def build_default
|
91
|
+
associations.each do |association, options|
|
92
|
+
association_class = options[:class_name].constantize
|
93
|
+
instance_variable_set("@#{association}", association_class.new)
|
94
|
+
add_relationship association_class::TYPE, association_class::XML_PATH unless options[:isolate]
|
95
|
+
end
|
96
|
+
|
97
|
+
content_types.add_override "/#{Goldendocx::Parts::App::XML_PATH}", Goldendocx::Parts::App::CONTENT_TYPE
|
98
|
+
content_types.add_override "/#{Goldendocx::Parts::Core::XML_PATH}", Goldendocx::Parts::Core::CONTENT_TYPE
|
99
|
+
content_types.add_override "/#{Goldendocx::Documents::Styles::XML_PATH}", Goldendocx::Documents::Styles::CONTENT_TYPE
|
100
|
+
content_types.add_override "/#{Goldendocx::Documents::Settings::XML_PATH}", Goldendocx::Documents::Settings::CONTENT_TYPE
|
101
|
+
|
102
|
+
@documents = Goldendocx::Parts::Documents.new
|
103
|
+
add_relationship Goldendocx::Documents::Document::TYPE, Goldendocx::Documents::Document::XML_PATH
|
104
|
+
content_types.add_override "/#{Goldendocx::Documents::Document::XML_PATH}", Goldendocx::Documents::Document::CONTENT_TYPE
|
105
|
+
|
106
|
+
@unstructured_entries = []
|
107
|
+
end
|
108
|
+
|
68
109
|
def ensure_image_content_type!(_image_data)
|
69
110
|
extension = 'png'
|
70
111
|
content_type = 'image/png'
|
@@ -79,10 +120,12 @@ module Goldendocx
|
|
79
120
|
|
80
121
|
def to_stream
|
81
122
|
Zip::OutputStream.write_buffer do |zos|
|
82
|
-
zos
|
83
|
-
|
123
|
+
write_relationships(zos)
|
124
|
+
|
125
|
+
associations.each_key do |association|
|
126
|
+
send(association).write_to(zos)
|
127
|
+
end
|
84
128
|
|
85
|
-
zos.put_next_entry Goldendocx::Parts::Documents::XML_PATH
|
86
129
|
documents.write_stream(zos)
|
87
130
|
|
88
131
|
@unstructured_entries.each { |unstructured_entry| unstructured_entry.write_to(zos) }
|
data/lib/goldendocx/element.rb
CHANGED
@@ -1,19 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
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
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
30
|
-
|
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
|
76
|
-
|
77
|
-
|
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
|
86
|
-
|
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
|
99
|
-
|
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
|
-
|
116
|
-
|
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
|
146
|
-
attributes
|
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
|
158
|
-
@
|
63
|
+
def tag_name
|
64
|
+
@tag_name ||= [namespace, tag].compact.join(':')
|
159
65
|
end
|
160
66
|
|
161
|
-
def
|
162
|
-
|
163
|
-
|
164
|
-
self.class.superclass.children.keys.flat_map { |name| send(name) }
|
67
|
+
def to_element(**context, &block)
|
68
|
+
Goldendocx.xml_serializer.build_element(tag_name, **context) { |xml| build_element(xml, &block) }
|
165
69
|
end
|
166
70
|
|
167
|
-
def
|
168
|
-
|
169
|
-
send(name)
|
170
|
-
end.concat(siblings).compact
|
71
|
+
def to_xml(&block)
|
72
|
+
Goldendocx.xml_serializer.build_xml(tag_name) { |xml| build_element(xml, &block) }
|
171
73
|
end
|
172
74
|
|
173
|
-
def
|
174
|
-
|
175
|
-
|
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
|
-
|
179
|
-
|
180
|
-
end
|
79
|
+
children.each { |child| xml << child }
|
80
|
+
unparsed_children.each { |child| xml << child }
|
181
81
|
|
182
|
-
|
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,52 @@
|
|
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
|
+
class_methods do
|
15
|
+
def relationships_at(xml_path)
|
16
|
+
self.relationships_xml_path = xml_path
|
17
|
+
end
|
18
|
+
|
19
|
+
def associate(name, class_name:, isolate: false)
|
20
|
+
named = name.to_s
|
21
|
+
associations[named] = { class_name: class_name, isolate: isolate }
|
22
|
+
|
23
|
+
define_method named do
|
24
|
+
return instance_variable_get("@#{name}") if instance_variable_defined?("@#{name}")
|
25
|
+
|
26
|
+
new_instance = class_name.constantize.new
|
27
|
+
instance_variable_set("@#{name}", new_instance)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def read_associations(docx_file)
|
33
|
+
associations.each do |association, options|
|
34
|
+
association_class = options[:class_name].constantize
|
35
|
+
association_document_xml = docx_file.read(association_class::XML_PATH)
|
36
|
+
instance_variable_set("@#{association}", association_class.parse(association_document_xml))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def read_relationships(docx_file)
|
41
|
+
@relationships = Goldendocx::Models::Relationships.parse(docx_file.read(relationships_xml_path))
|
42
|
+
end
|
43
|
+
|
44
|
+
def write_relationships(zos)
|
45
|
+
relationships.write_to(zos, relationships_xml_path)
|
46
|
+
end
|
47
|
+
|
48
|
+
def relationships
|
49
|
+
@relationships ||= Goldendocx::Models::Relationships.new
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|