1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
require "forwardable"
module OpenGraphReader
class Parser
# A Graph to represent OpenGraph tags.
class Graph
# A node in the graph.
Node = Struct.new(:name, :content) do
extend Forwardable
include Enumerable
# The parent node.
#
# @return [Node, nil]
attr_accessor :parent
# @!method empty?
# Does this node have any children?
#
# @return [Bool]
def_delegators :children, :empty?
# Children of this node.
def children
@children ||= []
end
# Iterate over all children.
#
# @yield [Node]
def each(&block)
children.each do |child|
yield child
child.each(&block)
end
end
# Add children.
#
# @param [Node] node
def << node
node.parent = self
children << node
end
# Get node's namespace.
#
# @return [String]
def namespace
parent.fullname if parent
end
# Get node's namespace as array.
#
# @return [Array<String>]
def path
@path ||= fullname.split(":")
end
# Get node's full name.
#
# @return [String]
def fullname
@fullname ||= [namespace, name].compact.join(":")
@fullname unless @fullname.empty?
end
end
extend Forwardable
include Enumerable
# The initial node.
#
# @return [Node, nil]
attr_reader :root
# @!method empty?
# Does this graph have any nodes?
#
# @return [Bool]
def_delegators :root, :empty?
# Create new graph.
def initialize
@root = Node.new
end
# Iterate through all nodes that have a value.
#
# @yield [Node]
def each
root.each do |child|
yield child if child.content
end
end
# Fetch first node's value.
#
# @param [String] property The fully qualified name, for example <tt>og:type</tt>.
# @param [String] default The default in case the a value is not found.
# @yield Return a default in case the value is not found. Supersedes the default parameter.
# @return [String, Bool, Integer, Float, DateTime, nil]
def fetch property, default=nil
node = find_by(property)
return yield if node.nil? && block_given?
return default if node.nil?
node.content
end
# Fetch first node
#
# @param [String] property The fully qualified name, for example <tt>og:type</tt>.
# @return [Node, nil]
def find_by property
property = normalize_property property
find {|node| node.fullname == property }
end
# Fetch all nodes
#
# @param [String] property The fully qualified name, for example <tt>og:type</tt>.
# @return [Array<Node>]
def select_by property
property = normalize_property property
select {|node| node.fullname == property }
end
def find_or_create_path path
path.inject(root) {|node, name|
child = node.children.reverse.find {|child| child.name == name }
unless child
child = Node.new name
node << child
end
child
}
end
private
def normalize_property property
property.is_a?(Enumerable) ? property.join(":") : property
end
end
end
end
|