class Krikri::Parser::ValueArray
A specialized Array object for containing Parser::Values. Provides methods for accessing and filtering values that can be chained. @example chaining methods to select values my_value_array.field('dc:creator', 'foaf:name') .match_attribute('first_name').values Methods defined on this class should return another ValueArray, an Array of literal values (retrieved from Parser::Value#value), or a single literal value. Uses `#bindings` to track variables for recovery via `#else`, `#from`, and
`#back`. Methods that return a `ValueArray` pass `#bindings` down to the
new instance. @example my_value_array.field('dc:creator').bind(:creator) @example `#if` sets `bindings[:top]`, `#else` recoveres if empty my_value_array.field('dc:creator', 'foaf:name').if.field('empty:field') .else { |vs| vs.field('some:otherField') }
Attributes
@!attribute [r] bindings
A hash containing bindings of variables to (Symbols) to ValueArrays
Public Class Methods
Wraps the root node of the given record in this class.
@param record [Krikri::Parser] a parsed record to wrap in a ValueArray
@return [ValueArray]
# File lib/krikri/parser.rb, line 504 def self.build(record) new([record.root]) end
@param array [Array] an array of values to delegate array operations to @param bindings [Hash<Symbol, ValueArray] a set of variable bindings
This is overloaded to accept an instance of this class to use as a `:top` recovery node in, e.g. `#else`. `:top` to `self` if none is passed.
# File lib/krikri/parser.rb, line 190 def initialize(array = [], bindings = {}) @array = array if bindings.is_a?(ValueArray) # this way is deprected! # how should we handle deprecations? @bindings = {} @bindings[:top] ||= bindings else @bindings = bindings @bindings[:top] ||= self end end
Public Instance Methods
@see Array#<< @raise [InvalidParserValueError] when the value is not a Parser::Value
# File lib/krikri/parser.rb, line 216 def <<(value) raise InvalidParserValueError unless value.is_a? Value @array << value value end
@see Array#[]= @raise [InvalidParserValueError] when the value is not a Parser::Value
# File lib/krikri/parser.rb, line 207 def []=(index, value) raise InvalidParserValueError unless value.is_a? Value @array[index] = value self end
Returns to a bound variable unless the current value is empty. If no variable is given, returns to the top of the call chain.
This allows checking complex conditionals, where mappings depend on data from deep within multiple branches of the tree.
@example use with `#bind`
value_array.field(:subject).bind(:subj) .field('some', 'subfield') .select { |a| condition(a) } .and(:subj).field(:label)
@param from [#to_sym] a symbol respresenting a bound variable name;
default: :top
@return [ValueArray] self
@raise [ArgumentError] when an unbound variable is given as `var`
# File lib/krikri/parser.rb, line 240 def and(from: :top) return self if self.empty? bindings[from.to_sym] or raise ArgumentError, "Tried to return to unbound variable: #{from}" end
@param idx [#to_i, Range] @return [ValueArray] an array containing the node(s) in the
specified index posiition(s).
# File lib/krikri/parser.rb, line 274 def at(idx) self.class.new(Array(@array[idx])) end
Binds the current array to the variable name given
@param var [#to_sym] a symbol respresenting the variable name @return [ValueArray] self
# File lib/krikri/parser.rb, line 251 def bind(var) bindings[var.to_sym] = self self end
@see Array#compact @return [ValueArray]
# File lib/krikri/parser.rb, line 392 def compact self.class.new(@array.compact, bindings) end
@see Array#concat @return [ValueArray]
# File lib/krikri/parser.rb, line 259 def concat(*args, &block) self.class.new(@array.concat(*args, &block), bindings) end
Short circuits if `self` is not empty, else passes the top of the call chain (`@bindings`) to the given block.
@example usage with `#if`
value_array.if { |arry| arry.field(:a_field) } .else { |arry| arry.field(:alternate_field) } # use other filters at will value_array.if.field(:a_field).reject { |v| v == 'SKIP ME' } .else { |arry| arry.field(:alternate_field) }
@example standalone use; resetting to record root
value_array.field(:a_field).else { |arry| arry.field(:alternate_field) }
@yield gives `@bindings` if self is empty @yieldparam arry [ValueArray] the value of `@bindings`
@return [ValueArray] `self` unless empty; otherwise the result of the
block
# File lib/krikri/parser.rb, line 361 def else(&block) raise ArgumentError, 'No block given for `#else`' unless block_given? return self unless self.empty? yield bindings[:top] end
Accesses a given field. Use multiple arguments to travel down the node hierarchy.
@return [ValueArray] an array containing the nodes available in a
particular field.
# File lib/krikri/parser.rb, line 284 def field(*args) result = self args.each { |name| result = result.get_field(name) } result end
Lists field names on this node.
This can be useful in cases where the desired value is a key or child node label, rather than a value.
@return [ValueArray] an array containing the field names.
@see select_child, reject_child
for methods that filter on these names
# File lib/krikri/parser.rb, line 312 def field_names self.class.new(flat_map(&:children).uniq, bindings) end
Accesses the union of multiple specified fields.
@return [ValueArray] an array containing the nodes available in the given fields.
# File lib/krikri/parser.rb, line 296 def fields(*args) results = args.map do |f| field(*Array(f)) end self.class.new(results.flatten, bindings) end
Retrieves the first element of a ValueArray
. Uses an optional argument to specify how many items to return. By design, it behaves similarly to Array#first, but it intentionally doesn't override it.
@return [ValueArray] a Krikri::Parser::ValueArray
for first n elements
# File lib/krikri/parser.rb, line 373 def first_value(*args) return self.class.new(@array.first(*args)) unless args.empty? self.class.new([@array.first].compact, bindings) end
@see Array#concat @return [ValueArray]
# File lib/krikri/parser.rb, line 399 def flatten(*args, &block) self.class.new(@array.flatten(*args, &block), bindings) end
Sets the top of the call chain to self and returns or yields self.
This is syntactic sugar for `#bind(:top)`, with the addition of block syntax.
@example with method chain syntax
value_array.if.field(:a_field).else do |arry| arry.field(:alternate_field) end
@example with block syntax
value_array.if { |arry| arry.field(:a_field) } .else { |arry| arry.field(:alternate_field) }
@yield gives self @yieldparam arry [ValueArray] self
@return [ValueArray] the result of the block, if given; or self with :top set
# File lib/krikri/parser.rb, line 335 def if(&block) bind(:top) return yield self if block_given? self end
Retrieves the last element of a ValueArray
. Uses an optional argument to specify how many items to return. By design, it behaves similarly to Array#last, but it intentionally doesn't override it.
@return [ValueArray] a Krikri::Parser::ValueArray
for last n elements
# File lib/krikri/parser.rb, line 384 def last_value(*args) return self.class.new(@array.last(*args)) unless args.empty? self.class.new([@array.last].compact, bindings) end
Wraps the result of Array#map in a ValueArray
@see Array#map @return [ValueArray]
# File lib/krikri/parser.rb, line 408 def map(*args, &block) self.class.new(@array.map(*args, &block), bindings) end
@example selecting by presence of an attribute; returns all nodes where
`#attribute?(:type)` is true match_attribute(:type)
@example selecting by the value of an attribute; returns all nodes with
`#attribute(:type) == other` match_attribute(:type, other)
@example selecting by block against an attribute; returns all nodes with
`block.call(attribute(:type))` is true match_attribute(:type) { |value| value.starts_with? 'blah' }
@example selecting by block against an attribute; returns all nodes with
`block.call(attribute(:type)) == other` is true match_attribute(:type, 'moomin') { |value| value.downcase }
@param name [#to_sym] an attribute name @param other [Object] an object to check for equality with the
values from the given attribute.
@yield [value] yields each value with the attribute in name to the block
@return [ValueArray] an array containing nodes for which the specified
attribute has a value matching the given attribute name, object, and block.
# File lib/krikri/parser.rb, line 460 def match_attribute(name, other = nil, &block) select(&compare_to_attribute(name, other, &block)) end
@param *children [Array<String>]
@return [ValueArray] an array containing nodes for which the specified
attribute does not have a child node matching the given child name
# File lib/krikri/parser.rb, line 469 def match_child(*children) select { |v| !(v.children & children).empty? } end
Wraps the result of Array#reject in a ValueArray
@see Array#reject @return [ValueArray]
# File lib/krikri/parser.rb, line 426 def reject(*args, &block) self.class.new(@array.reject(*args, &block), bindings) end
@param name [#to_sym] an attribute name @param other [Object] an object to check for equality with the
values from the given attribute.
@yield [value] yields each value with the attribute in name to the block
@return [ValueArray] an array containing nodes for which the specified
attribute does not have a value matching the given attribute name, object, and block.
@see match_attribute
for examples; this calls reject
, where it calls
#select.
# File lib/krikri/parser.rb, line 486 def reject_attribute(name, other = nil, &block) reject(&compare_to_attribute(name, other, &block)) end
@param *children [Array<String>]
@return [ValueArray] an array containing nodes for which the specified
attribute does not have a childnode matching the given child name
# File lib/krikri/parser.rb, line 495 def reject_child(*children) reject { |v| !(v.children & children).empty? } end
Wraps the result of Array#select in a ValueArray
@see Array#select @return [ValueArray]
# File lib/krikri/parser.rb, line 417 def select(*args, &block) self.class.new(@array.select(*args, &block), bindings) end
@return [Array] literal values from the objects in this array. @see Parser::Value#value
# File lib/krikri/parser.rb, line 266 def values @array.map { |v| v.respond_to?(:value) ? v.value : v } end
Protected Instance Methods
# File lib/krikri/parser.rb, line 510 def get_field(name) self.class.new(flat_map { |val| val[name] }, bindings) end
Private Instance Methods
@see match_attribute
, reject_attribute
# File lib/krikri/parser.rb, line 518 def compare_to_attribute(name, other, &block) lambda do |v| next unless v.attribute?(name.to_sym) result = v.send(name) result = yield(result) if block_given? return result == other if other result end end