openxml-package 0.2.9 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c894c5ccef5327f4463ca9ddd3401bc6f3aa7e27
4
- data.tar.gz: c34eb930e46eff3af421c9ac49bf0bedc78113d6
2
+ SHA256:
3
+ metadata.gz: 7679189d1fa0249404f348c35516577f71094f29154a4d6db8d18d73a69c0890
4
+ data.tar.gz: a1a41c6458d7b4f6a90f29443b5778b61e74a44c9ce6a1e5fed2138077b6c2a7
5
5
  SHA512:
6
- metadata.gz: 2e6418b2b1dadf17009690fbfc6ada8d12f3e5b31493d4df9299bff672b8756098bb11d32c5fb9d3fe59180c7ee2838cf77ac167ca49b9ba6fe3120988cf3749
7
- data.tar.gz: f2a422bb660b761956f6a76ec856697115cc0d9ecd08b8f138c6e4093a6a74a8e83d5e2fad8325f62fda30eb6ab88f7f767b5acaa31e976e2bc231774f353cc5
6
+ metadata.gz: 8b739a383caa59890a27b389d020de3262d7017c3444ee49309b766eb211a1c67ce2c48e94fff0836c1f65b99193f5da6d13261555d5bf0a51b433d470d701b9
7
+ data.tar.gz: 2a78d1af5bce4eb2bdeb8ce20a2787070094b0590a5fa20ddf299a2b0650fb798ebf2e058ef493825b8807207b4e9d2946ea37a8930c5075912c3eeb4f12223f
@@ -0,0 +1,8 @@
1
+ ## 0.3.4 - Oct 16, 2019
2
+
3
+ - Removed rubyzip lock
4
+
5
+
6
+ ## 0.3.3 - Oct 2, 2018
7
+
8
+ - Optimized setting attributes
@@ -1,3 +1,3 @@
1
1
  module OpenXmlPackage
2
- VERSION = "0.2.9"
2
+ VERSION = "0.3.4"
3
3
  end
@@ -13,6 +13,7 @@ module OpenXml
13
13
  def initialize(options={})
14
14
  @to_s_options = { with_xml: true }
15
15
 
16
+ @ns = nil
16
17
  @document = Ox::Document.new(
17
18
  encoding: "UTF-8",
18
19
  version: "1.0",
@@ -8,7 +8,7 @@ module OpenXml
8
8
  end
9
9
 
10
10
  def []=(attribute, value)
11
- namespace_def = attribute.downcase.to_s.match /^xmlns(?:\:(?<prefix>.*))?$/
11
+ namespace_def = attribute.downcase.to_s.match(/^xmlns(?:\:(?<prefix>.*))?$/)
12
12
  namespaces << namespace_def[:prefix].to_sym if namespace_def && namespace_def[:prefix]
13
13
  super
14
14
  end
@@ -0,0 +1,29 @@
1
+ require "openxml/has_properties"
2
+
3
+ module OpenXml
4
+ module ContainsProperties
5
+
6
+ def self.included(base)
7
+ base.class_eval do
8
+ include HasProperties
9
+ include InstanceMethods
10
+ end
11
+ end
12
+
13
+ module InstanceMethods
14
+
15
+ def property_xml(xml)
16
+ ensure_required_choices
17
+ props = active_properties
18
+ return unless render_properties? props
19
+ props.each { |prop| prop.to_xml(xml) }
20
+ end
21
+
22
+ def properties_attributes
23
+ {}
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+ end
@@ -6,21 +6,20 @@ module OpenXml
6
6
 
7
7
  class << self
8
8
  attr_reader :property_name
9
- attr_reader :namespace
10
9
 
11
10
  def tag(*args)
12
11
  @tag = args.first if args.any?
13
- @tag
12
+ @tag ||= nil
14
13
  end
15
14
 
16
15
  def name(*args)
17
16
  @property_name = args.first if args.any?
18
- @name
17
+ @property_name ||= nil
19
18
  end
20
19
 
21
20
  def namespace(*args)
22
21
  @namespace = args.first if args.any?
23
- @namespace
22
+ @namespace ||= nil
24
23
  end
25
24
 
26
25
  end
@@ -1,3 +1,5 @@
1
+ require "openxml/unmet_requirement"
2
+
1
3
  module OpenXml
2
4
  module HasAttributes
3
5
 
@@ -6,9 +8,13 @@ module OpenXml
6
8
  end
7
9
 
8
10
  module ClassMethods
9
- def attribute(name, expects: nil, one_of: nil, in_range: nil, displays_as: nil, namespace: nil, matches: nil, deprecated: false)
10
- bad_names = %w(tag name namespace properties_tag)
11
- raise ArgumentError if bad_names.member? name
11
+
12
+ RESERVED_NAMES = %w{ tag name namespace properties_tag }.freeze
13
+
14
+ def attribute(name, expects: nil, one_of: nil, in_range: nil, displays_as: nil, namespace: nil, matches: nil, validation: nil, required: false, deprecated: false)
15
+ raise ArgumentError if RESERVED_NAMES.member? name.to_s
16
+
17
+ required_attributes.push(name) if required
12
18
 
13
19
  attr_reader name
14
20
 
@@ -17,21 +23,39 @@ module OpenXml
17
23
  send(expects, value) unless expects.nil?
18
24
  matches?(value, matches) unless matches.nil?
19
25
  in_range?(value, in_range) unless in_range.nil?
26
+ validation.call(value) if validation.respond_to? :call
20
27
  instance_variable_set "@#{name}", value
21
28
  end
22
29
 
23
30
  camelized_name = name.to_s.gsub(/_([a-z])/i) { $1.upcase }.to_sym
24
- attributes[name] = [displays_as || camelized_name, namespace || @attribute_namespace]
31
+ attributes[name] = [displays_as || camelized_name, namespace || attribute_namespace]
25
32
  end
26
33
 
27
34
  def attributes
28
- @attributes ||= {}
35
+ @attributes ||= {}.tap do |attrs|
36
+ if superclass.respond_to?(:attributes)
37
+ superclass.attributes.each do |key, value|
38
+ attrs[key] = value.dup
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ def required_attributes
45
+ @required_attributes ||= [].tap do |attrs|
46
+ attrs.push(*superclass.required_attributes) if superclass.respond_to?(:required_attributes)
47
+ end
29
48
  end
30
49
 
31
50
  def with_namespace(namespace, &block)
32
51
  @attribute_namespace = namespace
33
52
  instance_eval(&block)
34
53
  end
54
+
55
+ def attribute_namespace
56
+ @attribute_namespace ||= nil
57
+ end
58
+
35
59
  end
36
60
 
37
61
  def render?
@@ -42,85 +66,104 @@ module OpenXml
42
66
  self.class.attributes
43
67
  end
44
68
 
69
+ def required_attributes
70
+ self.class.required_attributes
71
+ end
72
+
45
73
  private
46
74
 
47
75
  def xml_attributes
76
+ ensure_required_attributes_set
48
77
  attributes.each_with_object({}) do |(name, options), attrs|
49
78
  display, namespace = options
50
79
  value = send(name)
51
80
  attr_name = "#{namespace}:#{display}"
52
- attr_name = "#{display}" if namespace.nil?
81
+ attr_name = display.to_s if namespace.nil?
53
82
  attrs[attr_name] = value unless value.nil?
54
83
  end
55
84
  end
56
85
 
86
+ def ensure_required_attributes_set
87
+ unset_attributes = required_attributes.reject do |attr|
88
+ instance_variable_defined?("@#{attr}")
89
+ end
90
+ return if unset_attributes.empty?
91
+
92
+ raise OpenXml::UnmetRequirementError, "Required attribute(s) #{unset_attributes.join(", ")} have not been set"
93
+ end
94
+
57
95
  def boolean(value)
58
- message = "Invalid #{name}: frame must be true or false"
59
- raise ArgumentError, message unless [true, false].member? value
96
+ return if [true, false].member? value
97
+ raise ArgumentError, "Invalid #{name}: frame must be true or false"
60
98
  end
61
99
 
62
100
  def hex_color(value)
63
- message = "Invalid #{name}: must be :auto or a hex color, e.g. 4F1B8C"
64
- raise ArgumentError, message unless value == :auto || value =~ /^[0-9A-F]{6}$/
101
+ return if value == :auto || value =~ /^[0-9A-F]{6}$/
102
+ raise ArgumentError, "Invalid #{name}: must be :auto or a hex color, e.g. 4F1B8C"
65
103
  end
66
104
 
67
105
  def hex_digit(value)
68
- message = "Invalid #{name}: must be a two-digit hex number, e.g. BF"
69
- raise ArgumentError, message unless value =~ /^[0-9A-F]{2}$/
106
+ return if value =~ /^[0-9A-F]{2}$/
107
+ raise ArgumentError, "Invalid #{name}: must be a two-digit hex number, e.g. BF"
70
108
  end
71
109
 
72
110
  def hex_digit_4(value)
73
- message = "Invalid #{name}: must be a four-digit hex number, e.g. BF12"
74
- raise ArgumentError, message unless value =~ /^[0-9A-F]{4}$/
111
+ return if value =~ /^[0-9A-F]{4}$/
112
+ raise ArgumentError, "Invalid #{name}: must be a four-digit hex number, e.g. BF12"
75
113
  end
76
114
 
77
115
  def long_hex_number(value)
78
- message = "Invalid #{name}: must be an eight-digit hex number, e.g., FFAC0013"
79
- raise ArgumentError, message unless value =~ /^[0-9A-F]{8}$/
116
+ return if value =~ /^[0-9A-F]{8}$/
117
+ raise ArgumentError, "Invalid #{name}: must be an eight-digit hex number, e.g., FFAC0013"
80
118
  end
81
119
 
82
120
  def hex_string(value)
83
- message = "Invalid #{name}: must be a string of hexadecimal numbers, e.g. FFA23C6E"
84
- raise ArgumentError, message unless value =~ /^[0-9A-F]+$/
121
+ return if value =~ /^[0-9A-F]+$/
122
+ raise ArgumentError, "Invalid #{name}: must be a string of hexadecimal numbers, e.g. FFA23C6E"
85
123
  end
86
124
 
87
125
  def integer(value)
88
- message = "Invalid #{name}: must be an integer"
89
- raise ArgumentError, message unless value.is_a?(Integer)
126
+ return if value.is_a?(Integer)
127
+ raise ArgumentError, "Invalid #{name}: must be an integer"
90
128
  end
91
129
 
92
130
  def positive_integer(value)
93
- message = "Invalid #{name}: must be a positive integer"
94
- raise ArgumentError, message unless value.is_a?(Integer) && value >= 0
131
+ return if value.is_a?(Integer) && value >= 0
132
+ raise ArgumentError, "Invalid #{name}: must be a positive integer"
95
133
  end
96
134
 
97
135
  def string(value)
98
- message = "Invalid #{name}: must be a string"
99
- raise ArgumentError, message if !value.is_a?(String) || value.length.zero?
136
+ return if value.is_a?(String) && value.length > 0
137
+ raise ArgumentError, "Invalid #{name}: must be a string"
138
+ end
139
+
140
+ def string_or_blank(value)
141
+ return if value.is_a?(String)
142
+ raise ArgumentError, "Invalid #{name}: must be a string, even if the string is empty"
100
143
  end
101
144
 
102
145
  def in_range?(value, range)
103
- message = "Invalid #{name}: must be a number between #{range.begin} and #{range.end}"
104
- raise ArgumentError, message unless range.include?(value.to_i)
146
+ return if range.include?(value.to_i)
147
+ raise ArgumentError, "Invalid #{name}: must be a number between #{range.begin} and #{range.end}"
105
148
  end
106
149
 
107
150
  def percentage(value)
108
- message = "Invalid #{name}: must be a percentage"
109
- raise ArgumentError, message unless value.is_a?(String) && value =~ /-?[0-9]+(\.[0-9]+)?%/ # Regex supplied in sec. 22.9.2.9 of Office Open XML docs
151
+ return if value.is_a?(String) && value =~ /-?[0-9]+(\.[0-9]+)?%/ # Regex supplied in sec. 22.9.2.9 of Office Open XML docs
152
+ raise ArgumentError, "Invalid #{name}: must be a percentage"
110
153
  end
111
154
 
112
155
  def on_or_off(value)
113
- valid_in? value, [:on, :off]
156
+ valid_in? value, %i{ on off }
114
157
  end
115
158
 
116
159
  def valid_in?(value, list)
117
- message = "Invalid #{name}: must be one of #{list} (was #{value.inspect})"
118
- raise ArgumentError, message unless list.member?(value)
160
+ return if list.member?(value)
161
+ raise ArgumentError, "Invalid #{name}: must be one of #{list} (was #{value.inspect})"
119
162
  end
120
163
 
121
164
  def matches?(value, regexp)
122
- message = "Value does not match #{regexp}"
123
- raise ArgumentError, message unless value =~ regexp
165
+ return if value =~ regexp
166
+ raise ArgumentError, "Value does not match #{regexp}"
124
167
  end
125
168
 
126
169
  end
@@ -2,7 +2,8 @@ module OpenXml
2
2
  module HasChildren
3
3
  attr_reader :children
4
4
 
5
- def initialize
5
+ def initialize(*)
6
+ super
6
7
  @children = []
7
8
  end
8
9
 
@@ -26,5 +27,6 @@ module OpenXml
26
27
  def render?
27
28
  super || children.any?
28
29
  end
30
+
29
31
  end
30
32
  end
@@ -0,0 +1,223 @@
1
+ require "openxml/unmet_requirement"
2
+
3
+ module OpenXml
4
+ module HasProperties
5
+
6
+ class ChoiceGroupUniqueError < RuntimeError; end
7
+
8
+ def self.included(base)
9
+ base.extend ClassMethods
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ def properties_tag(*args)
15
+ @properties_tag = args.first if args.any?
16
+ @properties_tag ||= nil
17
+ end
18
+
19
+ def value_property(name, as: nil, klass: nil, required: false, default_value: nil)
20
+ attr_reader name
21
+
22
+ properties[name] = (as || name).to_s
23
+ required_properties[name] = default_value if required
24
+ classified_name = properties[name].split("_").map(&:capitalize).join
25
+ class_name = klass.to_s unless klass.nil?
26
+ class_name ||= (to_s.split("::")[0...-2] + ["Properties", classified_name]).join("::")
27
+
28
+ (choice_groups[current_group] ||= []).push(name) unless current_group.nil?
29
+
30
+ class_eval <<-CODE, __FILE__, __LINE__ + 1
31
+ def #{name}=(value)
32
+ group_index = #{@current_group.inspect}
33
+ ensure_unique_in_group(:#{name}, group_index) unless group_index.nil?
34
+ instance_variable_set "@#{name}", #{class_name}.new(value)
35
+ end
36
+ CODE
37
+ end
38
+
39
+ def property(name, as: nil, klass: nil, required: false)
40
+ properties[name] = (as || name).to_s
41
+ required_properties[name] = true if required
42
+ classified_name = properties[name].split("_").map(&:capitalize).join
43
+ class_name = klass.to_s unless klass.nil?
44
+ class_name ||= (to_s.split("::")[0...-2] + ["Properties", classified_name]).join("::")
45
+
46
+ (choice_groups[current_group] ||= []).push(name) unless current_group.nil?
47
+
48
+ class_eval <<-CODE, __FILE__, __LINE__ + 1
49
+ def #{name}(*args)
50
+ unless instance_variable_defined?("@#{name}")
51
+ group_index = #{@current_group.inspect}
52
+ ensure_unique_in_group(:#{name}, group_index) unless group_index.nil?
53
+ instance_variable_set "@#{name}", #{class_name}.new(*args)
54
+ end
55
+
56
+ instance_variable_get "@#{name}"
57
+ end
58
+ CODE
59
+ end
60
+
61
+ def property_choice(required: false)
62
+ @current_group = choice_groups.length
63
+ required_choices << @current_group if required
64
+ yield
65
+ @current_group = nil
66
+ end
67
+
68
+ def current_group
69
+ @current_group ||= nil
70
+ end
71
+
72
+ def properties
73
+ @properties ||= {}.tap do |props|
74
+ props.merge!(superclass.properties) if superclass.respond_to?(:properties)
75
+ end
76
+ end
77
+
78
+ def choice_groups
79
+ @choice_groups ||= [].tap do |choices|
80
+ choices.push(*superclass.choice_groups.map(&:dup)) if superclass.respond_to?(:choice_groups)
81
+ end
82
+ end
83
+
84
+ def required_properties
85
+ @required_properties ||= {}.tap do |props|
86
+ props.merge!(superclass.required_properties) if superclass.respond_to?(:required_properties)
87
+ end
88
+ end
89
+
90
+ def required_choices
91
+ @required_choices ||= [].tap do |choices|
92
+ choices.push(*superclass.required_choices) if superclass.respond_to?(:required_choices)
93
+ end
94
+ end
95
+
96
+ def properties_attribute(name, **args)
97
+ properties_element.attribute name, **args
98
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
99
+ def #{name}=(value)
100
+ properties_element.#{name} = value
101
+ end
102
+
103
+ def #{name}
104
+ properties_element.#{name}
105
+ end
106
+ RUBY
107
+ end
108
+
109
+ def properties_element
110
+ this = self
111
+ parent_klass = superclass.respond_to?(:properties_element) ? superclass.properties_element : OpenXml::Element
112
+ @properties_element ||= Class.new(parent_klass) do
113
+ tag :"#{this.properties_tag || this.default_properties_tag}"
114
+ namespace :"#{this.namespace}"
115
+ end
116
+ end
117
+
118
+ def default_properties_tag
119
+ :"#{tag}Pr"
120
+ end
121
+
122
+ end
123
+
124
+ def initialize(*_args)
125
+ super
126
+ build_required_properties
127
+ end
128
+
129
+ def properties_element
130
+ @properties_element ||= self.class.properties_element.new
131
+ end
132
+
133
+ def properties_attributes
134
+ properties_element.attributes
135
+ end
136
+
137
+ def render?
138
+ return true unless defined?(super)
139
+ render_properties? || super
140
+ end
141
+
142
+ def to_xml(xml)
143
+ super(xml) do
144
+ property_xml(xml)
145
+ yield xml if block_given?
146
+ end
147
+ end
148
+
149
+ def property_xml(xml)
150
+ ensure_required_choices
151
+ props = active_properties
152
+ return unless render_properties? props
153
+
154
+ properties_element.to_xml(xml) do
155
+ props.each { |prop| prop.to_xml(xml) }
156
+ end
157
+ end
158
+
159
+ def build_required_properties
160
+ required_properties.each do |prop, default_value|
161
+ public_send(:"#{prop}=", default_value) if respond_to? :"#{prop}="
162
+ public_send(:"#{prop}")
163
+ end
164
+ end
165
+
166
+ private
167
+
168
+ def properties
169
+ self.class.properties
170
+ end
171
+
172
+ def active_properties
173
+ properties.keys.map { |property| instance_variable_get("@#{property}") }.compact
174
+ end
175
+
176
+ def render_properties?(properties=active_properties)
177
+ properties.any?(&:render?) || properties_attributes.keys.any? do |key|
178
+ properties_element.instance_variable_defined?("@#{key}")
179
+ end
180
+ end
181
+
182
+ def properties_tag
183
+ self.class.properties_tag || default_properties_tag
184
+ end
185
+
186
+ def default_properties_tag
187
+ :"#{tag}Pr"
188
+ end
189
+
190
+ def choice_groups
191
+ self.class.choice_groups
192
+ end
193
+
194
+ def required_properties
195
+ self.class.required_properties
196
+ end
197
+
198
+ def required_choices
199
+ self.class.required_choices
200
+ end
201
+
202
+ def ensure_unique_in_group(name, group_index)
203
+ other_names = (choice_groups[group_index] - [name])
204
+ return if other_names.none? { |other_name| instance_variable_defined?("@#{other_name}") }
205
+ raise ChoiceGroupUniqueError, "Property #{name} cannot also be set with #{other_names.join(", ")}."
206
+ end
207
+
208
+ def unmet_choices
209
+ required_choices.reject do |choice_index|
210
+ choice_groups[choice_index].one? do |prop_name|
211
+ instance_variable_defined?("@#{prop_name}")
212
+ end
213
+ end
214
+ end
215
+
216
+ def ensure_required_choices
217
+ unmet_choice_groups = unmet_choices.map { |index| choice_groups[index].join(", ") }
218
+ return if unmet_choice_groups.empty?
219
+ raise OpenXml::UnmetRequirementError, "Required choice from among group(s) (#{unmet_choice_groups.join("), (")}) not made"
220
+ end
221
+
222
+ end
223
+ end
@@ -18,7 +18,7 @@ module OpenXml
18
18
  end
19
19
 
20
20
  def content_types(&block)
21
- content_types_presets.instance_eval &block
21
+ content_types_presets.instance_eval(&block)
22
22
  end
23
23
 
24
24
  def open(path)
@@ -80,7 +80,7 @@ module OpenXml
80
80
  end
81
81
 
82
82
  def write_to(path)
83
- File.open(path, "w") do |file|
83
+ File.open(path, "wb") do |file|
84
84
  file.write to_stream.string
85
85
  end
86
86
  end
@@ -40,7 +40,7 @@ module OpenXml
40
40
  end
41
41
 
42
42
  def to_xml
43
- build_xml do |xml|
43
+ build_standalone_xml do |xml|
44
44
  xml.Types(xmlns: "http://schemas.openxmlformats.org/package/2006/content-types") {
45
45
  defaults.each { |extension, content_type| xml.Default("Extension" => extension, "ContentType" => content_type) }
46
46
  overrides.each { |part_name, content_type| xml.Override("PartName" => part_name, "ContentType" => content_type) }
@@ -23,7 +23,7 @@ module OpenXml
23
23
  end
24
24
  end
25
25
 
26
- def add_relationship(type, target, id=nil, target_mode=nil)
26
+ def add_relationship(type, target, id=next_id, target_mode=nil)
27
27
  OpenXml::Elements::Relationship.new(type, target, id, target_mode).tap do |relationship|
28
28
  relationships.push relationship
29
29
  end
@@ -56,6 +56,11 @@ module OpenXml
56
56
  private
57
57
  attr_reader :relationships
58
58
 
59
+ def next_id
60
+ @current_id = (@current_id || 0) + 1
61
+ "rId#{@current_id}"
62
+ end
63
+
59
64
  end
60
65
  end
61
66
  end
@@ -0,0 +1,22 @@
1
+ module OpenXml
2
+ module Properties
3
+ end
4
+ end
5
+
6
+ require "openxml/properties/base_property"
7
+ require "openxml/properties/complex_property"
8
+ require "openxml/properties/value_property"
9
+
10
+ require "openxml/properties/boolean_property"
11
+ require "openxml/properties/integer_property"
12
+ require "openxml/properties/positive_integer_property"
13
+ require "openxml/properties/string_property"
14
+ require "openxml/properties/on_off_property"
15
+ require "openxml/properties/toggle_property"
16
+
17
+ require "openxml/properties/container_property"
18
+ require "openxml/properties/transparent_container_property"
19
+
20
+ Dir.glob(File.join(File.dirname(__FILE__), "properties", "*.rb").to_s).each do |file|
21
+ require file
22
+ end
@@ -0,0 +1,80 @@
1
+ module OpenXml
2
+ module Properties
3
+ class BaseProperty
4
+ attr_reader :value
5
+
6
+ class << self
7
+ attr_reader :property_name
8
+ attr_reader :allowed_tags
9
+
10
+ def tag_is_one_of(tags)
11
+ attr_accessor :tag
12
+ @allowed_tags = tags
13
+ end
14
+
15
+ def tag(*args)
16
+ @tag = args.first if args.any?
17
+ @tag ||= nil
18
+ end
19
+
20
+ def name(*args)
21
+ @property_name = args.first if args.any?
22
+ @property_name ||= nil
23
+ end
24
+
25
+ def namespace(*args)
26
+ @namespace = args.first if args.any?
27
+ @namespace ||= nil
28
+ end
29
+
30
+ end
31
+
32
+ def initialize(tag=nil, *_args)
33
+ return unless self.class.allowed_tags
34
+ validate_tag tag
35
+ @tag = tag
36
+ end
37
+
38
+ def validate_tag(tag)
39
+ return if self.class.allowed_tags.include?(tag)
40
+ allowed = self.class.allowed_tags.join(", ")
41
+ raise ArgumentError, "Invalid tag name for #{name}: #{tag.inspect}. It should be one of #{allowed}."
42
+ end
43
+
44
+ def render?
45
+ !value.nil?
46
+ end
47
+
48
+ def name
49
+ self.class.property_name || default_name
50
+ end
51
+
52
+ def default_name
53
+ class_name.gsub(/(.)([A-Z])/, '\1_\2').downcase
54
+ end
55
+
56
+ def tag
57
+ self.class.tag || default_tag
58
+ end
59
+
60
+ def default_tag
61
+ (class_name[0, 1].downcase + class_name[1..-1]).to_sym
62
+ end
63
+
64
+ def namespace
65
+ self.class.namespace
66
+ end
67
+
68
+ private
69
+
70
+ def apply_namespace(xml)
71
+ namespace.nil? ? xml : xml[namespace]
72
+ end
73
+
74
+ def class_name
75
+ self.class.to_s.split(/::/).last
76
+ end
77
+
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,15 @@
1
+ module OpenXml
2
+ module Properties
3
+ class BooleanProperty < ValueProperty
4
+
5
+ def ok_values
6
+ [nil, true, false]
7
+ end
8
+
9
+ def to_xml(xml)
10
+ super if value
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ require "openxml/has_attributes"
2
+
3
+ module OpenXml
4
+ module Properties
5
+ class ComplexProperty < BaseProperty
6
+ include HasAttributes
7
+
8
+ def to_xml(xml)
9
+ return unless render?
10
+ apply_namespace(xml).public_send(tag, xml_attributes) do
11
+ yield xml if block_given?
12
+ end
13
+ end
14
+
15
+ def render?
16
+ !xml_attributes.empty?
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,70 @@
1
+ require "openxml/has_attributes"
2
+
3
+ module OpenXml
4
+ module Properties
5
+ class ContainerProperty < BaseProperty
6
+ include Enumerable
7
+ include HasAttributes
8
+
9
+ class << self
10
+
11
+ def child_class(*args)
12
+ unless args.empty?
13
+ @child_classes = args.map { |arg|
14
+ prop_name = arg.to_s.split(/_/).map(&:capitalize).join # LazyCamelCase
15
+ const_name = (self.to_s.split(/::/)[0...-1] + [prop_name]).join("::")
16
+ Object.const_get const_name
17
+ }
18
+ end
19
+
20
+ @child_classes
21
+ end
22
+ alias child_classes child_class
23
+
24
+ end
25
+
26
+ def initialize
27
+ @children = []
28
+ end
29
+
30
+ def <<(child)
31
+ raise ArgumentError, invalid_child_message unless valid_child?(child)
32
+ children << child
33
+ end
34
+
35
+ def each(*args, &block)
36
+ children.each(*args, &block)
37
+ end
38
+
39
+ def render?
40
+ !children.length.zero?
41
+ end
42
+
43
+ def to_xml(xml)
44
+ return unless render?
45
+
46
+ apply_namespace(xml).public_send(tag, xml_attributes) {
47
+ each { |child| child.to_xml(xml) }
48
+ }
49
+ end
50
+
51
+ private
52
+
53
+ attr_reader :children
54
+
55
+ def invalid_child_message
56
+ class_name = self.class.to_s.split(/::/).last
57
+ "#{class_name} must be instances of one of the following: #{child_classes}"
58
+ end
59
+
60
+ def valid_child?(child)
61
+ child_classes.any? { |child_class| child.is_a?(child_class) }
62
+ end
63
+
64
+ def child_classes
65
+ self.class.child_classes
66
+ end
67
+
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,15 @@
1
+ module OpenXml
2
+ module Properties
3
+ class IntegerProperty < ValueProperty
4
+
5
+ def valid?
6
+ value.is_a? Integer
7
+ end
8
+
9
+ def invalid_message
10
+ "Invalid #{name}: must be an integer"
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ module OpenXml
2
+ module Properties
3
+ class OnOffProperty < ValueProperty
4
+
5
+ def ok_values
6
+ [true, false, :on, :off] # :on and :off are from the Transitional Spec
7
+ end
8
+
9
+ def to_xml(xml)
10
+ if value == true
11
+ apply_namespace(xml).public_send(tag) do
12
+ yield xml if block_given?
13
+ end
14
+ else
15
+ super
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ module OpenXml
2
+ module Properties
3
+ class PositiveIntegerProperty < IntegerProperty
4
+
5
+ def valid?
6
+ super && value >= 0
7
+ end
8
+
9
+ def invalid_message
10
+ "Invalid #{name}: must be a positive integer"
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module OpenXml
2
+ module Properties
3
+ class StringProperty < ValueProperty
4
+
5
+ def valid?
6
+ value.is_a?(String) && !value.length.zero?
7
+ end
8
+
9
+ def invalid_message
10
+ "Invalid value for #{name}; string expected (provided: #{value.inspect})"
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module OpenXml
2
+ module Properties
3
+ class ToggleProperty < OnOffProperty
4
+ # Toggle properties are no different in representation than on/off properties;
5
+ # rather, the difference is in how they compose with one another (cf.
6
+ # Section 17.7.3). It's helpful, then, to retain the concept, but entirely
7
+ # unnecessary to duplicate implementation.
8
+ # cf. Section A.6.9 of the spec, and Section A.7.9 of the transitional spec.
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module OpenXml
2
+ module Properties
3
+ class TransparentContainerProperty < ContainerProperty
4
+
5
+ def to_xml(xml)
6
+ return unless render?
7
+ each { |child| child.to_xml(xml) }
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,37 @@
1
+ module OpenXml
2
+ module Properties
3
+ class ValueProperty < BaseProperty
4
+ attr_reader :value
5
+
6
+ def initialize(value)
7
+ @value = value
8
+ raise ArgumentError, invalid_message unless valid?
9
+ end
10
+
11
+ def valid?
12
+ ok_values.member? value
13
+ end
14
+
15
+ def invalid_message
16
+ "#{value.inspect} is an invalid value for #{name}; acceptable: #{ok_values.join(", ")}"
17
+ end
18
+
19
+ def render?
20
+ !value.nil?
21
+ end
22
+
23
+ def to_xml(xml)
24
+ apply_namespace(xml).public_send(tag, :"#{value_attribute}" => value) do
25
+ yield xml if block_given?
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def value_attribute
32
+ namespace.nil? ? "val" : "#{namespace}:val"
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,9 @@
1
+ module OpenXml
2
+ module RenderWhenEmpty
3
+
4
+ def render?
5
+ true
6
+ end
7
+
8
+ end
9
+ end
@@ -4,10 +4,12 @@ module Zip
4
4
  class InputStream
5
5
  protected
6
6
 
7
+ alias _old_get_io get_io
8
+
7
9
  # The problem in RubyZip 1.1.0 is that we only call `seek`
8
10
  # when `io` is a File. We need to move the cursor to the
9
11
  # right position when `io` is a StringIO as well.
10
- def get_io(io, offset = 0)
12
+ def get_io(io, offset=0)
11
13
  io = ::File.open(io, "rb") unless io.is_a?(IO) || io.is_a?(StringIO)
12
14
  io.seek(offset, ::IO::SEEK_SET)
13
15
  io
@@ -0,0 +1,3 @@
1
+ module OpenXml
2
+ class UnmetRequirementError < RuntimeError; end
3
+ end
@@ -19,11 +19,11 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.add_dependency "rubyzip", "~> 1.2.1"
22
+ spec.add_dependency "rubyzip"
23
23
  spec.add_dependency "nokogiri"
24
24
  spec.add_dependency "ox"
25
25
 
26
- spec.add_development_dependency "bundler", "~> 1.6"
26
+ spec.add_development_dependency "bundler"
27
27
  spec.add_development_dependency "rake"
28
28
  spec.add_development_dependency "minitest"
29
29
  spec.add_development_dependency "minitest-reporters"
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openxml-package
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.9
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bob Lail
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-06-13 00:00:00.000000000 Z
11
+ date: 2021-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyzip
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 1.2.1
19
+ version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 1.2.1
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: nokogiri
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -56,16 +56,16 @@ dependencies:
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '1.6'
61
+ version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '1.6'
68
+ version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -187,6 +187,7 @@ extra_rdoc_files: []
187
187
  files:
188
188
  - ".gitignore"
189
189
  - ".travis.yml"
190
+ - CHANGELOG.md
190
191
  - Gemfile
191
192
  - LICENSE.txt
192
193
  - README.md
@@ -195,20 +196,36 @@ files:
195
196
  - lib/openxml-package/version.rb
196
197
  - lib/openxml/builder.rb
197
198
  - lib/openxml/builder/element.rb
199
+ - lib/openxml/contains_properties.rb
198
200
  - lib/openxml/content_types_presets.rb
199
201
  - lib/openxml/element.rb
200
202
  - lib/openxml/errors.rb
201
203
  - lib/openxml/has_attributes.rb
202
204
  - lib/openxml/has_children.rb
205
+ - lib/openxml/has_properties.rb
203
206
  - lib/openxml/package.rb
204
207
  - lib/openxml/part.rb
205
208
  - lib/openxml/parts.rb
206
209
  - lib/openxml/parts/content_types.rb
207
210
  - lib/openxml/parts/rels.rb
208
211
  - lib/openxml/parts/unparsed_part.rb
212
+ - lib/openxml/properties.rb
213
+ - lib/openxml/properties/base_property.rb
214
+ - lib/openxml/properties/boolean_property.rb
215
+ - lib/openxml/properties/complex_property.rb
216
+ - lib/openxml/properties/container_property.rb
217
+ - lib/openxml/properties/integer_property.rb
218
+ - lib/openxml/properties/on_off_property.rb
219
+ - lib/openxml/properties/positive_integer_property.rb
220
+ - lib/openxml/properties/string_property.rb
221
+ - lib/openxml/properties/toggle_property.rb
222
+ - lib/openxml/properties/transparent_container_property.rb
223
+ - lib/openxml/properties/value_property.rb
209
224
  - lib/openxml/relationship.rb
225
+ - lib/openxml/render_when_empty.rb
210
226
  - lib/openxml/rubyzip_fix.rb
211
227
  - lib/openxml/types.rb
228
+ - lib/openxml/unmet_requirement.rb
212
229
  - openxml-package.gemspec
213
230
  - tmp/.keep
214
231
  homepage: https://github.com/openxml/openxml-package
@@ -230,8 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
230
247
  - !ruby/object:Gem::Version
231
248
  version: '0'
232
249
  requirements: []
233
- rubyforge_project:
234
- rubygems_version: 2.6.11
250
+ rubygems_version: 3.1.4
235
251
  signing_key:
236
252
  specification_version: 4
237
253
  summary: A Ruby implementation of OpenXmlPackage from Microsoft's Open XML SDK