lib/search_cop_grammar.rb
require "search_cop_grammar/attributes"
require "search_cop_grammar/nodes"
module SearchCopGrammar
class BaseNode < Treetop::Runtime::SyntaxNode
attr_writer :query_info, :query_options
def query_info
(@query_info ||= nil) || parent.query_info
end
def query_options
(@query_options ||= nil) || parent.query_options
end
def evaluate
elements.collect(&:evaluate).inject(:and)
end
def elements
super.reject { |element| element.instance_of?(Treetop::Runtime::SyntaxNode) }
end
def collection_for(key)
raise(SearchCop::UnknownColumn, "Unknown column #{key}") if query_info.scope.reflection.attributes[key].nil?
Attributes::Collection.new query_info, key
end
end
class OperatorNode < Treetop::Runtime::SyntaxNode
def evaluate
text_value
end
end
class ComplexExpression < BaseNode; end
class ParenthesesExpression < BaseNode; end
class ComparativeExpression < BaseNode
def evaluate
elements[0].collection.send elements[1].method_name, elements[2].text_value
end
end
class IncludesOperator < OperatorNode
def method_name
:matches
end
end
class EqualOperator < OperatorNode
def method_name
:eq
end
end
class UnequalOperator < OperatorNode
def method_name
:not_eq
end
end
class GreaterEqualOperator < OperatorNode
def method_name
:gteq
end
end
class GreaterOperator < OperatorNode
def method_name
:gt
end
end
class LessEqualOperator < OperatorNode
def method_name
:lteq
end
end
class LessOperator < OperatorNode
def method_name
:lt
end
end
class AnywhereExpression < BaseNode
def evaluate
queries = query_info.scope.reflection.default_attributes.keys.collect { |key| collection_for key }.select { |collection| collection.compatible? text_value }.collect { |collection| collection.matches text_value }
raise SearchCop::NoSearchableAttributes if queries.empty?
queries.flatten.inject(:or)
end
end
class SingleQuotedAnywhereExpression < AnywhereExpression
def text_value
super.gsub(/^'|'$/, "")
end
end
class DoubleQuotedAnywhereExpression < AnywhereExpression
def text_value
super.gsub(/^"|"$/, "")
end
end
class AndExpression < BaseNode
def evaluate
[elements.first.evaluate, elements.last.evaluate].inject(:and)
end
end
class AndOrExpression < BaseNode
def evaluate
default_operator = SearchCop::Helpers.sanitize_default_operator(query_options)
[elements.first.evaluate, elements.last.evaluate].inject(default_operator)
end
end
class OrExpression < BaseNode
def evaluate
[elements.first.evaluate, elements.last.evaluate].inject(:or)
end
end
class NotExpression < BaseNode
def evaluate
elements.first.evaluate.not
end
end
class Column < BaseNode
def collection
collection_for text_value
end
end
class SingleQuotedValue < BaseNode
def text_value
super.gsub(/^'|'$/, "")
end
end
class DoubleQuotedValue < BaseNode
def text_value
super.gsub(/^"|"$/, "")
end
end
class Value < BaseNode; end
end