2013-12-28 01:26:31 +01:00
|
|
|
require 'active_support/core_ext/object/inclusion'
|
2012-10-25 00:02:32 +02:00
|
|
|
|
|
|
|
module Middleman
|
|
|
|
module Sitemap
|
2013-12-28 19:14:15 +01:00
|
|
|
|
2012-10-25 00:02:32 +02:00
|
|
|
# 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
|
2013-10-25 05:48:23 +02:00
|
|
|
def initialize(model, opts={})
|
2012-10-25 00:02:32 +02:00
|
|
|
@model = model
|
2013-10-25 05:48:23 +02:00
|
|
|
@where = opts[:where] || {}
|
|
|
|
@order_by = opts[:order_by]
|
|
|
|
@offset = opts[:offset]
|
|
|
|
@limit = opts[:limit]
|
2012-10-25 00:02:32 +02:00
|
|
|
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
|
2013-10-25 09:09:36 +02:00
|
|
|
Query.new @model, opts(:where => @where.merge(selector_hash))
|
2013-10-25 05:48:23 +02:00
|
|
|
end
|
|
|
|
|
2013-10-25 09:09:36 +02:00
|
|
|
def opts new_opts
|
|
|
|
{ :where => {}.merge(@where),
|
2013-10-25 05:48:23 +02:00
|
|
|
:order_by => @order_by,
|
|
|
|
:offset => @offset,
|
|
|
|
:limit => @limit
|
2013-10-25 09:09:36 +02:00
|
|
|
}.merge(new_opts)
|
2012-10-25 00:02:32 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
def order_by(field)
|
2013-10-25 09:09:36 +02:00
|
|
|
Query.new @model, opts(:order_by => field.is_a?(Symbol) ? {field => :asc} : field)
|
2012-10-25 00:02:32 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
def offset(number)
|
2013-10-25 09:09:36 +02:00
|
|
|
Query.new @model, opts(:offset => number)
|
2012-10-25 00:02:32 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
def limit(number)
|
2013-10-25 09:09:36 +02:00
|
|
|
Query.new @model, opts(:limit => number)
|
2012-10-25 00:02:32 +02:00
|
|
|
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
|
2013-12-28 19:14:15 +01:00
|
|
|
|
2012-10-25 00:02:32 +02:00
|
|
|
unless method_defined?(:"<=>")
|
|
|
|
def <=>(other)
|
|
|
|
self.to_s <=> other.to_s
|
|
|
|
end
|
|
|
|
end
|
2013-10-25 05:48:23 +02:00
|
|
|
end
|