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,114 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "Friendly::Memcached" do
|
|
4
|
+
before do
|
|
5
|
+
@cache = stub(:set => nil)
|
|
6
|
+
@memcached = Friendly::Memcached.new(@cache)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
describe "setting a key" do
|
|
10
|
+
before do
|
|
11
|
+
@memcached.set("key", "value")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "sets the key in memcached" do
|
|
15
|
+
@cache.should have_received(:set).with("key", "value")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe "getting an existing key" do
|
|
20
|
+
before do
|
|
21
|
+
@cache.stubs(:get).with("Some Key").returns("Some Value")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "returns the value from cache" do
|
|
25
|
+
@memcached.get("Some Key").should == "Some Value"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe "getting a missing key" do
|
|
30
|
+
before do
|
|
31
|
+
@cache.stubs(:get).raises(Memcached::NotFound)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe "with no block supplied" do
|
|
35
|
+
it "returns nil if no block is supplied" do
|
|
36
|
+
@memcached.get("Some Key").should be_nil
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe "with a block" do
|
|
41
|
+
before do
|
|
42
|
+
@returned = @memcached.get("Some Key") { "THE VALUE!" }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "returns the value of the supplied block" do
|
|
46
|
+
@returned.should == "THE VALUE!"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "sets the key to that value" do
|
|
50
|
+
@cache.should have_received(:set).with("Some Key", "THE VALUE!")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe "getting multiple keys" do
|
|
56
|
+
describe "when all keys are found" do
|
|
57
|
+
before do
|
|
58
|
+
@hits = {"a" => "foo", "b" => "bar", "c" => "baz"}
|
|
59
|
+
@keys = ["a", "b", "c"]
|
|
60
|
+
@cache.stubs(:get).with(@keys).returns(@hits)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "delegates to the cache object" do
|
|
64
|
+
@memcached.multiget(@keys).should == @hits
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
describe "when only some of the keys are found" do
|
|
69
|
+
before do
|
|
70
|
+
@hits = {"a" => "foo", "b" => "bar"}
|
|
71
|
+
@keys = ["a", "b", "c"]
|
|
72
|
+
@cache.stubs(:get).with(@keys).returns(@hits)
|
|
73
|
+
@returned = @memcached.multiget(@keys) { |k| "#{k}/fromblock" }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "fetches the rest of the keys by yielding to the block" do
|
|
77
|
+
@returned.should == @hits.merge("c" => "c/fromblock")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "sets the missing keys in the cache" do
|
|
81
|
+
@cache.should have_received(:set).with("c", "c/fromblock")
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
describe "when the list of keys is empty" do
|
|
86
|
+
it "returns {}" do
|
|
87
|
+
@memcached.multiget([]).should == {}
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
describe "deleting" do
|
|
93
|
+
describe "an existing key" do
|
|
94
|
+
before do
|
|
95
|
+
@cache.stubs(:delete).returns(nil)
|
|
96
|
+
@memcached.delete("some key")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "asks the cache to delete" do
|
|
100
|
+
@cache.should have_received(:delete).with("some key")
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
describe "a missing key" do
|
|
105
|
+
before do
|
|
106
|
+
@cache.stubs(:delete).raises(Memcached::NotFound)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "just returns nil" do
|
|
110
|
+
lambda { @memcached.delete("some key") }.should_not raise_error
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "Friendly::Query" do
|
|
4
|
+
before do
|
|
5
|
+
@order = :created_at.desc
|
|
6
|
+
@query = Friendly::Query.new(:name => "x",
|
|
7
|
+
:limit! => 10,
|
|
8
|
+
:order! => @order,
|
|
9
|
+
:preserve_order! => true,
|
|
10
|
+
:offset! => 2)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "extracts the conditions" do
|
|
14
|
+
@query.conditions.should == {:name => "x"}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "extracts the limit parameter" do
|
|
18
|
+
@query.limit.should == 10
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "extracts the order parameter" do
|
|
22
|
+
@query.order.should == @order
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "extracts the preserve order parameter" do
|
|
26
|
+
@query.should be_preserve_order
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "extracts the offset parameter" do
|
|
30
|
+
@query.should be_offset
|
|
31
|
+
@query.offset.should == 2
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "should not be preserver order by default" do
|
|
35
|
+
Friendly::Query.new({}).should_not be_preserve_order
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "converts string representations of UUID to UUID" do
|
|
39
|
+
uuid = stub
|
|
40
|
+
uuid_klass = stub
|
|
41
|
+
uuid_klass.stubs(:new).with("asdf").returns(uuid)
|
|
42
|
+
query = Friendly::Query.new({:id => "asdf"}, uuid_klass)
|
|
43
|
+
query.conditions[:id].should == uuid
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "converts arrays of ids to UUID" do
|
|
47
|
+
uuid = stub
|
|
48
|
+
uuid_klass = stub
|
|
49
|
+
uuid_klass.stubs(:new).with("asdf").returns(uuid)
|
|
50
|
+
query = Friendly::Query.new({:id => ["asdf"]}, uuid_klass)
|
|
51
|
+
query.conditions[:id].should == [uuid]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe "a pagination query" do
|
|
55
|
+
describe "page nil" do
|
|
56
|
+
before do
|
|
57
|
+
@query = Friendly::Query.new(:page! => nil,
|
|
58
|
+
:per_page! => 5)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "is page 1" do
|
|
62
|
+
@query.page.should == 1
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "has an offset of 0" do
|
|
66
|
+
@query.offset.should == 0
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "has a limit of :per_page" do
|
|
70
|
+
@query.limit.should == 5
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
describe "page 2" do
|
|
75
|
+
before do
|
|
76
|
+
@query = Friendly::Query.new(:page! => 2,
|
|
77
|
+
:per_page! => 5)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "has an offset of :per_page * page-1" do
|
|
81
|
+
@query.offset.should == 5
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "has a limit of :per_page" do
|
|
85
|
+
@query.limit.should == 5
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe "when page is a string" do
|
|
90
|
+
before do
|
|
91
|
+
@query = Friendly::Query.new(:page! => "2",
|
|
92
|
+
:per_page! => 5)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "has an offset of :per_page * page-1" do
|
|
96
|
+
@query.offset.should == 5
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "has a limit of :per_page" do
|
|
100
|
+
@query.limit.should == 5
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "Friendly::StorageFactory" do
|
|
4
|
+
before do
|
|
5
|
+
@doc_table = stub
|
|
6
|
+
@doc_table_klass = stub(:new => @doc_table)
|
|
7
|
+
@index = stub
|
|
8
|
+
@index_klass = stub(:new => @index)
|
|
9
|
+
@cache_by_id = stub
|
|
10
|
+
@cache_klass = stub(:cache_for => @cache_by_id)
|
|
11
|
+
@factory = Friendly::StorageFactory.new(@doc_table_klass, @index_klass,
|
|
12
|
+
@cache_klass)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe "creating a document table" do
|
|
16
|
+
before do
|
|
17
|
+
@klass = stub
|
|
18
|
+
@returned = @factory.document_table(@klass)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "returns the result of the constructor" do
|
|
22
|
+
@returned.should == @doc_table
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "passes along the arguments to the constructor" do
|
|
26
|
+
@doc_table_klass.should have_received(:new).with(@klass)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "creating an index table" do
|
|
31
|
+
before do
|
|
32
|
+
@klass = stub
|
|
33
|
+
@returned = @factory.index(@klass)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "returns the result of the constructor" do
|
|
37
|
+
@returned.should == @index
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "passes along the arguments to the constructor" do
|
|
41
|
+
@index_klass.should have_received(:new).with(@klass)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe "creating an id cache table" do
|
|
46
|
+
before do
|
|
47
|
+
@klass = stub
|
|
48
|
+
@returned = @factory.cache(@klass, [:id])
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "delegates to Cache.for" do
|
|
52
|
+
@returned.should == @cache_by_id
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "supplies arguments to Cache" do
|
|
56
|
+
@cache_klass.should have_received(:cache_for).with(@klass, [:id])
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "Friendly::StorageProxy" do
|
|
4
|
+
before do
|
|
5
|
+
@klass = stub
|
|
6
|
+
@index = stub(:create => nil,
|
|
7
|
+
:update => nil,
|
|
8
|
+
:destroy => nil,
|
|
9
|
+
:satsifies? => nil)
|
|
10
|
+
@table = stub(:satisfies? => false, :create => nil,
|
|
11
|
+
:update => nil, :destroy => nil)
|
|
12
|
+
@storage_factory = stub(:index => @index)
|
|
13
|
+
@storage_factory.stubs(:document_table).with(@klass).returns(@table)
|
|
14
|
+
@storage = Friendly::StorageProxy.new(@klass, @storage_factory)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "instantiates and adds a document table by default" do
|
|
18
|
+
@storage.tables.should include(@table)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe "doing a `first`" do
|
|
22
|
+
before do
|
|
23
|
+
@id = stub
|
|
24
|
+
@index = stub(:satisfies? => true, :first => @id)
|
|
25
|
+
@storage.tables << @index
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe "when there's an index that matches the conditions" do
|
|
29
|
+
before do
|
|
30
|
+
@result = @storage.first(:name => "x")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "delegates to the index that satisfies the conditions" do
|
|
34
|
+
@index.should have_received(:first).once
|
|
35
|
+
@index.should have_received(:first).with(:name => "x")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "returns id" do
|
|
39
|
+
@result.should == @id
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe "when there's no index that matches" do
|
|
44
|
+
before do
|
|
45
|
+
@index.stubs(:satisfies?).returns(false)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "raises MissingIndex" do
|
|
49
|
+
lambda {
|
|
50
|
+
@storage.first(:name => "x")
|
|
51
|
+
}.should raise_error(Friendly::MissingIndex)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe "doing an `all`" do
|
|
57
|
+
before do
|
|
58
|
+
@ids = [stub]
|
|
59
|
+
@index = stub(:satisfies? => true, :all => @ids)
|
|
60
|
+
@storage.tables << @index
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe "when there's an index that matches the conditions" do
|
|
64
|
+
before do
|
|
65
|
+
@query = query(:name => "x")
|
|
66
|
+
@result = @storage.all(@query)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "delegates to the index that satisfies the conditions" do
|
|
70
|
+
@index.should have_received(:all).once
|
|
71
|
+
@index.should have_received(:all).with(@query)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "returns the results" do
|
|
75
|
+
@result.should == @ids
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
describe "when there's no index that matches" do
|
|
80
|
+
before do
|
|
81
|
+
@index.stubs(:satisfies?).returns(false)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "raises MissingIndex" do
|
|
85
|
+
lambda {
|
|
86
|
+
@storage.all(:name => "x")
|
|
87
|
+
}.should raise_error(Friendly::MissingIndex)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe "when :preserve_order is true" do
|
|
92
|
+
before do
|
|
93
|
+
@docs = [stub(:id => 4), stub(:id => 3), stub(:id => 2)]
|
|
94
|
+
@index.stubs(:all).returns(@docs)
|
|
95
|
+
@result = @storage.all(query(:id => [2,3,4], :preserve_order! => true))
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "sorts the results based on the order they're supplied in the query" do
|
|
99
|
+
@result.should == @docs.reverse
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
describe "adding an index to the set" do
|
|
105
|
+
before do
|
|
106
|
+
@storage.add(:name, :age)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "creates an index" do
|
|
110
|
+
@storage_factory.should have_received(:index).once
|
|
111
|
+
@storage_factory.should have_received(:index).with(@klass, :name, :age)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "adds the index to the set" do
|
|
115
|
+
@storage.tables.should include(@index)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe "saving data" do
|
|
120
|
+
before do
|
|
121
|
+
@index_two = stub(:create => nil, :update => nil, :destroy => nil)
|
|
122
|
+
@storage_factory.stubs(:index).returns(@index).then.returns(@index_two)
|
|
123
|
+
@cache = stub(:create => nil, :update => nil, :destroy => nil)
|
|
124
|
+
@storage_factory.stubs(:cache).returns(@cache)
|
|
125
|
+
@storage.add(:name)
|
|
126
|
+
@storage.add(:age)
|
|
127
|
+
@storage.cache([:id])
|
|
128
|
+
@doc = stub
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
describe "on create" do
|
|
132
|
+
before do
|
|
133
|
+
@storage.create(@doc)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "delegates to all of the storage including caches" do
|
|
137
|
+
@index.should have_received(:create).with(@doc)
|
|
138
|
+
@index_two.should have_received(:create).with(@doc)
|
|
139
|
+
@table.should have_received(:create).with(@doc)
|
|
140
|
+
@cache.should have_received(:create).with(@doc)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
describe "on update" do
|
|
145
|
+
before do
|
|
146
|
+
@storage.update(@doc)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "delegates to all of the storage including caches" do
|
|
150
|
+
@index.should have_received(:update).with(@doc)
|
|
151
|
+
@index_two.should have_received(:update).with(@doc)
|
|
152
|
+
@table.should have_received(:update).with(@doc)
|
|
153
|
+
@cache.should have_received(:update).with(@doc)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
describe "on destroy" do
|
|
158
|
+
before do
|
|
159
|
+
@storage.destroy(@doc)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it "delegates to all of the storage including caches" do
|
|
163
|
+
@index.should have_received(:destroy).with(@doc)
|
|
164
|
+
@index_two.should have_received(:destroy).with(@doc)
|
|
165
|
+
@table.should have_received(:destroy).with(@doc)
|
|
166
|
+
@cache.should have_received(:destroy).with(@doc)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
describe "adding a cache object" do
|
|
172
|
+
before do
|
|
173
|
+
@cache = stub
|
|
174
|
+
@storage_factory.stubs(:cache).returns(@cache)
|
|
175
|
+
@storage.cache([:id])
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
it "gets one from the storage_proxy" do
|
|
179
|
+
@storage_factory.should have_received(:cache).with(@klass, [:id], {})
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it "adds it to its set of caches" do
|
|
183
|
+
@storage.caches.should include(@cache)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
describe "finding with a matching cache" do
|
|
188
|
+
before do
|
|
189
|
+
@cache = stub(:satisfies? => true, :first => nil)
|
|
190
|
+
@storage_factory.stubs(:cache).returns(@cache)
|
|
191
|
+
@storage.cache([:id])
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
describe "when there's a hit" do
|
|
195
|
+
before do
|
|
196
|
+
@doc = stub
|
|
197
|
+
@cache.stubs(:first).returns(@doc)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
it "returns the result from the cache" do
|
|
201
|
+
@storage.first(:id => "x").should == @doc
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
describe "counting objects" do
|
|
207
|
+
before do
|
|
208
|
+
@index = stub(:count => 10)
|
|
209
|
+
@index.stubs(:satisfies?).with(:x => 1).returns(true)
|
|
210
|
+
@storage_factory.stubs(:index).returns(@index)
|
|
211
|
+
@storage.add(:a)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
it "delegates to a satisfying index" do
|
|
215
|
+
@storage.count(:x => 1).should == 10
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
|
2
|
+
require 'ostruct'
|
|
3
|
+
|
|
4
|
+
describe "Friendly::Translator" do
|
|
5
|
+
before do
|
|
6
|
+
@serializer = stub
|
|
7
|
+
@time = stub
|
|
8
|
+
@translator = Friendly::Translator.new(@serializer, @time)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe "translating a row to an object" do
|
|
12
|
+
before do
|
|
13
|
+
@serializer.stubs(:parse).with("THE JSON").returns(:name => "Stewie")
|
|
14
|
+
@time = Time.new
|
|
15
|
+
@row = {:added_id => 12345,
|
|
16
|
+
:created_at => @time,
|
|
17
|
+
:updated_at => @time,
|
|
18
|
+
:attributes => "THE JSON"}
|
|
19
|
+
@klass = FakeDocument
|
|
20
|
+
@doc = @translator.to_object(@klass, @row)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "creates a klass with the attributes from the json" do
|
|
24
|
+
@doc.name.should == "Stewie"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "sets updated_at" do
|
|
28
|
+
@doc.updated_at.should == @time
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "sets new_record to false" do
|
|
32
|
+
@doc.new_record.should be_false
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe "translating from a document in to a record" do
|
|
37
|
+
describe "when the document has yet to be saved" do
|
|
38
|
+
before do
|
|
39
|
+
@hash = {:name => "Stewie"}
|
|
40
|
+
@time.stubs(:new).returns(Time.new)
|
|
41
|
+
@serializer.stubs(:generate).with(@hash).returns("SOME JSON")
|
|
42
|
+
@document = stub(:to_hash => @hash,
|
|
43
|
+
:new_record? => true,
|
|
44
|
+
:created_at => nil,
|
|
45
|
+
:id => 12345)
|
|
46
|
+
@record = @translator.to_record(@document)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "serializes the attributes" do
|
|
50
|
+
@record[:attributes].should == "SOME JSON"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "sets updated_at" do
|
|
54
|
+
@record[:updated_at].should == @time.new
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "sets the id from the document" do
|
|
58
|
+
@record[:id].should == 12345
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe "when the document has already been saved" do
|
|
63
|
+
before do
|
|
64
|
+
@created_at = Time.new
|
|
65
|
+
@hash = {:name => "Stewie",
|
|
66
|
+
:id => 1,
|
|
67
|
+
:created_at => @created_at,
|
|
68
|
+
:updated_at => Time.new}
|
|
69
|
+
@time.stubs(:new).returns(Time.new + 5000)
|
|
70
|
+
@serializer.stubs(:generate).returns("SOME JSON")
|
|
71
|
+
@document = stub(:to_hash => @hash,
|
|
72
|
+
:created_at => @created_at,
|
|
73
|
+
:new_record? => false,
|
|
74
|
+
:id => 12345)
|
|
75
|
+
@record = @translator.to_record(@document)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "serializes the attributes" do
|
|
79
|
+
@serializer.should have_received(:generate).with(:name => "Stewie")
|
|
80
|
+
@record[:attributes].should == "SOME JSON"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "doesn't bump the created_at" do
|
|
84
|
+
@record[:created_at].should == @created_at
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "should bump the updated_at" do
|
|
88
|
+
@record[:updated_at].should == @time.new
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "takes the id from the documetn" do
|
|
92
|
+
@record[:id].should == 12345
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|