class CSL::Node
Attributes
Public Class Methods
# File lib/csl/node.rb, line 44 def constantize(name) pattern = /:#{name.to_s.tr('-', '')}$/i klass = types.detect { |t| t.matches?(pattern) } case when !klass.nil? klass when nesting[-2].respond_to?(:constantize) nesting[-2].constantize(name) else nil end end
Returns a new node with the passed in name and attributes.
# File lib/csl/node.rb, line 66 def create(name, attributes = {}, &block) klass = constantize(name) node = (klass || Node).new(attributes, &block) node.nodename = name node end
# File lib/csl/node.rb, line 74 def create_attributes(attributes) if const?(:Attributes) const_get(:Attributes).new(default_attributes.merge(attributes)) else default_attributes.merge(attributes) end end
# File lib/csl/node.rb, line 28 def default_attributes @default_attributes ||= {} end
# File lib/csl/node.rb, line 36 def hide_default_attributes! @show_default_attributes = false end
# File lib/csl/node.rb, line 32 def hide_default_attributes? !@show_default_attributes end
# File lib/csl/node.rb, line 17 def inherited(subclass) types << subclass subclass.nesting.each do |klass| klass.types << subclass if klass < Node end end
@return [Boolean] whether or not the node's name matches the
passed-in name pattern
# File lib/csl/node.rb, line 60 def match?(name_pattern) name_pattern === name end
# File lib/csl/node.rb, line 229 def initialize(attributes = {}) @attributes = self.class.create_attributes(attributes) @children = self.class.create_children yield self if block_given? end
# File lib/csl/node.rb, line 82 def parse(data) parse!(data) rescue nil end
# File lib/csl/node.rb, line 88 def parse!(data) node = CSL.parse!(data, self) raise ParseError, "root node not #{self.name}: #{node.inspect}" unless node.class == self || Node.equal?(self) node end
# File lib/csl/node.rb, line 40 def show_default_attributes! @show_default_attributes = true end
# File lib/csl/node.rb, line 24 def types @types ||= Set.new end
Private Class Methods
# File lib/csl/node.rb, line 121 def attr_defaults(attributes) default_attributes.merge! attributes end
Creates a new Struct for the passed-in attributes. Node instances will create an instance of this struct to manage their respective attributes.
The new Struct will be available as Attributes in the current node's class scope.
# File lib/csl/node.rb, line 131 def attr_struct(*attributes) const_set(:Attributes, Struct.new(*attributes) { # 1.8 Compatibility @keys = attributes.map(&:to_sym).freeze class << self attr_reader :keys end CSL.silence_warnings do def initialize(attrs = {}) super(*attrs.symbolize_keys.values_at(*keys)) end end # @return [<Symbol>] a list of symbols representing the names/keys # of the attribute variables. def keys __class__.keys end def symbolize_keys self end def values super.compact end def to_hash Hash[keys.zip(values_at(*keys)).reject { |_, v| v.nil? }] end # @return [Boolean] true if all the attribute values are nil; # false otherwise. def empty? values.compact.empty? end def fetch(key, default = nil) value = keys.include?(key.to_sym) && send(:'[]', key) if block_given? value || yield(key) else value || default end end # Merges the current with the passed-in attributes. # # @param other [#each_pair] the other attributes # @return [self] def merge(other) raise ArgumentError, "failed to merge #{other.class} into Attributes" unless other.respond_to?(:each_pair) other.each_pair do |part, value| part = part.to_sym send(:'[]=', part, value) if !value.nil? && keys.include?(part) end self end alias merge! merge # @overload values_at(selector, ... ) # Returns an array containing the attributes in self according # to the given selector(s). The selectors may be either integer # indices, ranges (functionality inherited from Struct) or # symbols identifying valid keys (similar to Hash#values_at). # # @example # attributes.values_at(:family, :nick) #=> ['Matsumoto', 'Matz'] # # @see Struct#values_at # @return [Array] the list of values def values_at(*arguments) arguments.flatten.inject([]) do |values, key| if key.is_a?(Symbol) values.push fetch(key) else values.concat super(key) end end end }) end
@return [Boolean] true if all the attribute values are nil;
false otherwise.
# File lib/csl/node.rb, line 167 def empty? values.compact.empty? end
# File lib/csl/node.rb, line 171 def fetch(key, default = nil) value = keys.include?(key.to_sym) && send(:'[]', key) if block_given? value || yield(key) else value || default end end
# File lib/csl/node.rb, line 99 def has_language attr_accessor :language define_method :has_language? do !language.nil? end public :language, :language=, :has_language? alias_method :original_attribute_assignments, :attribute_assignments define_method :attribute_assignments do if has_language? original_attribute_assignments.unshift('xml:lang="%s"' % language) else original_attribute_assignments end end private :original_attribute_assignments, :attribute_assignments end
# File lib/csl/node.rb, line 142 def initialize(attrs = {}) super(*attrs.symbolize_keys.values_at(*keys)) end
Merges the current with the passed-in attributes.
@param other [#each_pair] the other attributes @return [self]
# File lib/csl/node.rb, line 185 def merge(other) raise ArgumentError, "failed to merge #{other.class} into Attributes" unless other.respond_to?(:each_pair) other.each_pair do |part, value| part = part.to_sym send(:'[]=', part, value) if !value.nil? && keys.include?(part) end self end
# File lib/csl/node.rb, line 153 def symbolize_keys self end
# File lib/csl/node.rb, line 161 def to_hash Hash[keys.zip(values_at(*keys)).reject { |_, v| v.nil? }] end
# File lib/csl/node.rb, line 157 def values super.compact end
@overload ::values_at(selector, … )
Returns an array containing the attributes in self according to the given selector(s). The selectors may be either integer indices, ranges (functionality inherited from Struct) or symbols identifying valid keys (similar to Hash#values_at).
@example
attributes.values_at(:family, :nick) #=> ['Matsumoto', 'Matz']
@see Struct#values_at @return [Array] the list of values
# File lib/csl/node.rb, line 209 def values_at(*arguments) arguments.flatten.inject([]) do |values, key| if key.is_a?(Symbol) values.push fetch(key) else values.concat super(key) end end end
Public Instance Methods
# File lib/csl/node.rb, line 478 def <=>(other) return nil unless other.is_a?(Node) comparables <=> other.comparables rescue nil end
Returns true if the node contains an attribute with the passed-in name; false otherwise.
# File lib/csl/node.rb, line 307 def attribute?(name) attributes.fetch(name, false) end
@param [[String]] names list of attribute names @return [Boolean] true if the node contains attributes for all
passed-in names; false otherwise.
# File lib/csl/node.rb, line 314 def attributes?(*names) names.flatten(1).all? do |name| attribute?(name) end end
@option filter [Array] a list of attribute names @return [Hash] the node's attributes matching the filter
# File lib/csl/node.rb, line 427 def attributes_for(*filter) filter.flatten! Hash[map { |name, value| !value.nil? && filter.include?(name) ? [name, value.to_s] : nil }.compact] end
@return [Hash] the attributes currently not set to their default values
# File lib/csl/node.rb, line 299 def custom_attributes attributes.to_hash.reject do |name, _| default_attribute?(name) end end
# File lib/csl/node.rb, line 241 def deep_copy copy = dup each_child do |child| copy.add_child child.deep_copy end copy end
@param name [#to_sym] the name of the attribute @return [Boolean] whether or not key is set to the default value
# File lib/csl/node.rb, line 283 def default_attribute?(name) defaults = self.class.default_attributes name, value = name.to_sym, attributes.fetch(name) return false unless !value.nil? || defaults.key?(name) defaults[name] == value end
@return [Hash] the attributes currently set to their default values
# File lib/csl/node.rb, line 292 def default_attributes attributes.to_hash.select do |name, _| default_attribute?(name) end end
Iterates through the Node's attributes
# File lib/csl/node.rb, line 271 def each(&block) if block_given? attributes.each_pair(&block) self else to_enum end end
Tests whether or not the Name matches the passed-in node name and attribute conditions exactly; if a Hash is passed as a single argument, it is taken as the conditions parameter (the name parameter is automatically matches in this case).
Whether or not the arguments match the node is determined as follows:
-
The name must match {#nodename}
-
All attribute name/value pairs of the node must match the corresponding pairs in the passed-in Hash
Note that all node attributes are used by this method – if you want to match only a subset of attributes {#match?} should be used instead.
@see match?
@example
node.exact_match?(name, conditions) node.exact_match?(conditions) node.exact_match?(other_node)
@param name [String,Regexp,Node] must match the nodename; alternatively
you can pass a node
@param conditions [Hash] the conditions
@return [Boolean] whether or not the query matches the node exactly
# File lib/csl/node.rb, line 412 def exact_match?(name = nodename, conditions = {}) name, conditions = match_conditions_for(name, conditions) return false unless name === nodename return true if conditions.empty? conditions.values_at(*attributes.keys).zip( attributes.values_at(*attributes.keys)).all? do |condition, value| condition === value end end
Whether or not page ranges should be formatted when rendering this node.
Page ranges must be formatted if the node is part of a {Style} with a page-range-format value.
@return [Boolean] whether or not page ranges should
be formatted
# File lib/csl/node.rb, line 460 def format_page_ranges? root.respond_to?(:has_page_range_format?) && root.has_page_range_format? end
The node's formatting options. If the node's parent responds to `inheritable_formatting_options`, these will be included in the result. This makes it easy for nodes to push formatting options to their child nodes.
@return [Hash] the node's formatting options
# File lib/csl/node.rb, line 442 def formatting_options options = attributes_for Schema.attr(:formatting) if !root? && parent.respond_to?(:inheritable_formatting_options) parent.inheritable_formatting_options.merge(options) else options end end
Returns true if the node contains any attributes (ignores nil values); false otherwise.
# File lib/csl/node.rb, line 322 def has_attributes? !attributes.empty? end
@return [Boolean] whether or not the node has default attributes
# File lib/csl/node.rb, line 265 def has_default_attributes? !default_attributes.empty? end
# File lib/csl/node.rb, line 326 def has_language? false end
# File lib/csl/node.rb, line 236 def initialize_copy(other) @parent, @ancestors, @descendants, @siblings, @root, @depth = nil initialize(other.attributes) end
# File lib/csl/node.rb, line 503 def inspect "#<#{[self.class.name, *attribute_assignments].join(' ')} children=[#{children.count}]>" end
Tests whether or not the Name matches the passed-in node name and attribute conditions; if a Hash is passed as a single argument, it is taken as the conditions parameter (the name parameter automatically matches in this case).
Whether or not the arguments match the node is determined as follows:
-
The name must match {#nodename}
-
All attribute name/value pairs passed as conditions must match the corresponding attributes of the node
Note that only attributes present in the passed-in conditions influence the match – if you want to match only nodes that contain no other attributes than specified by the conditions, {#exact_match?} should be used instead.
@see exact_match?
@example
node.match?(name, conditions) node.match?(conditions) node.match?(other_node)
@param name [String,Regexp,Node] must match the nodename; alternatively
you can pass a node
@param conditions [Hash] the conditions
@return [Boolean] whether or not the query matches the node
# File lib/csl/node.rb, line 372 def match?(name = nodename, conditions = {}) name, conditions = match_conditions_for(name, conditions) return false unless name === nodename return true if conditions.empty? conditions.values.zip( attributes.values_at(*conditions.keys)).all? do |condition, value| condition === value end end
# File lib/csl/node.rb, line 251 def merge!(options) attributes.merge!(options) self end
# File lib/csl/node.rb, line 464 def page_range_format return unless format_page_ranges? root.page_range_format end
# File lib/csl/node.rb, line 473 def quotes? attribute?(:'quotes') && !!(attributes[:'quotes'].to_s =~ /^true$/i) end
# File lib/csl/node.rb, line 256 def reverse_merge!(options) options.each_pair do |key, value| attributes[key] = value unless attribute? key end self end
# File lib/csl/node.rb, line 335 def save_to(path, options = {}) File.open(path, 'w:UTF-8') do |f| f << (options[:compact] ? to_xml : pretty_print) end self end
# File lib/csl/node.rb, line 469 def strip_periods? attribute?(:'strip-periods') && !!(attributes[:'strip-periods'].to_s =~ /^true$/i) end
# File lib/csl/node.rb, line 330 def textnode? false end
Protected Instance Methods
# File lib/csl/node.rb, line 515 def comparables c = [] c << nodename c << attributes c << (textnode? ? text : '') c << (has_children? ? children : []) c end
# File lib/csl/node.rb, line 512 def match_conditions end
Private Instance Methods
# File lib/csl/node.rb, line 529 def attribute_assignments attrs = self.class.hide_default_attributes? ? custom_attributes : attributes.to_hash attrs.map { |name, value| value.nil? ? nil : [name, CSL.encode_xml_attr(value.to_s)].join('=') }.compact end
# File lib/csl/node.rb, line 538 def match_conditions_for(name, conditions) case name when Node conditions, name = name.attributes.to_hash, name.nodename when Hash conditions, name = name, nodename when Symbol name = name.to_s end [name, conditions.symbolize_keys] end