couchrest_model 2.2.0.beta1 → 2.2.0.beta2

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