module Masheri class QueryBuilder def initialize(klass = nil, options = {}) @klass = klass @options = options @conditions = Where.new @select = Select.new end def select(argument) @select = Select.new(argument) clone end def from(argument) @from = From.new(argument) clone end def where(argument) @conditions.add argument clone end def order(column, order) @order = Order.new(column, order) clone end def items(argument) @items = Items.new(argument) clone end def page(argument) @page = Page.new(argument) clone end def query [@select, @from, @order, @conditions, @page, @items].map(&:to_s).reject {|s| s.blank? }.compact.join(" ").strip end def reify(*args) raise NoClassGiven.new if @klass.blank? @klass.new(*args) end alias :to_s :query def to_json Masheri.rpc.query(query) end def all Masheri::RpcResponse.new(self) end def find_each(&block) Masheri::RpcResponse.new(self).find_each(&block) end def first Masheri::RpcResponse.new(items(1)).to_objects.first end protected class Select def initialize(argument = nil) @argument = argument end def query @argument || "*" end def to_s "SELECT #{query}" end end class From def initialize(argument) @argument = argument end def to_s raise MissingFrom.new if @argument.nil? "FROM #{@argument}" end end class Where def initialize(hash = {}) @hash = hash end def add(hash) @hash.merge!(hash) end def to_s if @hash.blank? "" else "WHERE #{compute_relation}" end end def compute_relation @hash.map do |key, value| if value.is_a? String "#{key} = '#{value}'" else "#{key} = #{value}" end end.join(" AND ") end end # An ASC or DESC modifier may appear after each field in the ORDER BY list. If no modifer appears, ASC is assumed. # > SELECT * FROM members ORDER BY created DESC class Order def initialize(column, order) @column = column @order = order end def to_s if @column.blank? || @order.blank? "" else "ORDER BY #{@column} #{@desc}" end end end # the ITEMS clause can specify an alternative number of records to return per page. # > SELECT * FROM members ITEMS 25 class Items def initialize(count) @count = count end def to_s if @count.blank? "" else "ITEMS #{@count}" end end end # By default the first page is returned. The PAGE clause allows additional pages to be returned. # > SELECT * FROM members PAGE 5 class Page def initialize(page) @page = page end def to_s if @page.blank? "" else "PAGE #{@page}" end end end end end