friendly 0.3.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 +5 -0
- data/.gitignore +23 -0
- data/APACHE-LICENSE +202 -0
- data/LICENSE +20 -0
- data/README.md +173 -0
- data/Rakefile +67 -0
- data/VERSION +1 -0
- data/examples/friendly.yml +7 -0
- data/friendly.gemspec +201 -0
- data/lib/friendly/attribute.rb +65 -0
- data/lib/friendly/boolean.rb +6 -0
- data/lib/friendly/cache/by_id.rb +33 -0
- data/lib/friendly/cache.rb +24 -0
- data/lib/friendly/config.rb +5 -0
- data/lib/friendly/data_store.rb +72 -0
- data/lib/friendly/document.rb +165 -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/newrelic.rb +6 -0
- data/lib/friendly/query.rb +42 -0
- data/lib/friendly/sequel_monkey_patches.rb +35 -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 +43 -0
- data/lib/friendly/time.rb +14 -0
- data/lib/friendly/translator.rb +32 -0
- data/lib/friendly/uuid.rb +143 -0
- data/lib/friendly.rb +49 -0
- data/rails/init.rb +3 -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/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/index_spec.rb +57 -0
- data/spec/integration/pagination_spec.rb +63 -0
- data/spec/integration/table_creator_spec.rb +52 -0
- data/spec/integration/write_through_cache_spec.rb +53 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +90 -0
- data/spec/unit/attribute_spec.rb +64 -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 +311 -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/query_spec.rb +104 -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 +264 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class DatasetFake
|
|
2
|
+
attr_accessor :where, :insert, :inserts, :update, :updates, :first
|
|
3
|
+
|
|
4
|
+
def initialize(opts = {})
|
|
5
|
+
opts.each { |k,v| send("#{k}=", v) }
|
|
6
|
+
@inserts ||= []
|
|
7
|
+
@updates ||= []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def where(conditions)
|
|
11
|
+
@where[conditions]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def insert(attributes)
|
|
15
|
+
inserts << attributes
|
|
16
|
+
@insert
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def update(attributes)
|
|
20
|
+
updates << attributes
|
|
21
|
+
@update
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def first(conditions)
|
|
25
|
+
@first[conditions]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class FakeDocument
|
|
2
|
+
attr_accessor :id, :created_at, :to_hash, :new_record, :table_name,
|
|
3
|
+
:indexes, :name, :updated_at, :where_clause
|
|
4
|
+
|
|
5
|
+
def initialize(opts = {})
|
|
6
|
+
opts.each { |k,v| send("#{k}=", v) }
|
|
7
|
+
new_record = true if new_record.nil?
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def new_record?
|
|
11
|
+
new_record
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def attributes=(attrs)
|
|
15
|
+
attrs.each { |k,v| send("#{k}=", v) }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "Creating and retrieving an object" do
|
|
4
|
+
before do
|
|
5
|
+
@user = User.new :name => "Stewie Griffin",
|
|
6
|
+
:age => 3
|
|
7
|
+
@user.save
|
|
8
|
+
@found_user = User.find(@user.id)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "finds the user in the database" do
|
|
12
|
+
@found_user.name.should == @user.name
|
|
13
|
+
@found_user.age.should == @user.age
|
|
14
|
+
@found_user.id.should == @user.id
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "locates an object that is == to the created object" do
|
|
18
|
+
@found_user.should == @user
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "sets the created_at timestamp for the record" do
|
|
22
|
+
@user.created_at.should_not be_nil
|
|
23
|
+
@user.created_at.should be_instance_of(Time)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "sets the created_at on the way out of the database" do
|
|
27
|
+
@found_user.created_at.should_not be_nil
|
|
28
|
+
@found_user.created_at.to_i.should == @user.created_at.to_i
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "sets the updated_at on the way out of the database" do
|
|
32
|
+
@found_user.updated_at.should_not be_nil
|
|
33
|
+
@found_user.updated_at.to_i.should == @user.updated_at.to_i
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "sets the updated_at" do
|
|
37
|
+
@user.updated_at.to_i.should == @user.created_at.to_i
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "doesn't serialize id, created_at, or updated_at in the attributes column" do
|
|
41
|
+
result = $db.from("users").first(:id => @user.id)
|
|
42
|
+
attrs = JSON.parse(result[:attributes])
|
|
43
|
+
attrs.keys.should_not include("id")
|
|
44
|
+
attrs.keys.should_not include("created_at")
|
|
45
|
+
attrs.keys.should_not include("updated_at")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "has an id of type Friendly::UUID" do
|
|
49
|
+
@user.id.should be_kind_of(Friendly::UUID)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe "Updating an object" do
|
|
54
|
+
before do
|
|
55
|
+
@user = User.new :name => "Stewie Griffin",
|
|
56
|
+
:age => 3
|
|
57
|
+
@user.save
|
|
58
|
+
@created_id = @user.id
|
|
59
|
+
@created_at = @user.created_at
|
|
60
|
+
|
|
61
|
+
sleep(0.1)
|
|
62
|
+
@user.name = "Brian Griffin"
|
|
63
|
+
@user.age = 8
|
|
64
|
+
@user.save
|
|
65
|
+
|
|
66
|
+
@found_user = User.find(@created_id)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "sets the updated_at column" do
|
|
70
|
+
@user.updated_at.should_not be_nil
|
|
71
|
+
@user.updated_at.should_not == @user.created_at
|
|
72
|
+
@user.updated_at.should > @user.created_at
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "doesn't change the created_at" do
|
|
76
|
+
@user.created_at.should == @created_at
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "doesn't change the id" do
|
|
80
|
+
@user.id.should == @created_id
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "saves the new attrs to the db" do
|
|
84
|
+
@found_user.name.should == "Brian Griffin"
|
|
85
|
+
@found_user.age.should == 8
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe "destroying a document" do
|
|
90
|
+
before do
|
|
91
|
+
@user = User.new :name => "Stewie Griffin"
|
|
92
|
+
@user.save
|
|
93
|
+
@user.destroy
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "removes it from the database" do
|
|
97
|
+
User.first(:id => @user.id).should be_nil
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
describe "Finding an object by id" do
|
|
102
|
+
it "raises Friendly::RecordNotFound if it doesn't exist" do
|
|
103
|
+
lambda { User.find(12345) }.should raise_error(Friendly::RecordNotFound)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
describe "An object that has a foreign key" do
|
|
108
|
+
it "is saveable in the database" do
|
|
109
|
+
lambda {
|
|
110
|
+
Address.create :user_id => Friendly::UUID.new
|
|
111
|
+
}.should_not raise_error
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "Batch inserting several documents" do
|
|
4
|
+
it "inserts them when the block returns" do
|
|
5
|
+
Friendly.batch do
|
|
6
|
+
user = User.new(:name => "Lois")
|
|
7
|
+
user.save
|
|
8
|
+
User.all(:name => "Lois").should be_empty
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
User.all(:name => "Lois").should_not be_empty
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "doesn't insert anything if an error is raised" do
|
|
15
|
+
begin
|
|
16
|
+
Friendly.batch do
|
|
17
|
+
user = User.new(:name => "Meg")
|
|
18
|
+
user.save
|
|
19
|
+
raise "AHHHH!"
|
|
20
|
+
end
|
|
21
|
+
rescue RuntimeError => e
|
|
22
|
+
@bubbled_up = true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@bubbled_up.should be_true
|
|
26
|
+
|
|
27
|
+
User.all(:name => "Meg").should be_empty
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "Document.create" do
|
|
4
|
+
before do
|
|
5
|
+
@user = User.create(:name => "James")
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it "initializes the document, saves it, and returns it" do
|
|
9
|
+
@user.name.should == "James"
|
|
10
|
+
@user.should_not be_new_record
|
|
11
|
+
User.find(@user.id).should_not be_nil
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe "Document#update_attributes" do
|
|
16
|
+
before do
|
|
17
|
+
@user = User.create
|
|
18
|
+
@user.update_attributes :name => "James"
|
|
19
|
+
@user = User.find(@user.id)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "updates the attributes in the database" do
|
|
23
|
+
@user.name.should == "James"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "Counting the objects matching a query" do
|
|
4
|
+
before do
|
|
5
|
+
User.all(:name => "Evil Monkey").each { |u| u.destroy }
|
|
6
|
+
5.times { User.create :name => "Evil Monkey" }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "does a count in the index to find out what matches" do
|
|
10
|
+
User.count(:name => "Evil Monkey").should == 5
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "An attribute with a default value" do
|
|
4
|
+
before do
|
|
5
|
+
@user = User.new
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it "has the value by default" do
|
|
9
|
+
@user.happy.should be_true
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "has a default vaue even when it's false" do
|
|
13
|
+
@user.sad.should be_false
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "Finding objects in the cache" do
|
|
4
|
+
def cache_key(user)
|
|
5
|
+
["Address", 0, user.id.to_guid].join("/")
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
describe "by id" do
|
|
9
|
+
before do
|
|
10
|
+
@address = Address.create :street => "Spooner"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe "if the user is in cache" do
|
|
14
|
+
before do
|
|
15
|
+
$cache.set(cache_key(@address), "the address")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "returns the object from cache if its there" do
|
|
19
|
+
Address.first(:id => @address.id).should == "the address"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe "if the user isn't there" do
|
|
24
|
+
before do
|
|
25
|
+
$cache.delete(cache_key(@address))
|
|
26
|
+
@found = Address.first(:id => @address.id)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "finds the object in the database" do
|
|
30
|
+
@found.should == @address
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "stores the object in cache (read through)" do
|
|
34
|
+
$cache.get(cache_key(@address)).should == @address
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe "several objects via id" do
|
|
40
|
+
before do
|
|
41
|
+
@addresses = (0..3).map { Address.create :street => "Spooner" }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe "when all objects are found" do
|
|
45
|
+
before do
|
|
46
|
+
@addresses.each { |a| $cache.set(cache_key(a), "the address") }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "returns all the objects from cache" do
|
|
50
|
+
Address.all(:id => @addresses.map { |u| u.id }).should == ["the address"] * 4
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe "when some objects are missing" do
|
|
55
|
+
before do
|
|
56
|
+
$cache.delete(cache_key(@addresses.first))
|
|
57
|
+
@found = Address.all(:id => @addresses.map { |u| u.id })
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "finds the object and returns it" do
|
|
61
|
+
expected = @addresses.sort { |a,b| a.id <=> b.id }
|
|
62
|
+
@found.sort { |a,b| a.id <=> b.id }.should == expected
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "writes the object through to the cache" do
|
|
66
|
+
lambda {
|
|
67
|
+
$cache.get(cache_key(@addresses.first))
|
|
68
|
+
}.should_not raise_error
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe "calling all when the array of ids is empty" do
|
|
74
|
+
it "returns nothing" do
|
|
75
|
+
Address.all(:id => [])
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
describe "finding several objects in order via cache" do
|
|
80
|
+
before do
|
|
81
|
+
@addresses = (0...5).map { Address.create :street => "Spooner" }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "returns them in order" do
|
|
85
|
+
fnd = Address.all(:id => @addresses.map { |a| a.id }, :preserve_order! => true)
|
|
86
|
+
fnd.should == @addresses
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
describe "when there's an object in the index that doesn't really exist" do
|
|
91
|
+
before do
|
|
92
|
+
table = $db.from("index_addresses_on_street")
|
|
93
|
+
table.where(:street => "Quahog").delete
|
|
94
|
+
table.insert(:street => "Quahog", :id => "12345")
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "just returns [] silently on #all" do
|
|
98
|
+
Address.all(:street => "Quahog").should == []
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
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 multiple objects by id" do
|
|
9
|
+
before do
|
|
10
|
+
@user_one = User.new
|
|
11
|
+
@user_two = User.new
|
|
12
|
+
@user_one.save
|
|
13
|
+
@user_two.save
|
|
14
|
+
@users = User.all(:id => [@user_one.id, @user_two.id])
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "finds the objects in the database" do
|
|
18
|
+
@users.length.should == 2
|
|
19
|
+
@users.should include(@user_one)
|
|
20
|
+
@users.should include(@user_two)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe "when no objects are found" do
|
|
24
|
+
it "returns an empty array" do
|
|
25
|
+
User.all(:id => [9999, 12345, 999]).should == []
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe "when one object is found, but others aren't" do
|
|
30
|
+
it "returns the found objects" do
|
|
31
|
+
User.all(:id => [@user_one.id, 12345]).should == [@user_one]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe "Limiting a query" do
|
|
37
|
+
before do
|
|
38
|
+
10.times { User.new(:name => "Stewie").save }
|
|
39
|
+
@results = User.all(:name => "Stewie", :limit! => 5)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "returns the number of results you asked for" do
|
|
43
|
+
@results.length.should == 5
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe "limiting a query with offset" do
|
|
48
|
+
before do
|
|
49
|
+
@objects = (0..10).map do |i|
|
|
50
|
+
User.new(:name => "Joe", :created_at => i.minutes.from_now).tap do |u|
|
|
51
|
+
u.save
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
after { @objects.each { |o| o.destroy } }
|
|
57
|
+
|
|
58
|
+
it "returns results starting from the offset to hte limit" do
|
|
59
|
+
User.all(:name => "Joe",
|
|
60
|
+
:offset! => 2,
|
|
61
|
+
:limit! => 2,
|
|
62
|
+
:order! => :created_at.desc).should == @objects.reverse.slice(2, 2)
|
|
63
|
+
end
|
|
64
|
+
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,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,52 @@
|
|
|
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
|
+
|
|
12
|
+
indexes [:name, :created_at]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
@klass.create_tables!
|
|
16
|
+
@schema = Friendly.db.schema(:stuffs)
|
|
17
|
+
@table = Hash[*@schema.map { |s| [s.first, s.last] }.flatten]
|
|
18
|
+
@index_schema = Friendly.db.schema(:index_stuffs_on_name_and_created_at)
|
|
19
|
+
@index_table = Hash[*@index_schema.map { |s| [s.first, s.last] }.flatten]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
after do
|
|
23
|
+
Friendly.db.drop_table(:stuffs)
|
|
24
|
+
Friendly.db.drop_table(:index_stuffs_on_name_and_created_at)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "creates a table for the document" do
|
|
28
|
+
@table.keys.length.should == 5
|
|
29
|
+
@table[:added_id][:db_type].should == "int(11)"
|
|
30
|
+
@table[:added_id][:primary_key].should be_true
|
|
31
|
+
@table[:id][:db_type].should == "binary(16)"
|
|
32
|
+
@table[:attributes][:db_type].should == "text"
|
|
33
|
+
@table[:created_at][:db_type].should == "datetime"
|
|
34
|
+
@table[:updated_at][:db_type].should == "datetime"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "creates a table for each index" do
|
|
38
|
+
@index_table.keys.length.should == 3
|
|
39
|
+
@index_table[:name][:db_type].should == "varchar(255)"
|
|
40
|
+
@index_table[:name][:primary_key].should be_true
|
|
41
|
+
@index_table[:created_at][:db_type].should == "datetime"
|
|
42
|
+
@index_table[:created_at][:primary_key].should be_true
|
|
43
|
+
@index_table[:id][:db_type].should == "binary(16)"
|
|
44
|
+
@index_table[:id][:primary_key].should be_true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "doesn't raise if the tables already exist" do
|
|
48
|
+
lambda {
|
|
49
|
+
@klass.create_tables!
|
|
50
|
+
}.should_not raise_error
|
|
51
|
+
end
|
|
52
|
+
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
|