couchbase-model 0.1.0 → 0.2.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.
@@ -0,0 +1,44 @@
1
+ ## 0.2.0 / 2012-09-18
2
+
3
+ * Add Rails 3 configuration possibilities, allow configuring
4
+ ensure_design_documents to disable/enable the auto view upgrade
5
+ (thanks to David Rice)
6
+ * Ensure views directory is always set (thanks to David Rice)
7
+ * Fix tests for ruby 1.8.7
8
+ * Reword header in README
9
+ * Merge pull request #3 from davidjrice/master
10
+ * Use debugger gem
11
+ * Update Model wrapper to match latest API changes
12
+ * Strip contents of the JS file
13
+ * Do not submit empty views
14
+ * Allow to specify default view options
15
+ * Display only non-nil values
16
+ * Rename underscored methods
17
+ * Load spatial views into design document
18
+
19
+ ## 0.1.0 / 2012-04-10
20
+
21
+ * Allows to define several attributes at once
22
+ * Allow to specify default value
23
+ * Add missing @since and @return tags
24
+ * Add railtie
25
+ * Add config generator
26
+ * Use verbose mode by default for GET operation
27
+ * Add views generators
28
+ * Update document wrapper
29
+ * Add code to upgrade design docs automatically
30
+ * Cache design document signature in memory
31
+ * Use symbols for attribute hash
32
+ * Skip connection errors during start up
33
+ * Don't show config warning for config generator
34
+ * Calculate mtime of the design document
35
+ * Assign current_doc after creation
36
+ * Update readme file
37
+ * Use preview repository for travis
38
+ * Do not make zipball
39
+ * Show model attributes with model class
40
+ * Update test. The couchbase gem is using new defaults
41
+
42
+ ## 0.0.1/ 2012-03-17
43
+
44
+ * Initial version
@@ -83,7 +83,7 @@ You can define connection options on per model basis:
83
83
  connect :port => 80, :bucket => 'blog'
84
84
  end
85
85
 
86
- ## Views (aka Map/Reduce queries to Couchbase)
86
+ ## Views (aka Map/Reduce indexes)
87
87
 
88
88
  Views are stored in models directory in subdirectory named after the
89
89
  model (to be precious `design_document` attribute of the model class).
@@ -22,5 +22,5 @@ Gem::Specification.new do |s|
22
22
  s.add_development_dependency 'minitest'
23
23
  s.add_development_dependency 'rdiscount'
24
24
  s.add_development_dependency 'yard'
25
- s.add_development_dependency RUBY_VERSION =~ /^1\.9/ ? 'ruby-debug19' : 'ruby-debug'
25
+ s.add_development_dependency 'debugger'
26
26
  end
@@ -22,6 +22,10 @@ require 'couchbase/model/version'
22
22
  require 'couchbase/model/uuid'
23
23
  require 'couchbase/model/configuration'
24
24
 
25
+ unless Object.respond_to?(:singleton_class)
26
+ require 'couchbase/model/ext/singleton_class'
27
+ end
28
+
25
29
  module Couchbase
26
30
 
27
31
  # @since 0.0.1
@@ -82,23 +86,23 @@ module Couchbase
82
86
  # @since 0.0.1
83
87
  attr_accessor :id
84
88
 
85
- # @since 0.1.0
86
- attr_reader :_key
89
+ # @since 0.2.0
90
+ attr_reader :key
87
91
 
88
- # @since 0.1.0
89
- attr_reader :_value
92
+ # @since 0.2.0
93
+ attr_reader :value
90
94
 
91
- # @since 0.1.0
92
- attr_reader :_doc
95
+ # @since 0.2.0
96
+ attr_reader :doc
93
97
 
94
- # @since 0.1.0
95
- attr_reader :_meta
98
+ # @since 0.2.0
99
+ attr_reader :meta
96
100
 
97
101
  # @private Container for all attributes with defaults of all subclasses
98
102
  @@attributes = ::Hash.new {|hash, key| hash[key] = {}}
99
103
 
100
104
  # @private Container for all view names of all subclasses
101
- @@views = ::Hash.new {|hash, key| hash[key] = []}
105
+ @@views = ::Hash.new {|hash, key| hash[key] = {}}
102
106
 
103
107
  # Use custom connection options
104
108
  #
@@ -190,20 +194,31 @@ module Couchbase
190
194
  doc = {'_id' => "_design/#{design_document}", 'views' => {}}
191
195
  digest = Digest::MD5.new
192
196
  mtime = 0
193
- views.each do |name|
194
- doc['views'][name] = view = {}
195
- ['map', 'reduce'].each do |type|
197
+ views.each do |name, _|
198
+ doc['views'][name] = {}
199
+ doc['spatial'] = {}
200
+ ['map', 'reduce', 'spatial'].each do |type|
196
201
  Configuration.design_documents_paths.each do |path|
197
202
  ff = File.join(path, design_document.to_s, name.to_s, "#{type}.js")
198
203
  if File.file?(ff)
199
- view[type] = File.read(ff)
204
+ contents = File.read(ff).strip
205
+ next if contents.empty?
200
206
  mtime = [mtime, File.mtime(ff).to_i].max
201
- digest << view[type]
207
+ digest << contents
208
+ case type
209
+ when 'map', 'reduce'
210
+ doc['views'][name][type] = contents
211
+ when 'spatial'
212
+ doc['spatial'][name] = contents
213
+ end
202
214
  break # pick first matching file
203
215
  end
204
216
  end
205
217
  end
206
218
  end
219
+
220
+ doc['views'].delete_if {|_, v| v.empty? }
221
+ doc.delete('spatial') if doc['spatial'].empty?
207
222
  doc['signature'] = digest.to_s
208
223
  doc['timestamp'] = mtime
209
224
  if doc['signature'] != thread_storage[:signature] && doc['timestamp'] > thread_storage[:timestamp].to_i
@@ -259,30 +274,48 @@ module Couchbase
259
274
  options = names.pop
260
275
  end
261
276
  names.each do |name|
277
+ attributes[name] = options[:default]
262
278
  name = name.to_sym
279
+ next if self.instance_methods.include?(name)
263
280
  define_method(name) do
264
281
  @_attributes[name]
265
282
  end
266
283
  define_method(:"#{name}=") do |value|
267
284
  @_attributes[name] = value
268
285
  end
269
- attributes[name] = options[:default]
270
286
  end
271
287
  end
272
288
 
289
+ # Defines a view for the model
290
+ #
291
+ # @since 0.0.1
292
+ #
293
+ # @param [Symbol, String, Array] names names of the views
294
+ # @param [Hash] options options passed to the {Couchbase::View}
295
+ #
296
+ # @example Define some views for a model
297
+ # class Post < Couchbase::Model
298
+ # view :all, :published
299
+ # view :by_rating, :include_docs => false
300
+ # end
301
+ #
302
+ # post = Post.find("hello")
303
+ # post.by_rating.each do |r|
304
+ # # ...
305
+ # end
273
306
  def self.view(*names)
274
- options = {}
307
+ options = {:wrapper_class => self, :include_docs => true}
275
308
  if names.last.is_a?(Hash)
276
- options = names.pop
309
+ options.update(names.pop)
277
310
  end
311
+ is_spatial = options.delete(:spatial)
278
312
  names.each do |name|
279
- views << name
280
- self.instance_eval <<-EOV, __FILE__, __LINE__ + 1
281
- def #{name}(params = {})
282
- View.new(bucket, "_design/\#{design_document}/_view/#{name}",
283
- params.merge(:wrapper_class => self, :include_docs => true))
284
- end
285
- EOV
313
+ path = "_design/%s/_%s/%s" % [design_document, is_spatial ? "spatial" : "view", name]
314
+ views[name] = lambda do |*params|
315
+ params = options.merge(params.first || {})
316
+ View.new(bucket, path, params)
317
+ end
318
+ singleton_class.send(:define_method, name, &views[name])
286
319
  end
287
320
  end
288
321
 
@@ -299,7 +332,7 @@ module Couchbase
299
332
  def self.find(id)
300
333
  if id && (res = bucket.get(id, :quiet => false, :extended => true))
301
334
  obj, flags, cas = res
302
- new({:id => id, :_meta => {'flags' => flags, 'cas' => cas}}.merge(obj))
335
+ new({:id => id, :meta => {'flags' => flags, 'cas' => cas}}.merge(obj))
303
336
  end
304
337
  end
305
338
 
@@ -316,7 +349,7 @@ module Couchbase
316
349
  def self.find_by_id(id)
317
350
  if id && (res = bucket.get(id, :quiet => true))
318
351
  obj, flags, cas = res
319
- new({:id => id, :_meta => {'flags' => flags, 'cas' => cas}}.merge(obj))
352
+ new({:id => id, :meta => {'flags' => flags, 'cas' => cas}}.merge(obj))
320
353
  end
321
354
  end
322
355
 
@@ -342,10 +375,10 @@ module Couchbase
342
375
  attrs = attrs.with_indifferent_access
343
376
  end
344
377
  @id = attrs.delete(:id)
345
- @_key = attrs.delete(:_key)
346
- @_value = attrs.delete(:_value)
347
- @_doc = attrs.delete(:_doc)
348
- @_meta = attrs.delete(:_meta)
378
+ @key = attrs.delete(:key)
379
+ @value = attrs.delete(:value)
380
+ @doc = attrs.delete(:doc)
381
+ @meta = attrs.delete(:meta)
349
382
  @_attributes = ::Hash.new do |h, k|
350
383
  default = self.class.attributes[k]
351
384
  h[k] = if default.respond_to?(:call)
@@ -354,7 +387,7 @@ module Couchbase
354
387
  default
355
388
  end
356
389
  end
357
- update_attributes(@_doc || attrs)
390
+ update_attributes(@doc || attrs)
358
391
  end
359
392
 
360
393
  # Create this model and assign new id if necessary
@@ -558,21 +591,13 @@ module Couchbase
558
591
  # @return [Model]
559
592
  def self.wrap(bucket, data)
560
593
  doc = {
561
- :_key => data['key'],
562
- :_value => data['value'],
563
- :_meta => {},
564
- :id => data['id']
594
+ :id => data['id'],
595
+ :key => data['key'],
596
+ :value => data['value']
565
597
  }
566
- if doc[:_value].is_a?(Hash) && (_id = doc[:_value]['_id'])
567
- doc[:id] = _id
568
- end
569
598
  if data['doc']
570
- data['doc'].keys.each do |key|
571
- if key.start_with?("$")
572
- doc[:_meta][key.sub(/^\$/, '')] = data['doc'].delete(key)
573
- end
574
- end
575
- doc.update(data['doc'])
599
+ doc[:meta] = data['doc']['meta']
600
+ doc[:doc] = data['doc']['json']
576
601
  end
577
602
  new(doc)
578
603
  end
@@ -582,12 +607,16 @@ module Couchbase
582
607
  #
583
608
  # @since 0.0.1
584
609
  def inspect
585
- attrs = model.attributes.map do |attr, default|
586
- [attr.to_s, @_attributes[attr].inspect]
587
- end.sort
588
- sprintf("#<%s:%s %s>",
589
- model, new? ? "?" : id,
590
- attrs.map{|a| a.join("=")}.join(", "))
610
+ attrs = []
611
+ attrs << ["key", @key.inspect] unless @key.nil?
612
+ attrs << ["value", @value.inspect] unless @value.nil?
613
+ model.attributes.map do |attr, default|
614
+ val = @_attributes[attr]
615
+ attrs << [attr.to_s, val.inspect] unless val.nil?
616
+ end
617
+ attrs.sort!
618
+ attrs.unshift([:id, id]) unless new?
619
+ sprintf("#<%s %s>", model, attrs.map{|a| a.join(": ")}.join(", "))
591
620
  end
592
621
 
593
622
  def self.inspect
@@ -0,0 +1,24 @@
1
+ # Author:: Couchbase <info@couchbase.com>
2
+ # Copyright:: 2012 Couchbase, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ class Object
19
+ def singleton_class
20
+ class << self
21
+ self
22
+ end
23
+ end
24
+ end
@@ -19,7 +19,7 @@ module Couchbase
19
19
 
20
20
  class Model
21
21
 
22
- VERSION = "0.1.0"
22
+ VERSION = "0.2.0"
23
23
 
24
24
  end
25
25
 
@@ -23,6 +23,9 @@ module Rails #:nodoc:
23
23
  module Couchbase #:nodoc:
24
24
  class Railtie < Rails::Railtie #:nodoc:
25
25
 
26
+ config.couchbase = ActiveSupport::OrderedOptions.new
27
+ config.couchbase.ensure_design_documents ||= true
28
+
26
29
  # Determine which generator to use. app_generators was introduced after
27
30
  # 3.0.0.
28
31
  #
@@ -104,19 +107,21 @@ module Rails #:nodoc:
104
107
 
105
108
  # Check (and upgrade if needed) all design documents
106
109
  initializer "couchbase.upgrade_design_documents", :after =>"couchbase.setup_connection" do |app|
107
- config.to_prepare do
108
- ::Couchbase::Model::Configuration.design_documents_paths ||= app.config.paths["app/models"]
109
- app.config.paths["app/models"].each do |path|
110
- Dir.glob("#{path}/**/*.rb").sort.each do |file|
111
- require_dependency(file.gsub("#{path}/" , "").gsub(".rb", ""))
110
+ ::Couchbase::Model::Configuration.design_documents_paths ||= app.config.paths["app/models"]
111
+ if config.couchbase.ensure_design_documents
112
+ config.to_prepare do
113
+ app.config.paths["app/models"].each do |path|
114
+ Dir.glob("#{path}/**/*.rb").sort.each do |file|
115
+ require_dependency(file.gsub("#{path}/" , "").gsub(".rb", ""))
116
+ end
112
117
  end
113
- end
114
- begin
115
- ::Couchbase::Model.descendants.each do |model|
116
- model.ensure_design_document!
118
+ begin
119
+ ::Couchbase::Model.descendants.each do |model|
120
+ model.ensure_design_document!
121
+ end
122
+ rescue ::Couchbase::Error::Timeout, ::Couchbase::Error::Connect
123
+ # skip connection errors for now
117
124
  end
118
- rescue ::Couchbase::Error::Timeout, ::Couchbase::Error::Connect
119
- # skip connection errors for now
120
125
  end
121
126
  end
122
127
  end
@@ -21,7 +21,7 @@ class Post < Couchbase::Model
21
21
  attribute :title
22
22
  attribute :body
23
23
  attribute :author, :default => 'Anonymous'
24
- attribute :created_at, :default => lambda { Time.new("2010-01-01") }
24
+ attribute :created_at, :default => lambda { Time.utc("2010-01-01") }
25
25
  end
26
26
 
27
27
  class TestModel < MiniTest::Unit::TestCase
@@ -52,7 +52,7 @@ class TestModel < MiniTest::Unit::TestCase
52
52
 
53
53
  def test_allows_lambda_as_default_value
54
54
  post = Post.new(:title => "Hello, world")
55
- expected = Time.new("2010-01-01")
55
+ expected = Time.utc("2010-01-01")
56
56
  assert_equal expected, post.created_at
57
57
  assert_equal expected, post.attributes[:created_at]
58
58
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couchbase-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-10 00:00:00.000000000Z
12
+ date: 2012-09-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: couchbase
@@ -92,7 +92,7 @@ dependencies:
92
92
  - !ruby/object:Gem::Version
93
93
  version: '0'
94
94
  - !ruby/object:Gem::Dependency
95
- name: ruby-debug19
95
+ name: debugger
96
96
  requirement: !ruby/object:Gem::Requirement
97
97
  none: false
98
98
  requirements:
@@ -117,12 +117,14 @@ files:
117
117
  - .travis.yml
118
118
  - .yardopts
119
119
  - Gemfile
120
+ - HISTORY.markdown
120
121
  - README.markdown
121
122
  - Rakefile
122
123
  - couchbase-model.gemspec
123
124
  - lib/couchbase-model.rb
124
125
  - lib/couchbase/model.rb
125
126
  - lib/couchbase/model/configuration.rb
127
+ - lib/couchbase/model/ext/singleton_class.rb
126
128
  - lib/couchbase/model/uuid.rb
127
129
  - lib/couchbase/model/version.rb
128
130
  - lib/couchbase/railtie.rb
@@ -159,7 +161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
159
161
  version: '0'
160
162
  requirements: []
161
163
  rubyforge_project:
162
- rubygems_version: 1.8.18
164
+ rubygems_version: 1.8.23
163
165
  signing_key:
164
166
  specification_version: 3
165
167
  summary: Declarative interface to Couchbase