couchbase-model 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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