middleman/middleman-core/lib/middleman-core/sitemap/queryable.rb
2012-12-25 16:05:54 -08:00

148 lines
3.9 KiB
Ruby

require "active_support/core_ext/object/inclusion"
module Middleman
module Sitemap
# Code adapted from https://github.com/ralph/document_mapper/
module Queryable
OPERATOR_MAPPING = {
'equal' => :==,
'gt' => :>,
'gte' => :>=,
'in' => :in?,
'include' => :include?,
'lt' => :<,
'lte' => :<=
}
VALID_OPERATORS = OPERATOR_MAPPING.keys
FileNotFoundError = Class.new StandardError
OperatorNotSupportedError = Class.new StandardError
module API
def select(options = {})
documents = resources.select { |r| !r.raw_data.empty? }
options[:where].each do |selector, selector_value|
documents = documents.select do |document|
next unless document.raw_data.has_key? selector.attribute
document_value = document.raw_data[selector.attribute]
operator = OPERATOR_MAPPING[selector.operator]
document_value.send operator, selector_value
end
end
if options[:order_by].present?
order_attribute = options[:order_by].keys.first
asc_or_desc = options[:order_by].values.first
documents = documents.select do |document|
document.raw_data.include? order_attribute
end
documents = documents.sort_by do |document|
document.raw_data[order_attribute]
end
documents.reverse! if asc_or_desc == :desc
end
documents
end
def where(hash)
Query.new(self).where(hash)
end
def order_by(field)
Query.new(self).order_by(field)
end
def offset(number)
Query.new(self).offset(number)
end
def limit(number)
Query.new(self).limit(number)
end
end
class Query
def initialize(model)
@model = model
@where = {}
end
def where(constraints_hash)
selector_hash = constraints_hash.reject { |key, value| !key.is_a? Selector }
symbol_hash = constraints_hash.reject { |key, value| key.is_a? Selector }
symbol_hash.each do |attribute, value|
selector = Selector.new(:attribute => attribute, :operator => 'equal')
selector_hash.update({ selector => value })
end
@where.merge! selector_hash
self
end
def order_by(field)
@order_by = field.is_a?(Symbol) ? {field => :asc} : field
self
end
def offset(number)
@offset = number
self
end
def limit(number)
@limit = number
self
end
def first
self.all.first
end
def last
self.all.last
end
def all
result = @model.select(:where => @where, :order_by => @order_by)
if @offset.present?
result = result.last([result.size - @offset, 0].max)
end
if @limit.present?
result = result.first(@limit)
end
result
end
end
class Selector
attr_reader :attribute, :operator
def initialize(opts = {})
unless VALID_OPERATORS.include? opts[:operator]
raise OperatorNotSupportedError
end
@attribute, @operator = opts[:attribute], opts[:operator]
end
end
end
end
end
# Add operators to symbol objects
class Symbol
Middleman::Sitemap::Queryable::VALID_OPERATORS.each do |operator|
class_eval <<-OPERATORS
def #{operator}
Middleman::Sitemap::Queryable::Selector.new(:attribute => self, :operator => '#{operator}')
end
OPERATORS
end
unless method_defined?(:"<=>")
def <=>(other)
self.to_s <=> other.to_s
end
end
end