module HtmlConditionalComment
class VisitError < StandardError
def initialize(klass)
super("Cannot visit #{klass}")
end
end
module Visitors
class Visitor
def initialize(features, version)
@features = features
@features = [@features] unless @features.is_a?(Enumerable)
@version = if version.is_a?(VersionVector)
version
else
VersionVector.new(version)
end
end
#Copied from https://blog.bigbinary.com/2013/07/07/visitor-pattern-and-double-dispatch.html
def visit(subject)
method_name = :"visit_#{(subject.class.name || '').gsub('::', '_')}"
__send__(method_name, subject)
end
#Provide method missing for better interpretation
def method_missing(method, *args)
if method.to_s() =~ /^visit\_(.+)/
raise VisitError.new($1)
else
super(method, args)
end
end
end
##
# Evaluates conditions to boolean
#
class Eval < Visitor
protected
def visit_HtmlConditionalComment_Nodes_True(subject)
true
end
def visit_HtmlConditionalComment_Nodes_False(subject)
false
end
def visit_HtmlConditionalComment_Nodes_Browser(subject)
@features.include?(subject.feature)
end
def visit_HtmlConditionalComment_Nodes_Equal(subject)
subject.child.accept(self) && @version == subject.child.version_vector
end
def visit_HtmlConditionalComment_Nodes_LessThan(subject)
subject.child.accept(self) && @version < subject.child.version_vector
end
def visit_HtmlConditionalComment_Nodes_LessThanEqual(subject)
subject.child.accept(self) && @version <= subject.child.version_vector
end
def visit_HtmlConditionalComment_Nodes_GreaterThan(subject)
subject.child.accept(self) && @version > subject.child.version_vector
end
def visit_HtmlConditionalComment_Nodes_GreaterThanEqual(subject)
subject.child.accept(self) && @version >= subject.child.version_vector
end
def visit_HtmlConditionalComment_Nodes_Or(subject)
subject.left.accept(self) || subject.right.accept(self)
end
def visit_HtmlConditionalComment_Nodes_And(subject)
subject.left.accept(self) && subject.right.accept(self)
end
def visit_HtmlConditionalComment_Nodes_Not(subject)
!subject.child.accept(self)
end
end
##
# Converts parser nodes to a string by evaluating each conditional comment
#
class ToString < Visitor
protected
def visit_HtmlConditionalComment_Nodes_Nodes(subject)
subject.map{|node| node.accept(self)}.join
end
def visit_HtmlConditionalComment_Nodes_Condition(subject)
if subject.left.accept(Eval.new(@features, @version))
subject.right.accept(self)
end
end
def visit_HtmlConditionalComment_Nodes_Html(subject)
subject.content
end
end
end
end