Skip to content

Commit

Permalink
Merge pull request #149 from ruby-docx/refactoring-extract-attribute-…
Browse files Browse the repository at this point in the history
…class

Refactor style.rb to introduce Attribute class
  • Loading branch information
satoryu authored Mar 11, 2024
2 parents a765bb1 + c1f5fbf commit c5bcb57
Showing 1 changed file with 70 additions and 47 deletions.
117 changes: 70 additions & 47 deletions lib/docx/elements/style.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,64 +8,89 @@ module Elements
class Style
include Docx::SimpleInspect

@attributes = []
class Attribute
attr_reader :name, :selectors, :required, :converter, :validator

def initialize(name, selectors, required: false, converter:, validator:)
@name = name
@selectors = selectors
@required = required
@converter = converter || Converters::DefaultValueConverter
@validator = validator || Validators::DefaultValidator
end

class << self
attr_accessor :attributes
def required?
required
end

def required_attributes
attributes.select { |a| a[:required] }
def retrieve_from(style)
selectors
.lazy
.filter_map { |node_xpath| style.node.at_xpath(node_xpath)&.value }
.map { |value| converter.decode(value) }
.first
end

def attribute(name, *selectors, required: false, converter: Converters::DefaultValueConverter, validator: Validators::DefaultValidator)
attributes << {name: name, selectors: selectors, required: required, converter: converter, validator: validator}
def assign_to(style, value)
(required && value.nil?) &&
raise(Errors::StyleRequiredPropertyValue, "Required value #{name}")

define_method(name) do
selectors
.lazy
.filter_map { |node_xpath| node.at_xpath(node_xpath)&.value }
.map { |value| converter.decode(value) }
.first
end
validator.validate(value) ||
raise(Errors::StyleInvalidPropertyValue, "Invalid value for #{name}: '#{value.nil? ? "nil" : value}'")

define_method("#{name}=") do |value|
(required && value.nil?) &&
raise(Errors::StyleRequiredPropertyValue, "Required value #{name}")
encoded_value = converter.encode(value)

validator.validate(value) ||
raise(Errors::StyleInvalidPropertyValue, "Invalid value for #{name}: '#{value.nil? ? "nil" : value}'")
selectors.map do |attribute_xpath|
if (existing_attribute = style.node.at_xpath(attribute_xpath))
if encoded_value.nil?
existing_attribute.remove
else
existing_attribute.value = encoded_value.to_s
end

encoded_value = converter.encode(value)
next encoded_value
end

selectors.map do |attribute_xpath|
if (existing_attribute = node.at_xpath(attribute_xpath))
if encoded_value.nil?
existing_attribute.remove
else
existing_attribute.value = encoded_value.to_s
next encoded_value if encoded_value.nil?

node_xpath, attribute = attribute_xpath.split("/@")

created_node =
node_xpath
.split("/")
.reduce(style.node) do |parent_node, child_xpath|
# find the child node
parent_node.at_xpath(child_xpath) ||
# or create the child node
Nokogiri::XML::Node.new(child_xpath, parent_node)
.tap { |created_child_node| parent_node << created_child_node }
end

next encoded_value
end
created_node.set_attribute(attribute, encoded_value)
end
.first
end
end

@attributes = []

class << self
attr_accessor :attributes

next encoded_value if encoded_value.nil?
def required_attributes
attributes.select(&:required?)
end

node_xpath, attribute = attribute_xpath.split("/@")
def attribute(name, *selectors, required: false, converter: nil, validator: nil)
new_attribute = Attribute.new(name, selectors, required: required, converter: converter, validator: validator)
attributes << new_attribute

created_node =
node_xpath
.split("/")
.reduce(node) do |parent_node, child_xpath|
# find the child node
parent_node.at_xpath(child_xpath) ||
# or create the child node
Nokogiri::XML::Node.new(child_xpath, parent_node)
.tap { |created_child_node| parent_node << created_child_node }
end
define_method(name) do
new_attribute.retrieve_from(self)
end

created_node.set_attribute(attribute, encoded_value)
end
.first
define_method("#{name}=") do |value|
new_attribute.assign_to(self, value)
end
end

Expand Down Expand Up @@ -135,11 +160,9 @@ def initialize(configuration, node, **attributes)

def valid?
self.class.required_attributes.all? do |a|
validator = a[:validator]
attribute_name = a[:name]
attribute_value = self.send(attribute_name)
attribute_value = a.retrieve_from(self)

validator&.validate(attribute_value)
a.validator&.validate(attribute_value)
end
end

Expand Down

0 comments on commit c5bcb57

Please sign in to comment.