class CSL::Node

Attributes

keys[R]
attributes[R]

Public Class Methods

constantize(name) click to toggle source
# 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
create(name, attributes = {}, &block) click to toggle source

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
create_attributes(attributes) click to toggle source
# 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
default_attributes() click to toggle source
# File lib/csl/node.rb, line 28
def default_attributes
  @default_attributes ||= {}
end
hide_default_attributes!() click to toggle source
# File lib/csl/node.rb, line 36
def hide_default_attributes!
  @show_default_attributes = false
end
hide_default_attributes?() click to toggle source
# File lib/csl/node.rb, line 32
def hide_default_attributes?
  !@show_default_attributes
end
inherited(subclass) click to toggle source
# 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
match?(name_pattern) click to toggle source

@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
Also aliased as: matches?
matches?(name_pattern)
Alias for: match?
new(attributes = {}) { |self| ... } click to toggle source
# 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
parse(data) click to toggle source
# File lib/csl/node.rb, line 82
def parse(data)
  parse!(data)
rescue
  nil
end
parse!(data) click to toggle source
# 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
show_default_attributes!() click to toggle source
# File lib/csl/node.rb, line 40
def show_default_attributes!
  @show_default_attributes = true
end
types() click to toggle source
# File lib/csl/node.rb, line 24
def types
  @types ||= Set.new
end

Private Class Methods

attr_defaults(attributes) click to toggle source
# File lib/csl/node.rb, line 121
def attr_defaults(attributes)
  default_attributes.merge! attributes
end
attr_struct(*attributes) click to toggle source

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
empty?() click to toggle source

@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
fetch(key, default = nil) { |key| ... } click to toggle source
# 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
has_language() click to toggle source
# 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
initialize(attrs = {}) click to toggle source
Calls superclass method
# File lib/csl/node.rb, line 142
def initialize(attrs = {})
  super(*attrs.symbolize_keys.values_at(*keys))
end
merge(other) click to toggle source

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
symbolize_keys() click to toggle source
# File lib/csl/node.rb, line 153
def symbolize_keys
  self
end
to_hash() click to toggle source
# File lib/csl/node.rb, line 161
def to_hash
  Hash[keys.zip(values_at(*keys)).reject { |_, v| v.nil? }]
end
values() click to toggle source
Calls superclass method
# File lib/csl/node.rb, line 157
def values
  super.compact
end
values_at(*arguments) click to toggle source

@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

Calls superclass method
# 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

<=>(other) click to toggle source
# File lib/csl/node.rb, line 478
def <=>(other)
  return nil unless other.is_a?(Node)
  comparables <=> other.comparables
rescue
  nil
end
attribute?(name) click to toggle source

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
attributes?(*names) click to toggle source

@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
attributes_for(*filter) click to toggle source

@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
custom_attributes() click to toggle source

@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
deep_copy() click to toggle source
# 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
default_attribute?(name) click to toggle source

@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
default_attributes() click to toggle source

@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
each(&block) click to toggle source

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
Also aliased as: each_pair
each_pair(&block)
Alias for: each
exact_match?(name = nodename, conditions = {}) click to toggle source

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:

  1. The name must match {#nodename}

  2. 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
Also aliased as: matches_exactly?
format_page_ranges?() click to toggle source

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
formatting_options() click to toggle source

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
has_attributes?() click to toggle source

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
has_default_attributes?() click to toggle source

@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
Also aliased as: has_defaults?
has_defaults?()
has_language?() click to toggle source
# File lib/csl/node.rb, line 326
def has_language?
  false
end
has_text?()
Alias for: textnode?
initialize_copy(other) click to toggle source
# File lib/csl/node.rb, line 236
def initialize_copy(other)
  @parent, @ancestors, @descendants, @siblings, @root, @depth = nil
  initialize(other.attributes)
end
inspect() click to toggle source
# File lib/csl/node.rb, line 503
def inspect
  "#<#{[self.class.name, *attribute_assignments].join(' ')} children=[#{children.count}]>"
end
match?(name = nodename, conditions = {}) click to toggle source

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:

  1. The name must match {#nodename}

  2. 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
Also aliased as: matches?
matches?(name = nodename, conditions = {})
Alias for: match?
matches_exactly?(name = nodename, conditions = {})
Alias for: exact_match?
merge!(options) click to toggle source
# File lib/csl/node.rb, line 251
def merge!(options)
  attributes.merge!(options)
  self
end
page_range_format() click to toggle source
# File lib/csl/node.rb, line 464
def page_range_format
  return unless format_page_ranges?
  root.page_range_format
end
quotes?() click to toggle source
# File lib/csl/node.rb, line 473
def quotes?
  attribute?(:'quotes') && !!(attributes[:'quotes'].to_s =~ /^true$/i)
end
reverse_merge!(options) click to toggle source
# 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
save_to(path, options = {}) click to toggle source
# 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
strip_periods?() click to toggle source
# File lib/csl/node.rb, line 469
def strip_periods?
  attribute?(:'strip-periods') && !!(attributes[:'strip-periods'].to_s =~ /^true$/i)
end
tags() click to toggle source

Returns the node’ XML tags (including attribute assignments) as an array of strings.

# File lib/csl/node.rb, line 487
def tags
  if has_children?
    tags = []
    tags << "<#{[nodename, *attribute_assignments].join(' ')}>"

    tags << children.map { |node|
      node.respond_to?(:tags) ? node.tags : [node.to_s]
    }.flatten(1)

    tags << "</#{nodename}>"
    tags
  else
    ["<#{[nodename, *attribute_assignments].join(' ')}/>"]
  end
end
textnode?() click to toggle source
# File lib/csl/node.rb, line 330
def textnode?
  false
end
Also aliased as: has_text?

Protected Instance Methods

comparables() click to toggle source
# File lib/csl/node.rb, line 515
def comparables
  c = []

  c << nodename
  c << attributes

  c << (textnode? ? text : '')
  c << (has_children? ? children : [])

  c
end
match_conditions() click to toggle source
# File lib/csl/node.rb, line 512
def match_conditions
end

Private Instance Methods

attribute_assignments() click to toggle source
# 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
match_conditions_for(name, conditions) click to toggle source
# 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