couchrest_model 2.0.0.beta → 2.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0.beta
1
+ 2.0.0.beta2
@@ -23,14 +23,15 @@ Gem::Specification.new do |s|
23
23
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
24
  s.require_paths = ["lib"]
25
25
 
26
- s.add_dependency(%q<couchrest>, "~> 1.1.2")
26
+ s.add_dependency(%q<couchrest>, "~> 1.1.3")
27
27
  s.add_dependency(%q<mime-types>, "~> 1.15")
28
- s.add_dependency(%q<activemodel>, "~> 3.1.0")
28
+ s.add_dependency(%q<activemodel>, "~> 3.0")
29
29
  s.add_dependency(%q<tzinfo>, "~> 0.3.22")
30
30
  s.add_development_dependency(%q<rspec>, "~> 2.6.0")
31
31
  s.add_development_dependency(%q<json>, ["~> 1.5.1"])
32
32
  s.add_development_dependency(%q<rack-test>, ">= 0.5.7")
33
33
  s.add_development_dependency("rake", ">= 0.8.0")
34
+ s.add_development_dependency("debugger", "~> 1.2.0")
34
35
  # s.add_development_dependency("jruby-openssl", ">= 0.7.3")
35
36
  end
36
37
 
data/history.md CHANGED
@@ -1,11 +1,15 @@
1
1
  # CouchRest Model Change History
2
2
 
3
- ## 2.0.0.beta - 2012-06-14
3
+ ## 2.0.0.beta2 - 2012-08-02
4
4
 
5
5
  * Added design doc migration support, including for proxied models
6
6
  * Rake tasks available for migrations
7
7
  * Rails config option now available: `config.couchrest_model.auto_update_design_docs = false`
8
8
  * Skipping 1.2 version due to design doc API changes
9
+ * Added 'couchrest_typecast' class method support for typecasting with special classes.
10
+ * Added :allow_blank option to properties so that empty strings are forced to nil.
11
+ * Modified associations to use allow_blank property
12
+ * Incorported Rails 3.2 support changes (Thanks @jodosha)
9
13
 
10
14
  ## 1.2.0.beta - 2012-06-08
11
15
 
@@ -38,7 +38,7 @@ module CouchRest
38
38
  def belongs_to(attrib, *options)
39
39
  opts = merge_belongs_to_association_options(attrib, options.first)
40
40
 
41
- property(opts[:foreign_key], opts)
41
+ property(opts[:foreign_key], String, opts)
42
42
 
43
43
  create_belongs_to_getter(attrib, opts)
44
44
  create_belongs_to_setter(attrib, opts)
@@ -88,7 +88,7 @@ module CouchRest
88
88
  opts[:foreign_key] = opts[:foreign_key].pluralize
89
89
  opts[:readonly] = true
90
90
 
91
- property(opts[:foreign_key], [], opts)
91
+ property(opts[:foreign_key], [String], opts)
92
92
 
93
93
  create_collection_of_property_setter(attrib, opts)
94
94
  create_collection_of_getter(attrib, opts)
@@ -101,8 +101,9 @@ module CouchRest
101
101
  def merge_belongs_to_association_options(attrib, options = nil)
102
102
  opts = {
103
103
  :foreign_key => attrib.to_s.singularize + '_id',
104
- :class_name => attrib.to_s.singularize.camelcase,
105
- :proxy_name => attrib.to_s.pluralize
104
+ :class_name => attrib.to_s.singularize.camelcase,
105
+ :proxy_name => attrib.to_s.pluralize,
106
+ :allow_blank => false
106
107
  }
107
108
  opts.merge!(options) if options.is_a?(Hash)
108
109
 
@@ -224,6 +225,15 @@ module CouchRest
224
225
  raise "Object cannot be added to #{casted_by.class.to_s}##{casted_by_property.to_s} collection unless saved" if obj.new?
225
226
  end
226
227
 
228
+ # Override CastedArray instantiation_and_cast method for a simpler
229
+ # version that will not try to cast the model.
230
+ def instantiate_and_cast(obj, change = true)
231
+ couchrest_parent_will_change! if change && use_dirty?
232
+ obj.casted_by = casted_by if obj.respond_to?(:casted_by)
233
+ obj.casted_by_property = casted_by_property if obj.respond_to?(:casted_by_property)
234
+ obj
235
+ end
236
+
227
237
  end
228
238
 
229
239
  end
@@ -2,7 +2,8 @@ module CouchRest
2
2
  module Model
3
3
  class Base < CouchRest::Document
4
4
 
5
- extend ActiveModel::Naming
5
+ extend ActiveModel::Naming
6
+ include ActiveModel::Conversion
6
7
 
7
8
  include CouchRest::Model::Configuration
8
9
  include CouchRest::Model::Connection
@@ -62,11 +63,6 @@ module CouchRest
62
63
  run_callbacks(:initialize) { self }
63
64
  end
64
65
 
65
- def to_key
66
- new? ? nil : [id]
67
- end
68
-
69
- alias :to_param :id
70
66
  alias :new_record? :new?
71
67
  alias :new_document? :new?
72
68
 
@@ -40,7 +40,7 @@ module CouchRest
40
40
  # with a property and update dirty status
41
41
  def write_attribute(property, value)
42
42
  prop = find_property!(property)
43
- value = prop.is_a?(String) ? value : prop.cast(self, value)
43
+ value = prop.cast(self, value)
44
44
  couchrest_attribute_will_change!(prop.name) if use_dirty? && self[prop.name] != value
45
45
  self[prop.name] = value
46
46
  end
@@ -4,7 +4,7 @@ module CouchRest::Model
4
4
 
5
5
  include ::CouchRest::Model::Typecast
6
6
 
7
- attr_reader :name, :type, :type_class, :read_only, :alias, :default, :casted, :init_method, :options
7
+ attr_reader :name, :type, :type_class, :read_only, :alias, :default, :casted, :init_method, :options, :allow_blank
8
8
 
9
9
  # Attribute to define.
10
10
  # All Properties are assumed casted unless the type is nil.
@@ -33,6 +33,7 @@ module CouchRest::Model
33
33
  raise "Expecting an array or keyed hash for property #{parent.class.name}##{self.name}"
34
34
  end
35
35
  arr = value.collect { |data| cast_value(parent, data) }
36
+ arr.reject!{ |data| data.nil? } unless allow_blank
36
37
  # allow casted_by calls to be passed up chain by wrapping in CastedArray
37
38
  CastedArray.new(arr, self, parent)
38
39
  elsif (type == Object || type == Hash) && (value.is_a?(Hash))
@@ -45,8 +46,12 @@ module CouchRest::Model
45
46
 
46
47
  # Cast an individual value
47
48
  def cast_value(parent, value)
48
- value = typecast_value(value, self)
49
- associate_casted_value_to_parent(parent, value)
49
+ if !allow_blank && value.to_s.empty?
50
+ nil
51
+ else
52
+ value = typecast_value(parent, self, value)
53
+ associate_casted_value_to_parent(parent, value)
54
+ end
50
55
  end
51
56
 
52
57
  def default_value
@@ -64,6 +69,7 @@ module CouchRest::Model
64
69
  # a normal call to the class.
65
70
  def build(*args)
66
71
  raise StandardError, "Cannot build property without a class" if @type_class.nil?
72
+
67
73
  if @init_method.is_a?(Proc)
68
74
  @init_method.call(*args)
69
75
  else
@@ -102,13 +108,13 @@ module CouchRest::Model
102
108
  end
103
109
 
104
110
  def parse_options(options)
105
- @validation_format = options.delete(:format) if options[:format]
106
- @read_only = options.delete(:read_only) if options[:read_only]
107
- @alias = options.delete(:alias) if options[:alias]
108
- @default = options.delete(:default) unless options[:default].nil?
109
- @init_method = options[:init_method] ? options.delete(:init_method) : 'new'
111
+ @validation_format = options.delete(:format) if options[:format]
112
+ @read_only = options.delete(:read_only) if options[:read_only]
113
+ @alias = options.delete(:alias) if options[:alias]
114
+ @default = options.delete(:default) unless options[:default].nil?
115
+ @init_method = options.delete(:init_method) || 'new'
116
+ @allow_blank = options[:allow_blank].nil? ? true : options.delete(:allow_blank)
110
117
  @options = options
111
118
  end
112
-
113
119
  end
114
120
  end
@@ -2,7 +2,7 @@ module CouchRest
2
2
  module Model
3
3
  module Typecast
4
4
 
5
- def typecast_value(value, property) # klass, init_method)
5
+ def typecast_value(parent, property, value)
6
6
  return nil if value.nil?
7
7
  klass = property.type_class
8
8
  if value.instance_of?(klass) || klass == Object
@@ -11,6 +11,8 @@ module CouchRest
11
11
  else
12
12
  value
13
13
  end
14
+ elsif klass.respond_to?(:couchrest_typecast)
15
+ klass.couchrest_typecast(parent, property, value)
14
16
  elsif [String, TrueClass, Integer, Float, BigDecimal, DateTime, Time, Date, Class].include?(klass)
15
17
  send('typecast_to_'+klass.to_s.downcase, value)
16
18
  else
@@ -1,10 +1,12 @@
1
1
  require 'question'
2
2
  require 'person'
3
+ require 'money'
3
4
 
4
5
  class Course < CouchRest::Model::Base
5
6
  use_database TEST_SERVER.default_database
6
7
 
7
8
  property :title, String
9
+ property :subtitle, String, :allow_blank => false
8
10
  property :questions, [Question]
9
11
  property :professor, Person
10
12
  property :participants, [Object]
@@ -17,6 +19,8 @@ class Course < CouchRest::Model::Base
17
19
  property :active, :type => TrueClass
18
20
  property :very_active, :type => TrueClass
19
21
  property :klass, :type => Class
22
+ property :currency, String, :default => 'EUR'
23
+ property :price, Money
20
24
 
21
25
  design do
22
26
  view :by_title
@@ -0,0 +1,24 @@
1
+
2
+ # Really simple money class for testing
3
+ class Money
4
+
5
+ attr_accessor :cents, :currency
6
+
7
+ def initialize(cents, currency = nil)
8
+ self.cents = cents.to_i
9
+ self.currency = currency
10
+ end
11
+
12
+ def to_s
13
+ (self.cents.to_f / 100).to_s
14
+ end
15
+
16
+ def self.couchrest_typecast(parent, property, value)
17
+ if parent.respond_to?(:currency)
18
+ new(value, parent.currency)
19
+ else
20
+ new(value)
21
+ end
22
+ end
23
+
24
+ end
@@ -70,6 +70,11 @@ describe "Assocations" do
70
70
  @invoice.client
71
71
  end
72
72
 
73
+ it "should ignore blank ids" do
74
+ @invoice.client_id = ""
75
+ @invoice.client_id.should be_nil
76
+ end
77
+
73
78
  it "should allow override of foreign key" do
74
79
  @invoice.respond_to?(:alternate_client).should be_true
75
80
  @invoice.respond_to?("alternate_client=").should be_true
@@ -116,6 +121,11 @@ describe "Assocations" do
116
121
  @invoice.entries.first.should eql(@entries.first)
117
122
  end
118
123
 
124
+ it "should ignore blank ids when set directly" do
125
+ @invoice.entry_ids = ["", @entries.first.id]
126
+ @invoice.entry_ids.length.should be(1)
127
+ end
128
+
119
129
  it "should replace collection if ids replaced" do
120
130
  @invoice.entry_ids = @entries.collect{|i| i.id}
121
131
  @invoice.entries.length.should eql(3) # load once
@@ -560,24 +560,6 @@ describe "Model Base" do
560
560
  'index' => "function(doc) { ret = new Document(); ret.add(doc['name'], {'field':'name'}); return ret; }" }
561
561
  @db.save_doc({'_id' => '_design/search', 'fulltext' => {'cats' => search_function}})
562
562
  end
563
-
564
- it "should be able to paginate through a large set of search results" do
565
- if couchdb_lucene_available?
566
- names = []
567
- Cat.paginated_each(:design_doc => "search", :view_name => "cats",
568
- :q => 'name:S*', :search => true, :include_docs => true, :per_page => 3) do |cat|
569
- cat.should_not be_nil
570
- names << cat.name
571
- end
572
-
573
- names.size.should == 5
574
- names.should include('Sockington')
575
- names.should include('Smitty')
576
- names.should include('Sammy')
577
- names.should include('Samson')
578
- names.should include('Simon')
579
- end
580
- end
581
563
  end
582
564
 
583
565
  end
@@ -33,11 +33,26 @@ describe "Dirty" do
33
33
 
34
34
  describe "changes" do
35
35
 
36
- it "should return changes on an attribute" do
37
- @card = Card.new(:first_name => "matt")
38
- @card.first_name = "andrew"
39
- @card.first_name_changed?.should be_true
40
- @card.changes.should == { "first_name" => ["matt", "andrew"] }
36
+ context "when new record" do
37
+ it "should return changes on an attribute" do
38
+ @card = Card.new(:first_name => "matt")
39
+ @card.first_name = "andrew"
40
+ @card.first_name_changed?.should be_true
41
+ if ActiveModel::VERSION::STRING > "3.2.0"
42
+ @card.changes.should == { "first_name" => [nil, "andrew"] }
43
+ else
44
+ @card.changes.should == { "first_name" => ["matt", "andrew"] }
45
+ end
46
+ end
47
+ end
48
+
49
+ context "when persisted" do
50
+ it "should return changes on an attribute" do
51
+ @card = Card.create!(:first_name => "matt")
52
+ @card.first_name = "andrew"
53
+ @card.first_name_changed?.should be_true
54
+ @card.changes.should == { "first_name" => ["matt", "andrew"] }
55
+ end
41
56
  end
42
57
 
43
58
  end
@@ -328,7 +328,6 @@ describe "properties of array of casted models" do
328
328
  @course.questions.last.class.should eql(Question)
329
329
  end
330
330
 
331
-
332
331
  it "should raise an error if attempting to set single value for array type" do
333
332
  lambda {
334
333
  @course.questions = Question.new(:q => 'test1')
@@ -428,6 +427,16 @@ describe "Property Class" do
428
427
  property.init_method.should eql('parse')
429
428
  end
430
429
 
430
+ it "should set the allow_blank option to true by default" do
431
+ property = CouchRest::Model::Property.new(:test, String)
432
+ property.allow_blank.should be_true
433
+ end
434
+
435
+ it "should allow setting of the allow_blank option to false" do
436
+ property = CouchRest::Model::Property.new(:test, String, :allow_blank => false)
437
+ property.allow_blank.should be_false
438
+ end
439
+
431
440
  describe "#build" do
432
441
  it "should allow instantiation of new object" do
433
442
  property = CouchRest::Model::Property.new(:test, Date)
@@ -466,6 +475,22 @@ describe "Property Class" do
466
475
  property.cast(parent, ["2010-06-01", "2010-06-02"]).should eql([Date.new(2010, 6, 1), Date.new(2010, 6, 2)])
467
476
  end
468
477
 
478
+ context "when allow_blank is false" do
479
+ let :parent do
480
+ mock("FooObject")
481
+ end
482
+
483
+ it "should convert blank to nil" do
484
+ property = CouchRest::Model::Property.new(:test, String, :allow_blank => false)
485
+ property.cast(parent, "").should be_nil
486
+ end
487
+
488
+ it "should remove blank array entries" do
489
+ property = CouchRest::Model::Property.new(:test, [String], :allow_blank => false)
490
+ property.cast(parent, ["", "foo"]).should eql(["foo"])
491
+ end
492
+ end
493
+
469
494
  it "should set a CastedArray on array of Objects" do
470
495
  property = CouchRest::Model::Property.new(:test, [Object])
471
496
  parent = mock("FooObject")
@@ -14,6 +14,28 @@ describe "Type Casting" do
14
14
  end
15
15
  end
16
16
 
17
+ describe "when value is empty string" do
18
+ it "leaves the value unchanged" do
19
+ @course.title = ""
20
+ @course['title'].should == ""
21
+ end
22
+ end
23
+
24
+ describe "when blank is not allow on property" do
25
+ it "leaves nil as nil" do
26
+ @course.subtitle = nil
27
+ @course['subtitle'].should == nil
28
+ end
29
+ it "converts blank to nil" do
30
+ @course.subtitle = ""
31
+ @course['subtitle'].should == nil
32
+ end
33
+ it "leaves text as text" do
34
+ @course.subtitle = "Test"
35
+ @course['subtitle'].should == "Test"
36
+ end
37
+ end
38
+
17
39
  describe "when type primitive is an Object" do
18
40
  it "it should not cast given value" do
19
41
  @course.participants = [{}, 'q', 1]
@@ -26,6 +48,14 @@ describe "Type Casting" do
26
48
  end
27
49
  end
28
50
 
51
+ describe "when class responds to .couchrest_typecast" do
52
+ it "should accept call" do
53
+ @course.price = "1299"
54
+ @course.price.cents.should eql(1299)
55
+ @course.price.currency.should eql('EUR')
56
+ end
57
+ end
58
+
29
59
  describe "when type primitive is a String" do
30
60
  it "keeps string value unchanged" do
31
61
  value = "1.0"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couchrest_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.beta
4
+ version: 2.0.0.beta2
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2012-06-14 00:00:00.000000000 Z
16
+ date: 2012-08-02 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: couchrest
@@ -22,7 +22,7 @@ dependencies:
22
22
  requirements:
23
23
  - - ~>
24
24
  - !ruby/object:Gem::Version
25
- version: 1.1.2
25
+ version: 1.1.3
26
26
  type: :runtime
27
27
  prerelease: false
28
28
  version_requirements: !ruby/object:Gem::Requirement
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ~>
32
32
  - !ruby/object:Gem::Version
33
- version: 1.1.2
33
+ version: 1.1.3
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: mime-types
36
36
  requirement: !ruby/object:Gem::Requirement
@@ -54,7 +54,7 @@ dependencies:
54
54
  requirements:
55
55
  - - ~>
56
56
  - !ruby/object:Gem::Version
57
- version: 3.1.0
57
+ version: '3.0'
58
58
  type: :runtime
59
59
  prerelease: false
60
60
  version_requirements: !ruby/object:Gem::Requirement
@@ -62,7 +62,7 @@ dependencies:
62
62
  requirements:
63
63
  - - ~>
64
64
  - !ruby/object:Gem::Version
65
- version: 3.1.0
65
+ version: '3.0'
66
66
  - !ruby/object:Gem::Dependency
67
67
  name: tzinfo
68
68
  requirement: !ruby/object:Gem::Requirement
@@ -143,6 +143,22 @@ dependencies:
143
143
  - - ! '>='
144
144
  - !ruby/object:Gem::Version
145
145
  version: 0.8.0
146
+ - !ruby/object:Gem::Dependency
147
+ name: debugger
148
+ requirement: !ruby/object:Gem::Requirement
149
+ none: false
150
+ requirements:
151
+ - - ~>
152
+ - !ruby/object:Gem::Version
153
+ version: 1.2.0
154
+ type: :development
155
+ prerelease: false
156
+ version_requirements: !ruby/object:Gem::Requirement
157
+ none: false
158
+ requirements:
159
+ - - ~>
160
+ - !ruby/object:Gem::Version
161
+ version: 1.2.0
146
162
  description: CouchRest Model provides aditional features to the standard CouchRest
147
163
  Document class such as properties, view designs, associations, callbacks, typecasting
148
164
  and validations.
@@ -220,6 +236,7 @@ files:
220
236
  - spec/fixtures/models/invoice.rb
221
237
  - spec/fixtures/models/key_chain.rb
222
238
  - spec/fixtures/models/membership.rb
239
+ - spec/fixtures/models/money.rb
223
240
  - spec/fixtures/models/person.rb
224
241
  - spec/fixtures/models/project.rb
225
242
  - spec/fixtures/models/question.rb
@@ -267,6 +284,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
267
284
  - - ! '>='
268
285
  - !ruby/object:Gem::Version
269
286
  version: '0'
287
+ segments:
288
+ - 0
289
+ hash: 2665370531854453498
270
290
  required_rubygems_version: !ruby/object:Gem::Requirement
271
291
  none: false
272
292
  requirements:
@@ -294,6 +314,7 @@ test_files:
294
314
  - spec/fixtures/models/invoice.rb
295
315
  - spec/fixtures/models/key_chain.rb
296
316
  - spec/fixtures/models/membership.rb
317
+ - spec/fixtures/models/money.rb
297
318
  - spec/fixtures/models/person.rb
298
319
  - spec/fixtures/models/project.rb
299
320
  - spec/fixtures/models/question.rb