couchbase-model 0.4.4 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY.markdown CHANGED
@@ -1,3 +1,14 @@
1
+ ## 0.5.0 / 2012-11-21
2
+
3
+ * Update template for map function
4
+ * Use extended get for #find_by_id
5
+ * Do not use HashWithIndifferentAccess class unless it defined
6
+ * Pass options to #create method
7
+ * Ensure validness on create
8
+ * Fix storing raw data
9
+ * Define read_attribute and write_attribute methods
10
+ * Support couchbase 1.2.0.z.beta4
11
+
1
12
  ## 0.4.4 / 2012-10-17
2
13
 
3
14
  * Make #to_param aware about keys
@@ -42,7 +42,11 @@ module Couchbase
42
42
  attr_reader :record
43
43
  def initialize(record)
44
44
  @record = record
45
- super(@record.errors.full_messages.join(", "))
45
+ if @record.errors
46
+ super(@record.errors.full_messages.join(", "))
47
+ else
48
+ super("Record invalid")
49
+ end
46
50
  end
47
51
  end
48
52
 
@@ -113,6 +117,12 @@ module Couchbase
113
117
  # @since 0.2.0
114
118
  attr_reader :meta
115
119
 
120
+ # @since 0.4.5
121
+ attr_reader :errors
122
+
123
+ # @since 0.4.5
124
+ attr_reader :raw
125
+
116
126
  # @private Container for all attributes with defaults of all subclasses
117
127
  @@attributes = ::Hash.new {|hash, key| hash[key] = {}}
118
128
 
@@ -275,6 +285,14 @@ module Couchbase
275
285
  self.thread_storage[:uuid_algorithm] = algorithm
276
286
  end
277
287
 
288
+ def read_attribute(attr_name)
289
+ @_attributes[attr_name]
290
+ end
291
+
292
+ def write_attribute(attr_name, value)
293
+ @_attributes[attr_name] = value
294
+ end
295
+
278
296
  # Defines an attribute for the model
279
297
  #
280
298
  # @since 0.0.1
@@ -301,10 +319,10 @@ module Couchbase
301
319
  attributes[name] = options[:default]
302
320
  next if self.instance_methods.include?(name)
303
321
  define_method(name) do
304
- @_attributes[name]
322
+ read_attribute(name)
305
323
  end
306
324
  define_method(:"#{name}=") do |value|
307
- @_attributes[name] = value
325
+ write_attribute(name, value)
308
326
  end
309
327
  end
310
328
  end
@@ -384,6 +402,7 @@ module Couchbase
384
402
  def self.find(id)
385
403
  if id && (res = bucket.get(id, :quiet => false, :extended => true))
386
404
  obj, flags, cas = res
405
+ obj = {:raw => obj} unless obj.is_a?(Hash)
387
406
  new({:id => id, :meta => {'flags' => flags, 'cas' => cas}}.merge(obj))
388
407
  end
389
408
  end
@@ -399,8 +418,9 @@ module Couchbase
399
418
  # @example Find model using +id+
400
419
  # post = Post.find_by_id('the-id')
401
420
  def self.find_by_id(id)
402
- if id && (res = bucket.get(id, :quiet => true))
421
+ if id && (res = bucket.get(id, :quiet => true, :extended => true))
403
422
  obj, flags, cas = res
423
+ obj = {:raw => obj} unless obj.is_a?(Hash)
404
424
  new({:id => id, :meta => {'flags' => flags, 'cas' => cas}}.merge(obj))
405
425
  end
406
426
  end
@@ -424,9 +444,17 @@ module Couchbase
424
444
  # @param [Hash] attrs attribute-value pairs
425
445
  def initialize(attrs = {})
426
446
  @errors = ::ActiveModel::Errors.new(self) if defined?(::ActiveModel)
447
+ @_attributes = ::Hash.new do |h, k|
448
+ default = self.class.attributes[k]
449
+ h[k] = if default.respond_to?(:call)
450
+ default.call
451
+ else
452
+ default
453
+ end
454
+ end
427
455
  case attrs
428
- when Hash, HashWithIndifferentAccess
429
- if attrs.respond_to?(:with_indifferent_access)
456
+ when Hash
457
+ if defined?(HashWithIndifferentAccess) && !attrs.is_a?(HashWithIndifferentAccess)
430
458
  attrs = attrs.with_indifferent_access
431
459
  end
432
460
  @id = attrs.delete(:id)
@@ -434,17 +462,10 @@ module Couchbase
434
462
  @value = attrs.delete(:value)
435
463
  @doc = attrs.delete(:doc)
436
464
  @meta = attrs.delete(:meta)
437
- @_attributes = ::Hash.new do |h, k|
438
- default = self.class.attributes[k]
439
- h[k] = if default.respond_to?(:call)
440
- default.call
441
- else
442
- default
443
- end
444
- end
465
+ @raw = attrs.delete(:raw)
445
466
  update_attributes(@doc || attrs)
446
467
  else
447
- @_raw = attrs
468
+ @raw = attrs
448
469
  end
449
470
  end
450
471
 
@@ -462,14 +483,18 @@ module Couchbase
462
483
  # p.create
463
484
  def create(options = {})
464
485
  @id ||= Couchbase::Model::UUID.generator.next(1, model.thread_storage[:uuid_algorithm])
465
- value = @_raw ? @_raw : attributes_with_values
486
+ if respond_to?(:valid?) && !valid?
487
+ raise Couchbase::Error::RecordInvalid.new(self)
488
+ end
489
+ options = model.defaults.merge(options)
490
+ value = (options[:format] == :plain) ? @raw : attributes_with_values
466
491
  unless @meta
467
492
  @meta = {}
468
493
  if @meta.respond_to?(:with_indifferent_access)
469
494
  @meta = @meta.with_indifferent_access
470
495
  end
471
496
  end
472
- @meta['cas'] = model.bucket.add(@id, value, model.defaults.merge(options))
497
+ @meta['cas'] = model.bucket.add(@id, value, options)
473
498
  self
474
499
  end
475
500
 
@@ -491,18 +516,13 @@ module Couchbase
491
516
  # p.draft = false
492
517
  # p.save('cas' => p.meta['cas'])
493
518
  def save(options = {})
519
+ return create(options) unless @meta
494
520
  if respond_to?(:valid?) && !valid?
495
521
  raise Couchbase::Error::RecordInvalid.new(self)
496
522
  end
497
- return create unless meta
498
- value = @_raw ? @_raw : attributes_with_values
499
- unless @meta
500
- @meta = {}
501
- if @meta.respond_to?(:with_indifferent_access)
502
- @meta = @meta.with_indifferent_access
503
- end
504
- end
505
- @meta['cas'] = model.bucket.replace(@id, value, model.defaults.merge(options))
523
+ options = model.defaults.merge(options)
524
+ value = (options[:format] == :plain) ? @raw : attributes_with_values
525
+ @meta['cas'] = model.bucket.replace(@id, value, options)
506
526
  self
507
527
  end
508
528
 
@@ -689,7 +709,7 @@ module Couchbase
689
709
  }
690
710
  if data['doc']
691
711
  doc[:meta] = data['doc']['meta']
692
- doc[:doc] = data['doc']['json']
712
+ doc[:doc] = data['doc']['value'] || data['doc']['json']
693
713
  end
694
714
  new(doc)
695
715
  end
@@ -703,7 +723,7 @@ module Couchbase
703
723
  attrs << ["key", @key.inspect] unless @key.nil?
704
724
  attrs << ["value", @value.inspect] unless @value.nil?
705
725
  model.attributes.map do |attr, default|
706
- val = @_attributes[attr]
726
+ val = read_attribute(attr)
707
727
  attrs << [attr.to_s, val.inspect] unless val.nil?
708
728
  end
709
729
  attrs.sort!
@@ -725,7 +745,7 @@ module Couchbase
725
745
  def attributes_with_values
726
746
  ret = {:type => model.design_document}
727
747
  model.attributes.keys.each do |attr|
728
- ret[attr] = @_attributes[attr]
748
+ ret[attr] = read_attribute(attr)
729
749
  end
730
750
  ret
731
751
  end
@@ -19,7 +19,7 @@ module Couchbase
19
19
 
20
20
  class Model
21
21
 
22
- VERSION = "0.4.4"
22
+ VERSION = "0.5.0"
23
23
 
24
24
  end
25
25
 
@@ -5,24 +5,24 @@
5
5
  // Read more about how to write map functions at:
6
6
  // http://www.couchbase.com/docs/couchbase-manual-2.0/couchbase-views-writing-map.html
7
7
 
8
- function(doc) {
9
- emit(doc._id, null);
8
+ function(doc, meta) {
9
+ emit(meta.id, null);
10
10
  }
11
11
 
12
12
  // You can also check out following examples
13
13
  //
14
14
  // The simplest example of a map function:
15
15
  //
16
- // function(doc) {
17
- // emit(doc._id, doc);
16
+ // function(doc, meta) {
17
+ // emit(meta.id, doc);
18
18
  // }
19
19
  //
20
20
  // Slightly more complex example of a function that defines a view on values
21
21
  // computed from customer documents:
22
22
  //
23
- // function(doc) {
23
+ // function(doc, meta) {
24
24
  // if (doc.type == "customer") {
25
- // emit(doc._id, {last_name: doc.last_name, first_name: doc.first_name});
25
+ // emit(meta.id, {last_name: doc.last_name, first_name: doc.first_name});
26
26
  // }
27
27
  // }
28
28
  //
@@ -31,7 +31,7 @@ function(doc) {
31
31
  // would allow you to lookup customer documents by the last_name or
32
32
  // first_name fields (your keys could be compound, e.g. arrays):
33
33
  //
34
- // function(doc) {
34
+ // function(doc, meta) {
35
35
  // if (doc.type == "customer") {
36
36
  // emit(doc.last_name, {first_name: doc.first_name});
37
37
  // emit(doc.first_name, {last_name: doc.last_name});
data/test/test_model.rb CHANGED
@@ -24,6 +24,14 @@ class Post < Couchbase::Model
24
24
  attribute :created_at, :default => lambda { Time.utc("2010-01-01") }
25
25
  end
26
26
 
27
+ class ValidPost < Couchbase::Model
28
+ attribute :title
29
+
30
+ def valid?
31
+ title && !title.empty?
32
+ end
33
+ end
34
+
27
35
  class Brewery < Couchbase::Model
28
36
  attribute :name
29
37
  end
@@ -33,18 +41,50 @@ class Beer < Couchbase::Model
33
41
  belongs_to :brewery
34
42
  end
35
43
 
44
+ class Attachment < Couchbase::Model
45
+ defaults :format => :plain
46
+ end
47
+
36
48
  class TestModel < MiniTest::Unit::TestCase
37
49
 
38
50
  def setup
39
51
  @mock = start_mock
40
- Post.bucket = Couchbase.connect(:hostname => @mock.host,
41
- :port => @mock.port)
52
+ bucket = Couchbase.connect(:hostname => @mock.host, :port => @mock.port)
53
+ [Post, ValidPost, Brewery, Beer, Attachment].each do |model|
54
+ model.bucket = bucket
55
+ end
42
56
  end
43
57
 
44
58
  def teardown
45
59
  stop_mock(@mock)
46
60
  end
47
61
 
62
+ def test_it_supports_value_property
63
+ doc = {
64
+ "id" => "x",
65
+ "key" => "x",
66
+ "value" => "x",
67
+ "doc" => {
68
+ "value" => {"title" => "foo"}
69
+ }
70
+ }
71
+ post = Post.wrap(Post.bucket, doc)
72
+ assert_equal "foo", post.title
73
+ end
74
+
75
+ def test_it_supports_json_property
76
+ doc = {
77
+ "id" => "x",
78
+ "key" => "x",
79
+ "value" => "x",
80
+ "doc" => {
81
+ "json" => {"title" => "foo"}
82
+ }
83
+ }
84
+ post = Post.wrap(Post.bucket, doc)
85
+ assert_equal "foo", post.title
86
+ end
87
+
48
88
  def test_assigns_attributes_from_the_hash
49
89
  post = Post.new(:title => "Hello, world")
50
90
  assert_equal "Hello, world", post.title
@@ -181,4 +221,23 @@ class TestModel < MiniTest::Unit::TestCase
181
221
  assert_equal "the-key", Post.new(:key => ["the", "key"]).to_param
182
222
  end
183
223
 
224
+ def test_validation
225
+ post = ValidPost.create(:title => 'Hello, World!')
226
+ assert post.valid?, "post with title should be valid"
227
+ post.title = nil
228
+ assert_raises(Couchbase::Error::RecordInvalid) do
229
+ post.save
230
+ end
231
+ assert_raises(Couchbase::Error::RecordInvalid) do
232
+ ValidPost.create(:title => nil)
233
+ end
234
+ end
235
+
236
+ def test_blob_documents
237
+ contents = File.read(__FILE__)
238
+ id = Attachment.create(:raw => contents).id
239
+ blob = Attachment.find(id)
240
+ assert_equal contents, blob.raw
241
+ end
242
+
184
243
  end
metadata CHANGED
@@ -1,118 +1,121 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: couchbase-model
3
- version: !ruby/object:Gem::Version
4
- version: 0.4.4
3
+ version: !ruby/object:Gem::Version
4
+ hash: 11
5
5
  prerelease:
6
+ segments:
7
+ - 0
8
+ - 5
9
+ - 0
10
+ version: 0.5.0
6
11
  platform: ruby
7
- authors:
12
+ authors:
8
13
  - Couchbase
9
14
  autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
- date: 2012-10-17 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: couchbase
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ~>
20
- - !ruby/object:Gem::Version
21
- version: 1.2.0.dp
17
+
18
+ date: 2012-11-21 00:00:00 +03:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
22
  type: :runtime
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
23
+ requirement: &id001 !ruby/object:Gem::Requirement
25
24
  none: false
26
- requirements:
25
+ requirements:
27
26
  - - ~>
28
- - !ruby/object:Gem::Version
27
+ - !ruby/object:Gem::Version
28
+ hash: -168919795
29
+ segments:
30
+ - 1
31
+ - 2
32
+ - 0
33
+ - dp
29
34
  version: 1.2.0.dp
30
- - !ruby/object:Gem::Dependency
31
- name: rake
32
- requirement: !ruby/object:Gem::Requirement
35
+ name: couchbase
36
+ version_requirements: *id001
37
+ prerelease: false
38
+ - !ruby/object:Gem::Dependency
39
+ type: :development
40
+ requirement: &id002 !ruby/object:Gem::Requirement
33
41
  none: false
34
- requirements:
42
+ requirements:
35
43
  - - ~>
36
- - !ruby/object:Gem::Version
44
+ - !ruby/object:Gem::Version
45
+ hash: 49
46
+ segments:
47
+ - 0
48
+ - 8
49
+ - 7
37
50
  version: 0.8.7
38
- type: :development
51
+ name: rake
52
+ version_requirements: *id002
39
53
  prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
54
+ - !ruby/object:Gem::Dependency
55
+ type: :development
56
+ requirement: &id003 !ruby/object:Gem::Requirement
41
57
  none: false
42
- requirements:
43
- - - ~>
44
- - !ruby/object:Gem::Version
45
- version: 0.8.7
46
- - !ruby/object:Gem::Dependency
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
47
65
  name: minitest
48
- requirement: !ruby/object:Gem::Requirement
49
- none: false
50
- requirements:
51
- - - ! '>='
52
- - !ruby/object:Gem::Version
53
- version: '0'
54
- type: :development
66
+ version_requirements: *id003
55
67
  prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
68
+ - !ruby/object:Gem::Dependency
69
+ type: :development
70
+ requirement: &id004 !ruby/object:Gem::Requirement
57
71
  none: false
58
- requirements:
59
- - - ! '>='
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- - !ruby/object:Gem::Dependency
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
78
+ version: "0"
63
79
  name: rdiscount
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ! '>='
68
- - !ruby/object:Gem::Version
69
- version: '0'
70
- type: :development
80
+ version_requirements: *id004
71
81
  prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
82
+ - !ruby/object:Gem::Dependency
83
+ type: :development
84
+ requirement: &id005 !ruby/object:Gem::Requirement
73
85
  none: false
74
- requirements:
75
- - - ! '>='
76
- - !ruby/object:Gem::Version
77
- version: '0'
78
- - !ruby/object:Gem::Dependency
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ hash: 3
90
+ segments:
91
+ - 0
92
+ version: "0"
79
93
  name: yard
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ! '>='
84
- - !ruby/object:Gem::Version
85
- version: '0'
86
- type: :development
94
+ version_requirements: *id005
87
95
  prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
96
+ - !ruby/object:Gem::Dependency
97
+ type: :development
98
+ requirement: &id006 !ruby/object:Gem::Requirement
89
99
  none: false
90
- requirements:
91
- - - ! '>='
92
- - !ruby/object:Gem::Version
93
- version: '0'
94
- - !ruby/object:Gem::Dependency
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ hash: 3
104
+ segments:
105
+ - 0
106
+ version: "0"
95
107
  name: debugger
96
- requirement: !ruby/object:Gem::Requirement
97
- none: false
98
- requirements:
99
- - - ! '>='
100
- - !ruby/object:Gem::Version
101
- version: '0'
102
- type: :development
108
+ version_requirements: *id006
103
109
  prerelease: false
104
- version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
- requirements:
107
- - - ! '>='
108
- - !ruby/object:Gem::Version
109
- version: '0'
110
110
  description: ORM-like interface allows you to persist your models to Couchbase
111
111
  email: support@couchbase.com
112
112
  executables: []
113
+
113
114
  extensions: []
115
+
114
116
  extra_rdoc_files: []
115
- files:
117
+
118
+ files:
116
119
  - .gitignore
117
120
  - .travis.yml
118
121
  - .yardopts
@@ -143,31 +146,41 @@ files:
143
146
  - test/setup.rb
144
147
  - test/test_model.rb
145
148
  - test/test_uuid.rb
149
+ has_rdoc: true
146
150
  homepage: https://github.com/couchbase/couchbase-ruby-model
147
151
  licenses: []
152
+
148
153
  post_install_message:
149
154
  rdoc_options: []
150
- require_paths:
155
+
156
+ require_paths:
151
157
  - lib
152
- required_ruby_version: !ruby/object:Gem::Requirement
158
+ required_ruby_version: !ruby/object:Gem::Requirement
153
159
  none: false
154
- requirements:
155
- - - ! '>='
156
- - !ruby/object:Gem::Version
157
- version: '0'
158
- required_rubygems_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ hash: 3
164
+ segments:
165
+ - 0
166
+ version: "0"
167
+ required_rubygems_version: !ruby/object:Gem::Requirement
159
168
  none: false
160
- requirements:
161
- - - ! '>='
162
- - !ruby/object:Gem::Version
163
- version: '0'
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ hash: 3
173
+ segments:
174
+ - 0
175
+ version: "0"
164
176
  requirements: []
177
+
165
178
  rubyforge_project:
166
- rubygems_version: 1.8.23
179
+ rubygems_version: 1.6.2
167
180
  signing_key:
168
181
  specification_version: 3
169
182
  summary: Declarative interface to Couchbase
170
- test_files:
183
+ test_files:
171
184
  - test/setup.rb
172
185
  - test/test_model.rb
173
186
  - test/test_uuid.rb