couchbase-model 0.4.4 → 0.5.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/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