require 'active_record' module Liquor class Drop include Liquor::External attr_reader :source def self.inherited(klass) klass.instance_exec do const_set :Scope, Class.new(::Liquor::Drop::Scope) const_get(:Scope).instance_exec do export # refresh liquor_exports end end end def initialize(source) @source = source end def self.attributes(*attrs) attrs.each do |attr| define_method(attr) { @source.send(attr) } export attr end end def self.scopes(*scopes) const_get(:Scope).instance_exec do scopes.each do |scope| define_method(scope) { |*args| args = Drop.unwrap_scope_arguments(args) DropDelegation.wrap_scope @source.send(scope, *args) } export scope end end end def self.unwrap_scope_arguments(args) args.map do |arg| case arg when Drop arg.id when Drop::Scope unwrap_scope_arguments(arg.to_a) when Array unwrap_scope_arguments(arg) when Hash Hash[arg.keys.zip(unwrap_scope_arguments(arg.values))] else arg end end end def self.define_singular_association_accessor(name, options={}) if options.has_key?(:if) && options.has_key?(:unless) raise ArgumentError, "It is pointless to pass both :if and :unless conditions to has_one or belongs_to" end unsupported_options = (options.keys - [:if, :unless]) if unsupported_options.any? raise ArgumentError, "Unsupported options #{unsupported_options.join(", ")}" end define_method(name) { value = @source.send(name) if self.class.check_singular_condition(value, options) if value.nil? nil else DropDelegation.wrap_element(value) end end } export name end def self.check_singular_condition(object, options) execute = lambda do |object, condition| if condition.is_a? Symbol object.send(condition) elsif condition.respond_to? :call condition.(object) else raise ArgumentError, "Invalid condition type #{condition.class}" end end if options[:if] execute.(object, options[:if]) elsif options[:unless] !execute.(object, options[:unless]) else true end end def self.belongs_to(name, options={}) define_singular_association_accessor(name, options) end def self.has_one(name, options={}) define_singular_association_accessor(name, options) end def self.has_many(name, options={}) unsupported_options = (options.keys - [:scope, :include]) if unsupported_options.any? raise ArgumentError, "Unsupported options #{unsupported_options.join(", ")}" end define_method(name) { value = @source.send(name) if options[:scope] # Sequentally apply each symbol from options[:scope] # to the current scope, starting from `value' and using # each result as current scope for the next operation. value = Array(options[:scope]).reduce(value, &:send) end if options[:include] Array(options[:include]).each do |collection| value = value.includes(collection) end end DropDelegation.wrap_scope(value, value.klass) } export name end def entity @source.class.model_name.to_s end export :entity def ==(other) if other.is_a? Liquor::Drop self.source == other.source else other == self end end alias eql? == def hash @source.hash end def to_drop self end end end require_relative 'drop_delegation' require_relative 'drop_scope' require_relative 'dropable'