couchrest_extended_document 1.0.0.beta6 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +173 -2
- data/Rakefile +3 -3
- data/THANKS.md +4 -1
- data/history.txt +7 -1
- data/lib/couchrest/casted_array.rb +20 -6
- data/lib/couchrest/casted_model.rb +3 -5
- data/lib/couchrest/extended_document.rb +16 -78
- data/lib/couchrest/mixins.rb +1 -0
- data/lib/couchrest/mixins/attributes.rb +75 -0
- data/lib/couchrest/mixins/callbacks.rb +4 -2
- data/lib/couchrest/mixins/properties.rb +41 -73
- data/lib/couchrest/{typecast.rb → mixins/typecast.rb} +4 -5
- data/lib/couchrest/property.rb +64 -18
- data/lib/couchrest/validation.rb +6 -5
- data/lib/couchrest/validation/auto_validate.rb +4 -4
- data/lib/couchrest_extended_document.rb +1 -0
- data/spec/couchrest/casted_model_spec.rb +20 -2
- data/spec/couchrest/extended_doc_spec.rb +10 -9
- data/spec/couchrest/property_spec.rb +143 -1
- data/spec/fixtures/more/card.rb +1 -1
- data/spec/fixtures/more/question.rb +3 -2
- metadata +13 -16
- data/lib/couchrest/mixins/validation.rb +0 -245
| @@ -100,7 +100,7 @@ module CouchRest | |
| 100 100 | 
             
                    end
         | 
| 101 101 |  | 
| 102 102 | 
             
                    # length
         | 
| 103 | 
            -
                    if property. | 
| 103 | 
            +
                    if property.type_class == String
         | 
| 104 104 | 
             
                      # XXX: maybe length should always return a Range, with the min defaulting to 1
         | 
| 105 105 | 
             
                      # 52 being the max set 
         | 
| 106 106 | 
             
                      len = property.options.fetch(:length, property.options.fetch(:size, 52))
         | 
| @@ -137,16 +137,16 @@ module CouchRest | |
| 137 137 | 
             
                    end
         | 
| 138 138 |  | 
| 139 139 | 
             
                    # numeric validator
         | 
| 140 | 
            -
                    if  | 
| 140 | 
            +
                    if property.type_class == Integer
         | 
| 141 141 | 
             
                      opts[:integer_only] = true
         | 
| 142 142 | 
             
                      validates_numericality_of property.name, options_with_message(opts, property, :is_number)
         | 
| 143 | 
            -
                    elsif Float == property. | 
| 143 | 
            +
                    elsif Float == property.type_class
         | 
| 144 144 | 
             
                      opts[:precision] = property.precision
         | 
| 145 145 | 
             
                      opts[:scale]     = property.scale
         | 
| 146 146 | 
             
                      validates_numericality_of property.name, options_with_message(opts, property, :is_number)
         | 
| 147 147 | 
             
                    end
         | 
| 148 148 |  | 
| 149 | 
            -
                    # marked the property  | 
| 149 | 
            +
                    # marked the property as checked
         | 
| 150 150 | 
             
                    property.autovalidation_check = true
         | 
| 151 151 |  | 
| 152 152 | 
             
                  end
         | 
| @@ -19,8 +19,11 @@ end | |
| 19 19 | 
             
            class DummyModel < CouchRest::ExtendedDocument
         | 
| 20 20 | 
             
              use_database TEST_SERVER.default_database
         | 
| 21 21 | 
             
              raise "Default DB not set" if TEST_SERVER.default_database.nil?
         | 
| 22 | 
            -
              property :casted_attribute,  | 
| 23 | 
            -
              property :keywords,          | 
| 22 | 
            +
              property :casted_attribute, WithCastedModelMixin
         | 
| 23 | 
            +
              property :keywords,         ["String"]
         | 
| 24 | 
            +
              property :sub_models do |child|
         | 
| 25 | 
            +
                child.property :title
         | 
| 26 | 
            +
              end
         | 
| 24 27 | 
             
            end
         | 
| 25 28 |  | 
| 26 29 | 
             
            class CastedCallbackDoc < CouchRest::ExtendedDocument
         | 
| @@ -82,6 +85,20 @@ describe CouchRest::CastedModel do | |
| 82 85 | 
             
                  @casted_obj.should == nil
         | 
| 83 86 | 
             
                end
         | 
| 84 87 | 
             
              end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
              describe "anonymous sub casted models" do
         | 
| 90 | 
            +
                before :each do
         | 
| 91 | 
            +
                  @obj = DummyModel.new
         | 
| 92 | 
            +
                end
         | 
| 93 | 
            +
                it "should be empty initially" do
         | 
| 94 | 
            +
                  @obj.sub_models.should_not be_nil
         | 
| 95 | 
            +
                  @obj.sub_models.should be_empty
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
                it "should be updatable using a hash" do
         | 
| 98 | 
            +
                  @obj.sub_models << {:title => 'test'}
         | 
| 99 | 
            +
                  @obj.sub_models.first.title.should eql('test')
         | 
| 100 | 
            +
                end
         | 
| 101 | 
            +
              end
         | 
| 85 102 |  | 
| 86 103 | 
             
              describe "casted as attribute" do
         | 
| 87 104 | 
             
                before(:each) do
         | 
| @@ -308,6 +325,7 @@ describe CouchRest::CastedModel do | |
| 308 325 | 
             
                  @cat.create
         | 
| 309 326 | 
             
                  @cat.save
         | 
| 310 327 | 
             
                  @cat.favorite_toy.should_not be_new
         | 
| 328 | 
            +
                  @cat.toys.first.casted_by.should eql(@cat)
         | 
| 311 329 | 
             
                  @cat.toys.first.should_not be_new
         | 
| 312 330 | 
             
                end
         | 
| 313 331 |  | 
| @@ -226,18 +226,19 @@ describe "ExtendedDocument" do | |
| 226 226 | 
             
                  @art['title'].should == "something else"
         | 
| 227 227 | 
             
                end
         | 
| 228 228 |  | 
| 229 | 
            -
                it "should flip out if an attribute= method is missing" do
         | 
| 229 | 
            +
                it "should not flip out if an attribute= method is missing and ignore it" do
         | 
| 230 230 | 
             
                  lambda {
         | 
| 231 | 
            -
                    @art.update_attributes_without_saving('slug' => "new-slug", :title => "super danger") | 
| 232 | 
            -
                  }. | 
| 231 | 
            +
                    @art.update_attributes_without_saving('slug' => "new-slug", :title => "super danger")
         | 
| 232 | 
            +
                  }.should_not raise_error
         | 
| 233 | 
            +
                  @art.slug.should == "big-bad-danger"
         | 
| 233 234 | 
             
                end
         | 
| 234 235 |  | 
| 235 | 
            -
                it "should not change other attributes if there is an error" do
         | 
| 236 | 
            -
             | 
| 237 | 
            -
             | 
| 238 | 
            -
             | 
| 239 | 
            -
             | 
| 240 | 
            -
                end
         | 
| 236 | 
            +
                #it "should not change other attributes if there is an error" do
         | 
| 237 | 
            +
                #  lambda {
         | 
| 238 | 
            +
                #    @art.update_attributes_without_saving('slug' => "new-slug", :title => "super danger")        
         | 
| 239 | 
            +
                #  }.should raise_error
         | 
| 240 | 
            +
                #  @art['title'].should == "big bad danger"
         | 
| 241 | 
            +
                #end
         | 
| 241 242 | 
             
              end
         | 
| 242 243 |  | 
| 243 244 | 
             
              describe "update attributes" do
         | 
| @@ -57,7 +57,45 @@ describe "ExtendedDocument properties" do | |
| 57 57 | 
             
                @card.created_at.should_not be_nil
         | 
| 58 58 | 
             
                @card.updated_at.should_not be_nil
         | 
| 59 59 | 
             
              end
         | 
| 60 | 
            -
             | 
| 60 | 
            +
             | 
| 61 | 
            +
              it "should let you use read_attribute method" do
         | 
| 62 | 
            +
                @card.last_name = "Aimonetti"
         | 
| 63 | 
            +
                @card.read_attribute(:last_name).should eql('Aimonetti')
         | 
| 64 | 
            +
                @card.read_attribute('last_name').should eql('Aimonetti')
         | 
| 65 | 
            +
                last_name_prop = @card.properties.find{|p| p.name == 'last_name'}
         | 
| 66 | 
            +
                @card.read_attribute(last_name_prop).should eql('Aimonetti')
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
              it "should let you use write_attribute method" do
         | 
| 70 | 
            +
                @card.write_attribute(:last_name, 'Aimonetti 1')
         | 
| 71 | 
            +
                @card.last_name.should eql('Aimonetti 1')
         | 
| 72 | 
            +
                @card.write_attribute('last_name', 'Aimonetti 2')
         | 
| 73 | 
            +
                @card.last_name.should eql('Aimonetti 2')
         | 
| 74 | 
            +
                last_name_prop = @card.properties.find{|p| p.name == 'last_name'}
         | 
| 75 | 
            +
                @card.write_attribute(last_name_prop, 'Aimonetti 3')
         | 
| 76 | 
            +
                @card.last_name.should eql('Aimonetti 3')
         | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              it "should let you use write_attribute on readonly properties" do
         | 
| 80 | 
            +
                lambda {
         | 
| 81 | 
            +
                  @card.read_only_value = "foo"
         | 
| 82 | 
            +
                }.should raise_error
         | 
| 83 | 
            +
                @card.write_attribute(:read_only_value, "foo")
         | 
| 84 | 
            +
                @card.read_only_value.should == 'foo'
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              it "should cast via write_attribute" do
         | 
| 88 | 
            +
                @card.write_attribute(:cast_alias, {:name => ["Sam", "Lown"]})
         | 
| 89 | 
            +
                @card.cast_alias.class.should eql(Person)
         | 
| 90 | 
            +
                @card.cast_alias.name.last.should eql("Lown")
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
              it "should not cast via write_attribute if property not casted" do
         | 
| 94 | 
            +
                @card.write_attribute(:first_name, {:name => "Sam"})
         | 
| 95 | 
            +
                @card.first_name.class.should eql(Hash)
         | 
| 96 | 
            +
                @card.first_name[:name].should eql("Sam")
         | 
| 97 | 
            +
              end
         | 
| 98 | 
            +
             | 
| 61 99 |  | 
| 62 100 | 
             
              describe "mass assignment protection" do
         | 
| 63 101 |  | 
| @@ -646,3 +684,107 @@ describe "a casted model retrieved from the database" do | |
| 646 684 | 
             
                end
         | 
| 647 685 | 
             
              end
         | 
| 648 686 | 
             
            end
         | 
| 687 | 
            +
             | 
| 688 | 
            +
            describe "Property Class" do
         | 
| 689 | 
            +
             | 
| 690 | 
            +
              it "should provide name as string" do
         | 
| 691 | 
            +
                property = CouchRest::Property.new(:test, String)
         | 
| 692 | 
            +
                property.name.should eql('test')
         | 
| 693 | 
            +
                property.to_s.should eql('test')
         | 
| 694 | 
            +
              end
         | 
| 695 | 
            +
             | 
| 696 | 
            +
              it "should provide class from type" do
         | 
| 697 | 
            +
                property = CouchRest::Property.new(:test, String)
         | 
| 698 | 
            +
                property.type_class.should eql(String)
         | 
| 699 | 
            +
              end
         | 
| 700 | 
            +
             | 
| 701 | 
            +
              it "should provide base class from type in array" do
         | 
| 702 | 
            +
                property = CouchRest::Property.new(:test, [String])
         | 
| 703 | 
            +
                property.type_class.should eql(String)
         | 
| 704 | 
            +
              end
         | 
| 705 | 
            +
             | 
| 706 | 
            +
              it "should leave type as string if requested" do
         | 
| 707 | 
            +
                property = CouchRest::Property.new(:test, 'String')
         | 
| 708 | 
            +
                property.type.should eql('String')
         | 
| 709 | 
            +
                property.type_class.should eql(String)
         | 
| 710 | 
            +
              end
         | 
| 711 | 
            +
             | 
| 712 | 
            +
              it "should leave type nil and return string by default" do
         | 
| 713 | 
            +
                property = CouchRest::Property.new(:test, nil)
         | 
| 714 | 
            +
                property.type.should be_nil
         | 
| 715 | 
            +
                # Type cast should never be used on non-casted property!
         | 
| 716 | 
            +
                property.type_class.should eql(String)
         | 
| 717 | 
            +
              end
         | 
| 718 | 
            +
             | 
| 719 | 
            +
              it "should convert empty type array to [String]" do
         | 
| 720 | 
            +
                property = CouchRest::Property.new(:test, [])
         | 
| 721 | 
            +
                property.type_class.should eql(String)
         | 
| 722 | 
            +
              end
         | 
| 723 | 
            +
             | 
| 724 | 
            +
              it "should convert boolean text-type TrueClass" do
         | 
| 725 | 
            +
                property = CouchRest::Property.new(:test, 'boolean')
         | 
| 726 | 
            +
                property.type.should eql('boolean') # no change
         | 
| 727 | 
            +
                property.type_class.should eql(TrueClass)
         | 
| 728 | 
            +
              end
         | 
| 729 | 
            +
             | 
| 730 | 
            +
              it "should set init method option or leave as 'new'" do
         | 
| 731 | 
            +
                # (bad example! Time already typecast)
         | 
| 732 | 
            +
                property = CouchRest::Property.new(:test, Time)
         | 
| 733 | 
            +
                property.init_method.should eql('new')
         | 
| 734 | 
            +
                property = CouchRest::Property.new(:test, Time, :init_method => 'parse')
         | 
| 735 | 
            +
                property.init_method.should eql('parse')
         | 
| 736 | 
            +
              end
         | 
| 737 | 
            +
             | 
| 738 | 
            +
              ## Property Casting method. More thoroughly tested earlier.
         | 
| 739 | 
            +
             | 
| 740 | 
            +
              describe "casting" do
         | 
| 741 | 
            +
                it "should cast a value" do
         | 
| 742 | 
            +
                  property = CouchRest::Property.new(:test, Date)
         | 
| 743 | 
            +
                  parent = mock("FooObject")
         | 
| 744 | 
            +
                  property.cast(parent, "2010-06-16").should eql(Date.new(2010, 6, 16))
         | 
| 745 | 
            +
                  property.cast_value(parent, "2010-06-16").should eql(Date.new(2010, 6, 16))
         | 
| 746 | 
            +
                end
         | 
| 747 | 
            +
             | 
| 748 | 
            +
                it "should cast an array of values" do
         | 
| 749 | 
            +
                  property = CouchRest::Property.new(:test, [Date])
         | 
| 750 | 
            +
                  parent = mock("FooObject")
         | 
| 751 | 
            +
                  property.cast(parent, ["2010-06-01", "2010-06-02"]).should eql([Date.new(2010, 6, 1), Date.new(2010, 6, 2)])
         | 
| 752 | 
            +
                end
         | 
| 753 | 
            +
             | 
| 754 | 
            +
                it "should set a CastedArray on array of Objects" do
         | 
| 755 | 
            +
                  property = CouchRest::Property.new(:test, [Object])
         | 
| 756 | 
            +
                  parent = mock("FooObject")
         | 
| 757 | 
            +
                  property.cast(parent, ["2010-06-01", "2010-06-02"]).class.should eql(::CouchRest::CastedArray)
         | 
| 758 | 
            +
                end
         | 
| 759 | 
            +
             | 
| 760 | 
            +
                it "should not set a CastedArray on array of Strings" do
         | 
| 761 | 
            +
                  property = CouchRest::Property.new(:test, [String])
         | 
| 762 | 
            +
                  parent = mock("FooObject")
         | 
| 763 | 
            +
                  property.cast(parent, ["2010-06-01", "2010-06-02"]).class.should_not eql(::CouchRest::CastedArray)
         | 
| 764 | 
            +
                end
         | 
| 765 | 
            +
             | 
| 766 | 
            +
                it "should raise and error if value is array when type is not" do
         | 
| 767 | 
            +
                  property = CouchRest::Property.new(:test, Date)
         | 
| 768 | 
            +
                  parent = mock("FooClass")
         | 
| 769 | 
            +
                  lambda {
         | 
| 770 | 
            +
                    cast = property.cast(parent, [Date.new(2010, 6, 1)])
         | 
| 771 | 
            +
                  }.should raise_error
         | 
| 772 | 
            +
                end
         | 
| 773 | 
            +
             | 
| 774 | 
            +
             | 
| 775 | 
            +
                it "should set parent as casted_by object in CastedArray" do
         | 
| 776 | 
            +
                  property = CouchRest::Property.new(:test, [Object])
         | 
| 777 | 
            +
                  parent = mock("FooObject")
         | 
| 778 | 
            +
                  property.cast(parent, ["2010-06-01", "2010-06-02"]).casted_by.should eql(parent)     
         | 
| 779 | 
            +
                end
         | 
| 780 | 
            +
             | 
| 781 | 
            +
                it "should set casted_by on new value" do
         | 
| 782 | 
            +
                  property = CouchRest::Property.new(:test, CatToy)
         | 
| 783 | 
            +
                  parent = mock("CatObject")
         | 
| 784 | 
            +
                  cast = property.cast(parent, {:name => 'catnip'})
         | 
| 785 | 
            +
                  cast.casted_by.should eql(parent)
         | 
| 786 | 
            +
                end
         | 
| 787 | 
            +
             | 
| 788 | 
            +
              end
         | 
| 789 | 
            +
             | 
| 790 | 
            +
            end
         | 
    
        data/spec/fixtures/more/card.rb
    CHANGED
    
    | @@ -11,7 +11,7 @@ class Card < CouchRest::ExtendedDocument | |
| 11 11 | 
             
              property :first_name
         | 
| 12 12 | 
             
              property :last_name,        :alias     => :family_name
         | 
| 13 13 | 
             
              property :read_only_value,  :read_only => true
         | 
| 14 | 
            -
              property :cast_alias,        | 
| 14 | 
            +
              property :cast_alias,       Person,  :alias  => :calias
         | 
| 15 15 |  | 
| 16 16 |  | 
| 17 17 | 
             
              timestamps!
         | 
    
        metadata
    CHANGED
    
    | @@ -1,25 +1,25 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: couchrest_extended_document
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
              hash:  | 
| 5 | 
            -
              prerelease:  | 
| 4 | 
            +
              hash: 23
         | 
| 5 | 
            +
              prerelease: false
         | 
| 6 6 | 
             
              segments: 
         | 
| 7 7 | 
             
              - 1
         | 
| 8 8 | 
             
              - 0
         | 
| 9 9 | 
             
              - 0
         | 
| 10 | 
            -
               | 
| 11 | 
            -
              version: 1.0.0.beta6
         | 
| 10 | 
            +
              version: 1.0.0
         | 
| 12 11 | 
             
            platform: ruby
         | 
| 13 12 | 
             
            authors: 
         | 
| 14 13 | 
             
            - J. Chris Anderson
         | 
| 15 14 | 
             
            - Matt Aimonetti
         | 
| 16 15 | 
             
            - Marcos Tapajos
         | 
| 17 16 | 
             
            - Will Leinweber
         | 
| 17 | 
            +
            - Sam Lown
         | 
| 18 18 | 
             
            autorequire: 
         | 
| 19 19 | 
             
            bindir: bin
         | 
| 20 20 | 
             
            cert_chain: []
         | 
| 21 21 |  | 
| 22 | 
            -
            date: 2010- | 
| 22 | 
            +
            date: 2010-08-01 00:00:00 -03:00
         | 
| 23 23 | 
             
            default_executable: 
         | 
| 24 24 | 
             
            dependencies: 
         | 
| 25 25 | 
             
            - !ruby/object:Gem::Dependency 
         | 
| @@ -30,13 +30,12 @@ dependencies: | |
| 30 30 | 
             
                requirements: 
         | 
| 31 31 | 
             
                - - ">="
         | 
| 32 32 | 
             
                  - !ruby/object:Gem::Version 
         | 
| 33 | 
            -
                    hash:  | 
| 33 | 
            +
                    hash: 23
         | 
| 34 34 | 
             
                    segments: 
         | 
| 35 35 | 
             
                    - 1
         | 
| 36 36 | 
             
                    - 0
         | 
| 37 37 | 
             
                    - 0
         | 
| 38 | 
            -
                     | 
| 39 | 
            -
                    version: 1.0.0.beta
         | 
| 38 | 
            +
                    version: 1.0.0
         | 
| 40 39 | 
             
              type: :runtime
         | 
| 41 40 | 
             
              version_requirements: *id001
         | 
| 42 41 | 
             
            - !ruby/object:Gem::Dependency 
         | 
| @@ -108,6 +107,7 @@ files: | |
| 108 107 | 
             
            - lib/couchrest/extended_document.rb
         | 
| 109 108 | 
             
            - lib/couchrest/mixins.rb
         | 
| 110 109 | 
             
            - lib/couchrest/mixins/attribute_protection.rb
         | 
| 110 | 
            +
            - lib/couchrest/mixins/attributes.rb
         | 
| 111 111 | 
             
            - lib/couchrest/mixins/callbacks.rb
         | 
| 112 112 | 
             
            - lib/couchrest/mixins/class_proxy.rb
         | 
| 113 113 | 
             
            - lib/couchrest/mixins/collection.rb
         | 
| @@ -115,12 +115,11 @@ files: | |
| 115 115 | 
             
            - lib/couchrest/mixins/document_queries.rb
         | 
| 116 116 | 
             
            - lib/couchrest/mixins/extended_attachments.rb
         | 
| 117 117 | 
             
            - lib/couchrest/mixins/properties.rb
         | 
| 118 | 
            -
            - lib/couchrest/mixins/ | 
| 118 | 
            +
            - lib/couchrest/mixins/typecast.rb
         | 
| 119 119 | 
             
            - lib/couchrest/mixins/views.rb
         | 
| 120 120 | 
             
            - lib/couchrest/property.rb
         | 
| 121 121 | 
             
            - lib/couchrest/support/couchrest.rb
         | 
| 122 122 | 
             
            - lib/couchrest/support/rails.rb
         | 
| 123 | 
            -
            - lib/couchrest/typecast.rb
         | 
| 124 123 | 
             
            - lib/couchrest/validation.rb
         | 
| 125 124 | 
             
            - lib/couchrest/validation/auto_validate.rb
         | 
| 126 125 | 
             
            - lib/couchrest/validation/contextual_validators.rb
         | 
| @@ -188,14 +187,12 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 188 187 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement 
         | 
| 189 188 | 
             
              none: false
         | 
| 190 189 | 
             
              requirements: 
         | 
| 191 | 
            -
              - - " | 
| 190 | 
            +
              - - ">="
         | 
| 192 191 | 
             
                - !ruby/object:Gem::Version 
         | 
| 193 | 
            -
                  hash:  | 
| 192 | 
            +
                  hash: 3
         | 
| 194 193 | 
             
                  segments: 
         | 
| 195 | 
            -
                  -  | 
| 196 | 
            -
                   | 
| 197 | 
            -
                  - 1
         | 
| 198 | 
            -
                  version: 1.3.1
         | 
| 194 | 
            +
                  - 0
         | 
| 195 | 
            +
                  version: "0"
         | 
| 199 196 | 
             
            requirements: []
         | 
| 200 197 |  | 
| 201 198 | 
             
            rubyforge_project: 
         | 
| @@ -1,245 +0,0 @@ | |
| 1 | 
            -
            # Extracted from dm-validations 0.9.10
         | 
| 2 | 
            -
            #
         | 
| 3 | 
            -
            # Copyright (c) 2007 Guy van den Berg
         | 
| 4 | 
            -
            # 
         | 
| 5 | 
            -
            # Permission is hereby granted, free of charge, to any person obtaining
         | 
| 6 | 
            -
            # a copy of this software and associated documentation files (the
         | 
| 7 | 
            -
            # "Software"), to deal in the Software without restriction, including
         | 
| 8 | 
            -
            # without limitation the rights to use, copy, modify, merge, publish,
         | 
| 9 | 
            -
            # distribute, sublicense, and/or sell copies of the Software, and to
         | 
| 10 | 
            -
            # permit persons to whom the Software is furnished to do so, subject to
         | 
| 11 | 
            -
            # the following conditions:
         | 
| 12 | 
            -
            # 
         | 
| 13 | 
            -
            # The above copyright notice and this permission notice shall be
         | 
| 14 | 
            -
            # included in all copies or substantial portions of the Software.
         | 
| 15 | 
            -
            # 
         | 
| 16 | 
            -
            # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         | 
| 17 | 
            -
            # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         | 
| 18 | 
            -
            # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         | 
| 19 | 
            -
            # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         | 
| 20 | 
            -
            # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         | 
| 21 | 
            -
            # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         | 
| 22 | 
            -
            # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
| 23 | 
            -
             | 
| 24 | 
            -
            class Object
         | 
| 25 | 
            -
              def validatable?
         | 
| 26 | 
            -
                false
         | 
| 27 | 
            -
              end
         | 
| 28 | 
            -
            end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
            require 'pathname'
         | 
| 31 | 
            -
             | 
| 32 | 
            -
            dir = File.join(Pathname(__FILE__).dirname.expand_path, '..', 'validation')
         | 
| 33 | 
            -
             | 
| 34 | 
            -
            require File.join(dir, 'validation_errors')
         | 
| 35 | 
            -
            require File.join(dir, 'contextual_validators')
         | 
| 36 | 
            -
            require File.join(dir, 'auto_validate')
         | 
| 37 | 
            -
             | 
| 38 | 
            -
            require File.join(dir, 'validators', 'generic_validator')
         | 
| 39 | 
            -
            require File.join(dir, 'validators', 'required_field_validator')
         | 
| 40 | 
            -
            require File.join(dir, 'validators', 'absent_field_validator')
         | 
| 41 | 
            -
            require File.join(dir, 'validators', 'format_validator')
         | 
| 42 | 
            -
            require File.join(dir, 'validators', 'length_validator')
         | 
| 43 | 
            -
            require File.join(dir, 'validators', 'numeric_validator')
         | 
| 44 | 
            -
            require File.join(dir, 'validators', 'method_validator')
         | 
| 45 | 
            -
            require File.join(dir, 'validators', 'confirmation_validator')
         | 
| 46 | 
            -
             | 
| 47 | 
            -
            module CouchRest
         | 
| 48 | 
            -
              module Validation
         | 
| 49 | 
            -
                
         | 
| 50 | 
            -
                def self.included(base)
         | 
| 51 | 
            -
                  base.extlib_inheritable_accessor(:auto_validation)
         | 
| 52 | 
            -
                  base.class_eval <<-EOS, __FILE__, __LINE__ + 1
         | 
| 53 | 
            -
                      # Callbacks
         | 
| 54 | 
            -
                      define_callbacks :validate
         | 
| 55 | 
            -
                      
         | 
| 56 | 
            -
                      # Turn off auto validation by default
         | 
| 57 | 
            -
                      self.auto_validation ||= false
         | 
| 58 | 
            -
                      
         | 
| 59 | 
            -
                      # Force the auto validation for the class properties
         | 
| 60 | 
            -
                      # This feature is still not fully ported over,
         | 
| 61 | 
            -
                      # test are lacking, so please use with caution
         | 
| 62 | 
            -
                      def self.auto_validate!
         | 
| 63 | 
            -
                        self.auto_validation = true
         | 
| 64 | 
            -
                      end
         | 
| 65 | 
            -
                      
         | 
| 66 | 
            -
                      # share the validations with subclasses
         | 
| 67 | 
            -
                      def self.inherited(subklass)
         | 
| 68 | 
            -
                        self.validators.contexts.each do |k, v|
         | 
| 69 | 
            -
                          subklass.validators.contexts[k] = v.dup
         | 
| 70 | 
            -
                        end
         | 
| 71 | 
            -
                        super
         | 
| 72 | 
            -
                      end
         | 
| 73 | 
            -
                  EOS
         | 
| 74 | 
            -
                  
         | 
| 75 | 
            -
                  base.extend(ClassMethods)
         | 
| 76 | 
            -
                  base.class_eval <<-EOS, __FILE__, __LINE__ + 1
         | 
| 77 | 
            -
                    define_callbacks :validate
         | 
| 78 | 
            -
                    if method_defined?(:_run_save_callbacks)
         | 
| 79 | 
            -
                      set_callback :save, :before, :check_validations
         | 
| 80 | 
            -
                    end
         | 
| 81 | 
            -
                  EOS
         | 
| 82 | 
            -
                  base.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
         | 
| 83 | 
            -
                    def self.define_property(name, options={})
         | 
| 84 | 
            -
                      super
         | 
| 85 | 
            -
                      auto_generate_validations(properties.last) if properties && properties.size > 0
         | 
| 86 | 
            -
                      autovalidation_check = true
         | 
| 87 | 
            -
                    end
         | 
| 88 | 
            -
                  RUBY_EVAL
         | 
| 89 | 
            -
                end
         | 
| 90 | 
            -
             | 
| 91 | 
            -
                # Ensures the object is valid for the context provided, and otherwise
         | 
| 92 | 
            -
                # throws :halt and returns false.
         | 
| 93 | 
            -
                #
         | 
| 94 | 
            -
                def check_validations(context = :default)
         | 
| 95 | 
            -
                  throw(:halt, false) unless context.nil? || valid?(context)
         | 
| 96 | 
            -
                end
         | 
| 97 | 
            -
                
         | 
| 98 | 
            -
                # Return the ValidationErrors
         | 
| 99 | 
            -
                #
         | 
| 100 | 
            -
                def errors
         | 
| 101 | 
            -
                  @errors ||= ValidationErrors.new
         | 
| 102 | 
            -
                end
         | 
| 103 | 
            -
             | 
| 104 | 
            -
                # Mark this resource as validatable. When we validate associations of a
         | 
| 105 | 
            -
                # resource we can check if they respond to validatable? before trying to
         | 
| 106 | 
            -
                # recursivly validate them
         | 
| 107 | 
            -
                #
         | 
| 108 | 
            -
                def validatable?
         | 
| 109 | 
            -
                  true
         | 
| 110 | 
            -
                end
         | 
| 111 | 
            -
             | 
| 112 | 
            -
                # Alias for valid?(:default)
         | 
| 113 | 
            -
                #
         | 
| 114 | 
            -
                def valid_for_default?
         | 
| 115 | 
            -
                  valid?(:default)
         | 
| 116 | 
            -
                end
         | 
| 117 | 
            -
             | 
| 118 | 
            -
                # Check if a resource is valid in a given context
         | 
| 119 | 
            -
                #
         | 
| 120 | 
            -
                def valid?(context = :default)
         | 
| 121 | 
            -
                  recursive_valid?(self, context, true)
         | 
| 122 | 
            -
                end
         | 
| 123 | 
            -
                
         | 
| 124 | 
            -
                # checking on casted objects
         | 
| 125 | 
            -
                def validate_casted_arrays
         | 
| 126 | 
            -
                  result = true
         | 
| 127 | 
            -
                  array_casted_properties = self.class.properties.select { |property| property.casted && property.type.instance_of?(Array) }
         | 
| 128 | 
            -
                  array_casted_properties.each do |property|
         | 
| 129 | 
            -
                    casted_values = self.send(property.name)
         | 
| 130 | 
            -
                    next unless casted_values.is_a?(Array) && casted_values.first.respond_to?(:valid?)
         | 
| 131 | 
            -
                    casted_values.each do |value|
         | 
| 132 | 
            -
                      result = (result && value.valid?) if value.respond_to?(:valid?)
         | 
| 133 | 
            -
                    end
         | 
| 134 | 
            -
                  end
         | 
| 135 | 
            -
                  result
         | 
| 136 | 
            -
                end
         | 
| 137 | 
            -
             | 
| 138 | 
            -
                # Do recursive validity checking
         | 
| 139 | 
            -
                #
         | 
| 140 | 
            -
                def recursive_valid?(target, context, state)
         | 
| 141 | 
            -
                  valid = state
         | 
| 142 | 
            -
                  target.each do |key, prop|
         | 
| 143 | 
            -
                    if prop.is_a?(Array)
         | 
| 144 | 
            -
                      prop.each do |item|
         | 
| 145 | 
            -
                        if item.validatable?
         | 
| 146 | 
            -
                          valid = recursive_valid?(item, context, valid) && valid
         | 
| 147 | 
            -
                        end
         | 
| 148 | 
            -
                      end
         | 
| 149 | 
            -
                    elsif prop.validatable?
         | 
| 150 | 
            -
                      valid = recursive_valid?(prop, context, valid) && valid
         | 
| 151 | 
            -
                    end
         | 
| 152 | 
            -
                  end
         | 
| 153 | 
            -
                  target._run_validate_callbacks do
         | 
| 154 | 
            -
                    target.class.validators.execute(context, target) && valid
         | 
| 155 | 
            -
                  end
         | 
| 156 | 
            -
                end
         | 
| 157 | 
            -
             | 
| 158 | 
            -
             | 
| 159 | 
            -
                def validation_property_value(name)
         | 
| 160 | 
            -
                  self.respond_to?(name, true) ? self.send(name) : nil
         | 
| 161 | 
            -
                end
         | 
| 162 | 
            -
             | 
| 163 | 
            -
                # Get the corresponding Object property, if it exists.
         | 
| 164 | 
            -
                def validation_property(field_name)
         | 
| 165 | 
            -
                  properties.find{|p| p.name == field_name}
         | 
| 166 | 
            -
                end
         | 
| 167 | 
            -
             | 
| 168 | 
            -
                module ClassMethods
         | 
| 169 | 
            -
                  include CouchRest::Validation::ValidatesPresent
         | 
| 170 | 
            -
                  include CouchRest::Validation::ValidatesAbsent
         | 
| 171 | 
            -
                  include CouchRest::Validation::ValidatesIsConfirmed
         | 
| 172 | 
            -
                  # include CouchRest::Validation::ValidatesIsPrimitive
         | 
| 173 | 
            -
                  # include CouchRest::Validation::ValidatesIsAccepted
         | 
| 174 | 
            -
                  include CouchRest::Validation::ValidatesFormat
         | 
| 175 | 
            -
                  include CouchRest::Validation::ValidatesLength
         | 
| 176 | 
            -
                  # include CouchRest::Validation::ValidatesWithin
         | 
| 177 | 
            -
                  include CouchRest::Validation::ValidatesIsNumber
         | 
| 178 | 
            -
                  include CouchRest::Validation::ValidatesWithMethod
         | 
| 179 | 
            -
                  # include CouchRest::Validation::ValidatesWithBlock
         | 
| 180 | 
            -
                  # include CouchRest::Validation::ValidatesIsUnique
         | 
| 181 | 
            -
                  include CouchRest::Validation::AutoValidate
         | 
| 182 | 
            -
                  
         | 
| 183 | 
            -
                  # Return the set of contextual validators or create a new one
         | 
| 184 | 
            -
                  #
         | 
| 185 | 
            -
                  def validators
         | 
| 186 | 
            -
                    @validations ||= ContextualValidators.new
         | 
| 187 | 
            -
                  end
         | 
| 188 | 
            -
                  
         | 
| 189 | 
            -
                  # Clean up the argument list and return a opts hash, including the
         | 
| 190 | 
            -
                  # merging of any default opts. Set the context to default if none is
         | 
| 191 | 
            -
                  # provided. Also allow :context to be aliased to :on, :when & group
         | 
| 192 | 
            -
                  #
         | 
| 193 | 
            -
                  def opts_from_validator_args(args, defaults = nil)
         | 
| 194 | 
            -
                    opts = args.last.kind_of?(Hash) ? args.pop : {}
         | 
| 195 | 
            -
                    context = :default
         | 
| 196 | 
            -
                    context = opts[:context] if opts.has_key?(:context)
         | 
| 197 | 
            -
                    context = opts.delete(:on) if opts.has_key?(:on)
         | 
| 198 | 
            -
                    context = opts.delete(:when) if opts.has_key?(:when)
         | 
| 199 | 
            -
                    context = opts.delete(:group) if opts.has_key?(:group)
         | 
| 200 | 
            -
                    opts[:context] = context
         | 
| 201 | 
            -
                    opts.merge!(defaults) unless defaults.nil?
         | 
| 202 | 
            -
                    opts
         | 
| 203 | 
            -
                  end
         | 
| 204 | 
            -
                  
         | 
| 205 | 
            -
                  # Given a new context create an instance method of
         | 
| 206 | 
            -
                  # valid_for_<context>? which simply calls valid?(context)
         | 
| 207 | 
            -
                  # if it does not already exist
         | 
| 208 | 
            -
                  #
         | 
| 209 | 
            -
                  def create_context_instance_methods(context)
         | 
| 210 | 
            -
                    name = "valid_for_#{context.to_s}?"           # valid_for_signup?
         | 
| 211 | 
            -
                    if !self.instance_methods.include?(name)
         | 
| 212 | 
            -
                      class_eval <<-EOS, __FILE__, __LINE__ + 1
         | 
| 213 | 
            -
                        def #{name}                               # def valid_for_signup?
         | 
| 214 | 
            -
                          valid?('#{context.to_s}'.to_sym)        #   valid?('signup'.to_sym)
         | 
| 215 | 
            -
                        end                                       # end
         | 
| 216 | 
            -
                      EOS
         | 
| 217 | 
            -
                    end
         | 
| 218 | 
            -
                  end
         | 
| 219 | 
            -
             | 
| 220 | 
            -
                  # Create a new validator of the given klazz and push it onto the
         | 
| 221 | 
            -
                  # requested context for each of the attributes in the fields list
         | 
| 222 | 
            -
                  #
         | 
| 223 | 
            -
                  def add_validator_to_context(opts, fields, klazz)
         | 
| 224 | 
            -
                    fields.each do |field|
         | 
| 225 | 
            -
                      validator = klazz.new(field.to_sym, opts)
         | 
| 226 | 
            -
                      if opts[:context].is_a?(Symbol)
         | 
| 227 | 
            -
                        unless validators.context(opts[:context]).include?(validator)
         | 
| 228 | 
            -
                          validators.context(opts[:context]) << validator
         | 
| 229 | 
            -
                          create_context_instance_methods(opts[:context])
         | 
| 230 | 
            -
                        end
         | 
| 231 | 
            -
                      elsif opts[:context].is_a?(Array)
         | 
| 232 | 
            -
                        opts[:context].each do |c|
         | 
| 233 | 
            -
                          unless validators.context(c).include?(validator)
         | 
| 234 | 
            -
                            validators.context(c) << validator
         | 
| 235 | 
            -
                            create_context_instance_methods(c)
         | 
| 236 | 
            -
                          end
         | 
| 237 | 
            -
                        end
         | 
| 238 | 
            -
                      end
         | 
| 239 | 
            -
                    end
         | 
| 240 | 
            -
                  end
         | 
| 241 | 
            -
                  
         | 
| 242 | 
            -
                end # module ClassMethods
         | 
| 243 | 
            -
              end # module Validation
         | 
| 244 | 
            -
             | 
| 245 | 
            -
            end # module CouchRest
         |