From b01b49d9b9446a6fde748d24ac84840edf6d4346 Mon Sep 17 00:00:00 2001 From: Matt Aimonetti Date: Mon, 28 Sep 2009 21:00:14 -0700 Subject: [PATCH 01/49] changed the gemspec to avoid building a new gh version --- couchrest.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchrest.gemspec b/couchrest.gemspec index 32b9da0..6ff0d37 100644 --- a/couchrest.gemspec +++ b/couchrest.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |s| s.name = %q{couchrest} - s.version = "0.34" + s.version = "0.33" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["J. Chris Anderson", "Matt Aimonetti"] From d8e765268036decd065f2bcc8ae8b69e6ea4fc0b Mon Sep 17 00:00:00 2001 From: Mathias Meyer Date: Mon, 28 Sep 2009 20:28:33 +0800 Subject: [PATCH 02/49] Added support for https database URIs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tapajós --- lib/couchrest.rb | 6 ++-- spec/couchrest/core/couchrest_spec.rb | 50 +++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/lib/couchrest.rb b/lib/couchrest.rb index b69728b..03a6a65 100644 --- a/lib/couchrest.rb +++ b/lib/couchrest.rb @@ -96,14 +96,14 @@ module CouchRest def parse url case url - when /^http:\/\/(.*)\/(.*)\/(.*)/ + when /^https?:\/\/(.*)\/(.*)\/(.*)/ host = $1 db = $2 docid = $3 - when /^http:\/\/(.*)\/(.*)/ + when /^https?:\/\/(.*)\/(.*)/ host = $1 db = $2 - when /^http:\/\/(.*)/ + when /^https?:\/\/(.*)/ host = $1 when /(.*)\/(.*)\/(.*)/ host = $1 diff --git a/spec/couchrest/core/couchrest_spec.rb b/spec/couchrest/core/couchrest_spec.rb index ef6637c..faf6847 100644 --- a/spec/couchrest/core/couchrest_spec.rb +++ b/spec/couchrest/core/couchrest_spec.rb @@ -54,7 +54,7 @@ describe CouchRest do db[:host].should == "127.0.0.1" end it "should parse a host and db with http" do - db = CouchRest.parse "http://127.0.0.1/my-db" + db = CouchRest.parse "https://127.0.0.1/my-db" db[:database].should == "my-db" db[:host].should == "127.0.0.1" end @@ -68,16 +68,31 @@ describe CouchRest do db[:database].should == "my-db" db[:host].should == "127.0.0.1:5555" end + it "should parse a host with a port and db with https" do + db = CouchRest.parse "https://127.0.0.1:5555/my-db" + db[:database].should == "my-db" + db[:host].should == "127.0.0.1:5555" + end it "should parse just a host" do db = CouchRest.parse "http://127.0.0.1:5555/" db[:database].should be_nil db[:host].should == "127.0.0.1:5555" end + it "should parse just a host with https" do + db = CouchRest.parse "https://127.0.0.1:5555/" + db[:database].should be_nil + db[:host].should == "127.0.0.1:5555" + end it "should parse just a host no slash" do db = CouchRest.parse "http://127.0.0.1:5555" db[:host].should == "127.0.0.1:5555" db[:database].should be_nil end + it "should parse just a host no slash and https" do + db = CouchRest.parse "https://127.0.0.1:5555" + db[:host].should == "127.0.0.1:5555" + db[:database].should be_nil + end it "should get docid" do db = CouchRest.parse "127.0.0.1:5555/my-db/my-doc" db[:database].should == "my-db" @@ -90,7 +105,12 @@ describe CouchRest do db[:host].should == "127.0.0.1:5555" db[:doc].should == "my-doc" end - + it "should get docid with https" do + db = CouchRest.parse "https://127.0.0.1:5555/my-db/my-doc" + db[:database].should == "my-db" + db[:host].should == "127.0.0.1:5555" + db[:doc].should == "my-doc" + end it "should parse a host and db" do db = CouchRest.parse "127.0.0.1/my-db" db[:database].should == "my-db" @@ -101,6 +121,11 @@ describe CouchRest do db[:database].should == "my-db" db[:host].should == "127.0.0.1" end + it "should parse a host and db with https" do + db = CouchRest.parse "https://127.0.0.1/my-db" + db[:database].should == "my-db" + db[:host].should == "127.0.0.1" + end it "should parse a host with a port and db" do db = CouchRest.parse "127.0.0.1:5555/my-db" db[:database].should == "my-db" @@ -111,16 +136,31 @@ describe CouchRest do db[:database].should == "my-db" db[:host].should == "127.0.0.1:5555" end + it "should parse a host with a port and db with https" do + db = CouchRest.parse "http://127.0.0.1:5555/my-db" + db[:database].should == "my-db" + db[:host].should == "127.0.0.1:5555" + end it "should parse just a host" do db = CouchRest.parse "http://127.0.0.1:5555/" db[:database].should be_nil db[:host].should == "127.0.0.1:5555" end + it "should parse just a host with https" do + db = CouchRest.parse "https://127.0.0.1:5555/" + db[:database].should be_nil + db[:host].should == "127.0.0.1:5555" + end it "should parse just a host no slash" do db = CouchRest.parse "http://127.0.0.1:5555" db[:host].should == "127.0.0.1:5555" db[:database].should be_nil end + it "should parse just a host no slash and https" do + db = CouchRest.parse "https://127.0.0.1:5555" + db[:host].should == "127.0.0.1:5555" + db[:database].should be_nil + end it "should get docid" do db = CouchRest.parse "127.0.0.1:5555/my-db/my-doc" db[:database].should == "my-db" @@ -133,6 +173,12 @@ describe CouchRest do db[:host].should == "127.0.0.1:5555" db[:doc].should == "my-doc" end + it "should get docid with https" do + db = CouchRest.parse "https://127.0.0.1:5555/my-db/my-doc" + db[:database].should == "my-db" + db[:host].should == "127.0.0.1:5555" + db[:doc].should == "my-doc" + end end describe "easy initializing a database adapter" do From 1c43a8f3d3734d807b50ff595e19a546f6ff1dbc Mon Sep 17 00:00:00 2001 From: Julien Sanchez Date: Thu, 8 Oct 2009 18:27:22 +0800 Subject: [PATCH 03/49] Streamer fixes * url must be quoted inside the curl command otherwise '&' between params are interpreted by shells * View url format updated * Streamer yielded the last row returned by curl ("}]") as nil * Specs --- lib/couchrest/helper/streamer.rb | 15 ++++++++--- spec/couchrest/core/database_spec.rb | 11 +++++++-- spec/couchrest/helpers/streamer_spec.rb | 33 +++++++++++++++++++++++-- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/lib/couchrest/helper/streamer.rb b/lib/couchrest/helper/streamer.rb index 6d48ca0..35eb824 100644 --- a/lib/couchrest/helper/streamer.rb +++ b/lib/couchrest/helper/streamer.rb @@ -7,15 +7,22 @@ module CouchRest # Stream a view, yielding one row at a time. Shells out to curl to keep RAM usage low when you have millions of rows. def view name, params = nil, &block - urlst = /^_/.match(name) ? "#{@db.root}/#{name}" : "#{@db.root}/_view/#{name}" + urlst = if /^_/.match(name) then + "#{@db.root}/#{name}" + else + name = name.split('/') + dname = name.shift + vname = name.join('/') + "#{@db.root}/_design/#{dname}/_view/#{vname}" + end url = CouchRest.paramify_url urlst, params # puts "stream #{url}" first = nil - IO.popen("curl --silent #{url}") do |view| + IO.popen("curl --silent \"#{url}\"") do |view| first = view.gets # discard header while line = view.gets row = parse_line(line) - block.call row + block.call row unless row.nil? # last line "}]" discarded end end parse_first(first) @@ -41,4 +48,4 @@ module CouchRest end end -end \ No newline at end of file +end diff --git a/spec/couchrest/core/database_spec.rb b/spec/couchrest/core/database_spec.rb index ff7982f..e315a6d 100644 --- a/spec/couchrest/core/database_spec.rb +++ b/spec/couchrest/core/database_spec.rb @@ -131,9 +131,16 @@ describe CouchRest::Database do rs = @db.view('first/test', :include_docs => true) do |row| rows << row end - rows.length.should == 4 + rows.length.should == 3 rs["total_rows"].should == 3 end + it "should accept a block with several params" do + rows = [] + rs = @db.view('first/test', :include_docs => true, :limit => 2) do |row| + rows << row + end + rows.length.should == 2 + end end describe "GET (document by id) when the doc exists" do @@ -711,4 +718,4 @@ describe CouchRest::Database do end -end \ No newline at end of file +end diff --git a/spec/couchrest/helpers/streamer_spec.rb b/spec/couchrest/helpers/streamer_spec.rb index cf828ca..48ec6c3 100644 --- a/spec/couchrest/helpers/streamer_spec.rb +++ b/spec/couchrest/helpers/streamer_spec.rb @@ -9,6 +9,14 @@ describe CouchRest::Streamer do @streamer = CouchRest::Streamer.new(@db) @docs = (1..1000).collect{|i| {:integer => i, :string => i.to_s}} @db.bulk_save(@docs) + @db.save_doc({ + "_id" => "_design/first", + :views => { + :test => { + :map => "function(doc){for(var w in doc){ if(!w.match(/^_/))emit(w,doc[w])}}" + } + } + }) end it "should yield each row in a view" do @@ -19,5 +27,26 @@ describe CouchRest::Streamer do end count.should == 1001 end - -end \ No newline at end of file + + it "should accept several params" do + count = 0 + @streamer.view("_design/first/_view/test", :include_docs => true, :limit => 5) do |row| + count += 1 + end + count.should == 5 + end + + it "should accept both view formats" do + count = 0 + @streamer.view("_design/first/_view/test") do |row| + count += 1 + end + count.should == 2000 + count = 0 + @streamer.view("first/test") do |row| + count += 1 + end + count.should == 2000 + end + +end From b5d6baaf11ff86b9fd9f4d20da815eff60e42234 Mon Sep 17 00:00:00 2001 From: Julien Sanchez Date: Mon, 12 Oct 2009 19:55:02 +0800 Subject: [PATCH 04/49] Save on Document & ExtendedDocument crashed if bulk - document#save expects to receive {"ok" => true} even with bulk mode - ExtendedDocument#save_without_callbacks reverted to previous code (expecting result["ok"] as in create_without_callbacks) --- lib/couchrest/core/database.rb | 2 +- lib/couchrest/more/extended_document.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/couchrest/core/database.rb b/lib/couchrest/core/database.rb index 2fb722a..96c4a0d 100644 --- a/lib/couchrest/core/database.rb +++ b/lib/couchrest/core/database.rb @@ -145,7 +145,7 @@ module CouchRest end if bulk @bulk_save_cache << doc - return bulk_save if @bulk_save_cache.length >= @bulk_save_cache_limit + bulk_save if @bulk_save_cache.length >= @bulk_save_cache_limit return {"ok" => true} # Compatibility with Document#save elsif !bulk && @bulk_save_cache.length > 0 bulk_save diff --git a/lib/couchrest/more/extended_document.rb b/lib/couchrest/more/extended_document.rb index 351c344..f33481d 100644 --- a/lib/couchrest/more/extended_document.rb +++ b/lib/couchrest/more/extended_document.rb @@ -238,7 +238,7 @@ module CouchRest set_unique_id if new? && self.respond_to?(:set_unique_id) result = database.save_doc(self, bulk) mark_as_saved - true + result["ok"] == true end # Saves the document to the db using save. Raises an exception From 164ef6bc9dab0f3e8a741fa316bfa174b1250288 Mon Sep 17 00:00:00 2001 From: Matt Aimonetti Date: Fri, 23 Oct 2009 15:47:20 -0200 Subject: [PATCH 05/49] fixed a encoding bug with ruby1.9 --- spec/couchrest/more/extended_doc_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/couchrest/more/extended_doc_spec.rb b/spec/couchrest/more/extended_doc_spec.rb index 75124b4..29d2ae1 100644 --- a/spec/couchrest/more/extended_doc_spec.rb +++ b/spec/couchrest/more/extended_doc_spec.rb @@ -1,3 +1,5 @@ +# encoding: utf-8 + require File.expand_path("../../../spec_helper", __FILE__) require File.join(FIXTURE_PATH, 'more', 'article') require File.join(FIXTURE_PATH, 'more', 'course') From d4010ad76e07c17984a57d2390521e591351b1e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tapaj=C3=B3s?= Date: Fri, 23 Oct 2009 23:42:48 -0200 Subject: [PATCH 06/49] I think that init_method is more semantic that send. --- README.md | 4 ++-- lib/couchrest/more/property.rb | 2 +- spec/fixtures/more/event.rb | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 73e9387..beb6d6c 100644 --- a/README.md +++ b/README.md @@ -149,8 +149,8 @@ you can define some casting rules. property :casted_attribute, :cast_as => 'WithCastedModelMixin' property :keywords, :cast_as => ["String"] - property :occurs_at, :cast_as => 'Time', :send => 'parse - property :end_date, :cast_as => 'Date', :send => 'parse + property :occurs_at, :cast_as => 'Time', :init_method => 'parse + property :end_date, :cast_as => 'Date', :init_method => 'parse If you want to cast an array of instances from a specific Class, use the trick shown above ["ClassName"] diff --git a/lib/couchrest/more/property.rb b/lib/couchrest/more/property.rb index dccc7dd..efede74 100644 --- a/lib/couchrest/more/property.rb +++ b/lib/couchrest/more/property.rb @@ -32,7 +32,7 @@ module CouchRest @alias = options.delete(:alias) if options[:alias] @default = options.delete(:default) unless options[:default].nil? @casted = options[:casted] ? true : false - @init_method = options[:send] ? options.delete(:send) : 'new' + @init_method = options[:init_method] ? options.delete(:init_method) : 'new' @options = options end diff --git a/spec/fixtures/more/event.rb b/spec/fixtures/more/event.rb index 988c5cd..97aa248 100644 --- a/spec/fixtures/more/event.rb +++ b/spec/fixtures/more/event.rb @@ -2,8 +2,8 @@ class Event < CouchRest::ExtendedDocument use_database DB property :subject - property :occurs_at, :cast_as => 'Time', :send => 'parse' - property :end_date, :cast_as => 'Date', :send => 'parse' + property :occurs_at, :cast_as => 'Time', :init_method => 'parse' + property :end_date, :cast_as => 'Date', :init_method => 'parse' end \ No newline at end of file From 58d621d3994cb77130b18c31d5469e42023315cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tapaj=C3=B3s?= Date: Fri, 30 Oct 2009 16:07:59 -0200 Subject: [PATCH 07/49] Changing some validations to be compatible with activemodel. --- lib/couchrest/validation/auto_validate.rb | 14 +++++--------- .../validators/confirmation_validator.rb | 14 +++++++++++--- .../validation/validators/format_validator.rb | 11 ++++++++--- .../validation/validators/length_validator.rb | 15 ++++++++++----- .../validation/validators/numeric_validator.rb | 7 ++++++- .../validators/required_field_validator.rb | 11 ++++++++--- spec/couchrest/more/casted_model_spec.rb | 2 +- spec/couchrest/more/extended_doc_spec.rb | 2 +- spec/fixtures/more/card.rb | 2 +- spec/fixtures/more/cat.rb | 2 +- spec/fixtures/more/invoice.rb | 4 ++-- 11 files changed, 54 insertions(+), 30 deletions(-) diff --git a/lib/couchrest/validation/auto_validate.rb b/lib/couchrest/validation/auto_validate.rb index 4b1b925..d277bb3 100644 --- a/lib/couchrest/validation/auto_validate.rb +++ b/lib/couchrest/validation/auto_validate.rb @@ -65,7 +65,7 @@ module CouchRest # validator to be automatically created on the property # # Integer type - # Using a Integer type causes a validates_is_number + # Using a Integer type causes a validates_numericality_of # validator to be created for the property. integer_only # is set to true # @@ -97,8 +97,7 @@ module CouchRest # presence if opts[:allow_nil] == false - # validates_present property.name, opts - validates_present property.name, options_with_message(opts, property, :presence) + validates_presence_of property.name, options_with_message(opts, property, :presence) end # length @@ -111,8 +110,7 @@ module CouchRest else opts[:maximum] = len end - # validates_length property.name, opts - validates_length property.name, options_with_message(opts, property, :length) + validates_length_of property.name, options_with_message(opts, property, :length) end # format @@ -142,13 +140,11 @@ module CouchRest # numeric validator if "Integer" == property.type opts[:integer_only] = true - # validates_is_number property.name, opts - validates_is_number property.name, options_with_message(opts, property, :is_number) + validates_numericality_of property.name, options_with_message(opts, property, :is_number) elsif Float == property.type opts[:precision] = property.precision opts[:scale] = property.scale - # validates_is_number property.name, opts - validates_is_number property.name, options_with_message(opts, property, :is_number) + validates_numericality_of property.name, options_with_message(opts, property, :is_number) end # marked the property has checked diff --git a/lib/couchrest/validation/validators/confirmation_validator.rb b/lib/couchrest/validation/validators/confirmation_validator.rb index d5fdff1..2a5c587 100644 --- a/lib/couchrest/validation/validators/confirmation_validator.rb +++ b/lib/couchrest/validation/validators/confirmation_validator.rb @@ -81,18 +81,26 @@ module CouchRest # attr_accessor :password_confirmation # attr_accessor :email_repeated # - # validates_is_confirmed :password - # validates_is_confirmed :email, :confirm => :email_repeated + # validates_confirmation_of :password + # validates_confirmation_of :email, :confirm => :email_repeated # # # a call to valid? will return false unless: # # password == password_confirmation # # and # # email == email_repeated # - def validates_is_confirmed(*fields) + def validates_confirmation_of(*fields) opts = opts_from_validator_args(fields) add_validator_to_context(opts, fields, CouchRest::Validation::ConfirmationValidator) end + + def validates_is_confirmed(*fields) + warn "[DEPRECATION] `validates_is_confirmed` is deprecated. Please use `validates_confirmation_of` instead." + validates_confirmation_of(*fields) + end + + + end # module ValidatesIsConfirmed end # module Validation diff --git a/lib/couchrest/validation/validators/format_validator.rb b/lib/couchrest/validation/validators/format_validator.rb index eaeb1e1..fa61212 100644 --- a/lib/couchrest/validation/validators/format_validator.rb +++ b/lib/couchrest/validation/validators/format_validator.rb @@ -99,18 +99,23 @@ module CouchRest # property :email, String # property :zip_code, String # - # validates_format :email, :as => :email_address - # validates_format :zip_code, :with => /^\d{5}$/ + # validates_format_of :email, :as => :email_address + # validates_format_of :zip_code, :with => /^\d{5}$/ # # # a call to valid? will return false unless: # # email is formatted like an email address # # and # # zip_code is a string of 5 digits # - def validates_format(*fields) + def validates_format_of(*fields) opts = opts_from_validator_args(fields) add_validator_to_context(opts, fields, CouchRest::Validation::FormatValidator) end + + def validates_format(*fields) + warn "[DEPRECATION] `validates_format` is deprecated. Please use `validates_format_of` instead." + validates_format_of(*fields) + end end # module ValidatesFormat end # module Validation diff --git a/lib/couchrest/validation/validators/length_validator.rb b/lib/couchrest/validation/validators/length_validator.rb index 272b479..ec80dff 100644 --- a/lib/couchrest/validation/validators/length_validator.rb +++ b/lib/couchrest/validation/validators/length_validator.rb @@ -115,20 +115,25 @@ module CouchRest # property low, Integer # property just_right, Integer # - # validates_length :high, :min => 100000000000 - # validates_length :low, :equals => 0 - # validates_length :just_right, :within => 1..10 + # validates_length_of :high, :min => 100000000000 + # validates_length_of :low, :equals => 0 + # validates_length_of :just_right, :within => 1..10 # # # a call to valid? will return false unless: # # high is greater than or equal to 100000000000 # # low is equal to 0 # # just_right is between 1 and 10 (inclusive of both 1 and 10) # - def validates_length(*fields) + def validates_length_of(*fields) opts = opts_from_validator_args(fields) add_validator_to_context(opts, fields, CouchRest::Validation::LengthValidator) end - + + def validates_length(*fields) + warn "[DEPRECATION] `validates_length` is deprecated. Please use `validates_length_of` instead." + validates_length_of(*fields) + end + end # module ValidatesLength end # module Validation end # module CouchRest diff --git a/lib/couchrest/validation/validators/numeric_validator.rb b/lib/couchrest/validation/validators/numeric_validator.rb index 05269b3..a27e274 100644 --- a/lib/couchrest/validation/validators/numeric_validator.rb +++ b/lib/couchrest/validation/validators/numeric_validator.rb @@ -94,10 +94,15 @@ module CouchRest # Validate whether a field is numeric # - def validates_is_number(*fields) + def validates_numericality_of(*fields) opts = opts_from_validator_args(fields) add_validator_to_context(opts, fields, CouchRest::Validation::NumericValidator) end + + def validates_is_number(*fields) + warn "[DEPRECATION] `validates_is_number` is deprecated. Please use `validates_numericality_of` instead." + validates_numericality_of(*fields) + end end # module ValidatesIsNumber end # module Validation diff --git a/lib/couchrest/validation/validators/required_field_validator.rb b/lib/couchrest/validation/validators/required_field_validator.rb index 17e3132..d8edd81 100644 --- a/lib/couchrest/validation/validators/required_field_validator.rb +++ b/lib/couchrest/validation/validators/required_field_validator.rb @@ -93,16 +93,21 @@ module CouchRest # property :another_required, String # property :yet_again, String # - # validates_present :required_attribute - # validates_present :another_required, :yet_again + # validates_presence_of :required_attribute + # validates_presence_of :another_required, :yet_again # # # a call to valid? will return false unless # # all three attributes are !blank? # end - def validates_present(*fields) + def validates_presence_of(*fields) opts = opts_from_validator_args(fields) add_validator_to_context(opts, fields, CouchRest::Validation::RequiredFieldValidator) end + + def validates_present(*fields) + warn "[DEPRECATION] `validates_present` is deprecated. Please use `validates_presence_of` instead." + validates_presence_of(*fields) + end end # module ValidatesPresent end # module Validation diff --git a/spec/couchrest/more/casted_model_spec.rb b/spec/couchrest/more/casted_model_spec.rb index 04ab737..a2a8a96 100644 --- a/spec/couchrest/more/casted_model_spec.rb +++ b/spec/couchrest/more/casted_model_spec.rb @@ -224,7 +224,7 @@ describe CouchRest::CastedModel do it "should not fail if the casted model doesn't have validation" do Cat.property :masters, :cast_as => ['Person'], :default => [] - Cat.validates_present :name + Cat.validates_presence_of :name cat = Cat.new(:name => 'kitty') cat.should be_valid cat.masters.push Person.new diff --git a/spec/couchrest/more/extended_doc_spec.rb b/spec/couchrest/more/extended_doc_spec.rb index 29d2ae1..1f91dcf 100644 --- a/spec/couchrest/more/extended_doc_spec.rb +++ b/spec/couchrest/more/extended_doc_spec.rb @@ -730,7 +730,7 @@ describe "ExtendedDocument" do it "should not fail if the nested casted model doesn't have validation" do Cat.property :trainer, :cast_as => 'Person' - Cat.validates_present :name + Cat.validates_presence_of :name cat = Cat.new(:name => 'Mr Bigglesworth') cat.trainer = Person.new cat.trainer.validatable?.should be_false diff --git a/spec/fixtures/more/card.rb b/spec/fixtures/more/card.rb index 484ba23..7365166 100644 --- a/spec/fixtures/more/card.rb +++ b/spec/fixtures/more/card.rb @@ -17,6 +17,6 @@ class Card < CouchRest::ExtendedDocument timestamps! # Validation - validates_present :first_name + validates_presence_of :first_name end \ No newline at end of file diff --git a/spec/fixtures/more/cat.rb b/spec/fixtures/more/cat.rb index a3cb054..2e40f85 100644 --- a/spec/fixtures/more/cat.rb +++ b/spec/fixtures/more/cat.rb @@ -15,5 +15,5 @@ class CatToy < Hash property :name - validates_present :name + validates_presence_of :name end \ No newline at end of file diff --git a/spec/fixtures/more/invoice.rb b/spec/fixtures/more/invoice.rb index 273e6f2..0666e5c 100644 --- a/spec/fixtures/more/invoice.rb +++ b/spec/fixtures/more/invoice.rb @@ -11,7 +11,7 @@ class Invoice < CouchRest::ExtendedDocument property :location # Validation - validates_present :client_name, :employee_name - validates_present :location, :message => "Hey stupid!, you forgot the location" + validates_presence_of :client_name, :employee_name + validates_presence_of :location, :message => "Hey stupid!, you forgot the location" end \ No newline at end of file From b5d09afef5e8614f25da8ee79a5abd55e0237b2b Mon Sep 17 00:00:00 2001 From: Will Leinweber Date: Sat, 26 Sep 2009 18:24:26 -0500 Subject: [PATCH 08/49] Adds attribute protection to properties Public Facing * through either :protected or :accessible8 flags * prevents protected attributes from being set in mass assignment Developer Facing * refactors #initialize and #update_attribute_without_saving to use same private methods to set attributes on ExtendedDocument * adds new mixin to do protection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tapajós --- .gitignore | 1 + lib/couchrest/mixins/attribute_protection.rb | 74 +++++++++++++++ .../mixins/extended_document_mixins.rb | 1 + lib/couchrest/more/extended_document.rb | 33 ++++--- .../more/attribute_protection_spec.rb | 94 +++++++++++++++++++ 5 files changed, 191 insertions(+), 12 deletions(-) create mode 100644 lib/couchrest/mixins/attribute_protection.rb create mode 100644 spec/couchrest/more/attribute_protection_spec.rb diff --git a/.gitignore b/.gitignore index b07b413..2318685 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .DS_Store html/* pkg +*.swp diff --git a/lib/couchrest/mixins/attribute_protection.rb b/lib/couchrest/mixins/attribute_protection.rb new file mode 100644 index 0000000..4eb04a0 --- /dev/null +++ b/lib/couchrest/mixins/attribute_protection.rb @@ -0,0 +1,74 @@ +module CouchRest + module Mixins + module AttributeProtection + # Attribute protection from mass assignment to CouchRest properties + # + # Protected methods will be removed from + # * new + # * update_attributes + # * upate_attributes_without_saving + # * attributes= + # + # There are two modes of protection + # 1) Declare accessible poperties, assume all the rest are protected + # property :name, :accessible => true + # property :admin # this will be automatically protected + # + # 2) Declare protected properties, assume all the rest are accessible + # property :name # this will not be protected + # property :admin, :protected => true + # + # Note: you cannot set both flags in a single class + + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + def accessible_properties + properties.select { |prop| prop.options[:accessible] } + end + + def protected_properties + properties.select { |prop| prop.options[:protected] } + end + end + + def accessible_properties + self.class.accessible_properties + end + + def protected_properties + self.class.protected_properties + end + + def remove_protected_attributes(attributes) + protected_names = properties_to_remove_from_mass_assignment.map { |prop| prop.name } + return attributes if protected_names.empty? + + attributes.reject! do |key, value| + protected_names.include?(key.to_s) + end + + attributes || {} + end + + private + + def properties_to_remove_from_mass_assignment + has_protected = !protected_properties.empty? + has_accessible = !accessible_properties.empty? + + if !has_protected && !has_accessible + [] + elsif has_protected && !has_accessible + protected_properties + elsif has_accessible && !has_protected + properties.reject { |prop| prop.options[:accessible] } + else + raise "Set either :accessible or :protected for #{self.class}, but not both" + end + end + end + end +end diff --git a/lib/couchrest/mixins/extended_document_mixins.rb b/lib/couchrest/mixins/extended_document_mixins.rb index 89b25d6..89d650a 100644 --- a/lib/couchrest/mixins/extended_document_mixins.rb +++ b/lib/couchrest/mixins/extended_document_mixins.rb @@ -6,3 +6,4 @@ require File.join(File.dirname(__FILE__), 'validation') require File.join(File.dirname(__FILE__), 'extended_attachments') require File.join(File.dirname(__FILE__), 'class_proxy') require File.join(File.dirname(__FILE__), 'collection') +require File.join(File.dirname(__FILE__), 'attribute_protection') diff --git a/lib/couchrest/more/extended_document.rb b/lib/couchrest/more/extended_document.rb index f33481d..ddb0c0c 100644 --- a/lib/couchrest/more/extended_document.rb +++ b/lib/couchrest/more/extended_document.rb @@ -14,6 +14,7 @@ module CouchRest include CouchRest::Mixins::ExtendedAttachments include CouchRest::Mixins::ClassProxy include CouchRest::Mixins::Collection + include CouchRest::Mixins::AttributeProtection def self.subclasses @subclasses ||= [] @@ -40,11 +41,7 @@ module CouchRest def initialize(passed_keys={}) apply_defaults # defined in CouchRest::Mixins::Properties - passed_keys.each do |k,v| - if self.respond_to?("#{k}=") - self.send("#{k}=", passed_keys.delete(k)) - end - end if passed_keys + set_attributes(passed_keys) super cast_keys # defined in CouchRest::Mixins::Properties unless self['_id'] && self['_rev'] @@ -150,12 +147,8 @@ module CouchRest # make a copy, we don't want to change arguments attrs = hash.dup %w[_id _rev created_at updated_at].each {|attr| attrs.delete(attr)} - attrs.each do |k, v| - raise NoMethodError, "#{k}= method not available, use property :#{k}" unless self.respond_to?("#{k}=") - end - attrs.each do |k, v| - self.send("#{k}=",v) - end + check_properties_exist(attrs) + set_attributes(attrs) end alias :attributes= :update_attributes_without_saving @@ -280,6 +273,22 @@ module CouchRest end end end - + + private + + def check_properties_exist(attrs) + attrs.each do |k, v| + raise NoMethodError, "#{k}= method not available, use property :#{k}" unless self.respond_to?("#{k}=") + end + end + + def set_attributes(hash) + attrs = remove_protected_attributes(hash) + attrs.each do |k,v| + if self.respond_to?("#{k}=") + self.send("#{k}=", attrs.delete(k)) + end + end + end end end diff --git a/spec/couchrest/more/attribute_protection_spec.rb b/spec/couchrest/more/attribute_protection_spec.rb new file mode 100644 index 0000000..3063531 --- /dev/null +++ b/spec/couchrest/more/attribute_protection_spec.rb @@ -0,0 +1,94 @@ +require File.expand_path("../../../spec_helper", __FILE__) + +describe "ExtendedDocument", "no declarations" do + class NoProtection < CouchRest::ExtendedDocument + use_database TEST_SERVER.default_database + property :name + property :phone + end + + it "should not protect anything through new" do + user = NoProtection.new(:name => "will", :phone => "555-5555") + + user.name.should == "will" + user.phone.should == "555-5555" + end + + it "should not protect anything through attributes=" do + user = NoProtection.new + user.attributes = {:name => "will", :phone => "555-5555"} + + user.name.should == "will" + user.phone.should == "555-5555" + end +end + +describe "ExtendedDocument", "accessible flag" do + class WithAccessible < CouchRest::ExtendedDocument + use_database TEST_SERVER.default_database + property :name, :accessible => true + property :admin, :default => false + end + + it "should recognize accessible properties" do + props = WithAccessible.accessible_properties.map { |prop| prop.name} + props.should include("name") + props.should_not include("admin") + end + + it "should protect non-accessible properties set through new" do + user = WithAccessible.new(:name => "will", :admin => true) + + user.name.should == "will" + user.admin.should == false + end + + it "should protect non-accessible properties set through attributes=" do + user = WithAccessible.new + user.attributes = {:name => "will", :admin => true} + + user.name.should == "will" + user.admin.should == false + end +end + +describe "ExtendedDocument", "protected flag" do + class WithProtected < CouchRest::ExtendedDocument + use_database TEST_SERVER.default_database + property :name + property :admin, :default => false, :protected => true + end + + it "should recognize protected properties" do + props = WithProtected.protected_properties.map { |prop| prop.name} + props.should_not include("name") + props.should include("admin") + end + + it "should protect non-accessible properties set through new" do + user = WithProtected.new(:name => "will", :admin => true) + + user.name.should == "will" + user.admin.should == false + end + + it "should protect non-accessible properties set through attributes=" do + user = WithProtected.new + user.attributes = {:name => "will", :admin => true} + + user.name.should == "will" + user.admin.should == false + end +end + +describe "ExtendedDocument", "protected flag" do + class WithBoth < CouchRest::ExtendedDocument + use_database TEST_SERVER.default_database + property :name, :accessible => true + property :admin, :default => false, :protected => true + end + + it "should raise an error when both are set" do + lambda { WithBoth.new }.should raise_error + end +end From 0ed741370350ee98d4a0c9d37280429cbd15f942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tapaj=C3=B3s?= Date: Sat, 31 Oct 2009 10:40:32 -0200 Subject: [PATCH 09/49] Adding tests to commit b5d09afef5e8614f25da8ee79a5abd55e0237b2b --- spec/couchrest/more/property_spec.rb | 26 ++++++++++++++++++++++++++ spec/fixtures/more/cat.rb | 7 ++++--- spec/fixtures/more/user.rb | 22 ++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 spec/fixtures/more/user.rb diff --git a/spec/couchrest/more/property_spec.rb b/spec/couchrest/more/property_spec.rb index 8df2867..0e25916 100644 --- a/spec/couchrest/more/property_spec.rb +++ b/spec/couchrest/more/property_spec.rb @@ -1,3 +1,4 @@ +# encoding: utf-8 require File.expand_path('../../../spec_helper', __FILE__) require File.join(FIXTURE_PATH, 'more', 'person') require File.join(FIXTURE_PATH, 'more', 'card') @@ -5,6 +6,7 @@ require File.join(FIXTURE_PATH, 'more', 'invoice') require File.join(FIXTURE_PATH, 'more', 'service') require File.join(FIXTURE_PATH, 'more', 'event') require File.join(FIXTURE_PATH, 'more', 'cat') +require File.join(FIXTURE_PATH, 'more', 'user') describe "ExtendedDocument properties" do @@ -55,6 +57,30 @@ describe "ExtendedDocument properties" do @card.updated_at.should_not be_nil end + + describe "mass assignment protection" do + + it "should not store protected attribute using mass assignment" do + cat_toy = CatToy.new(:name => "Zorro") + cat = Cat.create(:name => "Helena", :toys => [cat_toy], :favorite_toy => cat_toy, :number => 1) + cat.number.should be_nil + cat.number = 1 + cat.save + cat.number.should == 1 + end + + it "should not store protected attribute when 'declare accessible poperties, assume all the rest are protected'" do + user = User.create(:name => "Marcos Tapajós", :admin => true) + user.admin.should be_nil + end + + it "should not store protected attribute when 'declare protected properties, assume all the rest are accessible'" do + user = SpecialUser.create(:name => "Marcos Tapajós", :admin => true) + user.admin.should be_nil + end + + end + describe "validation" do before(:each) do @invoice = Invoice.new(:client_name => "matt", :employee_name => "Chris", :location => "San Diego, CA") diff --git a/spec/fixtures/more/cat.rb b/spec/fixtures/more/cat.rb index 2e40f85..68fcb43 100644 --- a/spec/fixtures/more/cat.rb +++ b/spec/fixtures/more/cat.rb @@ -4,9 +4,10 @@ class Cat < CouchRest::ExtendedDocument # Set the default database to use use_database DB - property :name - property :toys, :cast_as => ['CatToy'], :default => [] - property :favorite_toy, :cast_as => 'CatToy' + property :name, :accessible => true + property :toys, :cast_as => ['CatToy'], :default => [], :accessible => true + property :favorite_toy, :cast_as => 'CatToy', :accessible => true + property :number end class CatToy < Hash diff --git a/spec/fixtures/more/user.rb b/spec/fixtures/more/user.rb new file mode 100644 index 0000000..f9bbf97 --- /dev/null +++ b/spec/fixtures/more/user.rb @@ -0,0 +1,22 @@ +class User < CouchRest::ExtendedDocument + # Set the default database to use + use_database DB + property :name, :accessible => true + property :admin # this will be automatically protected +end + +class SpecialUser < CouchRest::ExtendedDocument + # Set the default database to use + use_database DB + property :name # this will not be protected + property :admin, :protected => true +end + +# There are two modes of protection +# 1) Declare accessible poperties, assume all the rest are protected +# property :name, :accessible => true +# property :admin # this will be automatically protected +# +# 2) Declare protected properties, assume all the rest are accessible +# property :name # this will not be protected +# property :admin, :protected => true From 1315d65681a3e3b9321990f2ec186fa1b5aec40f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tapaj=C3=B3s?= Date: Sat, 31 Oct 2009 10:42:36 -0200 Subject: [PATCH 10/49] Using |attribute_name, attribute_value| instead of |k,v|. --- lib/couchrest/more/extended_document.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/couchrest/more/extended_document.rb b/lib/couchrest/more/extended_document.rb index ddb0c0c..cce0d89 100644 --- a/lib/couchrest/more/extended_document.rb +++ b/lib/couchrest/more/extended_document.rb @@ -277,16 +277,16 @@ module CouchRest private def check_properties_exist(attrs) - attrs.each do |k, v| - raise NoMethodError, "#{k}= method not available, use property :#{k}" unless self.respond_to?("#{k}=") + attrs.each do |attribute_name, attribute_value| + raise NoMethodError, "#{attribute_name}= method not available, use property :#{attribute_name}" unless self.respond_to?("#{attribute_name}=") end end def set_attributes(hash) attrs = remove_protected_attributes(hash) - attrs.each do |k,v| - if self.respond_to?("#{k}=") - self.send("#{k}=", attrs.delete(k)) + attrs.each do |attribute_name, attribute_value| + if self.respond_to?("#{attribute_name}=") + self.send("#{attribute_name}=", attrs.delete(attribute_name)) end end end From ca2faa81fe166a0391b4724acf5cee1d359fe266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tapaj=C3=B3s?= Date: Sat, 31 Oct 2009 10:49:26 -0200 Subject: [PATCH 11/49] Using |property_name, property_value| instead of |key, value|. --- lib/couchrest/mixins/attribute_protection.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/couchrest/mixins/attribute_protection.rb b/lib/couchrest/mixins/attribute_protection.rb index 4eb04a0..b2efc53 100644 --- a/lib/couchrest/mixins/attribute_protection.rb +++ b/lib/couchrest/mixins/attribute_protection.rb @@ -46,8 +46,8 @@ module CouchRest protected_names = properties_to_remove_from_mass_assignment.map { |prop| prop.name } return attributes if protected_names.empty? - attributes.reject! do |key, value| - protected_names.include?(key.to_s) + attributes.reject! do |property_name, property_value| + protected_names.include?(property_name.to_s) end attributes || {} From 40bef6cafb9769652c1b19e4964fe1246abf663a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tapaj=C3=B3s?= Date: Sun, 1 Nov 2009 10:47:48 -0200 Subject: [PATCH 12/49] Updating README --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index beb6d6c..3b383b0 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,18 @@ Check the mixin or the ExtendedDocument class to see how to implement your own c property :read_only_value, :read_only => true property :name, :length => 4...20 property :price, :type => Integer + +Attribute protection from mass assignment to CouchRest properties. There are two modes of protection: + +1) Declare accessible poperties, assume all the rest are protected + property :name, :accessible => true + property :admin # this will be automatically protected + +2) Declare protected properties, assume all the rest are accessible + property :name # this will not be protected + property :admin, :protected => true + +Note: you cannot set both flags in a single class ### Casting From b8bda3bb02b6958e93bdd39845afe378a5bc9984 Mon Sep 17 00:00:00 2001 From: deepj Date: Tue, 10 Nov 2009 03:23:55 +0100 Subject: [PATCH 13/49] Initialization of ExtendentDocument model shouldn't failed on a nil value in argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tapajós --- lib/couchrest/more/extended_document.rb | 2 +- spec/couchrest/more/extended_doc_spec.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/couchrest/more/extended_document.rb b/lib/couchrest/more/extended_document.rb index cce0d89..73c1b17 100644 --- a/lib/couchrest/more/extended_document.rb +++ b/lib/couchrest/more/extended_document.rb @@ -41,7 +41,7 @@ module CouchRest def initialize(passed_keys={}) apply_defaults # defined in CouchRest::Mixins::Properties - set_attributes(passed_keys) + set_attributes(passed_keys) unless passed_keys.nil? super cast_keys # defined in CouchRest::Mixins::Properties unless self['_id'] && self['_rev'] diff --git a/spec/couchrest/more/extended_doc_spec.rb b/spec/couchrest/more/extended_doc_spec.rb index 1f91dcf..f055576 100644 --- a/spec/couchrest/more/extended_doc_spec.rb +++ b/spec/couchrest/more/extended_doc_spec.rb @@ -129,6 +129,11 @@ describe "ExtendedDocument" do @obj.should be_new_document @obj.should be_new_record end + + it "should not failed on a nil value in argument" do + @obj = Basic.new(nil) + @obj.should == { 'couchrest-type' => 'Basic' } + end end describe "creating a new document" do From 62ea68df63e51cc13bf521f134d57967c92c8741 Mon Sep 17 00:00:00 2001 From: Igal Koshevoy Date: Sun, 15 Nov 2009 23:14:09 -0800 Subject: [PATCH 14/49] Improved CouchRest::Database#save_doc, added "batch" mode to significantly speed up saves at cost of lower durability gurantees. --- lib/couchrest/core/database.rb | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/couchrest/core/database.rb b/lib/couchrest/core/database.rb index 96c4a0d..7ddd16a 100644 --- a/lib/couchrest/core/database.rb +++ b/lib/couchrest/core/database.rb @@ -129,7 +129,7 @@ module CouchRest end end end - + # Save a document to CouchDB. This will use the _id field from # the document as the id for PUT, or request a new UUID from CouchDB, if # no _id is present on the document. IDs are attached to @@ -139,7 +139,19 @@ module CouchRest # # If bulk is true (false by default) the document is cached for bulk-saving later. # Bulk saving happens automatically when #bulk_save_cache limit is exceded, or on the next non bulk save. - def save_doc(doc, bulk = false) + # + # If batch is true (false by default) the document is saved in + # batch mode, "used to achieve higher throughput at the cost of lower + # guarantees. When [...] sent using this option, it is not immediately + # written to disk. Instead it is stored in memory on a per-user basis for a + # second or so (or the number of docs in memory reaches a certain point). + # After the threshold has passed, the docs are committed to disk. Instead + # of waiting for the doc to be written to disk before responding, CouchDB + # sends an HTTP 202 Accepted response immediately. batch=ok is not suitable + # for crucial data, but it ideal for applications like logging which can + # accept the risk that a small proportion of updates could be lost due to a + # crash." + def save_doc(doc, bulk = false, batch = false) if doc['_attachments'] doc['_attachments'] = encode_attachments(doc['_attachments']) end @@ -153,7 +165,9 @@ module CouchRest result = if doc['_id'] slug = escape_docid(doc['_id']) begin - CouchRest.put "#{@root}/#{slug}", doc + uri = "#{@root}/#{slug}" + uri << "?batch=ok" if batch + CouchRest.put uri, doc rescue HttpAbstraction::ResourceNotFound p "resource not found when saving even tho an id was passed" slug = doc['_id'] = @server.next_uuid From d0d5eec199b0ccedb2935877be8e5cdf687b047b Mon Sep 17 00:00:00 2001 From: Igal Koshevoy Date: Sun, 15 Nov 2009 23:15:20 -0800 Subject: [PATCH 15/49] Added CouchRest::Database#bulk_save_doc and #batch_save_doc as human-friendlier wrappers around #save_doc. --- lib/couchrest/core/database.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/couchrest/core/database.rb b/lib/couchrest/core/database.rb index 7ddd16a..547e35c 100644 --- a/lib/couchrest/core/database.rb +++ b/lib/couchrest/core/database.rb @@ -189,6 +189,15 @@ module CouchRest result end + # Save a document to CouchDB in bulk mode. See #save_doc's +bulk+ argument. + def bulk_save_doc(doc) + save_doc(doc, true) + end + + # Save a document to CouchDB in batch mode. See #save_doc's +batch+ argument. + def batch_save_doc(doc) + save_doc(doc, false, true) + end # POST an array of documents to CouchDB. If any of the documents are # missing ids, supply one from the uuid cache. From b55a3ec0d367fde3dfdf40dffbda3d103b1d5787 Mon Sep 17 00:00:00 2001 From: Chris Anderson Date: Sun, 20 Dec 2009 11:07:53 -0800 Subject: [PATCH 16/49] fix regexp escaping issue in test fixture view --- spec/couchrest/core/database_spec.rb | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/spec/couchrest/core/database_spec.rb b/spec/couchrest/core/database_spec.rb index e315a6d..afcda6e 100644 --- a/spec/couchrest/core/database_spec.rb +++ b/spec/couchrest/core/database_spec.rb @@ -65,22 +65,27 @@ describe CouchRest::Database do describe "saving a view" do before(:each) do - @view = {'test' => {'map' => 'function(doc) { - if (doc.word && !/\W/.test(doc.word)) { - emit(doc.word,null); + @view = {'test' => {'map' => <<-JS + function(doc) { + var reg = new RegExp("\\\\W"); + if (doc.word && !reg.test(doc.word)) { + emit(doc.word,null); + } } - }'}} + JS + }} @db.save_doc({ "_id" => "_design/test", :views => @view }) end it "should work properly" do - @db.bulk_save([ + r = @db.bulk_save([ {"word" => "once"}, {"word" => "and again"} ]) - @db.view('test/test')['total_rows'].should == 1 + r = @db.view('test/test') + r['total_rows'].should == 1 end it "should round trip" do @db.get("_design/test")['views'].should == @view From 118c944b852458135003c8dcc2c9ddd92ba48011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tapaj=C3=B3s?= Date: Sat, 21 Nov 2009 09:17:38 -0200 Subject: [PATCH 17/49] Fixing typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b383b0..fa8952b 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ Check the mixin or the ExtendedDocument class to see how to implement your own c Attribute protection from mass assignment to CouchRest properties. There are two modes of protection: -1) Declare accessible poperties, assume all the rest are protected +1) Declare accessible properties, assume all the rest are protected property :name, :accessible => true property :admin # this will be automatically protected From 08cf9e59b68b6a0ec63c892e91519a6a16b15d47 Mon Sep 17 00:00:00 2001 From: Sam Lown iMac Date: Mon, 28 Sep 2009 21:42:17 +0200 Subject: [PATCH 18/49] Fixing incorrectly generated document URIs with testing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcos Tapajós --- lib/couchrest/core/document.rb | 2 +- spec/couchrest/core/document_spec.rb | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/couchrest/core/document.rb b/lib/couchrest/core/document.rb index cd66285..4b79206 100644 --- a/lib/couchrest/core/document.rb +++ b/lib/couchrest/core/document.rb @@ -65,7 +65,7 @@ module CouchRest # Returns the CouchDB uri for the document def uri(append_rev = false) return nil if new? - couch_uri = "http://#{database.root}/#{CGI.escape(id)}" + couch_uri = "#{database.root}/#{CGI.escape(id)}" if append_rev == true couch_uri << "?rev=#{rev}" elsif append_rev.kind_of?(Integer) diff --git a/spec/couchrest/core/document_spec.rb b/spec/couchrest/core/document_spec.rb index 287ee79..2241598 100644 --- a/spec/couchrest/core/document_spec.rb +++ b/spec/couchrest/core/document_spec.rb @@ -83,6 +83,14 @@ describe CouchRest::Document do @doc.id.should == @resp["id"] @doc.rev.should == @resp["rev"] end + it "should generate a correct URI" do + @doc.uri.should == "#{@db.root}/#{@doc.id}" + URI.parse(@doc.uri).to_s.should == @doc.uri + end + it "should generate a correct URI with revision" do + @doc.uri(true).should == "#{@db.root}/#{@doc.id}?rev=#{@doc.rev}" + URI.parse(@doc.uri(true)).to_s.should == @doc.uri(true) + end end describe "bulk saving" do @@ -264,4 +272,4 @@ describe "dealing with attachments" do end end -end \ No newline at end of file +end From 4a8015b119303ee2e5b1abd31d76c9cb6f5406a6 Mon Sep 17 00:00:00 2001 From: John Wood Date: Thu, 7 Jan 2010 08:51:06 -0600 Subject: [PATCH 19/49] Move the provides_collection declaration into the Article test fixture, and out of the test, fixing a test case that was failing in ruby 1.9. Signed-off-by: Will Leinweber --- spec/couchrest/more/extended_doc_view_spec.rb | 4 ---- spec/fixtures/more/article.rb | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/spec/couchrest/more/extended_doc_view_spec.rb b/spec/couchrest/more/extended_doc_view_spec.rb index de14f63..bf75f2d 100644 --- a/spec/couchrest/more/extended_doc_view_spec.rb +++ b/spec/couchrest/more/extended_doc_view_spec.rb @@ -408,10 +408,6 @@ describe "ExtendedDocument views" do end end it "should provide a class method to get a collection for a view" do - class Article - provides_collection :article_details, 'Article', 'by_date', :descending => true, :include_docs => true - end - articles = Article.find_all_article_details(:key => Date.today) articles.class.should == Array articles.size.should == 7 diff --git a/spec/fixtures/more/article.rb b/spec/fixtures/more/article.rb index 6f4bb7a..dbc9e8c 100644 --- a/spec/fixtures/more/article.rb +++ b/spec/fixtures/more/article.rb @@ -2,6 +2,7 @@ class Article < CouchRest::ExtendedDocument use_database DB unique_id :slug + provides_collection :article_details, 'Article', 'by_date', :descending => true, :include_docs => true view_by :date, :descending => true view_by :user_id, :date @@ -31,4 +32,4 @@ class Article < CouchRest::ExtendedDocument def generate_slug_from_title self['slug'] = title.downcase.gsub(/[^a-z0-9]/,'-').squeeze('-').gsub(/^\-|\-$/,'') if new? end -end \ No newline at end of file +end From 6702574b32b134f63d9a689f7d065e3200c8425b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Wed, 13 Jan 2010 21:07:59 -0200 Subject: [PATCH 20/49] moving all examples to wiki --- README.md | 164 +----------------------------------------------------- 1 file changed, 2 insertions(+), 162 deletions(-) diff --git a/README.md b/README.md index fa8952b..a1a9e29 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,6 @@ Note: CouchRest only support CouchDB 0.9.0 or newer. $ sudo gem install couchrest -Alternatively, you can install from Github: - - $ gem sources -a http://gems.github.com (you only have to do this once) - $ sudo gem install couchrest-couchrest - ### Relax, it's RESTful CouchRest rests on top of a HTTP abstraction layer using by default Heroku’s excellent REST Client Ruby HTTP wrapper. @@ -30,164 +25,9 @@ The most complete documentation is the spec/ directory. To validate your CouchRest install, from the project root directory run `rake`, or `autotest` (requires RSpec and optionally ZenTest for autotest support). -## Examples (CouchRest Core) +### Docs -Quick Start: - - # with !, it creates the database if it doesn't already exist - @db = CouchRest.database!("http://127.0.0.1:5984/couchrest-test") - response = @db.save_doc({:key => 'value', 'another key' => 'another value'}) - doc = @db.get(response['id']) - puts doc.inspect - -Bulk Save: - - @db.bulk_save([ - {"wild" => "and random"}, - {"mild" => "yet local"}, - {"another" => ["set","of","keys"]} - ]) - # returns ids and revs of the current docs - puts @db.documents.inspect - -Creating and Querying Views: - - @db.save_doc({ - "_id" => "_design/first", - :views => { - :test => { - :map => "function(doc){for(var w in doc){ if(!w.match(/^_/))emit(w,doc[w])}}" - } - } - }) - puts @db.view('first/test')['rows'].inspect - - -## CouchRest::ExtendedDocument - -CouchRest::ExtendedDocument is a DSL/ORM for CouchDB. Basically, ExtendedDocument seats on top of CouchRest Core to add the concept of Model. -ExtendedDocument offers a lot of the usual ORM tools such as optional yet defined schema, validation, callbacks, pagination, casting and much more. - -### Model example - -Check spec/couchrest/more and spec/fixtures/more for more examples - - class Article < CouchRest::ExtendedDocument - use_database DB - unique_id :slug - - view_by :date, :descending => true - view_by :user_id, :date - - view_by :tags, - :map => - "function(doc) { - if (doc['couchrest-type'] == 'Article' && doc.tags) { - doc.tags.forEach(function(tag){ - emit(tag, 1); - }); - } - }", - :reduce => - "function(keys, values, rereduce) { - return sum(values); - }" - - property :date - property :slug, :read_only => true - property :title - property :tags, :cast_as => ['String'] - - timestamps! - - save_callback :before, :generate_slug_from_title - - def generate_slug_from_title - self['slug'] = title.downcase.gsub(/[^a-z0-9]/,'-').squeeze('-').gsub(/^\-|\-$/,'') if new? - end - end - -### Callbacks - -`CouchRest::ExtendedDocuments` instances have 4 callbacks already defined for you: - `:validate`, `:create`, `:save`, `:update` and `:destroy` - -`CouchRest::CastedModel` instances have 1 callback already defined for you: - `:validate` - -Define your callback as follows: - - set_callback :save, :before, :generate_slug_from_name - -CouchRest uses a mixin you can find in lib/mixins/callbacks which is extracted from Rails 3, here are some simple usage examples: - - set_callback :save, :before, :before_method - set_callback :save, :after, :after_method, :if => :condition - set_callback :save, :around {|r| stuff; yield; stuff } - - Or the aliased short version: - - before_save :before_method, :another_method - after_save :after_method, :another_method, :if => :condition - around_save {|r| stuff; yield; stuff } - -To halt the callback, simply return a :halt symbol in your callback method. - -Check the mixin or the ExtendedDocument class to see how to implement your own callbacks. - -### Properties - - property :last_name, :alias => :family_name - property :read_only_value, :read_only => true - property :name, :length => 4...20 - property :price, :type => Integer - -Attribute protection from mass assignment to CouchRest properties. There are two modes of protection: - -1) Declare accessible properties, assume all the rest are protected - property :name, :accessible => true - property :admin # this will be automatically protected - -2) Declare protected properties, assume all the rest are accessible - property :name # this will not be protected - property :admin, :protected => true - -Note: you cannot set both flags in a single class - -### Casting - -Often, you will want to store multiple objects within a document, to be able to retrieve your objects when you load the document, -you can define some casting rules. - - property :casted_attribute, :cast_as => 'WithCastedModelMixin' - property :keywords, :cast_as => ["String"] - property :occurs_at, :cast_as => 'Time', :init_method => 'parse - property :end_date, :cast_as => 'Date', :init_method => 'parse - -If you want to cast an array of instances from a specific Class, use the trick shown above ["ClassName"] - -### Pagination - -Pagination is available in any ExtendedDocument classes. Here are some usage examples: - -basic usage: - - Article.all.paginate(:page => 1, :per_page => 5) - -note: the above query will look like: `GET /db/_design/Article/_view/all?include_docs=true&skip=0&limit=5&reduce=false` and only fetch 5 documents. - -Slightly more advance usage: - - Article.by_name(:startkey => 'a', :endkey => {}).paginate(:page => 1, :per_page => 5) - -note: the above query will look like: `GET /db/_design/Article/_view/by_name?startkey=%22a%22&limit=5&skip=0&endkey=%7B%7D&include_docs=true` -Basically, you can paginate through the articles starting by the letter a, 5 articles at a time. - - -Low level usage: - - Article.paginate(:design_doc => 'Article', :view_name => 'by_date', - :per_page => 3, :page => 2, :descending => true, :key => Date.today, :include_docs => true) +Check the wiki for documentation and examples [http://wiki.github.com/couchrest/couchrest](http://wiki.github.com/couchrest/couchrest) ## Ruby on Rails From b7209c258f29bbaab0e4b03293d92f1934a58194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Wed, 13 Jan 2010 21:19:03 -0200 Subject: [PATCH 21/49] updating history --- history.txt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/history.txt b/history.txt index 5ce94b2..4faa9b4 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,27 @@ +== Next Version + +* Major enhancements + + * Added support for https database URIs. (Mathias Meyer) + * Changing some validations to be compatible with activemodel. (Marcos Tapajós) + * Adds attribute protection to properties. (Will Leinweber) + * Improved CouchRest::Database#save_doc, added "batch" mode to significantly speed up saves at cost of lower durability gurantees. (Igal Koshevoy) + * Added CouchRest::Database#bulk_save_doc and #batch_save_doc as human-friendlier wrappers around #save_doc. (Igal Koshevoy) + +* Minor enhancements + + * Fix content_type handling for attachments + * Fixed a bug in the pagination code that caused it to paginate over records outside of the scope of the view parameters.(John Wood) + * Removed amount_pages calculation for the pagination collection, since it cannot be reliably calculated without a view.(John Wood) + * Bug fix: http://github.com/couchrest/couchrest/issues/#issue/2 (Luke Burton) + * Bug fix: http://github.com/couchrest/couchrest/issues/#issue/1 (Marcos Tapajós) + * Removed the Database class deprecation notices (Matt Aimonetti) + * Adding support to :cast_as => 'Date'. (Marcos Tapajós) + * Improve documentation (Marcos Tapajós) + * Streamer fixes (Julien Sanchez) + * Fix Save on Document & ExtendedDocument crashed if bulk (Julien Sanchez) + * Fix Initialization of ExtendentDocument model shouldn't failed on a nil value in argument (deepj) + == 0.33 * Major enhancements From 64ab53414aa2656e5b269152eadec6612de8d5f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Wed, 13 Jan 2010 21:23:08 -0200 Subject: [PATCH 22/49] fix repository url --- Rakefile | 2 +- THANKS.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Rakefile b/Rakefile index f5650f6..f9b809b 100644 --- a/Rakefile +++ b/Rakefile @@ -20,7 +20,7 @@ spec = Gem::Specification.new do |s| s.date = "2008-11-22" s.summary = "Lean and RESTful interface to CouchDB." s.email = "jchris@apache.org" - s.homepage = "http://github.com/jchris/couchrest" + s.homepage = "http://github.com/couchrest/couchrest" s.description = "CouchRest provides a simple interface on top of CouchDB's RESTful HTTP API, as well as including some utility scripts for managing views and attachments." s.has_rdoc = true s.authors = ["J. Chris Anderson", "Matt Aimonetti"] diff --git a/THANKS.md b/THANKS.md index 643cf5e..08edeec 100644 --- a/THANKS.md +++ b/THANKS.md @@ -14,6 +14,6 @@ changes. A list of these people is included below. * Simon Rozet (simon /at/ rozet /dot/ name) * [Marcos Tapajós](http://tapajos.me) -Patches are welcome. The primary source for this software project is [on Github](http://github.com/jchris/couchrest/tree/master) +Patches are welcome. The primary source for this software project is [on Github](http://github.com/couchrest/couchrest) A lot of people have active forks - thank you all - even the patches I don't end up using are helpful. From 096a4683812cf8b5aca535922c2f0edf32866639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Wed, 13 Jan 2010 21:33:23 -0200 Subject: [PATCH 23/49] change to use Jeweler and Gemcutter --- .gitignore | 1 + Rakefile | 55 ++++++++++++++++------------------------------- couchrest.gemspec | 35 ------------------------------ github_gemtest.rb | 20 ----------------- make-gemspec.sh | 2 -- 5 files changed, 20 insertions(+), 93 deletions(-) delete mode 100644 couchrest.gemspec delete mode 100644 github_gemtest.rb delete mode 100755 make-gemspec.sh diff --git a/.gitignore b/.gitignore index 2318685..e1d9273 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ html/* pkg *.swp +couchrest.gemspec diff --git a/Rakefile b/Rakefile index f9b809b..51b4973 100644 --- a/Rakefile +++ b/Rakefile @@ -1,9 +1,7 @@ require 'rake' require "rake/rdoctask" -require 'rake/gempackagetask' require File.join(File.expand_path(File.dirname(__FILE__)),'lib','couchrest') - begin require 'spec/rake/spectask' rescue LoadError @@ -14,41 +12,26 @@ EOS exit(0) end -spec = Gem::Specification.new do |s| - s.name = "couchrest" - s.version = CouchRest::VERSION - s.date = "2008-11-22" - s.summary = "Lean and RESTful interface to CouchDB." - s.email = "jchris@apache.org" - s.homepage = "http://github.com/couchrest/couchrest" - s.description = "CouchRest provides a simple interface on top of CouchDB's RESTful HTTP API, as well as including some utility scripts for managing views and attachments." - s.has_rdoc = true - s.authors = ["J. Chris Anderson", "Matt Aimonetti"] - s.files = %w( LICENSE README.md Rakefile THANKS.md history.txt) + - Dir["{examples,lib,spec,utils}/**/*"] - - Dir["spec/tmp"] - s.extra_rdoc_files = %w( README.md LICENSE THANKS.md ) - s.require_path = "lib" - s.add_dependency("rest-client", ">= 0.5") - s.add_dependency("mime-types", ">= 1.15") -end - - -desc "Create .gemspec file (useful for github)" -task :gemspec do - filename = "#{spec.name}.gemspec" - File.open(filename, "w") do |f| - f.puts spec.to_ruby +begin + require 'jeweler' + Jeweler::Tasks.new do |gemspec| + gemspec.name = "couchrest" + gemspec.summary = "Lean and RESTful interface to CouchDB." + gemspec.description = "CouchRest provides a simple interface on top of CouchDB's RESTful HTTP API, as well as including some utility scripts for managing views and attachments." + gemspec.email = "jchris@apache.org" + gemspec.homepage = "http://github.com/couchrest/couchrest" + gemspec.authors = ["J. Chris Anderson", "Matt Aimonetti", "Marcos Tapajos"] + gemspec.extra_rdoc_files = %w( README.md LICENSE THANKS.md ) + gemspec.files = %w( LICENSE README.md Rakefile THANKS.md history.txt) + Dir["{examples,lib,spec,utils}/**/*"] - Dir["spec/tmp"] + gemspec.has_rdoc = true + gemspec.add_dependency("rest-client", ">= 0.5") + gemspec.add_dependency("mime-types", ">= 1.15") + gemspec.version = CouchRest::VERSION + gemspec.date = "2008-11-22" + gemspec.require_path = "lib" end -end - -Rake::GemPackageTask.new(spec) do |pkg| - pkg.gem_spec = spec -end - -desc "Install the gem locally" -task :install => [:package] do - sh %{sudo gem install pkg/couchrest-#{CouchRest::VERSION}} +rescue LoadError + puts "Jeweler not available. Install it with: gem install jeweler" end desc "Run all specs" diff --git a/couchrest.gemspec b/couchrest.gemspec deleted file mode 100644 index 6ff0d37..0000000 --- a/couchrest.gemspec +++ /dev/null @@ -1,35 +0,0 @@ -# -*- encoding: utf-8 -*- - -Gem::Specification.new do |s| - s.name = %q{couchrest} - s.version = "0.33" - - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= - s.authors = ["J. Chris Anderson", "Matt Aimonetti"] - s.date = %q{2008-11-22} - s.description = %q{CouchRest provides a simple interface on top of CouchDB's RESTful HTTP API, as well as including some utility scripts for managing views and attachments.} - s.email = %q{jchris@apache.org} - s.extra_rdoc_files = ["README.md", "LICENSE", "THANKS.md"] - s.files = ["LICENSE", "README.md", "Rakefile", "THANKS.md", "history.txt", "examples/model", "examples/model/example.rb", "examples/word_count", "examples/word_count/markov", "examples/word_count/views", "examples/word_count/views/books", "examples/word_count/views/books/chunked-map.js", "examples/word_count/views/books/united-map.js", "examples/word_count/views/markov", "examples/word_count/views/markov/chain-map.js", "examples/word_count/views/markov/chain-reduce.js", "examples/word_count/views/word_count", "examples/word_count/views/word_count/count-map.js", "examples/word_count/views/word_count/count-reduce.js", "examples/word_count/word_count.rb", "examples/word_count/word_count_query.rb", "examples/word_count/word_count_views.rb", "lib/couchrest", "lib/couchrest/commands", "lib/couchrest/commands/generate.rb", "lib/couchrest/commands/push.rb", "lib/couchrest/core", "lib/couchrest/core/adapters", "lib/couchrest/core/adapters/restclient.rb", "lib/couchrest/core/database.rb", "lib/couchrest/core/design.rb", "lib/couchrest/core/document.rb", "lib/couchrest/core/http_abstraction.rb", "lib/couchrest/core/response.rb", "lib/couchrest/core/rest_api.rb", "lib/couchrest/core/server.rb", "lib/couchrest/core/view.rb", "lib/couchrest/helper", "lib/couchrest/helper/pager.rb", "lib/couchrest/helper/streamer.rb", "lib/couchrest/helper/upgrade.rb", "lib/couchrest/middlewares", "lib/couchrest/middlewares/logger.rb", "lib/couchrest/mixins", "lib/couchrest/mixins/attachments.rb", "lib/couchrest/mixins/callbacks.rb", "lib/couchrest/mixins/class_proxy.rb", "lib/couchrest/mixins/collection.rb", "lib/couchrest/mixins/design_doc.rb", "lib/couchrest/mixins/document_queries.rb", "lib/couchrest/mixins/extended_attachments.rb", "lib/couchrest/mixins/extended_document_mixins.rb", "lib/couchrest/mixins/properties.rb", "lib/couchrest/mixins/validation.rb", "lib/couchrest/mixins/views.rb", "lib/couchrest/mixins.rb", "lib/couchrest/monkeypatches.rb", "lib/couchrest/more", "lib/couchrest/more/casted_model.rb", "lib/couchrest/more/extended_document.rb", "lib/couchrest/more/property.rb", "lib/couchrest/support", "lib/couchrest/support/blank.rb", "lib/couchrest/support/class.rb", "lib/couchrest/support/rails.rb", "lib/couchrest/validation", "lib/couchrest/validation/auto_validate.rb", "lib/couchrest/validation/contextual_validators.rb", "lib/couchrest/validation/validation_errors.rb", "lib/couchrest/validation/validators", "lib/couchrest/validation/validators/absent_field_validator.rb", "lib/couchrest/validation/validators/confirmation_validator.rb", "lib/couchrest/validation/validators/format_validator.rb", "lib/couchrest/validation/validators/formats", "lib/couchrest/validation/validators/formats/email.rb", "lib/couchrest/validation/validators/formats/url.rb", "lib/couchrest/validation/validators/generic_validator.rb", "lib/couchrest/validation/validators/length_validator.rb", "lib/couchrest/validation/validators/method_validator.rb", "lib/couchrest/validation/validators/numeric_validator.rb", "lib/couchrest/validation/validators/required_field_validator.rb", "lib/couchrest.rb", "spec/couchrest", "spec/couchrest/core", "spec/couchrest/core/couchrest_spec.rb", "spec/couchrest/core/database_spec.rb", "spec/couchrest/core/design_spec.rb", "spec/couchrest/core/document_spec.rb", "spec/couchrest/core/server_spec.rb", "spec/couchrest/helpers", "spec/couchrest/helpers/pager_spec.rb", "spec/couchrest/helpers/streamer_spec.rb", "spec/couchrest/more", "spec/couchrest/more/casted_extended_doc_spec.rb", "spec/couchrest/more/casted_model_spec.rb", "spec/couchrest/more/extended_doc_attachment_spec.rb", "spec/couchrest/more/extended_doc_spec.rb", "spec/couchrest/more/extended_doc_subclass_spec.rb", "spec/couchrest/more/extended_doc_view_spec.rb", "spec/couchrest/more/property_spec.rb", "spec/fixtures", "spec/fixtures/attachments", "spec/fixtures/attachments/couchdb.png", "spec/fixtures/attachments/README", "spec/fixtures/attachments/test.html", "spec/fixtures/more", "spec/fixtures/more/article.rb", "spec/fixtures/more/card.rb", "spec/fixtures/more/cat.rb", "spec/fixtures/more/course.rb", "spec/fixtures/more/event.rb", "spec/fixtures/more/invoice.rb", "spec/fixtures/more/person.rb", "spec/fixtures/more/question.rb", "spec/fixtures/more/service.rb", "spec/fixtures/views", "spec/fixtures/views/lib.js", "spec/fixtures/views/test_view", "spec/fixtures/views/test_view/lib.js", "spec/fixtures/views/test_view/only-map.js", "spec/fixtures/views/test_view/test-map.js", "spec/fixtures/views/test_view/test-reduce.js", "spec/spec.opts", "spec/spec_helper.rb", "utils/remap.rb", "utils/subset.rb"] - s.has_rdoc = true - s.homepage = %q{http://github.com/jchris/couchrest} - s.require_paths = ["lib"] - s.rubygems_version = %q{1.3.1} - s.summary = %q{Lean and RESTful interface to CouchDB.} - - if s.respond_to? :specification_version then - current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION - s.specification_version = 2 - - if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, [">= 0.5"]) - s.add_runtime_dependency(%q, [">= 1.15"]) - else - s.add_dependency(%q, [">= 0.5"]) - s.add_dependency(%q, [">= 1.15"]) - end - else - s.add_dependency(%q, [">= 0.5"]) - s.add_dependency(%q, [">= 1.15"]) - end -end diff --git a/github_gemtest.rb b/github_gemtest.rb deleted file mode 100644 index f15aa0b..0000000 --- a/github_gemtest.rb +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env ruby -require 'yaml' - -if ARGV.size < 1 - puts "Usage: github-test.rb my-project.gemspec" - exit -end - -require 'rubygems/specification' -data = File.read(ARGV[0]) -spec = nil - -if data !~ %r{!ruby/object:Gem::Specification} - Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join -else - spec = YAML.load(data) -end - -puts spec -puts "OK" \ No newline at end of file diff --git a/make-gemspec.sh b/make-gemspec.sh deleted file mode 100755 index cd79b64..0000000 --- a/make-gemspec.sh +++ /dev/null @@ -1,2 +0,0 @@ -git clean -fxd -rake gemspec \ No newline at end of file From 40de0c455d76764593946f0a84debfca8e9ef55e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Wed, 13 Jan 2010 21:40:55 -0200 Subject: [PATCH 24/49] bumped the version number to 0.34 --- history.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/history.txt b/history.txt index 4faa9b4..8a66314 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,4 @@ -== Next Version +== 0.34 * Major enhancements From cd337b471c3a65cf31618a9e99163a8419e33e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Wed, 13 Jan 2010 21:43:37 -0200 Subject: [PATCH 25/49] fix readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a1a9e29..8e12dda 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ The most complete documentation is the spec/ directory. To validate your CouchRest install, from the project root directory run `rake`, or `autotest` (requires RSpec and optionally ZenTest for autotest support). -### Docs +## Docs Check the wiki for documentation and examples [http://wiki.github.com/couchrest/couchrest](http://wiki.github.com/couchrest/couchrest) From 08f2a0446f457ae9b48466a7e643642ee218c97b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Wed, 13 Jan 2010 21:48:15 -0200 Subject: [PATCH 26/49] update history --- history.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/history.txt b/history.txt index 8a66314..dc7e3fd 100644 --- a/history.txt +++ b/history.txt @@ -21,6 +21,7 @@ * Streamer fixes (Julien Sanchez) * Fix Save on Document & ExtendedDocument crashed if bulk (Julien Sanchez) * Fix Initialization of ExtendentDocument model shouldn't failed on a nil value in argument (deepj) + * Change to use Jeweler and Gemcutter (Marcos Tapajós) == 0.33 From 1b34fe4b60694683e98866a51c2109c1885f7e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Wed, 13 Jan 2010 22:56:21 -0200 Subject: [PATCH 27/49] add contact --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e12dda..70ce4d2 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,17 @@ The most complete documentation is the spec/ directory. To validate your CouchRest install, from the project root directory run `rake`, or `autotest` (requires RSpec and optionally ZenTest for autotest support). -## Docs +### Docs Check the wiki for documentation and examples [http://wiki.github.com/couchrest/couchrest](http://wiki.github.com/couchrest/couchrest) + +### Contact + +Please post bugs, suggestions and patches to the bug tracker at . + +Follow us on Twitter: http://twitter.com/couchrest + +Also, check http://twitter.com/#search?q=%23couchrest ## Ruby on Rails From c6eaca33160809c07772ceff8433808f49fbee06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Wed, 13 Jan 2010 23:16:16 -0200 Subject: [PATCH 28/49] add API link --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 70ce4d2..ddc7460 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,13 @@ The most complete documentation is the spec/ directory. To validate your CouchRest install, from the project root directory run `rake`, or `autotest` (requires RSpec and optionally ZenTest for autotest support). -### Docs +## Docs + +API: [http://rdoc.info/projects/couchrest/couchrest](http://rdoc.info/projects/couchrest/couchrest) Check the wiki for documentation and examples [http://wiki.github.com/couchrest/couchrest](http://wiki.github.com/couchrest/couchrest) -### Contact +## Contact Please post bugs, suggestions and patches to the bug tracker at . From 581f3b748c0e3a7c4076e3557741a217452c2dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Thu, 14 Jan 2010 09:56:28 -0200 Subject: [PATCH 29/49] removing github tasks(gem tasks) --- Rakefile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Rakefile b/Rakefile index 51b4973..6936814 100644 --- a/Rakefile +++ b/Rakefile @@ -56,3 +56,12 @@ end desc "Run the rspec" task :default => :spec + +module Rake + def self.remove_task(task_name) + Rake.application.instance_variable_get('@tasks').delete(task_name.to_s) + end +end + +Rake.remove_task("github:release") +Rake.remove_task("release") \ No newline at end of file From dad7e349a706a3ac413aad322bb8b149fcc8fdc2 Mon Sep 17 00:00:00 2001 From: "Johannes J. Schmidt" Date: Thu, 5 Nov 2009 00:58:14 +0100 Subject: [PATCH 30/49] fixed set_attachment_attr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit works with CouchDB 0.10.0 * use "content_type" instead of "content-type" * do not encode attachment Signed-off-by: Marcos Tapajós --- lib/couchrest/mixins/extended_attachments.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/couchrest/mixins/extended_attachments.rb b/lib/couchrest/mixins/extended_attachments.rb index 8dbac45..e81670b 100644 --- a/lib/couchrest/mixins/extended_attachments.rb +++ b/lib/couchrest/mixins/extended_attachments.rb @@ -65,10 +65,10 @@ module CouchRest content_type = args[:content_type] ? args[:content_type] : get_mime_type(args[:file]) self['_attachments'][args[:name]] = { 'content_type' => content_type, - 'data' => encode_attachment(args[:file].read) + 'data' => args[:file].read } end end # module ExtendedAttachments end -end \ No newline at end of file +end From a7ee95b33cecb4320af49469e26390b12ea8c54a Mon Sep 17 00:00:00 2001 From: "Johannes J. Schmidt" Date: Fri, 29 Jan 2010 20:00:06 +0100 Subject: [PATCH 31/49] fixed extended attachment encoding part 2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit encoding was done twice, once while setting attachment and once while saving the document. Also the attachment was decoded while retreaving, but this is done by CouchDB itself automatically, as described in http://wiki.apache.org/couchdb/HTTP_Document_API#line-463. Signed-off-by: Marcos Tapajós --- lib/couchrest/mixins/extended_attachments.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/couchrest/mixins/extended_attachments.rb b/lib/couchrest/mixins/extended_attachments.rb index e81670b..5b5e2b8 100644 --- a/lib/couchrest/mixins/extended_attachments.rb +++ b/lib/couchrest/mixins/extended_attachments.rb @@ -14,7 +14,7 @@ module CouchRest # reads the data from an attachment def read_attachment(attachment_name) - Base64.decode64(database.fetch_attachment(self, attachment_name)) + database.fetch_attachment(self, attachment_name) end # modifies a file attachment on the current doc @@ -52,10 +52,6 @@ module CouchRest private - def encode_attachment(data) - ::Base64.encode64(data).gsub(/\r|\n/,'') - end - def get_mime_type(file) ::MIME::Types.type_for(file.path).empty? ? 'text\/plain' : MIME::Types.type_for(file.path).first.content_type.gsub(/\//,'\/') From d5b354367dd3c80430dc213c117b4079ef3998b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Fri, 29 Jan 2010 22:20:39 -0200 Subject: [PATCH 32/49] updating the history --- history.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/history.txt b/history.txt index dc7e3fd..9bd539a 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,10 @@ +== Next Version + +* Major enhancements + +* Minor enhancements + * Fix attachment bug (Johannes Jörg Schmidt) + == 0.34 * Major enhancements From ec51c34f7d78de1f99e15f5a328b3ffe2f5d2dba Mon Sep 17 00:00:00 2001 From: Damien Mathieu Date: Wed, 3 Feb 2010 15:57:43 +0100 Subject: [PATCH 33/49] recreating a non-existant database shouldn't raise any exception (and we make sure of it) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcos Tapajós --- lib/couchrest/core/database.rb | 2 +- spec/couchrest/core/database_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/couchrest/core/database.rb b/lib/couchrest/core/database.rb index 547e35c..825882d 100644 --- a/lib/couchrest/core/database.rb +++ b/lib/couchrest/core/database.rb @@ -265,7 +265,7 @@ module CouchRest def recreate! delete! create! - rescue HttpAbstraction::ResourceNotFound + rescue RestClient::ResourceNotFound ensure create! end diff --git a/spec/couchrest/core/database_spec.rb b/spec/couchrest/core/database_spec.rb index afcda6e..121548c 100644 --- a/spec/couchrest/core/database_spec.rb +++ b/spec/couchrest/core/database_spec.rb @@ -716,7 +716,7 @@ describe CouchRest::Database do it "should recreate a db even tho it doesn't exist" do @cr.databases.should_not include(@db2.name) - begin @db2.recreate! rescue nil end + @db2.recreate! @cr.databases.should include(@db2.name) end From b147e2cfb178479c66b1fe0d8041308684c519ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Wed, 3 Feb 2010 14:10:31 -0200 Subject: [PATCH 34/49] updating history.txt --- history.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/history.txt b/history.txt index 9bd539a..b39de22 100644 --- a/history.txt +++ b/history.txt @@ -4,6 +4,7 @@ * Minor enhancements * Fix attachment bug (Johannes Jörg Schmidt) + * Fix create database exception bug (Damien Mathieu) == 0.34 From 38dad4bc7849cc97c4248656056813129a8d26b1 Mon Sep 17 00:00:00 2001 From: Julien Kirch Date: Sat, 20 Feb 2010 18:45:53 +0100 Subject: [PATCH 35/49] added ascending compatability with restclient-1.4.0 Signed-off-by: Will Leinweber --- lib/couchrest/core/adapters/restclient.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/couchrest/core/adapters/restclient.rb b/lib/couchrest/core/adapters/restclient.rb index ed02228..1be0433 100644 --- a/lib/couchrest/core/adapters/restclient.rb +++ b/lib/couchrest/core/adapters/restclient.rb @@ -10,25 +10,25 @@ module RestClientAdapter end def get(uri, headers={}) - RestClient.get(uri, headers) + RestClient.get(uri, headers).to_s end def post(uri, payload, headers={}) - RestClient.post(uri, payload, headers) + RestClient.post(uri, payload, headers).to_s end def put(uri, payload, headers={}) - RestClient.put(uri, payload, headers) + RestClient.put(uri, payload, headers).to_s end def delete(uri, headers={}) - RestClient.delete(uri, headers) + RestClient.delete(uri, headers).to_s end def copy(uri, headers) RestClient::Request.execute( :method => :copy, :url => uri, - :headers => headers) + :headers => headers).to_s end end From 5707d89290ded82073da3f06a2895a7ecc4624b4 Mon Sep 17 00:00:00 2001 From: Will Leinweber Date: Tue, 23 Feb 2010 16:22:51 -0600 Subject: [PATCH 36/49] Update history.txt --- history.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/history.txt b/history.txt index b39de22..10f1399 100644 --- a/history.txt +++ b/history.txt @@ -5,6 +5,7 @@ * Minor enhancements * Fix attachment bug (Johannes Jörg Schmidt) * Fix create database exception bug (Damien Mathieu) + * Compatible with restclient >= 1.4.0 new responses (Julien Kirch) == 0.34 @@ -93,4 +94,4 @@ --- Unfortunately, before 0.30 we did not keep a track of the modifications made to CouchRest. -You can see the full commit history on GitHub: http://github.com/couchrest/couchrest/commits/master/ \ No newline at end of file +You can see the full commit history on GitHub: http://github.com/couchrest/couchrest/commits/master/ From d41c7c96da99b4d3b937cfb2f3be3e2f7c250500 Mon Sep 17 00:00:00 2001 From: Will Leinweber Date: Fri, 4 Dec 2009 01:58:07 -0600 Subject: [PATCH 37/49] BUGFIX: attribute protection Fixes bug where documents recreated from the database were being stripped of their protected attributes when instantiated Signed-off-by: Marcos Tapajos --- lib/couchrest/mixins/collection.rb | 2 +- lib/couchrest/mixins/document_queries.rb | 8 +-- lib/couchrest/mixins/views.rb | 6 +- lib/couchrest/more/extended_document.rb | 29 +++++++--- .../more/attribute_protection_spec.rb | 56 +++++++++++++++++++ 5 files changed, 84 insertions(+), 17 deletions(-) diff --git a/lib/couchrest/mixins/collection.rb b/lib/couchrest/mixins/collection.rb index b248825..696dae7 100644 --- a/lib/couchrest/mixins/collection.rb +++ b/lib/couchrest/mixins/collection.rb @@ -183,7 +183,7 @@ module CouchRest if @container_class.nil? results else - results['rows'].collect { |row| @container_class.new(row['doc']) } unless results['rows'].nil? + results['rows'].collect { |row| @container_class.create_from_database(row['doc']) } unless results['rows'].nil? end end diff --git a/lib/couchrest/mixins/document_queries.rb b/lib/couchrest/mixins/document_queries.rb index d7c8a11..b19442e 100644 --- a/lib/couchrest/mixins/document_queries.rb +++ b/lib/couchrest/mixins/document_queries.rb @@ -51,11 +51,9 @@ module CouchRest # db:: optional option to pass a custom database to use def get(id, db = database) begin - doc = db.get id + get!(id, db) rescue nil - else - new(doc) end end @@ -72,11 +70,11 @@ module CouchRest # db:: optional option to pass a custom database to use def get!(id, db = database) doc = db.get id - new(doc) + create_from_database(doc) end end end end -end \ No newline at end of file +end diff --git a/lib/couchrest/mixins/views.rb b/lib/couchrest/mixins/views.rb index de530e3..6c95066 100644 --- a/lib/couchrest/mixins/views.rb +++ b/lib/couchrest/mixins/views.rb @@ -137,13 +137,13 @@ module CouchRest collection_proxy_for(design_doc, name, opts.merge({:include_docs => true})) else view = fetch_view db, name, opts.merge({:include_docs => true}), &block - view['rows'].collect{|r|new(r['doc'])} if view['rows'] + view['rows'].collect{|r|create_from_database(r['doc'])} if view['rows'] end rescue # fallback for old versions of couchdb that don't # have include_docs support view = fetch_view(db, name, opts, &block) - view['rows'].collect{|r|new(db.get(r['id']))} if view['rows'] + view['rows'].collect{|r|create_from_database(db.get(r['id']))} if view['rows'] end end end @@ -170,4 +170,4 @@ module CouchRest end end -end \ No newline at end of file +end diff --git a/lib/couchrest/more/extended_document.rb b/lib/couchrest/more/extended_document.rb index 73c1b17..0c26240 100644 --- a/lib/couchrest/more/extended_document.rb +++ b/lib/couchrest/more/extended_document.rb @@ -38,11 +38,20 @@ module CouchRest define_callbacks :save, "result == :halt" define_callbacks :update, "result == :halt" define_callbacks :destroy, "result == :halt" + + # Creates a new instance, bypassing attribute protection + # + # ==== Returns + # a document instance + def self.create_from_database(passed_keys={}) + new(passed_keys, :directly_set_attributes => true) + end - def initialize(passed_keys={}) + def initialize(passed_keys={}, options={}) apply_defaults # defined in CouchRest::Mixins::Properties - set_attributes(passed_keys) unless passed_keys.nil? - super + remove_protected_attributes(passed_keys) unless options[:directly_set_attributes] + directly_set_attributes(passed_keys) unless passed_keys.nil? + super(passed_keys) cast_keys # defined in CouchRest::Mixins::Properties unless self['_id'] && self['_rev'] self['couchrest-type'] = self.class.to_s @@ -281,14 +290,18 @@ module CouchRest raise NoMethodError, "#{attribute_name}= method not available, use property :#{attribute_name}" unless self.respond_to?("#{attribute_name}=") end end - - def set_attributes(hash) - attrs = remove_protected_attributes(hash) - attrs.each do |attribute_name, attribute_value| + + def directly_set_attributes(hash) + hash.each do |attribute_name, attribute_value| if self.respond_to?("#{attribute_name}=") - self.send("#{attribute_name}=", attrs.delete(attribute_name)) + self.send("#{attribute_name}=", hash.delete(attribute_name)) end end + end + + def set_attributes(hash) + attrs = remove_protected_attributes(hash) + directly_set_attributes(attrs) end end end diff --git a/spec/couchrest/more/attribute_protection_spec.rb b/spec/couchrest/more/attribute_protection_spec.rb index 3063531..f773167 100644 --- a/spec/couchrest/more/attribute_protection_spec.rb +++ b/spec/couchrest/more/attribute_protection_spec.rb @@ -21,6 +21,17 @@ describe "ExtendedDocument", "no declarations" do user.name.should == "will" user.phone.should == "555-5555" end + + it "should recreate from the database properly" do + user = NoProtection.new + user.name = "will" + user.phone = "555-5555" + user.save! + + user = NoProtection.get(user.id) + user.name.should == "will" + user.phone.should == "555-5555" + end end describe "ExtendedDocument", "accessible flag" do @@ -92,3 +103,48 @@ describe "ExtendedDocument", "protected flag" do lambda { WithBoth.new }.should raise_error end end + +describe "ExtendedDocument", "from database" do + class WithProtected < CouchRest::ExtendedDocument + use_database TEST_SERVER.default_database + property :name + property :admin, :default => false, :protected => true + view_by :name + end + + before(:each) do + @user = WithProtected.new + @user.name = "will" + @user.admin = true + @user.save! + end + + def verify_attrs(user) + user.name.should == "will" + user.admin.should == true + end + + it "ExtendedDocument#get should not strip protected attributes" do + reloaded = WithProtected.get( @user.id ) + verify_attrs reloaded + end + + it "ExtendedDocument#get! should not strip protected attributes" do + reloaded = WithProtected.get!( @user.id ) + verify_attrs reloaded + end + + it "ExtendedDocument#all should not strip protected attributes" do + # all creates a CollectionProxy + docs = WithProtected.all(:key => @user.id) + docs.size.should == 1 + reloaded = docs.first + verify_attrs reloaded + end + + it "views should not strip protected attributes" do + docs = WithProtected.by_name(:startkey => "will", :endkey => "will") + reloaded = docs.first + verify_attrs reloaded + end +end From f16e514d52234dcf645fcbd828ac08aadf745427 Mon Sep 17 00:00:00 2001 From: Will Leinweber Date: Fri, 26 Feb 2010 17:27:24 -0600 Subject: [PATCH 38/49] update history.txt --- history.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/history.txt b/history.txt index 10f1399..afa9ee5 100644 --- a/history.txt +++ b/history.txt @@ -6,6 +6,7 @@ * Fix attachment bug (Johannes Jörg Schmidt) * Fix create database exception bug (Damien Mathieu) * Compatible with restclient >= 1.4.0 new responses (Julien Kirch) + * Bug fix: Attribute protection no longer strips attributes coming from the database (Will Leinweber) == 0.34 From a856e98a6afdc0f0d7952afbb37ad8df7685678b Mon Sep 17 00:00:00 2001 From: Noah Zoschke Date: Thu, 18 Feb 2010 18:26:04 -0800 Subject: [PATCH 39/49] remove double CGI escape when PUTting an attachment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcos Tapajós --- lib/couchrest/core/database.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/couchrest/core/database.rb b/lib/couchrest/core/database.rb index 825882d..1d2433c 100644 --- a/lib/couchrest/core/database.rb +++ b/lib/couchrest/core/database.rb @@ -107,7 +107,6 @@ module CouchRest # PUT an attachment directly to CouchDB def put_attachment(doc, name, file, options = {}) docid = escape_docid(doc['_id']) - name = CGI.escape(name) uri = url_for_attachment(doc, name) JSON.parse(HttpAbstraction.put(uri, file, options)) end From b1f4040a44d71cb292700ac2010f248127fe7b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Fri, 26 Feb 2010 21:02:18 -0300 Subject: [PATCH 40/49] update history.txt --- history.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/history.txt b/history.txt index afa9ee5..fecc3c3 100644 --- a/history.txt +++ b/history.txt @@ -7,6 +7,7 @@ * Fix create database exception bug (Damien Mathieu) * Compatible with restclient >= 1.4.0 new responses (Julien Kirch) * Bug fix: Attribute protection no longer strips attributes coming from the database (Will Leinweber) + * Bug fix: Remove double CGI escape when PUTting an attachment (nzoschke) == 0.34 From 17dac85a02eed3ddb7fa9ccc7ab8ff4ef55f649e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Fri, 26 Feb 2010 21:39:09 -0300 Subject: [PATCH 41/49] Changing CouchRest::ExtendedDocument to allow chaining the inherit class callback --- history.txt | 1 + lib/couchrest/more/extended_document.rb | 2 + .../more/casted_extended_doc_spec.rb | 4 +- .../more/extended_doc_inherited_spec.rb | 40 +++++++++++++++++++ 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 spec/couchrest/more/extended_doc_inherited_spec.rb diff --git a/history.txt b/history.txt index fecc3c3..967ea12 100644 --- a/history.txt +++ b/history.txt @@ -1,6 +1,7 @@ == Next Version * Major enhancements + * CouchRest::ExtendedDocument allow chaining the inherit class callback (Kenneth Kalmer) - http://github.com/couchrest/couchrest/issues#issue/8 * Minor enhancements * Fix attachment bug (Johannes Jörg Schmidt) diff --git a/lib/couchrest/more/extended_document.rb b/lib/couchrest/more/extended_document.rb index 0c26240..c6f3c85 100644 --- a/lib/couchrest/more/extended_document.rb +++ b/lib/couchrest/more/extended_document.rb @@ -21,9 +21,11 @@ module CouchRest end def self.inherited(subklass) + super subklass.send(:include, CouchRest::Mixins::Properties) subklass.class_eval <<-EOS, __FILE__, __LINE__ + 1 def self.inherited(subklass) + super subklass.properties = self.properties.dup end EOS diff --git a/spec/couchrest/more/casted_extended_doc_spec.rb b/spec/couchrest/more/casted_extended_doc_spec.rb index 4df60c1..e92d34f 100644 --- a/spec/couchrest/more/casted_extended_doc_spec.rb +++ b/spec/couchrest/more/casted_extended_doc_spec.rb @@ -49,7 +49,7 @@ describe "assigning a value to casted attribute after initializing an object" do end it "should cast attribute" do - @car.driver = JSON.parse(JSON.generate(@driver)) + @car.driver = JSON.parse(@driver.to_json) @car.driver.should be_instance_of(Driver) end @@ -60,7 +60,7 @@ describe "casting an extended document from parsed JSON" do before(:each) do @driver = Driver.new(:name => 'Matt') @car = Car.new(:name => 'Renault 306', :driver => @driver) - @new_car = Car.new(JSON.parse(JSON.generate(@car))) + @new_car = Car.new(JSON.parse(@car.to_json)) end it "should cast casted attribute" do diff --git a/spec/couchrest/more/extended_doc_inherited_spec.rb b/spec/couchrest/more/extended_doc_inherited_spec.rb new file mode 100644 index 0000000..d52fbd9 --- /dev/null +++ b/spec/couchrest/more/extended_doc_inherited_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +begin + require 'rubygems' unless ENV['SKIP_RUBYGEMS'] + require 'activesupport' + ActiveSupport::JSON.backend = :JSONGem + + class PlainParent + class_inheritable_accessor :foo + self.foo = :bar + end + + class PlainChild < PlainParent + end + + class ExtendedParent < CouchRest::ExtendedDocument + class_inheritable_accessor :foo + self.foo = :bar + end + + class ExtendedChild < ExtendedParent + end + + describe "Using chained inheritance without CouchRest::ExtendedDocument" do + it "should preserve inheritable attributes" do + PlainParent.foo.should == :bar + PlainChild.foo.should == :bar + end + end + + describe "Using chained inheritance with CouchRest::ExtendedDocument" do + it "should preserve inheritable attributes" do + ExtendedParent.foo.should == :bar + ExtendedChild.foo.should == :bar + end + end + +rescue LoadError + puts "This spec requires 'active_support' to be loaded" +end From 6d571a7d818278fad4c459d2f00f6e6d2d70c7ce Mon Sep 17 00:00:00 2001 From: Peter Gumeson Date: Wed, 9 Sep 2009 00:08:59 -0700 Subject: [PATCH 42/49] Class proxy was not setting database on result sets --- lib/couchrest/mixins/class_proxy.rb | 16 +++-- spec/couchrest/more/extended_doc_view_spec.rb | 66 +++++++++++++------ 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/lib/couchrest/mixins/class_proxy.rb b/lib/couchrest/mixins/class_proxy.rb index 0101639..5f7bd5b 100644 --- a/lib/couchrest/mixins/class_proxy.rb +++ b/lib/couchrest/mixins/class_proxy.rb @@ -56,7 +56,9 @@ module CouchRest # Mixins::DocumentQueries def all(opts = {}, &block) - @klass.all({:database => @database}.merge(opts), &block) + docs = @klass.all({:database => @database}.merge(opts), &block) + docs.each { |doc| doc.database = @database if doc.respond_to?(:database) } if docs + docs end def count(opts = {}, &block) @@ -64,11 +66,15 @@ module CouchRest end def first(opts = {}) - @klass.first({:database => @database}.merge(opts)) + doc = @klass.first({:database => @database}.merge(opts)) + doc.database = @database if doc && doc.respond_to?(:database) + doc end def get(id) - @klass.get(id, @database) + doc = @klass.get(id, @database) + doc.database = @database if doc && doc.respond_to?(:database) + doc end # Mixins::Views @@ -78,7 +84,9 @@ module CouchRest end def view(name, query={}, &block) - @klass.view(name, {:database => @database}.merge(query), &block) + docs = @klass.view(name, {:database => @database}.merge(query), &block) + docs.each { |doc| doc.database = @database if doc.respond_to?(:database) } if docs + docs end def all_design_doc_versions diff --git a/spec/couchrest/more/extended_doc_view_spec.rb b/spec/couchrest/more/extended_doc_view_spec.rb index bf75f2d..c8032cf 100644 --- a/spec/couchrest/more/extended_doc_view_spec.rb +++ b/spec/couchrest/more/extended_doc_view_spec.rb @@ -25,39 +25,33 @@ describe "ExtendedDocument views" do written_at += 24 * 3600 end end - it "should have a design doc" do Article.design_doc["views"]["by_date"].should_not be_nil end - it "should save the design doc" do Article.by_date #rescue nil doc = Article.database.get Article.design_doc.id doc['views']['by_date'].should_not be_nil end - it "should return the matching raw view result" do view = Article.by_date :raw => true view['rows'].length.should == 4 end - it "should not include non-Articles" do Article.database.save_doc({"date" => 1}) view = Article.by_date :raw => true view['rows'].length.should == 4 end - it "should return the matching objects (with default argument :descending => true)" do articles = Article.by_date articles.collect{|a|a.title}.should == @titles.reverse end - it "should allow you to override default args" do articles = Article.by_date :descending => false articles.collect{|a|a.title}.should == @titles end end - + describe "another model with a simple view" do before(:all) do reset_test_db! @@ -96,8 +90,7 @@ describe "ExtendedDocument views" do courses[0]["doc"]["title"].should =='aaa' end end - - + describe "a ducktype view" do before(:all) do reset_test_db! @@ -117,7 +110,7 @@ describe "ExtendedDocument views" do @as[0]['_id'].should == @id end end - + describe "a model class not tied to a database" do before(:all) do reset_test_db! @@ -198,7 +191,7 @@ describe "ExtendedDocument views" do Unattached.model_design_doc(@db)['_rev'].should_not == original_revision end end - + describe "class proxy" do before(:all) do reset_test_db! @@ -254,6 +247,36 @@ describe "ExtendedDocument views" do u = @us.first u.title.should =~ /\A...\z/ end + it "should set database on first retreived document" do + u = @us.first + u.database.should === DB + end + it "should set database on all retreived documents" do + @us.all.each do |u| + u.database.should === DB + end + end + it "should set database on each retreived document" do + rs = @us.by_title :startkey=>"bbb", :endkey=>"eee" + rs.length.should == 3 + rs.each do |u| + u.database.should === DB + end + end + it "should set database on document retreived by id" do + u = @us.get(@first_id) + u.database.should === DB + end + it "should not attempt to set database on raw results using :all" do + @us.all(:raw => true).each do |u| + u.respond_to?(:database).should be_false + end + end + it "should not attempt to set database on raw results using view" do + @us.by_title(:raw => true).each do |u| + u.respond_to?(:database).should be_false + end + end it "should clean up design docs left around on specific database" do @us.by_title original_id = @us.model_design_doc['_rev'] @@ -262,7 +285,7 @@ describe "ExtendedDocument views" do @us.model_design_doc['_rev'].should_not == original_id end end - + describe "a model with a compound key view" do before(:all) do Article.by_user_id_and_date.each{|a| a.destroy(true)} @@ -295,7 +318,7 @@ describe "ExtendedDocument views" do articles[0].title.should == "even more interesting" end end - + describe "with a custom view" do before(:all) do @titles = ["very uniq one", "even less interesting", "some fun", @@ -311,18 +334,18 @@ describe "ExtendedDocument views" do view = Article.by_tags :raw => true view['rows'].length.should == 5 end - + it "should be default to :reduce => false" do ars = Article.by_tags ars.first.tags.first.should == 'cool' end - + it "should be raw when reduce is true" do view = Article.by_tags :reduce => true, :group => true view['rows'].find{|r|r['key'] == 'cool'}['value'].should == 3 end end - + # TODO: moved to Design, delete describe "adding a view" do before(:each) do @@ -344,7 +367,7 @@ describe "ExtendedDocument views" do Article.design_doc["views"].keys.should include("by_updated_at") end end - + describe "with a collection" do before(:all) do reset_test_db! @@ -354,7 +377,7 @@ describe "ExtendedDocument views" do a = Article.new(:title => title, :date => Date.today) a.save end - + titles = ["yesterday very uniq one", "yesterday really interesting", "yesterday some fun", "yesterday really awesome", "yesterday crazy bob", "yesterday this rocks"] titles.each_with_index do |title,i| @@ -390,11 +413,11 @@ describe "ExtendedDocument views" do articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date', :per_page => 3, :descending => true, :key => Date.today, :include_docs => true) articles.size.should == 3 - + articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date', :per_page => 3, :page => 2, :descending => true, :key => Date.today, :include_docs => true) articles.size.should == 3 - + articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date', :per_page => 3, :page => 3, :descending => true, :key => Date.today, :include_docs => true) articles.size.should == 1 @@ -408,6 +431,9 @@ describe "ExtendedDocument views" do end end it "should provide a class method to get a collection for a view" do + class Article + provides_collection :article_details, 'Article', 'by_date', :descending => true, :include_docs => true + end articles = Article.find_all_article_details(:key => Date.today) articles.class.should == Array articles.size.should == 7 From 3724a33de62f883915fd8b326d86bbdc460708e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Sat, 27 Feb 2010 18:43:53 -0300 Subject: [PATCH 43/49] update history.txt --- history.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/history.txt b/history.txt index 967ea12..35d8dc4 100644 --- a/history.txt +++ b/history.txt @@ -9,6 +9,7 @@ * Compatible with restclient >= 1.4.0 new responses (Julien Kirch) * Bug fix: Attribute protection no longer strips attributes coming from the database (Will Leinweber) * Bug fix: Remove double CGI escape when PUTting an attachment (nzoschke) + * Bug fix: Changing Class proxy to set database on result sets (Peter Gumeson) == 0.34 From 62db7853c32c42a59fb721f87e90d6b9ae7d671b Mon Sep 17 00:00:00 2001 From: Nolan Darilek Date: Tue, 22 Sep 2009 08:19:08 -0500 Subject: [PATCH 44/49] Updated time regexp. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcos Tapajós --- lib/couchrest/mixins/properties.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/couchrest/mixins/properties.rb b/lib/couchrest/mixins/properties.rb index d0d8537..9a9c3a4 100644 --- a/lib/couchrest/mixins/properties.rb +++ b/lib/couchrest/mixins/properties.rb @@ -4,7 +4,7 @@ require File.join(File.dirname(__FILE__), '..', 'more', 'property') class Time # returns a local time value much faster than Time.parse def self.mktime_with_offset(string) - string =~ /(\d{4})\/(\d{2})\/(\d{2}) (\d{2}):(\d{2}):(\d{2}) ([\+\-])(\d{2})/ + string =~ /(\d{4})[\-|\/](\d{2})[\-|\/](\d{2})[T|\s](\d{2}):(\d{2}):(\d{2})([\+|\s|\-])*(\d{2}):?(\d{2})/ # $1 = year # $2 = month # $3 = day @@ -197,4 +197,4 @@ module CouchRest end end -end \ No newline at end of file +end From 0c1da919a0ffe27808c6669ce574583469404f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Sat, 27 Feb 2010 18:47:42 -0300 Subject: [PATCH 45/49] update history.txt --- history.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/history.txt b/history.txt index 35d8dc4..8a89f47 100644 --- a/history.txt +++ b/history.txt @@ -10,6 +10,7 @@ * Bug fix: Attribute protection no longer strips attributes coming from the database (Will Leinweber) * Bug fix: Remove double CGI escape when PUTting an attachment (nzoschke) * Bug fix: Changing Class proxy to set database on result sets (Peter Gumeson) + * Bug fix: Updated time regexp (Nolan Darilek) == 0.34 From cc9ed83cd3cc15bcb7443599180fd8024545f9e9 Mon Sep 17 00:00:00 2001 From: Pierre Larochelle Date: Fri, 18 Dec 2009 00:03:05 -0500 Subject: [PATCH 46/49] Added an update_doc method to database to handle conflicts during atomic updates. Also added appropriate specs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcos Tapajós --- lib/couchrest/core/database.rb | 27 ++++++++++++++++ spec/couchrest/core/database_spec.rb | 47 ++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/lib/couchrest/core/database.rb b/lib/couchrest/core/database.rb index 1d2433c..36597fc 100644 --- a/lib/couchrest/core/database.rb +++ b/lib/couchrest/core/database.rb @@ -249,6 +249,33 @@ module CouchRest CouchRest.copy "#{@root}/#{slug}", destination end + # Updates the given doc by yielding the current state of the doc + # and trying to update update_limit times. Returns the new doc + # if the doc was successfully updated without hitting the limit + def update_doc(doc_id, params = {}, update_limit=10) + resp = {'ok' => false} + new_doc = nil + last_fail = nil + + until resp['ok'] or update_limit <= 0 + doc = self.get(doc_id, params) # grab the doc + new_doc = yield doc # give it to the caller to be updated + begin + resp = self.save_doc new_doc # try to PUT the updated doc into the db + rescue RestClient::RequestFailed => e + if e.http_code == 409 # Update collision + update_limit -= 1 + last_fail = e + else # some other error + raise e + end + end + end + + raise last_fail unless resp['ok'] + new_doc + end + # Compact the database, removing old document revisions and optimizing space use. def compact! CouchRest.post "#{@root}/_compact" diff --git a/spec/couchrest/core/database_spec.rb b/spec/couchrest/core/database_spec.rb index 121548c..28a57dd 100644 --- a/spec/couchrest/core/database_spec.rb +++ b/spec/couchrest/core/database_spec.rb @@ -551,6 +551,53 @@ describe CouchRest::Database do end + describe "UPDATE existing document" do + before :each do + @id = @db.save_doc({ + 'article' => 'Pete Doherty Kicked Out For Nazi Anthem', + 'upvotes' => 10, + 'link' => 'http://beatcrave.com/2009-11-30/pete-doherty-kicked-out-for-nazi-anthem/'})['id'] + end + it "should work under normal conditions" do + @db.update_doc @id do |doc| + doc['upvotes'] += 1 + doc + end + @db.get(@id)['upvotes'].should == 11 + end + it "should fail if update_limit is reached" do + lambda do + @db.update_doc @id do |doc| + # modify and save the doc so that a collision happens + conflicting_doc = @db.get @id + conflicting_doc['upvotes'] += 1 + @db.save_doc conflicting_doc + + # then try saving it through the update + doc['upvotes'] += 1 + doc + end + end.should raise_error(RestClient::RequestFailed) + end + it "should not fail if update_limit is not reached" do + limit = 5 + lambda do + @db.update_doc @id do |doc| + # same as the last spec except we're only forcing 5 conflicts + if limit > 0 + conflicting_doc = @db.get @id + conflicting_doc['upvotes'] += 1 + @db.save_doc conflicting_doc + limit -= 1 + end + doc['upvotes'] += 1 + doc + end + end.should_not raise_error + @db.get(@id)['upvotes'].should == 16 + end + end + describe "COPY existing document" do before :each do @r = @db.save_doc({'artist' => 'Zappa', 'title' => 'Muffin Man'}) From 67ee6e3e749e47f1ade0fa1c8685dc888ce992fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Sat, 27 Feb 2010 18:59:23 -0300 Subject: [PATCH 47/49] update history.txt --- history.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/history.txt b/history.txt index 8a89f47..d05aca0 100644 --- a/history.txt +++ b/history.txt @@ -11,6 +11,7 @@ * Bug fix: Remove double CGI escape when PUTting an attachment (nzoschke) * Bug fix: Changing Class proxy to set database on result sets (Peter Gumeson) * Bug fix: Updated time regexp (Nolan Darilek) + * Added an update_doc method to database to handle conflicts during atomic updates. (Pierre Larochelle) == 0.34 From 228783d45329a9c038295c877f125572446844cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Sat, 27 Feb 2010 19:23:02 -0300 Subject: [PATCH 48/49] Removing duplicated code --- spec/couchrest/more/extended_doc_view_spec.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/spec/couchrest/more/extended_doc_view_spec.rb b/spec/couchrest/more/extended_doc_view_spec.rb index c8032cf..c903cf5 100644 --- a/spec/couchrest/more/extended_doc_view_spec.rb +++ b/spec/couchrest/more/extended_doc_view_spec.rb @@ -431,9 +431,6 @@ describe "ExtendedDocument views" do end end it "should provide a class method to get a collection for a view" do - class Article - provides_collection :article_details, 'Article', 'by_date', :descending => true, :include_docs => true - end articles = Article.find_all_article_details(:key => Date.today) articles.class.should == Array articles.size.should == 7 From 0d0a8ae6b8639806b164cf535e88f592ac8ba518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Tapaj=C3=B3s?= Date: Sat, 27 Feb 2010 21:38:20 -0300 Subject: [PATCH 49/49] bumped the version number to 0.35 --- history.txt | 7 +++++++ lib/couchrest.rb | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/history.txt b/history.txt index d05aca0..3ac3c2c 100644 --- a/history.txt +++ b/history.txt @@ -1,5 +1,11 @@ == Next Version +* Major enhancements + +* Minor enhancements + +== 0.35 + * Major enhancements * CouchRest::ExtendedDocument allow chaining the inherit class callback (Kenneth Kalmer) - http://github.com/couchrest/couchrest/issues#issue/8 @@ -12,6 +18,7 @@ * Bug fix: Changing Class proxy to set database on result sets (Peter Gumeson) * Bug fix: Updated time regexp (Nolan Darilek) * Added an update_doc method to database to handle conflicts during atomic updates. (Pierre Larochelle) + * Bug fix: http://github.com/couchrest/couchrest/issues/#issue/2 (Luke Burton) == 0.34 diff --git a/lib/couchrest.rb b/lib/couchrest.rb index 03a6a65..e88d5a8 100644 --- a/lib/couchrest.rb +++ b/lib/couchrest.rb @@ -28,7 +28,7 @@ require 'couchrest/monkeypatches' # = CouchDB, close to the metal module CouchRest - VERSION = '0.34' unless self.const_defined?("VERSION") + VERSION = '0.35' unless self.const_defined?("VERSION") autoload :Server, 'couchrest/core/server' autoload :Database, 'couchrest/core/database'