Adding configuration support and changing 'couchrest-type' key to 'model' along with config options

This commit is contained in:
Sam Lown 2010-09-17 23:00:55 +02:00
parent 5c21de8586
commit 85cd1308bc
14 changed files with 157 additions and 17 deletions

View file

@ -1,6 +1,8 @@
== Next Version == Next Version
* Major enhancements * Major enhancements
* IMPORTANT: Model's class name key changed from 'couchrest-type' to 'model'
* Support for configuration module and "model_type_key" option for overriding model's type key
* Minor enhancements * Minor enhancements
* Fixing find("") issue (thanks epochwolf) * Fixing find("") issue (thanks epochwolf)

View file

@ -4,6 +4,7 @@ module CouchRest
extend ActiveModel::Naming extend ActiveModel::Naming
include CouchRest::Model::Configuration
include CouchRest::Model::Persistence include CouchRest::Model::Persistence
include CouchRest::Model::Callbacks include CouchRest::Model::Callbacks
include CouchRest::Model::DocumentQueries include CouchRest::Model::DocumentQueries
@ -50,7 +51,7 @@ module CouchRest
prepare_all_attributes(doc, options) prepare_all_attributes(doc, options)
super(doc) super(doc)
unless self['_id'] && self['_rev'] unless self['_id'] && self['_rev']
self['couchrest-type'] = self.class.to_s self[self.model_type_key] = self.class.to_s
end end
after_initialize if respond_to?(:after_initialize) after_initialize if respond_to?(:after_initialize)
end end

View file

@ -0,0 +1,49 @@
module CouchRest
# CouchRest Model Configuration support, stolen from Carrierwave by jnicklas
# http://github.com/jnicklas/carrierwave/blob/master/lib/carrierwave/uploader/configuration.rb
module Model
module Configuration
extend ActiveSupport::Concern
included do
add_config :model_type_key
configure do |config|
config.model_type_key = 'model'
end
end
module ClassMethods
def add_config(name)
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def self.#{name}(value=nil)
@#{name} = value if value
return @#{name} if self.object_id == #{self.object_id} || defined?(@#{name})
name = superclass.#{name}
return nil if name.nil? && !instance_variable_defined?("@#{name}")
@#{name} = name && !name.is_a?(Module) && !name.is_a?(Symbol) && !name.is_a?(Numeric) && !name.is_a?(TrueClass) && !name.is_a?(FalseClass) ? name.dup : name
end
def self.#{name}=(value)
@#{name} = value
end
def #{name}
self.class.#{name}
end
RUBY
end
def configure
yield self
end
end
end
end
end

View file

@ -31,7 +31,7 @@ module CouchRest
"views" => { "views" => {
'all' => { 'all' => {
'map' => "function(doc) { 'map' => "function(doc) {
if (doc['couchrest-type'] == '#{self.to_s}') { if (doc['#{self.model_type_key}'] == '#{self.to_s}') {
emit(doc['_id'],1); emit(doc['_id'],1);
} }
}" }"

View file

@ -8,21 +8,21 @@ module CouchRest
module ClassMethods module ClassMethods
# Load all documents that have the "couchrest-type" field equal to the # Load all documents that have the model_type_key's field equal to the
# name of the current class. Take the standard set of # name of the current class. Take the standard set of
# CouchRest::Database#view options. # CouchRest::Database#view options.
def all(opts = {}, &block) def all(opts = {}, &block)
view(:all, opts, &block) view(:all, opts, &block)
end end
# Returns the number of documents that have the "couchrest-type" field # Returns the number of documents that have the model_type_key's field
# equal to the name of the current class. Takes the standard set of # equal to the name of the current class. Takes the standard set of
# CouchRest::Database#view options # CouchRest::Database#view options
def count(opts = {}, &block) def count(opts = {}, &block)
all({:raw => true, :limit => 0}.merge(opts), &block)['total_rows'] all({:raw => true, :limit => 0}.merge(opts), &block)['total_rows']
end end
# Load the first document that have the "couchrest-type" field equal to # Load the first document that have the model_type_key's field equal to
# the name of the current class. # the name of the current class.
# #
# ==== Returns # ==== Returns

View file

@ -97,7 +97,7 @@ module CouchRest
# ==== Returns # ==== Returns
# a document instance # a document instance
def create_from_database(doc = {}) def create_from_database(doc = {})
base = (doc['couchrest-type'].blank? || doc['couchrest-type'] == self.to_s) ? self : doc['couchrest-type'].constantize base = (doc[model_type_key].blank? || doc[model_type_key] == self.to_s) ? self : doc[model_type_key].constantize
base.new(doc, :directly_set_attributes => true) base.new(doc, :directly_set_attributes => true)
end end

View file

@ -23,7 +23,7 @@ module CouchRest
# view_by :tags, # view_by :tags,
# :map => # :map =>
# "function(doc) { # "function(doc) {
# if (doc['couchrest-type'] == 'Post' && doc.tags) { # if (doc['model'] == 'Post' && doc.tags) {
# doc.tags.forEach(function(tag){ # doc.tags.forEach(function(tag){
# emit(doc.tag, 1); # emit(doc.tag, 1);
# }); # });
@ -39,7 +39,7 @@ module CouchRest
# function: # function:
# #
# function(doc) { # function(doc) {
# if (doc['couchrest-type'] == 'Post' && doc.date) { # if (doc['model'] == 'Post' && doc.date) {
# emit(doc.date, null); # emit(doc.date, null);
# } # }
# } # }
@ -77,7 +77,7 @@ module CouchRest
ducktype = opts.delete(:ducktype) ducktype = opts.delete(:ducktype)
unless ducktype || opts[:map] unless ducktype || opts[:map]
opts[:guards] ||= [] opts[:guards] ||= []
opts[:guards].push "(doc['couchrest-type'] == '#{self.to_s}')" opts[:guards].push "(doc['#{model_type_key}'] == '#{self.to_s}')"
end end
keys.push opts keys.push opts
design_doc.view_by(*keys) design_doc.view_by(*keys)

View file

@ -48,6 +48,7 @@ require "couchrest/model/collection"
require "couchrest/model/attribute_protection" require "couchrest/model/attribute_protection"
require "couchrest/model/attributes" require "couchrest/model/attributes"
require "couchrest/model/associations" require "couchrest/model/associations"
require "couchrest/model/configuration"
# Monkey patches applied to couchrest # Monkey patches applied to couchrest
require "couchrest/model/support/couchrest" require "couchrest/model/support/couchrest"

View file

@ -150,7 +150,7 @@ describe "Model Attributes" do
it "Base#all should not strip protected attributes" do it "Base#all should not strip protected attributes" do
# all creates a CollectionProxy # all creates a CollectionProxy
docs = WithProtected.all(:key => @user.id) docs = WithProtected.all(:key => @user.id)
docs.size.should == 1 docs.length.should == 1
reloaded = docs.first reloaded = docs.first
verify_attrs reloaded verify_attrs reloaded
end end

View file

@ -36,7 +36,7 @@ describe "Model Base" do
it "should not failed on a nil value in argument" do it "should not failed on a nil value in argument" do
@obj = Basic.new(nil) @obj = Basic.new(nil)
@obj.should == { 'couchrest-type' => 'Basic' } @obj.should_not be_nil
end end
end end

View file

@ -0,0 +1,87 @@
# encoding: utf-8
require File.expand_path('../../spec_helper', __FILE__)
require File.join(FIXTURE_PATH, 'more', 'cat')
describe CouchRest::Model::Base do
before do
@class = Class.new(CouchRest::Model::Base)
end
describe '.configure' do
it "should set a configuration parameter" do
@class.add_config :foo_bar
@class.configure do |config|
config.foo_bar = 'monkey'
end
@class.foo_bar.should == 'monkey'
end
end
describe '.add_config' do
it "should add a class level accessor" do
@class.add_config :foo_bar
@class.foo_bar = 'foo'
@class.foo_bar.should == 'foo'
end
['foo', :foo, 45, ['foo', :bar]].each do |val|
it "should be inheritable for a #{val.class}" do
@class.add_config :foo_bar
@child_class = Class.new(@class)
@class.foo_bar = val
@class.foo_bar.should == val
@child_class.foo_bar.should == val
@child_class.foo_bar = "bar"
@child_class.foo_bar.should == "bar"
@class.foo_bar.should == val
end
end
it "should add an instance level accessor" do
@class.add_config :foo_bar
@class.foo_bar = 'foo'
@class.new.foo_bar.should == 'foo'
end
it "should add a convenient in-class setter" do
@class.add_config :foo_bar
@class.foo_bar "monkey"
@class.foo_bar.should == "monkey"
end
end
describe "General examples" do
before(:all) do
@default_model_key = 'model'
end
it "should set default configuration options on Model::Base" do
CouchRest::Model::Base.model_type_key.should eql(@default_model_key)
end
it "should provide options from instance" do
cat = Cat.new
cat.model_type_key.should eql(@default_model_key)
end
it "should be possible to override on class using configure method" do
Cat.instance_eval do
configure do |config|
config.model_type_key = 'cat-type'
end
end
CouchRest::Model::Base.model_type_key.should eql(@default_model_key)
Cat.model_type_key.should eql('cat-type')
cat = Cat.new
cat.model_type_key.should eql('cat-type')
end
end
end

View file

@ -25,7 +25,7 @@ describe "Model Persistence" do
end end
it "should instantialize document of different type" do it "should instantialize document of different type" do
doc = Article.create_from_database({'_id' => 'testitem2', '_rev' => 123, 'couchrest-type' => 'WithTemplateAndUniqueID', 'name' => 'my test'}) doc = Article.create_from_database({'_id' => 'testitem2', '_rev' => 123, Article.model_type_key => 'WithTemplateAndUniqueID', 'name' => 'my test'})
doc.class.should eql(WithTemplateAndUniqueID) doc.class.should eql(WithTemplateAndUniqueID)
end end
@ -114,7 +114,7 @@ describe "Model Persistence" do
end end
it "should set the type" do it "should set the type" do
@sobj['couchrest-type'].should == 'Basic' @sobj[@sobj.model_type_key].should == 'Basic'
end end
it "should accept true or false on save for validation" do it "should accept true or false on save for validation" do

View file

@ -92,8 +92,8 @@ describe "Subclassing a Model" do
OnlineCourse.design_doc['views'].keys.should_not include('by_title') OnlineCourse.design_doc['views'].keys.should_not include('by_title')
end end
it "should have an all view with a guard clause for couchrest-type == subclass name in the map function" do it "should have an all view with a guard clause for model == subclass name in the map function" do
OnlineCourse.design_doc['views']['all']['map'].should =~ /if \(doc\['couchrest-type'\] == 'OnlineCourse'\)/ OnlineCourse.design_doc['views']['all']['map'].should =~ /if \(doc\['model'\] == 'OnlineCourse'\)/
end end
end end

View file

@ -9,7 +9,7 @@ class Article < CouchRest::Model::Base
view_by :tags, view_by :tags,
:map => :map =>
"function(doc) { "function(doc) {
if (doc['couchrest-type'] == 'Article' && doc.tags) { if (doc['#{model_type_key}'] == 'Article' && doc.tags) {
doc.tags.forEach(function(tag){ doc.tags.forEach(function(tag){
emit(tag, 1); emit(tag, 1);
}); });