couchrest_model/lib/couchrest/model/proxyable.rb

153 lines
4.2 KiB
Ruby

module CouchRest
module Model
# :nodoc: Because I like inventing words
module Proxyable
extend ActiveSupport::Concern
included do
attr_accessor :model_proxy
end
module ClassMethods
# Define a collection that will use the base model for the database connection
# details.
def proxy_for(model_name, options = {})
db_method = options[:database_method] || "proxy_database"
options[:class_name] ||= model_name.to_s.singularize.camelize
class_eval <<-EOS, __FILE__, __LINE__ + 1
def #{model_name}
unless respond_to?('#{db_method}')
raise "Missing ##{db_method} method for proxy"
end
@#{model_name} ||= CouchRest::Model::Proxyable::ModelProxy.new(::#{options[:class_name]}, self, self.class.to_s.underscore, #{db_method})
end
EOS
end
def proxied_by(model_name, options = {})
raise "Model can only be proxied once or ##{model_name} already defined" if method_defined?(model_name)
attr_accessor model_name
end
end
class ModelProxy
attr_reader :model, :owner, :owner_name, :database
def initialize(model, owner, owner_name, database)
@model = model
@owner = owner
@owner_name = owner_name
@database = database
end
# Base
def new(*args)
proxy_update(model.new(*args))
end
def build_from_database(doc = {})
proxy_update(model.build_from_database(doc))
end
def method_missing(m, *args, &block)
if has_view?(m)
if model.respond_to?(m)
return model.send(m, *args).proxy(self)
else
query = args.shift || {}
return view(m, query, *args, &block)
end
elsif m.to_s =~ /^find_(by_.+)/
view_name = $1
if has_view?(view_name)
return first_from_view(view_name, *args)
end
end
super
end
# DocumentQueries
def all(opts = {}, &block)
proxy_update_all(@model.all({:database => @database}.merge(opts), &block))
end
def count(opts = {})
@model.count({:database => @database}.merge(opts))
end
def first(opts = {})
proxy_update(@model.first({:database => @database}.merge(opts)))
end
def last(opts = {})
proxy_update(@model.last({:database => @database}.merge(opts)))
end
def get(id)
proxy_update(@model.get(id, @database))
end
alias :find :get
# Views
def has_view?(view)
@model.has_view?(view)
end
def view_by(*args)
@model.view_by(*args)
end
def view(name, query={}, &block)
proxy_update_all(@model.view(name, {:database => @database}.merge(query), &block))
end
def first_from_view(name, *args)
# add to first hash available, or add to end
(args.last.is_a?(Hash) ? args.last : (args << {}).last)[:database] = @database
proxy_update(@model.first_from_view(name, *args))
end
# DesignDoc
def design_doc
@model.design_doc
end
def refresh_design_doc(db = nil)
@model.refresh_design_doc(db || @database)
end
def save_design_doc(db = nil)
@model.save_design_doc(db || @database)
end
protected
# Update the document's proxy details, specifically, the fields that
# link back to the original document.
def proxy_update(doc)
if doc
doc.database = @database if doc.respond_to?(:database=)
doc.model_proxy = self if doc.respond_to?(:model_proxy=)
doc.send("#{owner_name}=", owner) if doc.respond_to?("#{owner_name}=")
end
doc
end
def proxy_update_all(docs)
docs.each do |doc|
proxy_update(doc)
end
end
end
end
end
end