friendly_postgres 0.4.3
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/.document +2 -0
- data/.gitignore +26 -0
- data/APACHE-LICENSE +202 -0
- data/CHANGELOG.md +19 -0
- data/CONTRIBUTORS.md +7 -0
- data/LICENSE +20 -0
- data/README.md +265 -0
- data/Rakefile +68 -0
- data/TODO.md +5 -0
- data/VERSION +1 -0
- data/examples/friendly.yml +7 -0
- data/friendly.gemspec +232 -0
- data/lib/friendly.rb +54 -0
- data/lib/friendly/associations.rb +7 -0
- data/lib/friendly/associations/association.rb +34 -0
- data/lib/friendly/associations/set.rb +37 -0
- data/lib/friendly/attribute.rb +91 -0
- data/lib/friendly/boolean.rb +10 -0
- data/lib/friendly/cache.rb +24 -0
- data/lib/friendly/cache/by_id.rb +33 -0
- data/lib/friendly/config.rb +5 -0
- data/lib/friendly/data_store.rb +72 -0
- data/lib/friendly/document.rb +257 -0
- data/lib/friendly/document_table.rb +56 -0
- data/lib/friendly/index.rb +73 -0
- data/lib/friendly/memcached.rb +48 -0
- data/lib/friendly/named_scope.rb +17 -0
- data/lib/friendly/newrelic.rb +6 -0
- data/lib/friendly/query.rb +42 -0
- data/lib/friendly/scope.rb +100 -0
- data/lib/friendly/scope_proxy.rb +45 -0
- data/lib/friendly/sequel_monkey_patches.rb +34 -0
- data/lib/friendly/storage.rb +31 -0
- data/lib/friendly/storage_factory.rb +24 -0
- data/lib/friendly/storage_proxy.rb +103 -0
- data/lib/friendly/table.rb +15 -0
- data/lib/friendly/table_creator.rb +48 -0
- data/lib/friendly/time.rb +14 -0
- data/lib/friendly/translator.rb +32 -0
- data/lib/friendly/uuid.rb +148 -0
- data/rails/init.rb +3 -0
- data/spec/config.yml.example +7 -0
- data/spec/fakes/data_store_fake.rb +29 -0
- data/spec/fakes/database_fake.rb +12 -0
- data/spec/fakes/dataset_fake.rb +28 -0
- data/spec/fakes/document.rb +18 -0
- data/spec/fakes/serializer_fake.rb +12 -0
- data/spec/fakes/time_fake.rb +12 -0
- data/spec/integration/ad_hoc_scopes_spec.rb +42 -0
- data/spec/integration/basic_object_lifecycle_spec.rb +114 -0
- data/spec/integration/batch_insertion_spec.rb +29 -0
- data/spec/integration/convenience_api_spec.rb +25 -0
- data/spec/integration/count_spec.rb +12 -0
- data/spec/integration/default_value_spec.rb +15 -0
- data/spec/integration/find_via_cache_spec.rb +101 -0
- data/spec/integration/finder_spec.rb +64 -0
- data/spec/integration/has_many_spec.rb +18 -0
- data/spec/integration/index_spec.rb +57 -0
- data/spec/integration/named_scope_spec.rb +34 -0
- data/spec/integration/pagination_spec.rb +63 -0
- data/spec/integration/scope_chaining_spec.rb +22 -0
- data/spec/integration/table_creator_spec.rb +64 -0
- data/spec/integration/write_through_cache_spec.rb +53 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +103 -0
- data/spec/unit/associations/association_spec.rb +57 -0
- data/spec/unit/associations/set_spec.rb +43 -0
- data/spec/unit/attribute_spec.rb +105 -0
- data/spec/unit/cache_by_id_spec.rb +102 -0
- data/spec/unit/cache_spec.rb +21 -0
- data/spec/unit/config_spec.rb +4 -0
- data/spec/unit/data_store_spec.rb +188 -0
- data/spec/unit/document_spec.rb +358 -0
- data/spec/unit/document_table_spec.rb +126 -0
- data/spec/unit/friendly_spec.rb +25 -0
- data/spec/unit/index_spec.rb +196 -0
- data/spec/unit/memcached_spec.rb +114 -0
- data/spec/unit/named_scope_spec.rb +16 -0
- data/spec/unit/query_spec.rb +104 -0
- data/spec/unit/scope_proxy_spec.rb +44 -0
- data/spec/unit/scope_spec.rb +113 -0
- data/spec/unit/storage_factory_spec.rb +59 -0
- data/spec/unit/storage_proxy_spec.rb +218 -0
- data/spec/unit/translator_spec.rb +96 -0
- data/website/index.html +210 -0
- data/website/scripts/clipboard.swf +0 -0
- data/website/scripts/shBrushAS3.js +61 -0
- data/website/scripts/shBrushBash.js +66 -0
- data/website/scripts/shBrushCSharp.js +67 -0
- data/website/scripts/shBrushColdFusion.js +102 -0
- data/website/scripts/shBrushCpp.js +99 -0
- data/website/scripts/shBrushCss.js +93 -0
- data/website/scripts/shBrushDelphi.js +57 -0
- data/website/scripts/shBrushDiff.js +43 -0
- data/website/scripts/shBrushErlang.js +54 -0
- data/website/scripts/shBrushGroovy.js +69 -0
- data/website/scripts/shBrushJScript.js +52 -0
- data/website/scripts/shBrushJava.js +59 -0
- data/website/scripts/shBrushJavaFX.js +60 -0
- data/website/scripts/shBrushPerl.js +74 -0
- data/website/scripts/shBrushPhp.js +91 -0
- data/website/scripts/shBrushPlain.js +35 -0
- data/website/scripts/shBrushPowerShell.js +76 -0
- data/website/scripts/shBrushPython.js +66 -0
- data/website/scripts/shBrushRuby.js +57 -0
- data/website/scripts/shBrushScala.js +53 -0
- data/website/scripts/shBrushSql.js +68 -0
- data/website/scripts/shBrushVb.js +58 -0
- data/website/scripts/shBrushXml.js +71 -0
- data/website/scripts/shCore.js +30 -0
- data/website/scripts/shLegacy.js +30 -0
- data/website/styles/friendly.css +103 -0
- data/website/styles/help.png +0 -0
- data/website/styles/ie.css +35 -0
- data/website/styles/magnifier.png +0 -0
- data/website/styles/page_white_code.png +0 -0
- data/website/styles/page_white_copy.png +0 -0
- data/website/styles/print.css +29 -0
- data/website/styles/printer.png +0 -0
- data/website/styles/screen.css +257 -0
- data/website/styles/shCore.css +330 -0
- data/website/styles/shThemeDefault.css +173 -0
- data/website/styles/shThemeDjango.css +176 -0
- data/website/styles/shThemeEclipse.css +190 -0
- data/website/styles/shThemeEmacs.css +175 -0
- data/website/styles/shThemeFadeToGrey.css +177 -0
- data/website/styles/shThemeMidnight.css +175 -0
- data/website/styles/shThemeRDark.css +175 -0
- metadata +302 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "Has many associations" do
|
|
4
|
+
before do
|
|
5
|
+
@user = User.create :name => "Fred"
|
|
6
|
+
@addresses = (0..2).map { Address.create :user_id => @user.id }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "returns the objects whose foreign keys match the object's id" do
|
|
10
|
+
found = @user.addresses.all.sort { |a, b| a.id <=> b.id }
|
|
11
|
+
found.should == @addresses.sort { |a, b| a.id <=> b.id }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "accepts class_name and foreign_key overrides" do
|
|
15
|
+
found = @user.addresses_override.all.sort { |a, b| a.id <=> b.id }
|
|
16
|
+
found.should == @addresses.sort { |a, b| a.id <=> b.id }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
require 'active_support/duration'
|
|
3
|
+
require 'active_support/core_ext/integer'
|
|
4
|
+
require 'active_support/core_ext/time'
|
|
5
|
+
require 'active_support/core_ext/object'
|
|
6
|
+
require 'active_support/core_ext/numeric'
|
|
7
|
+
|
|
8
|
+
describe "Finding one object with an index" do
|
|
9
|
+
before do
|
|
10
|
+
@created_user = User.new(:name => "Cleveland")
|
|
11
|
+
@created_user.save
|
|
12
|
+
@found_user = User.first(:name => "Cleveland")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "finds the object" do
|
|
16
|
+
@found_user.should == @created_user
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe "Finding one object that doesn't exist in the index" do
|
|
21
|
+
it "returns nil" do
|
|
22
|
+
User.first(:name => "asdf").should be_nil
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe "Finding object on an index that doesn't exist" do
|
|
27
|
+
it "raises Friendly::MissingIndex" do
|
|
28
|
+
lambda {
|
|
29
|
+
User.first(:age => 3)
|
|
30
|
+
}.should raise_error(Friendly::MissingIndex)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe "Finding ordered objects" do
|
|
35
|
+
before do
|
|
36
|
+
@three = User.new(:name => "Brian", :created_at => 4.hours.ago)
|
|
37
|
+
@two = User.new(:name => "Brian", :created_at => 2.hours.ago)
|
|
38
|
+
@one = User.new(:name => "Brian", :created_at => 5.minutes.ago)
|
|
39
|
+
[@one, @two, @three].each { |i| i.save }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
after do
|
|
43
|
+
[@one, @two, @three].each { |i| i.destroy }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "can return the objects in desc order" do
|
|
47
|
+
found_users = User.all(:name => "Brian", :order! => :created_at.desc)
|
|
48
|
+
found_users.should == [@one, @two, @three]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "can return the objects in asc order" do
|
|
52
|
+
found_users = User.all(:name => "Brian", :order! => :created_at.asc)
|
|
53
|
+
found_users.length.should == 3
|
|
54
|
+
found_users.should == [@three, @two, @one]
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "named_scope" do
|
|
4
|
+
describe "calling a single named_scope" do
|
|
5
|
+
before do
|
|
6
|
+
User.all(:name => "Quagmire").each { |q| q.destroy }
|
|
7
|
+
5.times { User.create(:name => "Quagmire") }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe "all" do
|
|
11
|
+
it "returns all objects matching the conditions" do
|
|
12
|
+
User.named_quagmire.all.should == User.all(:name => "Quagmire")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "accepts extra conditions" do
|
|
16
|
+
User.create(:name => "Fred")
|
|
17
|
+
found = User.named_quagmire.all(:name => "Fred")
|
|
18
|
+
found.should == User.all(:name => "Fred")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe "first" do
|
|
23
|
+
it "returns the first object matching the conditions" do
|
|
24
|
+
User.named_quagmire.first.should == User.first(:name => "Quagmire")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "accepts extra conditions" do
|
|
28
|
+
User.create(:name => "Fred")
|
|
29
|
+
found = User.named_quagmire.first(:name => "Fred")
|
|
30
|
+
found.should == User.first(:name => "Fred")
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
require 'active_support/core_ext'
|
|
3
|
+
|
|
4
|
+
describe "Paginating" do
|
|
5
|
+
before do
|
|
6
|
+
User.all(:name => "Fred").each { |u| u.destroy }
|
|
7
|
+
@users = (0...15).map do |i|
|
|
8
|
+
User.create(:name => "Fred",
|
|
9
|
+
:created_at => i.hours.ago)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe "fetching the nil page" do
|
|
14
|
+
before do
|
|
15
|
+
@found = User.paginate(:name => "Fred",
|
|
16
|
+
:order! => :created_at.desc,
|
|
17
|
+
:page! => nil,
|
|
18
|
+
:per_page! => 5)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "returns the first :per_page results" do
|
|
22
|
+
@found.should == @users.slice(0,5)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "returns an instance of WillPaginate::Collection" do
|
|
26
|
+
@found.should be_instance_of(WillPaginate::Collection)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "fetching a page by number" do
|
|
31
|
+
before do
|
|
32
|
+
@found = User.paginate(:name => "Fred",
|
|
33
|
+
:order! => :created_at.desc,
|
|
34
|
+
:page! => 2,
|
|
35
|
+
:per_page! => 5)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "returns the :per_page results starting at offset :per_page * page" do
|
|
39
|
+
@found.should == @users.slice(5,5)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "returns an instance of WillPaginate::Collection" do
|
|
43
|
+
@found.should be_instance_of(WillPaginate::Collection)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe "when :page is a string" do
|
|
48
|
+
before do
|
|
49
|
+
@found = User.paginate(:name => "Fred",
|
|
50
|
+
:order! => :created_at.desc,
|
|
51
|
+
:page! => "2",
|
|
52
|
+
:per_page! => 5)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "returns the :per_page results starting at offset :per_page * page" do
|
|
56
|
+
@found.should == @users.slice(5,5)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "returns an instance of WillPaginate::Collection" do
|
|
60
|
+
@found.should be_instance_of(WillPaginate::Collection)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
require "active_support/core_ext"
|
|
3
|
+
|
|
4
|
+
describe "Chaining scopes together" do
|
|
5
|
+
describe "then calling #all" do
|
|
6
|
+
before do
|
|
7
|
+
User.all(:name => "Quagmire").each { |q| q.destroy }
|
|
8
|
+
@users = (0...10).map do |i|
|
|
9
|
+
User.create :name => "Quagmire",
|
|
10
|
+
:created_at => i.hours.ago
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "queries using a combination of both scopes" do
|
|
15
|
+
User.named_quagmire.recent.all.should == @users.slice(0, 3)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "gives scopes on the right priority" do
|
|
19
|
+
User.named_joe.named_quagmire.first.name.should == "Quagmire"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "Creating the tables for a model" do
|
|
4
|
+
before do
|
|
5
|
+
@klass = Class.new do
|
|
6
|
+
include Friendly::Document
|
|
7
|
+
|
|
8
|
+
def self.table_name; "stuffs"; end
|
|
9
|
+
|
|
10
|
+
attribute :name, String
|
|
11
|
+
attribute :user_id, Friendly::UUID
|
|
12
|
+
|
|
13
|
+
indexes [:name, :created_at]
|
|
14
|
+
indexes :user_id
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
@klass.create_tables!
|
|
18
|
+
@schema = Friendly.db.schema(:stuffs)
|
|
19
|
+
@table = Hash[*@schema.map { |s| [s.first, s.last] }.flatten]
|
|
20
|
+
@index_schema = Friendly.db.schema(:index_stuffs_on_name_and_created_at)
|
|
21
|
+
@index_table = Hash[*@index_schema.map { |s| [s.first, s.last] }.flatten]
|
|
22
|
+
@id_idx_schema = Friendly.db.schema(:index_stuffs_on_user_id)
|
|
23
|
+
@id_index_table = Hash[*@id_idx_schema.map { |s| [s.first, s.last] }.flatten]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
after do
|
|
27
|
+
Friendly.db.drop_table(:stuffs)
|
|
28
|
+
Friendly.db.drop_table(:index_stuffs_on_name_and_created_at)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "creates a table for the document" do
|
|
32
|
+
@table.keys.length.should == 5
|
|
33
|
+
@table[:added_id][:db_type].should == "int(11)"
|
|
34
|
+
@table[:added_id][:primary_key].should be_true
|
|
35
|
+
@table[:id][:db_type].should == "binary(16)"
|
|
36
|
+
@table[:attributes][:db_type].should == "text"
|
|
37
|
+
@table[:created_at][:db_type].should == "datetime"
|
|
38
|
+
@table[:updated_at][:db_type].should == "datetime"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "creates a table for each index" do
|
|
42
|
+
@index_table.keys.length.should == 3
|
|
43
|
+
@index_table[:name][:db_type].should == "varchar(255)"
|
|
44
|
+
@index_table[:name][:primary_key].should be_true
|
|
45
|
+
@index_table[:created_at][:db_type].should == "datetime"
|
|
46
|
+
@index_table[:created_at][:primary_key].should be_true
|
|
47
|
+
@index_table[:id][:db_type].should == "binary(16)"
|
|
48
|
+
@index_table[:id][:primary_key].should be_true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "knows how to create an index for a field of a custom type" do
|
|
52
|
+
@id_index_table.keys.length.should == 2
|
|
53
|
+
@id_index_table[:user_id][:db_type].should == "binary(16)"
|
|
54
|
+
@id_index_table[:user_id][:primary_key].should be_true
|
|
55
|
+
@id_index_table[:id][:db_type].should == "binary(16)"
|
|
56
|
+
@id_index_table[:id][:primary_key].should be_true
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "doesn't raise if the tables already exist" do
|
|
60
|
+
lambda {
|
|
61
|
+
@klass.create_tables!
|
|
62
|
+
}.should_not raise_error
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "Writing through to cache on create" do
|
|
4
|
+
before do
|
|
5
|
+
@address = Address.create :street => "Spooner"
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it "writes through to memcache using the model street and guid as cache key" do
|
|
9
|
+
$cache.get("Address/0/#{@address.id.to_guid}").should == @address
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe "Writing with a version number" do
|
|
14
|
+
before do
|
|
15
|
+
Address.storage_proxy.caches.clear
|
|
16
|
+
Address.caches_by :id, :version => 1
|
|
17
|
+
@address = Address.create :street => "Spooner"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
after do
|
|
21
|
+
Address.storage_proxy.caches.clear
|
|
22
|
+
Address.caches_by :id
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "adds the version number to the cache key" do
|
|
26
|
+
$cache.get("Address/1/#{@address.id.to_guid}").should == @address
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "Writing through to cache on update" do
|
|
31
|
+
before do
|
|
32
|
+
@address = Address.create :street => "Spooner"
|
|
33
|
+
@address.street = "Joe"
|
|
34
|
+
@address.save
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "writes through to memcache using the model street and guid as cache key" do
|
|
38
|
+
$cache.get("Address/0/#{@address.id.to_guid}").street.should == @address.street
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe "Writing through to cache on destroy" do
|
|
43
|
+
before do
|
|
44
|
+
@address = Address.create :street => "Spooner"
|
|
45
|
+
@address.destroy
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "removes the object from cache" do
|
|
49
|
+
lambda {
|
|
50
|
+
$cache.get("Address/0/#{@address.id.to_guid}")
|
|
51
|
+
}.should raise_error(Memcached::NotFound)
|
|
52
|
+
end
|
|
53
|
+
end
|
data/spec/spec.opts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--color --backtrace
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
3
|
+
Dir[File.expand_path(File.dirname(__FILE__)) + "/fakes/*.rb"].each do |f|
|
|
4
|
+
require f
|
|
5
|
+
end
|
|
6
|
+
require 'rubygems'
|
|
7
|
+
require 'spec'
|
|
8
|
+
require 'spec/autorun'
|
|
9
|
+
require 'sequel'
|
|
10
|
+
require 'json/pure'
|
|
11
|
+
gem 'jferris-mocha'
|
|
12
|
+
require 'mocha'
|
|
13
|
+
require 'memcached'
|
|
14
|
+
require 'friendly'
|
|
15
|
+
|
|
16
|
+
config = YAML.load(File.read(File.dirname(__FILE__) + "/config.yml"))['test']
|
|
17
|
+
Friendly.configure config
|
|
18
|
+
$db = Friendly.db
|
|
19
|
+
#Sequel::MySQL.default_engine = "InnoDB"
|
|
20
|
+
|
|
21
|
+
$db.drop_table :users if $db.table_exists?("users")
|
|
22
|
+
$db.drop_table :index_users_on_name if $db.table_exists?("index_users_on_name")
|
|
23
|
+
if $db.table_exists?("index_users_on_name_and_created_at")
|
|
24
|
+
$db.drop_table :index_users_on_name_and_created_at
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
datastore = Friendly::DataStore.new($db)
|
|
28
|
+
Friendly.datastore = datastore
|
|
29
|
+
|
|
30
|
+
$cache = Memcached.new
|
|
31
|
+
Friendly.cache = Friendly::Memcached.new($cache)
|
|
32
|
+
|
|
33
|
+
class User
|
|
34
|
+
include Friendly::Document
|
|
35
|
+
|
|
36
|
+
attribute :name, String
|
|
37
|
+
attribute :age, Integer
|
|
38
|
+
attribute :happy, Friendly::Boolean, :default => true
|
|
39
|
+
attribute :sad, Friendly::Boolean, :default => false
|
|
40
|
+
attribute :friend, Friendly::UUID
|
|
41
|
+
|
|
42
|
+
indexes :happy
|
|
43
|
+
indexes :friend
|
|
44
|
+
indexes :name
|
|
45
|
+
indexes :name, :created_at
|
|
46
|
+
|
|
47
|
+
named_scope :named_joe, :name => "Joe"
|
|
48
|
+
named_scope :named_quagmire, :name => "Quagmire"
|
|
49
|
+
named_scope :recent, :order! => :created_at.desc,
|
|
50
|
+
:limit! => 3
|
|
51
|
+
|
|
52
|
+
has_many :addresses
|
|
53
|
+
has_many :addresses_override, :class_name => "Address",
|
|
54
|
+
:foreign_key => :user_id
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
User.create_tables!
|
|
58
|
+
|
|
59
|
+
class Address
|
|
60
|
+
include Friendly::Document
|
|
61
|
+
|
|
62
|
+
attribute :user_id, Friendly::UUID
|
|
63
|
+
attribute :street, String
|
|
64
|
+
|
|
65
|
+
indexes :user_id
|
|
66
|
+
indexes :street
|
|
67
|
+
caches_by :id
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
Address.create_tables!
|
|
71
|
+
|
|
72
|
+
module Mocha
|
|
73
|
+
module API
|
|
74
|
+
def setup_mocks_for_rspec
|
|
75
|
+
mocha_setup
|
|
76
|
+
end
|
|
77
|
+
def verify_mocks_for_rspec
|
|
78
|
+
mocha_verify
|
|
79
|
+
end
|
|
80
|
+
def teardown_mocks_for_rspec
|
|
81
|
+
mocha_teardown
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
module Factory
|
|
87
|
+
def row(opts = {})
|
|
88
|
+
{ :id => 1, :created_at => Time.new, :updated_at => Time.new }.merge(opts)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def query(conditions)
|
|
92
|
+
stub(:order => conditions.delete(:order!),
|
|
93
|
+
:limit => conditions.delete(:limit!),
|
|
94
|
+
:preserve_order? => conditions.delete(:preserve_order!),
|
|
95
|
+
:conditions => conditions,
|
|
96
|
+
:offset => conditions.delete(:offset!))
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
Spec::Runner.configure do |config|
|
|
101
|
+
config.mock_with Mocha::API
|
|
102
|
+
config.include Factory
|
|
103
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require File.expand_path("../../../spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "Friendly::Associations::Association" do
|
|
4
|
+
before do
|
|
5
|
+
@owner_klass = stub(:name => "User")
|
|
6
|
+
@klass = stub
|
|
7
|
+
# FIXME: ugh.
|
|
8
|
+
String.any_instance.stubs(:constantize).returns(@klass)
|
|
9
|
+
@assoc_klass = Friendly::Associations::Association
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe "with defaults" do
|
|
13
|
+
before do
|
|
14
|
+
@association = @assoc_klass.new(@owner_klass, :addresses)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "has a default klass of name.classify.constantize" do
|
|
18
|
+
@association.klass.should == @klass
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "has a foreign_key of owner_klass.name.singularize + '_id'" do
|
|
22
|
+
@association.foreign_key.should == :user_id
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "returns a scope on klass of {:foreign_key => document.id}" do
|
|
26
|
+
@scope = stub
|
|
27
|
+
@klass.stubs(:scope).with(:user_id => 42).returns(@scope)
|
|
28
|
+
|
|
29
|
+
@association.scope(stub(:id => 42)).should == @scope
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe "with overridden attributes" do
|
|
34
|
+
before do
|
|
35
|
+
@klass = stub
|
|
36
|
+
@class_name = "SomeOtherClass"
|
|
37
|
+
@class_name.stubs(:constantize).returns(@klass)
|
|
38
|
+
@association = @assoc_klass.new @owner_klass, :whatever,
|
|
39
|
+
:class_name => @class_name,
|
|
40
|
+
:foreign_key => :other_id
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "uses the overridden class_name to get the class" do
|
|
44
|
+
@association.klass.should == @klass
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "uses the overridden foreign_key" do
|
|
48
|
+
@association.foreign_key.should == :other_id
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "uses the override foreign_key in the scope" do
|
|
52
|
+
@scope = stub
|
|
53
|
+
@klass.stubs(:scope).with(:other_id => 42).returns(@scope)
|
|
54
|
+
@association.scope(stub(:id => 42)).should == @scope
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|