require 'much-plugin' require 'mr/read_model/query_expression' require 'mr/record' require 'mr/query' module MR::ReadModel module Querying include MuchPlugin plugin_included do extend ClassMethods end module ClassMethods def relation @relation ||= Relation.new end def record_class self.relation.from_record_class end def find(id, params = nil) self.new(self.relation.build_for_find(id, params || {}).first!) end def query(params = nil) MR::Query.new(self, self.relation.build_for_all(params || {})) end def count(*args) self.query(*args).count end def build_sql(params = nil) self.relation.build_sql(params) end def find_attr(column = nil) self.relation.find_attr = column if !column.nil? self.relation.find_attr end def select(*args, &block) add_query_expression(:select, *args, &block) rescue InvalidQueryExpressionError, ArgumentError => exception raise ArgumentError, exception.message, caller end def from(record_class) relation.from(record_class) rescue ArgumentError => exception raise ArgumentError, exception.message, caller end def from_subquery(&block) relation.from_subquery(&block) rescue ArgumentError => exception raise ArgumentError, exception.message, caller end def joins(*args, &block) add_query_expression(:joins, *args, &block) rescue InvalidQueryExpressionError, ArgumentError => exception raise ArgumentError, exception.message, caller end def inner_join_subquery(&block) add_subquery_expression(:joins, :inner, &block) rescue InvalidQueryExpressionError, ArgumentError => exception raise ArgumentError, exception.message, caller end def left_outer_join_subquery(&block) add_subquery_expression(:joins, :left, &block) rescue InvalidQueryExpressionError, ArgumentError => exception raise ArgumentError, exception.message, caller end alias :left_join_subquery :left_outer_join_subquery def right_outer_join_subquery(&block) add_subquery_expression(:joins, :right, &block) rescue InvalidQueryExpressionError, ArgumentError => exception raise ArgumentError, exception.message, caller end alias :right_join_subquery :right_outer_join_subquery def full_outer_join_subquery(&block) add_subquery_expression(:joins, :full, &block) rescue InvalidQueryExpressionError, ArgumentError => exception raise ArgumentError, exception.message, caller end alias :full_join_subquery :full_outer_join_subquery def where(*args, &block) add_merge_query_expression(:where, *args, &block) rescue InvalidQueryExpressionError, ArgumentError => exception raise ArgumentError, exception.message, caller end def order(*args, &block) add_merge_query_expression(:order, *args, &block) rescue InvalidQueryExpressionError, ArgumentError => exception raise ArgumentError, exception.message, caller end def group(*args, &block) add_query_expression(:group, *args, &block) rescue InvalidQueryExpressionError, ArgumentError => exception raise ArgumentError, exception.message, caller end def having(*args, &block) add_query_expression(:having, *args, &block) rescue InvalidQueryExpressionError, ArgumentError => exception raise ArgumentError, exception.message, caller end def limit(*args, &block) add_query_expression(:limit, *args, &block) rescue InvalidQueryExpressionError, ArgumentError => exception raise ArgumentError, exception.message, caller end def offset(*args, &block) add_query_expression(:offset, *args, &block) rescue InvalidQueryExpressionError, ArgumentError => exception raise ArgumentError, exception.message, caller end def merge(*args, &block) add_merge_query_expression(:merge, *args, &block) rescue InvalidQueryExpressionError, ArgumentError => exception raise ArgumentError, exception.message, caller end private def add_query_expression(type, *args, &block) QueryExpression.new(type, *args, &block).tap do |expression| relation.query_expressions << expression end end def add_merge_query_expression(type, *args, &block) MergeQueryExpression.new(type, *args, &block).tap do |expression| relation.query_expressions << expression end end def add_subquery_expression(type, *args, &block) SubqueryExpression.new(type, *args, &block).tap do |expression| relation.query_expressions << expression end end end end class Relation attr_reader :from_expression, :query_expressions, :set_expressions attr_writer :find_attr def initialize @from_expression = NullFromExpression.new @query_expressions = [] @set_expressions = [] end def from(record_class) @from_expression = FromExpression.new(record_class) end def from_subquery(&block) @from_expression = FromSubqueryExpression.new(&block) end def from_record_class self.from_expression.record_class end def find_attr @find_attr ||= self.from_expression.default_find_attr end FIND_EXCLUDED_TYPES = [:where, :limit, :offset].freeze def build_for_find(id, params = nil) query_expressions = self.query_expressions.reject do |e| FIND_EXCLUDED_TYPES.include?(e.type) end ar_relation = build_ar_relation_for_all(query_expressions, params) ar_relation.where(self.find_attr => id).limit(1) end def build_for_all(params = nil) build_ar_relation_for_all(self.query_expressions, params) end def build_sql(params = nil) self.build_for_all(params).to_sql.strip end private def build_ar_relation_for_all(query_expressions, params) ar_relation = self.from_expression.ar_relation(params) query_expressions.inject(ar_relation){ |r, e| e.apply_to(r, params) } end end end