Compare commits

..

14 commits

Author SHA1 Message Date
Sam Lown 819ddb7643 Merge pull request #132 from pezra/improve-auto-update-design-doc-flag-suppport
Improve auto update design doc flag suppport
2011-12-02 07:15:27 -08:00
Peter Williams 447b11a397 Improved auto_update_design_doc handling. 2011-12-01 09:19:15 -07:00
Peter Williams 88bb413ec2 Merge remote branch 'refs/remotes/canonical/master' into design-mapper-more-auto-update-design-doc-aware 2011-11-29 08:22:30 -07:00
Peter Williams 39c60d77d2 Used stored design document if auto_update_design_doc is disabled 2011-11-28 16:47:13 -07:00
Peter Williams f2c16144b0 #view method works when auto_update_design_doc is disabled 2011-11-16 15:30:46 -07:00
Marcos Tapajos 1c695f58bf update readme 2011-08-21 02:02:56 -03:00
Marcos Tapajos 2f00599209 added rake to gemfile 2011-08-21 01:59:48 -03:00
Marcos Tapajós 29de79290f Merge pull request #107 from Burgestrand/base-respond_to
Add CouchRest::Model::Base.respond_to_missing? and respond_to?
2011-08-20 21:58:11 -07:00
Marcos Tapajós 44e09f6083 Merge pull request #108 from Burgestrand/fix-specs
Tell contextual validations specs which database to use
2011-08-20 21:55:08 -07:00
Kim Burgestrand 465c0681e2 Tell contextual validations specs which database to use 2011-07-31 04:44:35 +02:00
Kim Burgestrand 72e3ac37d6 Add CouchRest::Model::Base.respond_to_missing? and respond_to? 2011-07-31 04:40:31 +02:00
Sam Lown 80e5ed2767 Updating histories and ensuring VERSION and date are good for 1.1.2 2011-07-23 17:38:49 +02:00
Sam Lown 3258ac22e9 updating to couchrest 1.1.2 and as_couch_json method 2011-07-19 21:28:44 +02:00
Sam Lown 3f1b2ea0c6 Casting array type properties now possible 2011-07-19 18:03:31 +02:00
14 changed files with 174 additions and 56 deletions

View file

@ -1 +1 @@
1.1.1 1.1.2

View file

@ -6,7 +6,7 @@ Gem::Specification.new do |s|
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
s.authors = ["J. Chris Anderson", "Matt Aimonetti", "Marcos Tapajos", "Will Leinweber", "Sam Lown"] s.authors = ["J. Chris Anderson", "Matt Aimonetti", "Marcos Tapajos", "Will Leinweber", "Sam Lown"]
s.date = %q{2011-04-29} s.date = File.mtime('VERSION')
s.description = %q{CouchRest Model provides aditional features to the standard CouchRest Document class such as properties, view designs, associations, callbacks, typecasting and validations.} s.description = %q{CouchRest Model provides aditional features to the standard CouchRest Document class such as properties, view designs, associations, callbacks, typecasting and validations.}
s.email = %q{jchris@apache.org} s.email = %q{jchris@apache.org}
s.extra_rdoc_files = [ s.extra_rdoc_files = [
@ -23,13 +23,14 @@ Gem::Specification.new do |s|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"] s.require_paths = ["lib"]
s.add_dependency(%q<couchrest>, "1.1.1") s.add_dependency(%q<couchrest>, "~> 1.1.2")
s.add_dependency(%q<mime-types>, "~> 1.15") s.add_dependency(%q<mime-types>, "~> 1.15")
s.add_dependency(%q<activemodel>, "~> 3.0") s.add_dependency(%q<activemodel>, "~> 3.0")
s.add_dependency(%q<tzinfo>, "~> 0.3.22") s.add_dependency(%q<tzinfo>, "~> 0.3.22")
s.add_development_dependency(%q<rspec>, "~> 2.6.0") s.add_development_dependency(%q<rspec>, "~> 2.6.0")
s.add_development_dependency(%q<json>, ["~> 1.5.1"]) s.add_development_dependency(%q<json>, ["~> 1.5.1"])
s.add_development_dependency(%q<rack-test>, ">= 0.5.7") s.add_development_dependency(%q<rack-test>, ">= 0.5.7")
s.add_development_dependency("rake", ">= 0.8.0")
# s.add_development_dependency("jruby-openssl", ">= 0.7.3") # s.add_development_dependency("jruby-openssl", ">= 0.7.3")
end end

View file

@ -1,5 +1,16 @@
# CouchRest Model Change History # CouchRest Model Change History
## 1.1.3
* CouchRest::Model::Base.respond_to_missing? and respond_to? (Kim Burgestrand)
## 1.1.2 - 2011-07-23
* Minor fixes
* Upgrade to couchrest 1.1.2
* Override as_couch_json to ensure nil values not stored
* Removing restriction that prohibited objects that cast as an array to be loaded.
## 1.1.1 - 2011-07-04 ## 1.1.1 - 2011-07-04
* Minor fix * Minor fix

View file

@ -82,6 +82,21 @@ module CouchRest
super super
end end
# compatbility for 1.8, it does not use respond_to_missing?
# thing is, when using it like this only, doing method(:find_by_view)
# will throw an error
def self.respond_to?(m, include_private = false)
super || respond_to_missing?(m, include_private)
end
# ruby 1.9 feature
# this allows ruby to know that the method is defined using
# method_missing, and as such, method(:find_by_view) will actually
# give a Method back, and not throw an error like in 1.8!
def self.respond_to_missing?(m, include_private = false)
has_view?(m) || has_view?(m.to_s[/^find_(by_.+)/, 1])
end
def to_key def to_key
new? ? nil : [id] new? ? nil : [id]
end end

View file

@ -7,7 +7,11 @@ module CouchRest
module ClassMethods module ClassMethods
def design_doc def design_doc
@design_doc ||= ::CouchRest::Design.new(default_design_doc) @design_doc ||= if auto_update_design_doc
::CouchRest::Design.new(default_design_doc)
else
stored_design_doc || ::CouchRest::Design.new(default_design_doc)
end
end end
def design_doc_id def design_doc_id
@ -75,16 +79,25 @@ module CouchRest
# If auto updates enabled, check checksum cache # If auto updates enabled, check checksum cache
return design_doc if auto_update_design_doc && design_doc_cache_checksum(db) == checksum return design_doc if auto_update_design_doc && design_doc_cache_checksum(db) == checksum
# Load up the stored doc (if present), update, and save retries = 1
saved = stored_design_doc(db) begin
if saved # Load up the stored doc (if present), update, and save
if force || saved['couchrest-hash'] != checksum saved = stored_design_doc(db)
saved.merge!(design_doc) if saved
db.save_doc(saved) if force || saved['couchrest-hash'] != checksum
saved.merge!(design_doc)
db.save_doc(saved)
@design_doc = saved # update memo to point to the document we actually saved
end
else
design_doc.delete('_rev') # This is a new document and so doesn't have a revision yet
db.save_doc(design_doc)
end end
else rescue RestClient::Conflict
db.save_doc(design_doc) # if we get a conflict retry the operation...
design_doc.delete('_rev') # Prevent conflicts, never store rev as DB specific raise if retries < 1
retries -= 1
retry
end end
# Ensure checksum cached for next attempt if using auto updates # Ensure checksum cached for next attempt if using auto updates

View file

@ -58,10 +58,11 @@ module CouchRest
self.model = model self.model = model
end end
# Define a view and generate a method that will provide a new # Generate a method that will provide a new View instance when
# View instance when requested. # requested. This will also define the view in CouchDB unless
# auto_update_design_doc is disabled.
def view(name, opts = {}) def view(name, opts = {})
View.create(model, name, opts) View.create(model, name, opts) if model.auto_update_design_doc
create_view_method(name) create_view_method(name)
end end

View file

@ -12,8 +12,10 @@ module CouchRest
raise "You can only mixin Properties in a class responding to [] and []=, if you tried to mixin CastedModel, make sure your class inherits from Hash or responds to the proper methods" unless (method_defined?(:[]) && method_defined?(:[]=)) raise "You can only mixin Properties in a class responding to [] and []=, if you tried to mixin CastedModel, make sure your class inherits from Hash or responds to the proper methods" unless (method_defined?(:[]) && method_defined?(:[]=))
end end
def as_json(options = nil) # Provide an attribute hash ready to be sent to CouchDB but with
Hash[self].reject{|k,v| v.nil?}.as_json(options) # all the nil attributes removed.
def as_couch_json
super.delete_if{|k,v| v.nil?}
end end
# Returns the Class properties with their values # Returns the Class properties with their values

View file

@ -43,9 +43,8 @@ module CouchRest::Model
end end
end end
# Cast an individual value, not an array # Cast an individual value
def cast_value(parent, value) def cast_value(parent, value)
raise "An array inside an array cannot be casted, use Embeddable module" if value.is_a?(Array)
value = typecast_value(value, self) value = typecast_value(value, self)
associate_casted_value_to_parent(parent, value) associate_casted_value_to_parent(parent, value)
end end

View file

@ -72,9 +72,12 @@ module CouchRest
# <tt>spec/couchrest/more/extended_doc_spec.rb</tt>. # <tt>spec/couchrest/more/extended_doc_spec.rb</tt>.
def view_by(*keys) def view_by(*keys)
return unless auto_update_design_doc
opts = keys.pop if keys.last.is_a?(Hash) opts = keys.pop if keys.last.is_a?(Hash)
opts ||= {} opts ||= {}
ducktype = opts.delete(:ducktype) ducktype = opts.delete(:ducktype)
unless ducktype || opts[:map] unless ducktype || opts[:map]
opts[:guards] ||= [] opts[:guards] ||= []
opts[:guards].push "(doc['#{model_type_key}'] == '#{self.to_s}')" opts[:guards].push "(doc['#{model_type_key}'] == '#{self.to_s}')"

View file

@ -85,11 +85,13 @@ end
# Following two fixture classes have __intentionally__ diffent syntax for setting the validation context # Following two fixture classes have __intentionally__ diffent syntax for setting the validation context
class WithContextualValidationOnCreate < CouchRest::Model::Base class WithContextualValidationOnCreate < CouchRest::Model::Base
use_database TEST_SERVER.default_database
property(:name, String) property(:name, String)
validates(:name, :presence => {:on => :create}) validates(:name, :presence => {:on => :create})
end end
class WithContextualValidationOnUpdate < CouchRest::Model::Base class WithContextualValidationOnUpdate < CouchRest::Model::Base
use_database TEST_SERVER.default_database
property(:name, String) property(:name, String)
validates(:name, :presence => true, :on => :update) validates(:name, :presence => true, :on => :update)
end end

View file

@ -159,39 +159,68 @@ describe CouchRest::Model::DesignDoc do
end end
describe "when auto_update_design_doc false" do describe "when auto_update_design_doc false" do
# We really do need a new class for each of example. If we try
before :all do # to use the same class the examples interact with each in ways
Article.auto_update_design_doc = false # that can hide failures because the design document gets cached
Article.save_design_doc! # at the class level.
end let(:model_class) {
class_name = "#{example.metadata[:full_description].gsub(/\s+/,'_').camelize}Model"
doc = CouchRest::Document.new("_id" => "_design/#{class_name}")
doc["language"] = "javascript"
doc["views"] = {"all" => {"map" =>
"function(doc) {
if (doc['type'] == 'Article') {
emit(doc['_id'],1);
}
}"},
"by_name" => {"map" =>
"function(doc) {
if ((doc['type'] == '#{class_name}') && (doc['name'] != null)) {
emit(doc['name'], null);
}",
"reduce" =>
"function(keys, values, rereduce) {
return sum(values);
}"}}
DB.save_doc doc
after :all do eval <<-KLASS
Article.auto_update_design_doc = true class ::#{class_name} < CouchRest::Model::Base
end use_database DB
self.auto_update_design_doc = false
design do
view :by_name
end
property :name, String
end
KLASS
it "will not send a request for the saved design doc" do class_name.constantize
Article.should_not_receive(:stored_design_doc) }
Article.by_date
end
it "will not update stored design doc if view changed" do it "will not update stored design doc if view changed" do
Article.by_date model_class.by_name
orig = Article.stored_design_doc orig = model_class.stored_design_doc
design = Article.design_doc design = model_class.design_doc
view = design['views']['by_date']['map'] view = design['views']['by_name']['map']
design['views']['by_date']['map'] = view + ' ' design['views']['by_name']['map'] = view + ' '
Article.by_date model_class.by_name
Article.stored_design_doc['_rev'].should eql(orig['_rev']) model_class.stored_design_doc['_rev'].should eql(orig['_rev'])
end end
it "will update stored design if forced" do it "will update stored design if forced" do
Article.by_date model_class.by_name
orig = Article.stored_design_doc orig = model_class.stored_design_doc
design = Article.design_doc design = model_class.design_doc
view = design['views']['by_date']['map'] view = design['views']['by_name']['map']
design['views']['by_date']['map'] = view + ' ' design['views']['by_name']['map'] = view + ' '
Article.save_design_doc! model_class.save_design_doc!
Article.stored_design_doc['_rev'].should_not eql(orig['_rev']) model_class.stored_design_doc['_rev'].should_not eql(orig['_rev'])
end
it "is able to use predefined views" do
model_class.by_name(key: "special").all
end end
end end
end end

View file

@ -83,7 +83,23 @@ describe CouchRest::Model::Designs do
@object.should_receive(:create_view_method).with('test') @object.should_receive(:create_view_method).with('test')
@object.view('test') @object.view('test')
end end
end
context "for model with auto_update_design_doc disabled " do
class ::DesignModelAutoUpdateDesignDocDisabled < ::CouchRest::Model::Base
self.auto_update_design_doc = false
end
describe "#view" do
before :each do
@object = @klass.new(DesignModelAutoUpdateDesignDocDisabled)
end
it "does not attempt to create view" do
CouchRest::Model::Designs::View.should_not_receive(:create)
@object.view('test')
end
end
end end
describe "#filter" do describe "#filter" do

View file

@ -62,15 +62,23 @@ describe CouchRest::Model::Property do
@card.updated_at.should_not be_nil @card.updated_at.should_not be_nil
end end
describe "#as_json" do describe "#as_couch_json" do
it "should provide a simple hash from model" do it "should provide a simple hash from model" do
@card.as_json.class.should eql(Hash) @card.as_couch_json.class.should eql(Hash)
end end
it "should remove properties from Hash if value is nil" do it "should remove properties from Hash if value is nil" do
@card.last_name = nil @card.last_name = nil
@card.as_json.keys.include?('last_name').should be_false @card.as_couch_json.keys.include?('last_name').should be_false
end
end
describe "#as_json" do
it "should provide a simple hash from model" do
@card.as_json.class.should eql(Hash)
end end
it "should pass options to Active Support's as_json" do it "should pass options to Active Support's as_json" do
@ -442,15 +450,18 @@ describe "Property Class" do
ary.last.should eql(Date.new(2011, 05, 22)) ary.last.should eql(Date.new(2011, 05, 22))
end end
it "should raise and error if value is array when type is not" do it "should cast an object that provides an array" do
property = CouchRest::Model::Property.new(:test, Date) prop = Class.new do
attr_accessor :ary
def initialize(val); self.ary = val; end
def as_json; ary; end
end
property = CouchRest::Model::Property.new(:test, prop)
parent = mock("FooClass") parent = mock("FooClass")
lambda { cast = property.cast(parent, [1, 2])
cast = property.cast(parent, [Date.new(2010, 6, 1)]) cast.ary.should eql([1, 2])
}.should raise_error
end end
it "should set parent as casted_by object in CastedArray" do it "should set parent as casted_by object in CastedArray" do
property = CouchRest::Model::Property.new(:test, [Object]) property = CouchRest::Model::Property.new(:test, [Object])
parent = mock("FooObject") parent = mock("FooObject")

View file

@ -173,7 +173,22 @@ describe CouchRest::Model::Views do
end end
end end
describe "#method_missing for find_by methods" do
before(:all) { reset_test_db! }
specify { Course.should respond_to :find_by_title_and_active }
specify { Course.should respond_to :by_title }
specify "#method should work in ruby 1.9, but not 1.8" do
if RUBY_VERSION >= "1.9"
Course.method(:find_by_title_and_active).should be_a Method
else
expect { Course.method(:find_by_title_and_active) }.to raise_error(NameError)
end
end
end
describe "a ducktype view" do describe "a ducktype view" do
before(:all) do before(:all) do
reset_test_db! reset_test_db!