Class: CSL::Node

Inherits:
Object show all
Extended by:
Extensions::Nesting, Forwardable
Includes:
PrettyPrinter, Treelike, Comparable, Enumerable
Defined in:
lib/csl/node.rb

Instance Attribute Summary collapse

Attributes included from Treelike

#children, #nodename, #parent

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Extensions::Nesting

nesting

Methods included from PrettyPrinter

#pretty_print, #to_xml

Methods included from Treelike

#<<, #add_child, #add_children, #ancestors, #closest, #delete_child, #delete_children, #depth, #descendants, #each_ancestor, #each_child, #each_descendant, #each_sibling, #empty?, #find_child, #find_children, #has_children?, #root, #root?, #siblings, #unlink

Constructor Details

#initialize(attributes = {}) {|_self| ... } ⇒ Node

Returns a new instance of Node.

Yields:

  • (_self)

Yield Parameters:

  • _self (CSL::Node)

    the object that the method was called on



229
230
231
232
233
234
# 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

Instance Attribute Details

#attributesObject (readonly)

Returns the value of attribute attributes.



225
226
227
# File 'lib/csl/node.rb', line 225

def attributes
  @attributes
end

Class Method Details

.constantize(name) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
# 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) ⇒ Object

Returns a new node with the passed in name and attributes.



66
67
68
69
70
71
72
# 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) ⇒ Object



74
75
76
77
78
79
80
# 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_attributesObject



28
29
30
# File 'lib/csl/node.rb', line 28

def default_attributes
  @default_attributes ||= {}
end

.hide_default_attributes!Object



36
37
38
# File 'lib/csl/node.rb', line 36

def hide_default_attributes!
  @show_default_attributes = false
end

.hide_default_attributes?Boolean

Returns:

  • (Boolean)


32
33
34
# File 'lib/csl/node.rb', line 32

def hide_default_attributes?
  !@show_default_attributes
end

.match?(name_pattern) ⇒ Boolean

Returns whether or not the node’s name matches the passed-in name pattern.

Returns:

  • (Boolean)

    whether or not the node’s name matches the passed-in name pattern



60
61
62
# File 'lib/csl/node.rb', line 60

def match?(name_pattern)
  name_pattern === name
end

.matches?Boolean

Returns whether or not the node’s name matches the passed-in name pattern.

Returns:

  • (Boolean)

    whether or not the node’s name matches the passed-in name pattern



63
64
65
# File 'lib/csl/node.rb', line 63

def match?(name_pattern)
  name_pattern === name
end

.parse(data) ⇒ Object



82
83
84
85
86
# File 'lib/csl/node.rb', line 82

def parse(data)
  parse!(data)
rescue
  nil
end

.parse!(data) ⇒ Object

Raises:



88
89
90
91
92
93
94
95
# 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!Object



40
41
42
# File 'lib/csl/node.rb', line 40

def show_default_attributes!
  @show_default_attributes = true
end

.typesObject



24
25
26
# File 'lib/csl/node.rb', line 24

def types
  @types ||= Set.new
end

Instance Method Details

#<=>(other) ⇒ Object



478
479
480
481
482
483
# File 'lib/csl/node.rb', line 478

def <=>(other)
  return nil unless other.is_a?(Node)
  comparables <=> other.comparables
rescue
  nil
end

#attribute?(name) ⇒ Boolean

Returns true if the node contains an attribute with the passed-in name; false otherwise.

Returns:

  • (Boolean)


307
308
309
# File 'lib/csl/node.rb', line 307

def attribute?(name)
  attributes.fetch(name, false)
end

#attributes?(*names) ⇒ Boolean

Returns true if the node contains attributes for all passed-in names; false otherwise.

Parameters:

  • names ([String])

    list of attribute names

Returns:

  • (Boolean)

    true if the node contains attributes for all passed-in names; false otherwise.



314
315
316
317
318
# File 'lib/csl/node.rb', line 314

def attributes?(*names)
  names.flatten(1).all? do |name|
    attribute?(name)
  end
end

#attributes_for(*filter) ⇒ Hash

Returns the node’s attributes matching the filter.

Parameters:

  • filter (Hash)

    a customizable set of options

Options Hash (*filter):

  • a (Array)

    list of attribute names

Returns:

  • (Hash)

    the node’s attributes matching the filter



427
428
429
430
431
432
433
# 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_attributesHash

Returns the attributes currently not set to their default values.

Returns:

  • (Hash)

    the attributes currently not set to their default values



299
300
301
302
303
# File 'lib/csl/node.rb', line 299

def custom_attributes
  attributes.to_hash.reject do |name, _|
    default_attribute?(name)
  end
end

#deep_copyObject



241
242
243
244
245
246
247
248
249
# 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) ⇒ Boolean

Returns whether or not key is set to the default value.

Parameters:

  • name (#to_sym)

    the name of the attribute

Returns:

  • (Boolean)

    whether or not key is set to the default value



283
284
285
286
287
288
289
# 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_attributesHash

Returns the attributes currently set to their default values.

Returns:

  • (Hash)

    the attributes currently set to their default values



292
293
294
295
296
# File 'lib/csl/node.rb', line 292

def default_attributes
  attributes.to_hash.select do |name, _|
    default_attribute?(name)
  end
end

#each(&block) ⇒ Object Also known as: each_pair

Iterates through the Node’s attributes



271
272
273
274
275
276
277
278
# File 'lib/csl/node.rb', line 271

def each(&block)
  if block_given?
    attributes.each_pair(&block)
    self
  else
    to_enum
  end
end

#exact_match?(name = nodename, conditions = {}) ⇒ Boolean Also known as: matches_exactly?

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 Treelike#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.

Examples:

node.exact_match?(name, conditions)
node.exact_match?(conditions)
node.exact_match?(other_node)

Parameters:

  • name (String, Regexp, Node) (defaults to: nodename)

    must match the nodename; alternatively you can pass a node

  • conditions (Hash) (defaults to: {})

    the conditions

Returns:

  • (Boolean)

    whether or not the query matches the node exactly

See Also:



412
413
414
415
416
417
418
419
420
421
422
# 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

#format_page_ranges?Boolean

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.

Returns:

  • (Boolean)

    whether or not page ranges should be formatted



460
461
462
# 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_optionsHash

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.

Returns:

  • (Hash)

    the node’s formatting options



442
443
444
445
446
447
448
449
450
# 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?Boolean

Returns true if the node contains any attributes (ignores nil values); false otherwise.

Returns:

  • (Boolean)


322
323
324
# File 'lib/csl/node.rb', line 322

def has_attributes?
  !attributes.empty?
end

#has_default_attributes?Boolean Also known as: has_defaults?

Returns whether or not the node has default attributes.

Returns:

  • (Boolean)

    whether or not the node has default attributes



265
266
267
# File 'lib/csl/node.rb', line 265

def has_default_attributes?
  !default_attributes.empty?
end

#has_language?Boolean

Returns:

  • (Boolean)


326
327
328
# File 'lib/csl/node.rb', line 326

def has_language?
  false
end

#initialize_copy(other) ⇒ Object



236
237
238
239
# File 'lib/csl/node.rb', line 236

def initialize_copy(other)
  @parent, @ancestors, @descendants, @siblings, @root, @depth = nil
  initialize(other.attributes)
end

#inspectObject



503
504
505
# File 'lib/csl/node.rb', line 503

def inspect
  "#<#{[self.class.name, *attribute_assignments].join(' ')} children=[#{children.count}]>"
end

#match?(name = nodename, conditions = {}) ⇒ Boolean Also known as: matches?

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 Treelike#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.

Examples:

node.match?(name, conditions)
node.match?(conditions)
node.match?(other_node)

Parameters:

  • name (String, Regexp, Node) (defaults to: nodename)

    must match the nodename; alternatively you can pass a node

  • conditions (Hash) (defaults to: {})

    the conditions

Returns:

  • (Boolean)

    whether or not the query matches the node

See Also:



372
373
374
375
376
377
378
379
380
381
382
# 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

#merge!(options) ⇒ Object



251
252
253
254
# File 'lib/csl/node.rb', line 251

def merge!(options)
  attributes.merge!(options)
  self
end

#page_range_formatObject



464
465
466
467
# File 'lib/csl/node.rb', line 464

def page_range_format
  return unless format_page_ranges?
  root.page_range_format
end

#quotes?Boolean

Returns:

  • (Boolean)


473
474
475
# File 'lib/csl/node.rb', line 473

def quotes?
  attribute?(:'quotes') && !!(attributes[:'quotes'].to_s =~ /^true$/i)
end

#reverse_merge!(options) ⇒ Object



256
257
258
259
260
261
262
# 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 = {}) ⇒ Object



335
336
337
338
339
340
341
# 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?Boolean

Returns:

  • (Boolean)


469
470
471
# File 'lib/csl/node.rb', line 469

def strip_periods?
  attribute?(:'strip-periods') && !!(attributes[:'strip-periods'].to_s =~ /^true$/i)
end

#tagsObject

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



487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
# 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?Boolean Also known as: has_text?

Returns:

  • (Boolean)


330
331
332
# File 'lib/csl/node.rb', line 330

def textnode?
  false
end