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.
@@ -100,7 +100,7 @@ module CouchRest
100
100
  end
101
101
 
102
102
  # length
103
- if property.type == String
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 "Integer" == property.type
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.type
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 has checked
149
+ # marked the property as checked
150
150
  property.autovalidation_check = true
151
151
 
152
152
  end
@@ -5,6 +5,7 @@ gem "builder", ">=2.1.2"
5
5
 
6
6
  gem 'activesupport', ">= 2.3.0"
7
7
  require 'active_support/core_ext'
8
+ require 'active_support/json'
8
9
 
9
10
  gem "mime-types", ">= 1.15"
10
11
  require 'mime/types'
@@ -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, :cast_as => 'WithCastedModelMixin'
23
- property :keywords, :cast_as => ["String"]
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
- }.should raise_error
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
- lambda {
237
- @art.update_attributes_without_saving('slug' => "new-slug", :title => "super danger")
238
- }.should raise_error
239
- @art['title'].should == "big bad danger"
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
@@ -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, :cast_as => Person, :alias => :calias
14
+ property :cast_alias, Person, :alias => :calias
15
15
 
16
16
 
17
17
  timestamps!
@@ -2,5 +2,6 @@ class Question < Hash
2
2
  include ::CouchRest::CastedModel
3
3
 
4
4
  property :q
5
- property :a, :type => 'Object'
6
- end
5
+ property :a
6
+
7
+ end
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: -1848230058
5
- prerelease: true
4
+ hash: 23
5
+ prerelease: false
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
9
  - 0
10
- - beta6
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-07-01 00:00:00 -03:00
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: 31098193
33
+ hash: 23
34
34
  segments:
35
35
  - 1
36
36
  - 0
37
37
  - 0
38
- - beta
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/validation.rb
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: 25
192
+ hash: 3
194
193
  segments:
195
- - 1
196
- - 3
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