couchrest_model 2.2.0.beta1 → 2.2.0.beta2

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.
@@ -31,9 +31,8 @@ module CouchRest
31
31
  #
32
32
  # === Parameters
33
33
  # id<String, Integer>:: Document ID
34
- # db<Database>:: optional option to pass a custom database to use
35
- def get(id, db = database)
36
- get!(id, db)
34
+ def get(id)
35
+ get!(id)
37
36
  rescue CouchRest::Model::DocumentNotFound
38
37
  nil
39
38
  end
@@ -49,14 +48,28 @@ module CouchRest
49
48
  #
50
49
  # === Parameters
51
50
  # id<String, Integer>:: Document ID
51
+ def get!(id)
52
+ fetch_and_build_from_database(id, database)
53
+ end
54
+ alias :find! :get!
55
+
56
+
57
+ # Load the document and build from the provided database.
58
+ #
59
+ # ==== Returns
60
+ # Object:: if the document was found
61
+ # or
62
+ # Exception
63
+ #
64
+ # === Parameters
65
+ # id<String, Integer>:: Document ID
52
66
  # db<Database>:: optional option to pass a custom database to use
53
- def get!(id, db = database)
67
+ def fetch_and_build_from_database(id, db)
54
68
  raise CouchRest::Model::DocumentNotFound if id.blank?
55
69
  raise CouchRest::Model::DatabaseNotDefined if db.nil?
56
70
  doc = db.get(id) or raise CouchRest::Model::DocumentNotFound
57
71
  build_from_database(doc)
58
72
  end
59
- alias :find! :get!
60
73
 
61
74
  end
62
75
 
@@ -93,9 +93,11 @@ module CouchRest
93
93
  # Returns self.
94
94
  def reload
95
95
  write_attributes_for_initialization(
96
- database.get(id), :write_all_attributes => true
96
+ database.get!(id), :write_all_attributes => true
97
97
  )
98
98
  self
99
+ rescue CouchRest::NotFound
100
+ raise CouchRest::Model::DocumentNotFound
99
101
  end
100
102
 
101
103
  protected
@@ -8,8 +8,10 @@ module CouchRest
8
8
  raise StandardError, "Please set the #proxy_database_method" if self.class.proxy_database_method.nil?
9
9
  db_name = self.send(self.class.proxy_database_method)
10
10
  db_suffix = self.class.proxy_database_suffixes[assoc_name.to_sym]
11
- @proxy_databases ||= {}
12
- @proxy_databases[assoc_name.to_sym] ||= self.class.prepare_database([db_name, db_suffix].compact.reject(&:blank?).join(self.class.connection[:join]))
11
+ @_proxy_databases ||= {}
12
+ @_proxy_databases[assoc_name.to_sym] ||= begin
13
+ self.class.prepare_database([db_name, db_suffix].compact.reject(&:blank?).join(self.class.connection[:join]))
14
+ end
13
15
  end
14
16
 
15
17
  module ClassMethods
@@ -118,10 +120,17 @@ module CouchRest
118
120
  end
119
121
 
120
122
  def get(id)
121
- proxy_update(@model.get(id, @database))
123
+ get!(id)
124
+ rescue CouchRest::Model::DocumentNotFound
125
+ nil
122
126
  end
123
127
  alias :find :get
124
128
 
129
+ def get!(id)
130
+ proxy_update(@model.fetch_and_build_from_database(id, @database))
131
+ end
132
+ alias :find! :get!
133
+
125
134
  protected
126
135
 
127
136
  def create_view_methods
@@ -0,0 +1,22 @@
1
+ module CouchRest
2
+ module Model
3
+
4
+ # Simple Server Pool with thread safety so that a single server
5
+ # instance can be shared with multiple classes.
6
+ class ServerPool
7
+ include Singleton
8
+
9
+ def initialize
10
+ @servers = {}
11
+ @mutex = Mutex.new
12
+ end
13
+
14
+ def [](url)
15
+ @mutex.synchronize do
16
+ @servers[url] ||= CouchRest::Server.new(url)
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -17,5 +17,5 @@ module CouchRest::Model
17
17
  end
18
18
 
19
19
  class CouchRest::Database
20
- include(CouchRest::Model::Support::Database)
20
+ prepend CouchRest::Model::Support::Database
21
21
  end
@@ -59,7 +59,7 @@ module CouchRest
59
59
  def all_models(opts = {})
60
60
  opts.reverse_merge!(activate: true, with_proxies: false)
61
61
  callbacks = migrate_each_model(find_models)
62
- callbacks += migrate_each_proxying_model(find_proxying_models) if opts[:with_proxies]
62
+ callbacks += migrate_each_proxying_model(find_proxying_base_models) if opts[:with_proxies]
63
63
  activate_designs(callbacks) if opts[:activate]
64
64
  end
65
65
 
@@ -74,8 +74,8 @@ module CouchRest
74
74
  CouchRest::Model::Base.subclasses.reject{|m| m.proxy_owner_method.present?}
75
75
  end
76
76
 
77
- def find_proxying_models
78
- CouchRest::Model::Base.subclasses.reject{|m| m.proxy_method_names.empty?}
77
+ def find_proxying_base_models
78
+ CouchRest::Model::Base.subclasses.reject{|m| m.proxy_method_names.empty? || m.proxy_owner_method.present?}
79
79
  end
80
80
 
81
81
  def migrate_each_model(models, db = nil)
@@ -91,12 +91,15 @@ module CouchRest
91
91
  def migrate_each_proxying_model(models)
92
92
  callbacks = [ ]
93
93
  models.each do |model|
94
- methods = model.proxy_method_names
94
+ model_class = model.is_a?(CouchRest::Model::Proxyable::ModelProxy) ? model.model : model
95
+ methods = model_class.proxy_method_names
95
96
  methods.each do |method|
96
- puts "Finding proxied models for #{model}##{method}"
97
+ puts "Finding proxied models for #{model_class}##{method}"
98
+ model_class.design_doc.auto_update = false
97
99
  model.all.each do |obj|
98
100
  proxy = obj.send(method)
99
101
  callbacks += migrate_each_model([proxy.model], proxy.database)
102
+ callbacks += migrate_each_proxying_model([proxy]) unless model_class.proxy_method_names.empty?
100
103
  end
101
104
  end
102
105
  end
@@ -26,6 +26,8 @@ require "couchrest"
26
26
 
27
27
  require "couchrest/model"
28
28
  require "couchrest/model/errors"
29
+ require "couchrest/model/server_pool"
30
+ require "couchrest/model/connection_config"
29
31
  require "couchrest/model/configuration"
30
32
  require "couchrest/model/translation"
31
33
  require "couchrest/model/persistence"
data/spec/spec_helper.rb CHANGED
@@ -15,7 +15,7 @@ unless defined?(FIXTURE_PATH)
15
15
  FIXTURE_PATH = File.join(File.dirname(__FILE__), '/fixtures')
16
16
  SCRATCH_PATH = File.join(File.dirname(__FILE__), '/tmp')
17
17
 
18
- COUCHHOST = "http://127.0.0.1:5984"
18
+ COUCHHOST = ENV["COUCH_HOST"] || "http://127.0.0.1:5984"
19
19
  TESTDB = 'couchrest-model-test'
20
20
  TEST_SERVER = CouchRest.new COUCHHOST
21
21
  # TEST_SERVER.default_database = TESTDB
@@ -23,6 +23,21 @@ unless defined?(FIXTURE_PATH)
23
23
  end
24
24
 
25
25
  RSpec.configure do |config|
26
+ config.before(:suite) do
27
+ couch_uri = URI.parse(ENV['COUCH_HOST'] || "http://127.0.0.1:5984")
28
+ CouchRest::Model::Base.configure do |config|
29
+ config.connection = {
30
+ :protocol => couch_uri.scheme,
31
+ :host => couch_uri.host,
32
+ :port => couch_uri.port,
33
+ :username => couch_uri.user,
34
+ :password => couch_uri.password,
35
+ :prefix => "couchrest",
36
+ :join => "_"
37
+ }
38
+ end
39
+ end
40
+
26
41
  config.before(:all) { reset_test_db! }
27
42
 
28
43
  config.after(:all) do
@@ -0,0 +1,41 @@
1
+
2
+ require "spec_helper"
3
+
4
+ describe CouchRest::Model::ConnectionConfig do
5
+
6
+ subject { CouchRest::Model::ConnectionConfig }
7
+
8
+ describe ".instance" do
9
+
10
+ it "should provide a singleton" do
11
+ expect(subject.instance).to be_a(CouchRest::Model::ConnectionConfig)
12
+ end
13
+
14
+ end
15
+
16
+ describe "#[file]" do
17
+
18
+ let :file do
19
+ File.join(FIXTURE_PATH, "config", "couchdb.yml")
20
+ end
21
+
22
+ it "should provide a config file hash" do
23
+ conf = subject.instance[file]
24
+ expect(conf).to be_a(Hash)
25
+ end
26
+
27
+ it "should provide a config file hash with symbolized keys" do
28
+ conf = subject.instance[file]
29
+ expect(conf[:development]).to be_a(Hash)
30
+ expect(conf[:development]['host']).to be_a(String)
31
+ end
32
+
33
+ it "should always provide same hash" do
34
+ f1 = subject.instance[file]
35
+ f2 = subject.instance[file]
36
+ expect(f1.object_id).to eql(f2.object_id)
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -58,12 +58,6 @@ describe CouchRest::Model::Connection do
58
58
  @class.use_database(db)
59
59
  expect(@class.database).to eql(db)
60
60
  end
61
- it "should never prepare the database before it is needed" do
62
- db = @class.server.database('test')
63
- expect(@class).not_to receive(:prepare_database)
64
- @class.use_database('test')
65
- @class.use_database(db)
66
- end
67
61
  it "should use the database specified" do
68
62
  @class.use_database(:test)
69
63
  expect(@class.database.name).to eql('couchrest_test')
@@ -90,7 +84,7 @@ describe CouchRest::Model::Connection do
90
84
  expect(@class.server).to be_a(CouchRest::Server)
91
85
  end
92
86
  it "should provide a server with default config" do
93
- expect(@class.server.uri.to_s).to eql("http://localhost:5984")
87
+ expect(@class.server.uri.to_s).to eql(CouchRest::Model::Base.server.uri.to_s)
94
88
  end
95
89
  it "should allow the configuration to be overwritten" do
96
90
  @class.connection = {
@@ -131,9 +125,10 @@ describe CouchRest::Model::Connection do
131
125
 
132
126
  it "should use the .use_database value" do
133
127
  @class.use_database('testing')
134
- db = @class.prepare_database
128
+ db = @class.database
135
129
  expect(db.name).to eql('couchrest_testing')
136
130
  end
131
+
137
132
  end
138
133
 
139
134
  describe "protected methods" do
@@ -42,6 +42,7 @@ describe CouchRest::Model::Designs::Migrations do
42
42
  end
43
43
  doc = @db.get(@doc['_id'])
44
44
  expect(doc['views']['all']).to eql(@doc['views']['all'])
45
+ expect(doc['couchrest-hash']).not_to be_nil
45
46
  expect(callback).to be_nil
46
47
  end
47
48
 
@@ -65,20 +65,23 @@ describe "Design View" do
65
65
  end
66
66
 
67
67
  describe "with proxy in query for first initialization" do
68
- it "should set model to proxy object and remove from query" do
68
+ it "should set owner to proxy object and remove from query" do
69
69
  proxy = double("Proxy")
70
70
  @obj = @klass.new(@mod.design_doc, @mod, {:proxy => proxy}, 'test_view')
71
- expect(@obj.model).to eql(proxy)
71
+ expect(@obj.owner).to eql(proxy)
72
+ expect(@obj.model).to eql(@mod)
72
73
  end
73
74
  end
74
75
 
75
76
  describe "with proxy in query for chained instance" do
76
- it "should set the model to proxy object instead of parents model" do
77
+ it "should set the owner to proxy object instead of parents model" do
77
78
  proxy = double("Proxy")
78
79
  @obj = @klass.new(@mod.design_doc, @mod, {}, 'test_view')
80
+ expect(@obj.owner).to eql(@mod)
79
81
  expect(@obj.model).to eql(@mod)
80
82
  @obj = @obj.proxy(proxy)
81
- expect(@obj.model).to eql(proxy)
83
+ expect(@obj.owner).to eql(proxy)
84
+ expect(@obj.model).to eql(@mod)
82
85
  end
83
86
  end
84
87
 
@@ -175,6 +178,31 @@ describe "Design View" do
175
178
  str = @design_doc['views']['by_title']['map']
176
179
  expect(str).to include("emit(doc['title'], [1, doc['name']]);")
177
180
  end
181
+
182
+ it "should guard against nulls when emitting properties" do
183
+ @klass.define(@design_doc, 'by_title', :emit => :name)
184
+ str = @design_doc['views']['by_title']['map']
185
+ expect(str).to include("doc['name'] != null")
186
+ end
187
+
188
+ it "should guard against nulls when emitting multiple properties" do
189
+ @klass.define(@design_doc, 'by_title', :emit => [:name, :another_property])
190
+ str = @design_doc['views']['by_title']['map']
191
+ expect(str).to include("doc['name'] != null")
192
+ expect(str).to include("doc['another_property'] != null")
193
+ end
194
+
195
+ it "should not guard against nulls for non-symbol emits" do
196
+ @klass.define(@design_doc, 'by_title', :emit => [:name, 3])
197
+ str = @design_doc['views']['by_title']['map']
198
+ expect(str).not_to include("( != null)")
199
+ end
200
+
201
+ it "should not provide a default reduce function the emit value is overridden" do
202
+ @klass.define(@design_doc, 'by_title', :emit => :name)
203
+ str = @design_doc['views']['by_title']['reduce']
204
+ expect(str).to be_nil
205
+ end
178
206
  end
179
207
 
180
208
  describe ".create_model_methods" do
@@ -239,7 +267,7 @@ describe "Design View" do
239
267
  it "should wrap rows in ViewRow class" do
240
268
  expect(@obj).to receive(:execute).and_return(true)
241
269
  expect(@obj).to receive(:result).twice.and_return({'rows' => [{:foo => :bar}]})
242
- expect(CouchRest::Model::Designs::ViewRow).to receive(:new).with({:foo => :bar}, @obj.model, DB)
270
+ expect(CouchRest::Model::Designs::ViewRow).to receive(:new).with({:foo => :bar}, @obj.owner)
243
271
  @obj.rows
244
272
  end
245
273
 
@@ -447,13 +475,6 @@ describe "Design View" do
447
475
  end
448
476
  end
449
477
 
450
- describe "#database" do
451
- it "should update query with value" do
452
- expect(@obj).to receive(:update_query).with({:database => 'foo'})
453
- @obj.database('foo')
454
- end
455
- end
456
-
457
478
  describe "#key" do
458
479
  it "should update query with value" do
459
480
  expect(@obj).to receive(:update_query).with({:key => 'foo'})
@@ -739,6 +760,12 @@ describe "Design View" do
739
760
  end
740
761
  end
741
762
 
763
+ describe "#database" do
764
+ it "should reference the owners database" do
765
+ expect(@obj.database).to eql(@mod.database)
766
+ end
767
+ end
768
+
742
769
  describe "#execute" do
743
770
  before :each do
744
771
  # disable real execution!
@@ -754,10 +781,7 @@ describe "Design View" do
754
781
  end
755
782
 
756
783
  it "should raise issue if no database" do
757
- expect(@obj).to receive(:query).and_return({:database => nil})
758
- model = double("SomeModel")
759
- expect(model).to receive(:database).and_return(nil)
760
- expect(@obj).to receive(:model).and_return(model)
784
+ expect(@obj).to receive(:database).and_return(nil)
761
785
  expect { @obj.send(:execute) }.to raise_error(CouchRest::Model::DatabaseNotDefined)
762
786
  end
763
787
 
@@ -859,6 +883,14 @@ describe "Design View" do
859
883
  end
860
884
  end
861
885
  end
886
+
887
+ describe "ActiveRecord compatibility methods" do
888
+ describe "#model_name" do
889
+ it "should use the #model class" do
890
+ expect(@obj.model_name.to_s).to eql DesignViewModel.to_s
891
+ end
892
+ end
893
+ end
862
894
  end
863
895
  end
864
896
 
@@ -868,19 +900,19 @@ describe "Design View" do
868
900
  @klass = CouchRest::Model::Designs::ViewRow
869
901
  end
870
902
 
871
- let :model do
903
+ let :owner do
872
904
  m = double()
873
905
  allow(m).to receive(:database).and_return(DB)
874
906
  m
875
907
  end
876
908
 
877
909
  describe "intialize" do
878
- it "should store reference to model" do
879
- obj = @klass.new({}, model)
880
- expect(obj.model).to eql(model)
910
+ it "should store reference to owner" do
911
+ obj = @klass.new({}, owner)
912
+ expect(obj.owner).to eql(owner)
881
913
  end
882
914
  it "should copy details from hash" do
883
- obj = @klass.new({:foo => :bar, :test => :example}, model)
915
+ obj = @klass.new({:foo => :bar, :test => :example}, owner)
884
916
  expect(obj[:foo]).to eql(:bar)
885
917
  expect(obj[:test]).to eql(:example)
886
918
  end
@@ -891,32 +923,22 @@ describe "Design View" do
891
923
  end
892
924
 
893
925
  it "should provide id" do
894
- obj = @klass.new({'id' => '123456'}, model)
926
+ obj = @klass.new({'id' => '123456'}, owner)
895
927
  expect(obj.id).to eql('123456')
896
928
  end
897
929
 
898
- it "may be instantiated with a database" do
899
- obj = @klass.new({'id' => '123456'}, model, 'foo')
900
- expect(obj.db).to eql('foo')
901
- end
902
-
903
- it "may use model's database" do
904
- obj = @klass.new({'id' => '123456'}, model)
905
- expect(obj.db).to eql(DB)
906
- end
907
-
908
930
  it "should provide key" do
909
- obj = @klass.new({'key' => 'thekey'}, model)
931
+ obj = @klass.new({'key' => 'thekey'}, owner)
910
932
  expect(obj.key).to eql('thekey')
911
933
  end
912
934
 
913
935
  it "should provide the value" do
914
- obj = @klass.new({'value' => 'thevalue'}, model)
936
+ obj = @klass.new({'value' => 'thevalue'}, owner)
915
937
  expect(obj.value).to eql('thevalue')
916
938
  end
917
939
 
918
940
  it "should provide the raw document" do
919
- obj = @klass.new({'doc' => 'thedoc'}, model)
941
+ obj = @klass.new({'doc' => 'thedoc'}, owner)
920
942
  expect(obj.raw_doc).to eql('thedoc')
921
943
  end
922
944
 
@@ -924,8 +946,7 @@ describe "Design View" do
924
946
  hash = {'doc' => {'_id' => '12345', 'name' => 'sam'}}
925
947
  obj = @klass.new(hash, DesignViewModel)
926
948
  doc = double('DesignViewDoc')
927
- allow(doc).to receive(:database).and_return(DB)
928
- expect(obj.model).to receive(:build_from_database).with(hash['doc']).and_return(doc)
949
+ expect(obj.owner).to receive(:build_from_database).with(hash['doc']).and_return(doc)
929
950
  expect(obj.doc).to eql(doc)
930
951
  end
931
952
 
@@ -933,8 +954,7 @@ describe "Design View" do
933
954
  hash = {'id' => '12345', 'value' => 5}
934
955
  obj = @klass.new(hash, DesignViewModel)
935
956
  doc = double('DesignViewModel')
936
- allow(doc).to receive(:database).and_return(DB)
937
- expect(obj.model).to receive(:get).with('12345', DB).and_return(doc)
957
+ expect(obj.owner).to receive(:get).with('12345').and_return(doc)
938
958
  expect(obj.doc).to eql(doc)
939
959
  end
940
960
 
@@ -942,16 +962,14 @@ describe "Design View" do
942
962
  hash = {'id' => '12345', 'value' => {'_id' => '54321'}}
943
963
  obj = @klass.new(hash, DesignViewModel)
944
964
  doc = double('DesignViewModel')
945
- allow(doc).to receive(:database).and_return(DB)
946
- expect(obj.model).to receive(:get).with('54321', DB).and_return(doc)
965
+ expect(obj.owner).to receive(:get).with('54321').and_return(doc)
947
966
  expect(obj.doc).to eql(doc)
948
967
  end
949
968
 
950
969
  it "should try to return nil for document if none available" do
951
970
  hash = {'value' => 23} # simulate reduce
952
971
  obj = @klass.new(hash, DesignViewModel)
953
- doc = double('DesignViewModel')
954
- expect(obj.model).not_to receive(:get)
972
+ expect(obj.owner).not_to receive(:get)
955
973
  expect(obj.doc).to be_nil
956
974
  end
957
975