openxml-package 0.3.1 → 0.3.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4897a42b3b6601e4c9592fd0a0dfb280d00924bc
4
- data.tar.gz: e897af932351b707af9c4b954eb64437dcb85db3
3
+ metadata.gz: 1a0436dc9e0622316c63a9a049ebe243ebe50bd0
4
+ data.tar.gz: f598315aff5dea761320ef51fe2f64709bba9073
5
5
  SHA512:
6
- metadata.gz: 8ef6f7fd79923969da139af148dcb8ee285216d7794a24ee5d6e74602a9ac6ba7fe05f95bdef3b6ac877fd59900210f3742f9658296c2e40f53909c40839238c
7
- data.tar.gz: 94c22130fd32fca51a16ed4714534aa141a84c4a89e1399723ce4867b736cb3d440e9e134e045c21f663d3071df368483f233879540e267c335e150974d4573b
6
+ metadata.gz: c19ae66a76c4abcd5a4c431e567a0cf55060e35827afba22c2ba27bc35d9f7d1773c009e55b9e3a48d61221aca7c3811ec2a9907f6517432d7df7eebdcbd21ed
7
+ data.tar.gz: 2c17f6398cd89b909e36e8d3dd6d42a5f761ae7df2da319cf5ca99b609eb17a6c9e82dab5496cc13a5521dfc0e745a0ea2365589494ece87820fa919a7eed6d3
@@ -1,3 +1,3 @@
1
1
  module OpenXmlPackage
2
- VERSION = "0.3.1"
2
+ VERSION = "0.3.2"
3
3
  end
@@ -13,11 +13,16 @@ module OpenXml
13
13
  module InstanceMethods
14
14
 
15
15
  def property_xml(xml)
16
+ ensure_required_choices
16
17
  props = active_properties
17
18
  return unless render_properties? props
18
19
  props.each { |prop| prop.to_xml(xml) }
19
20
  end
20
21
 
22
+ def properties_attributes
23
+ {}
24
+ end
25
+
21
26
  end
22
27
 
23
28
  end
@@ -1,3 +1,5 @@
1
+ require "openxml/unmet_requirement"
2
+
1
3
  module OpenXml
2
4
  module HasAttributes
3
5
 
@@ -7,10 +9,12 @@ module OpenXml
7
9
 
8
10
  module ClassMethods
9
11
 
10
- def attribute(name, expects: nil, one_of: nil, in_range: nil, displays_as: nil, namespace: nil, matches: nil, validation: nil, deprecated: false)
12
+ def attribute(name, expects: nil, one_of: nil, in_range: nil, displays_as: nil, namespace: nil, matches: nil, validation: nil, required: false, deprecated: false)
11
13
  bad_names = %w{ tag name namespace properties_tag }
12
14
  raise ArgumentError if bad_names.member? name.to_s
13
15
 
16
+ required_attributes.push(name) if required
17
+
14
18
  attr_reader name
15
19
 
16
20
  define_method "#{name}=" do |value|
@@ -27,17 +31,21 @@ module OpenXml
27
31
  end
28
32
 
29
33
  def attributes
30
- @attributes ||= begin
34
+ @attributes ||= {}.tap do |attrs|
31
35
  if superclass.respond_to?(:attributes)
32
- Hash[superclass.attributes.map { |name, attribute|
33
- [ name, attribute.dup ]
34
- }]
35
- else
36
- {}
36
+ superclass.attributes.each do |key, value|
37
+ attrs[key] = value.dup
38
+ end
37
39
  end
38
40
  end
39
41
  end
40
42
 
43
+ def required_attributes
44
+ @required_attributes ||= [].tap do |attrs|
45
+ attrs.push(*superclass.required_attributes) if superclass.respond_to?(:required_attributes)
46
+ end
47
+ end
48
+
41
49
  def with_namespace(namespace, &block)
42
50
  @attribute_namespace = namespace
43
51
  instance_eval(&block)
@@ -57,9 +65,14 @@ module OpenXml
57
65
  self.class.attributes
58
66
  end
59
67
 
68
+ def required_attributes
69
+ self.class.required_attributes
70
+ end
71
+
60
72
  private
61
73
 
62
74
  def xml_attributes
75
+ ensure_required_attributes_set
63
76
  attributes.each_with_object({}) do |(name, options), attrs|
64
77
  display, namespace = options
65
78
  value = send(name)
@@ -69,6 +82,14 @@ module OpenXml
69
82
  end
70
83
  end
71
84
 
85
+ def ensure_required_attributes_set
86
+ unset_attributes = required_attributes.reject do |attr|
87
+ instance_variable_defined?("@#{attr}")
88
+ end
89
+ message = "Required attribute(s) #{unset_attributes.join(", ")} have not been set"
90
+ raise OpenXml::UnmetRequirementError, message if unset_attributes.any?
91
+ end
92
+
72
93
  def boolean(value)
73
94
  message = "Invalid #{name}: frame must be true or false"
74
95
  raise ArgumentError, message unless [true, false].member? value
@@ -114,6 +135,11 @@ module OpenXml
114
135
  raise ArgumentError, message if !value.is_a?(String) || value.length.zero?
115
136
  end
116
137
 
138
+ def string_or_blank(value)
139
+ message = "Invalid #{name}: must be a string, even if the string is empty"
140
+ raise ArgumentError, message unless value.is_a?(String)
141
+ end
142
+
117
143
  def in_range?(value, range)
118
144
  message = "Invalid #{name}: must be a number between #{range.begin} and #{range.end}"
119
145
  raise ArgumentError, message unless range.include?(value.to_i)
@@ -1,3 +1,5 @@
1
+ require "openxml/unmet_requirement"
2
+
1
3
  module OpenXml
2
4
  module HasProperties
3
5
 
@@ -14,12 +16,13 @@ module OpenXml
14
16
  @properties_tag ||= nil
15
17
  end
16
18
 
17
- def value_property(name, as: nil, klass: nil)
19
+ def value_property(name, as: nil, klass: nil, required: false, default_value: nil)
18
20
  attr_reader name
19
21
 
20
22
  properties[name] = (as || name).to_s
23
+ required_properties[name] = default_value if required
21
24
  classified_name = properties[name].split("_").map(&:capitalize).join
22
- class_name = klass.name unless klass.nil?
25
+ class_name = klass.to_s unless klass.nil?
23
26
  class_name ||= (to_s.split("::")[0...-2] + ["Properties", classified_name]).join("::")
24
27
 
25
28
  (choice_groups[current_group] ||= []).push(name) unless current_group.nil?
@@ -33,17 +36,18 @@ module OpenXml
33
36
  CODE
34
37
  end
35
38
 
36
- def property(name, as: nil, klass: nil)
39
+ def property(name, as: nil, klass: nil, required: false)
37
40
  properties[name] = (as || name).to_s
41
+ required_properties[name] = true if required
38
42
  classified_name = properties[name].split("_").map(&:capitalize).join
39
- class_name = klass.name unless klass.nil?
43
+ class_name = klass.to_s unless klass.nil?
40
44
  class_name ||= (to_s.split("::")[0...-2] + ["Properties", classified_name]).join("::")
41
45
 
42
46
  (choice_groups[current_group] ||= []).push(name) unless current_group.nil?
43
47
 
44
48
  class_eval <<-CODE, __FILE__, __LINE__ + 1
45
49
  def #{name}(*args)
46
- if instance_variable_get("@#{name}").nil?
50
+ unless instance_variable_defined?("@#{name}")
47
51
  group_index = #{@current_group.inspect}
48
52
  ensure_unique_in_group(:#{name}, group_index) unless group_index.nil?
49
53
  instance_variable_set "@#{name}", #{class_name}.new(*args)
@@ -54,8 +58,9 @@ module OpenXml
54
58
  CODE
55
59
  end
56
60
 
57
- def property_choice
61
+ def property_choice(required: false)
58
62
  @current_group = choice_groups.length
63
+ required_choices << @current_group if required
59
64
  yield
60
65
  @current_group = nil
61
66
  end
@@ -65,22 +70,26 @@ module OpenXml
65
70
  end
66
71
 
67
72
  def properties
68
- @properties ||= begin
69
- if superclass.respond_to?(:properties)
70
- Hash[superclass.properties.map { |key, klass_name| [ key, klass_name.dup ] }]
71
- else
72
- {}
73
- end
73
+ @properties ||= {}.tap do |props|
74
+ props.merge!(superclass.properties) if superclass.respond_to?(:properties)
74
75
  end
75
76
  end
76
77
 
77
78
  def choice_groups
78
- @choice_groups ||= begin
79
- if superclass.respond_to?(:choice_groups)
80
- superclass.choice_groups.map(&:dup)
81
- else
82
- []
83
- end
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)
84
93
  end
85
94
  end
86
95
 
@@ -110,6 +119,11 @@ module OpenXml
110
119
 
111
120
  end
112
121
 
122
+ def initialize(*_args)
123
+ super
124
+ build_required_properties
125
+ end
126
+
113
127
  def properties_element
114
128
  @properties_element ||= self.class.properties_element.new
115
129
  end
@@ -131,6 +145,7 @@ module OpenXml
131
145
  end
132
146
 
133
147
  def property_xml(xml)
148
+ ensure_required_choices
134
149
  props = active_properties
135
150
  return unless render_properties? props
136
151
 
@@ -139,6 +154,13 @@ module OpenXml
139
154
  end
140
155
  end
141
156
 
157
+ def build_required_properties
158
+ required_properties.each do |prop, default_value|
159
+ public_send(:"#{prop}=", default_value) if respond_to? :"#{prop}="
160
+ public_send(:"#{prop}")
161
+ end
162
+ end
163
+
142
164
  private
143
165
 
144
166
  def properties
@@ -150,7 +172,9 @@ module OpenXml
150
172
  end
151
173
 
152
174
  def render_properties?(properties=active_properties)
153
- properties.any?(&:render?) || properties_attributes.any?
175
+ properties.any?(&:render?) || properties_attributes.keys.any? do |key|
176
+ properties_element.instance_variable_defined?("@#{key}")
177
+ end
154
178
  end
155
179
 
156
180
  def properties_tag
@@ -165,12 +189,34 @@ module OpenXml
165
189
  self.class.choice_groups
166
190
  end
167
191
 
192
+ def required_properties
193
+ self.class.required_properties
194
+ end
195
+
196
+ def required_choices
197
+ self.class.required_choices
198
+ end
199
+
168
200
  def ensure_unique_in_group(name, group_index)
169
201
  other_names = (choice_groups[group_index] - [name])
170
- unique = other_names.all? { |other_name| !instance_variable_defined?("@#{other_name}") }
202
+ unique = other_names.none? { |other_name| instance_variable_defined?("@#{other_name}") }
171
203
  message = "Property #{name} cannot also be set with #{other_names.join(", ")}."
172
204
  raise ChoiceGroupUniqueError, message unless unique
173
205
  end
174
206
 
207
+ def unmet_choices
208
+ required_choices.reject do |choice_index|
209
+ choice_groups[choice_index].one? do |prop_name|
210
+ instance_variable_defined?("@#{prop_name}")
211
+ end
212
+ end
213
+ end
214
+
215
+ def ensure_required_choices
216
+ unmet_choice_groups = unmet_choices.map { |index| choice_groups[index].join(", ") }
217
+ message = "Required choice from among group(s) (#{unmet_choice_groups.join("), (")}) not made"
218
+ raise OpenXml::UnmetRequirementError, message if unmet_choice_groups.any?
219
+ end
220
+
175
221
  end
176
222
  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,9 @@
1
+ module OpenXml
2
+ module RenderWhenEmpty
3
+
4
+ def render?
5
+ true
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module OpenXml
2
+ class UnmetRequirementError < RuntimeError; end
3
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openxml-package
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
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-09-12 00:00:00.000000000 Z
11
+ date: 2017-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyzip
@@ -221,8 +221,10 @@ files:
221
221
  - lib/openxml/properties/transparent_container_property.rb
222
222
  - lib/openxml/properties/value_property.rb
223
223
  - lib/openxml/relationship.rb
224
+ - lib/openxml/render_when_empty.rb
224
225
  - lib/openxml/rubyzip_fix.rb
225
226
  - lib/openxml/types.rb
227
+ - lib/openxml/unmet_requirement.rb
226
228
  - openxml-package.gemspec
227
229
  - tmp/.keep
228
230
  homepage: https://github.com/openxml/openxml-package