148 lines
No EOL
3.9 KiB
Ruby
148 lines
No EOL
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 |