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.
Files changed (129) hide show
  1. data/.document +2 -0
  2. data/.gitignore +26 -0
  3. data/APACHE-LICENSE +202 -0
  4. data/CHANGELOG.md +19 -0
  5. data/CONTRIBUTORS.md +7 -0
  6. data/LICENSE +20 -0
  7. data/README.md +265 -0
  8. data/Rakefile +68 -0
  9. data/TODO.md +5 -0
  10. data/VERSION +1 -0
  11. data/examples/friendly.yml +7 -0
  12. data/friendly.gemspec +232 -0
  13. data/lib/friendly.rb +54 -0
  14. data/lib/friendly/associations.rb +7 -0
  15. data/lib/friendly/associations/association.rb +34 -0
  16. data/lib/friendly/associations/set.rb +37 -0
  17. data/lib/friendly/attribute.rb +91 -0
  18. data/lib/friendly/boolean.rb +10 -0
  19. data/lib/friendly/cache.rb +24 -0
  20. data/lib/friendly/cache/by_id.rb +33 -0
  21. data/lib/friendly/config.rb +5 -0
  22. data/lib/friendly/data_store.rb +72 -0
  23. data/lib/friendly/document.rb +257 -0
  24. data/lib/friendly/document_table.rb +56 -0
  25. data/lib/friendly/index.rb +73 -0
  26. data/lib/friendly/memcached.rb +48 -0
  27. data/lib/friendly/named_scope.rb +17 -0
  28. data/lib/friendly/newrelic.rb +6 -0
  29. data/lib/friendly/query.rb +42 -0
  30. data/lib/friendly/scope.rb +100 -0
  31. data/lib/friendly/scope_proxy.rb +45 -0
  32. data/lib/friendly/sequel_monkey_patches.rb +34 -0
  33. data/lib/friendly/storage.rb +31 -0
  34. data/lib/friendly/storage_factory.rb +24 -0
  35. data/lib/friendly/storage_proxy.rb +103 -0
  36. data/lib/friendly/table.rb +15 -0
  37. data/lib/friendly/table_creator.rb +48 -0
  38. data/lib/friendly/time.rb +14 -0
  39. data/lib/friendly/translator.rb +32 -0
  40. data/lib/friendly/uuid.rb +148 -0
  41. data/rails/init.rb +3 -0
  42. data/spec/config.yml.example +7 -0
  43. data/spec/fakes/data_store_fake.rb +29 -0
  44. data/spec/fakes/database_fake.rb +12 -0
  45. data/spec/fakes/dataset_fake.rb +28 -0
  46. data/spec/fakes/document.rb +18 -0
  47. data/spec/fakes/serializer_fake.rb +12 -0
  48. data/spec/fakes/time_fake.rb +12 -0
  49. data/spec/integration/ad_hoc_scopes_spec.rb +42 -0
  50. data/spec/integration/basic_object_lifecycle_spec.rb +114 -0
  51. data/spec/integration/batch_insertion_spec.rb +29 -0
  52. data/spec/integration/convenience_api_spec.rb +25 -0
  53. data/spec/integration/count_spec.rb +12 -0
  54. data/spec/integration/default_value_spec.rb +15 -0
  55. data/spec/integration/find_via_cache_spec.rb +101 -0
  56. data/spec/integration/finder_spec.rb +64 -0
  57. data/spec/integration/has_many_spec.rb +18 -0
  58. data/spec/integration/index_spec.rb +57 -0
  59. data/spec/integration/named_scope_spec.rb +34 -0
  60. data/spec/integration/pagination_spec.rb +63 -0
  61. data/spec/integration/scope_chaining_spec.rb +22 -0
  62. data/spec/integration/table_creator_spec.rb +64 -0
  63. data/spec/integration/write_through_cache_spec.rb +53 -0
  64. data/spec/spec.opts +1 -0
  65. data/spec/spec_helper.rb +103 -0
  66. data/spec/unit/associations/association_spec.rb +57 -0
  67. data/spec/unit/associations/set_spec.rb +43 -0
  68. data/spec/unit/attribute_spec.rb +105 -0
  69. data/spec/unit/cache_by_id_spec.rb +102 -0
  70. data/spec/unit/cache_spec.rb +21 -0
  71. data/spec/unit/config_spec.rb +4 -0
  72. data/spec/unit/data_store_spec.rb +188 -0
  73. data/spec/unit/document_spec.rb +358 -0
  74. data/spec/unit/document_table_spec.rb +126 -0
  75. data/spec/unit/friendly_spec.rb +25 -0
  76. data/spec/unit/index_spec.rb +196 -0
  77. data/spec/unit/memcached_spec.rb +114 -0
  78. data/spec/unit/named_scope_spec.rb +16 -0
  79. data/spec/unit/query_spec.rb +104 -0
  80. data/spec/unit/scope_proxy_spec.rb +44 -0
  81. data/spec/unit/scope_spec.rb +113 -0
  82. data/spec/unit/storage_factory_spec.rb +59 -0
  83. data/spec/unit/storage_proxy_spec.rb +218 -0
  84. data/spec/unit/translator_spec.rb +96 -0
  85. data/website/index.html +210 -0
  86. data/website/scripts/clipboard.swf +0 -0
  87. data/website/scripts/shBrushAS3.js +61 -0
  88. data/website/scripts/shBrushBash.js +66 -0
  89. data/website/scripts/shBrushCSharp.js +67 -0
  90. data/website/scripts/shBrushColdFusion.js +102 -0
  91. data/website/scripts/shBrushCpp.js +99 -0
  92. data/website/scripts/shBrushCss.js +93 -0
  93. data/website/scripts/shBrushDelphi.js +57 -0
  94. data/website/scripts/shBrushDiff.js +43 -0
  95. data/website/scripts/shBrushErlang.js +54 -0
  96. data/website/scripts/shBrushGroovy.js +69 -0
  97. data/website/scripts/shBrushJScript.js +52 -0
  98. data/website/scripts/shBrushJava.js +59 -0
  99. data/website/scripts/shBrushJavaFX.js +60 -0
  100. data/website/scripts/shBrushPerl.js +74 -0
  101. data/website/scripts/shBrushPhp.js +91 -0
  102. data/website/scripts/shBrushPlain.js +35 -0
  103. data/website/scripts/shBrushPowerShell.js +76 -0
  104. data/website/scripts/shBrushPython.js +66 -0
  105. data/website/scripts/shBrushRuby.js +57 -0
  106. data/website/scripts/shBrushScala.js +53 -0
  107. data/website/scripts/shBrushSql.js +68 -0
  108. data/website/scripts/shBrushVb.js +58 -0
  109. data/website/scripts/shBrushXml.js +71 -0
  110. data/website/scripts/shCore.js +30 -0
  111. data/website/scripts/shLegacy.js +30 -0
  112. data/website/styles/friendly.css +103 -0
  113. data/website/styles/help.png +0 -0
  114. data/website/styles/ie.css +35 -0
  115. data/website/styles/magnifier.png +0 -0
  116. data/website/styles/page_white_code.png +0 -0
  117. data/website/styles/page_white_copy.png +0 -0
  118. data/website/styles/print.css +29 -0
  119. data/website/styles/printer.png +0 -0
  120. data/website/styles/screen.css +257 -0
  121. data/website/styles/shCore.css +330 -0
  122. data/website/styles/shThemeDefault.css +173 -0
  123. data/website/styles/shThemeDjango.css +176 -0
  124. data/website/styles/shThemeEclipse.css +190 -0
  125. data/website/styles/shThemeEmacs.css +175 -0
  126. data/website/styles/shThemeFadeToGrey.css +177 -0
  127. data/website/styles/shThemeMidnight.css +175 -0
  128. data/website/styles/shThemeRDark.css +175 -0
  129. metadata +302 -0
@@ -0,0 +1,25 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "Friendly" do
4
+ describe "configuring friendly" do
5
+ before do
6
+ @datastore = stub
7
+ Friendly::DataStore.stubs(:new).returns(@datastore)
8
+ @db = stub(:meta_def => nil)
9
+ Sequel.stubs(:connect).returns(@db)
10
+ Friendly.configure(:host => "localhost")
11
+ end
12
+
13
+ it "creates a db object by delegating to Sequel" do
14
+ Sequel.should have_received(:connect).with(:host => "localhost")
15
+ end
16
+
17
+ it "creates a datastore object with the db object" do
18
+ Friendly::DataStore.should have_received(:new).with(@db)
19
+ end
20
+
21
+ it "sets the datastore as the default" do
22
+ Friendly.datastore.should == @datastore
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,196 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "Friendly::Index" do
4
+ before do
5
+ @klass = stub(:table_name => "users")
6
+ @index = Friendly::Index.new(@klass, [:name, :age])
7
+ end
8
+
9
+ it "satisfies query when all the fields are indexed" do
10
+ @index.should be_satisfies(query({:name => "x", :age => "y"}))
11
+ end
12
+
13
+ it "doesn't satisfy query when some fields are not indexed" do
14
+ @index.should_not be_satisfies(query({:name => "x", :dob => "12/01/1980"}))
15
+ end
16
+
17
+ it "doesn't satisfy if it only uses keys on the right of the index" do
18
+ @index.should_not be_satisfies(query({:age => "y"}))
19
+ end
20
+
21
+ it "satisfies if it only uses keys on the left of the index" do
22
+ @index.should be_satisfies(query({:name => "y"}))
23
+ end
24
+
25
+ it "satisfies an ordered query if it uses all fields and order is rightmost" do
26
+ @index.should be_satisfies(query(:name => "y", :order! => :age.desc))
27
+ end
28
+
29
+ it "doesn't satisfy an ordered query if it uses all fields and order ! leftmost" do
30
+ @index.should_not be_satisfies(query(:name => "Stewie", :order! => :name.desc))
31
+ end
32
+
33
+ it "doesn't satisfy an ordered query if it uses a field after a gap" do
34
+ ix = Friendly::Index.new(@klass, [:name, :height, :age])
35
+ ix.should_not be_satisfies(query(:name => "James", :order! => :age.desc))
36
+ end
37
+
38
+ describe "with one field" do
39
+ before do
40
+ @index = Friendly::Index.new(@klass, [:name])
41
+ end
42
+
43
+ it "has an appropriate table name" do
44
+ @index.table_name.should == "index_users_on_name"
45
+ end
46
+ end
47
+
48
+ describe "with multiple fields" do
49
+ before do
50
+ @index = Friendly::Index.new(@klass, [:name, :age])
51
+ end
52
+
53
+ it "has an appropriate table name" do
54
+ @index.table_name.should == "index_users_on_name_and_age"
55
+ end
56
+ end
57
+
58
+ describe "finding the first record matching a query" do
59
+ before do
60
+ @result = row(:id => 42)
61
+ @datastore = stub(:first => @result)
62
+ @index = Friendly::Index.new(@klass, [:name], @datastore)
63
+ @doc = stub
64
+ @klass.stubs(:first).with(:id => 42).returns(@doc)
65
+ @result = @index.first(:name => "x")
66
+ end
67
+
68
+ it "queries the datastore with the attributes from the query" do
69
+ @datastore.should have_received(:first).once
70
+ @datastore.should have_received(:first).with(@index, :name => "x")
71
+ end
72
+
73
+ it "finds the document by the id returned by the datastore" do
74
+ @klass.should have_received(:first).with(:id => 42)
75
+ end
76
+
77
+ it "returns the document returned by the klass" do
78
+ @result.should == @doc
79
+ end
80
+
81
+ describe "when no result is found" do
82
+ before do
83
+ @datastore.stubs(:first).returns(nil)
84
+ @result = @index.first(:name => "x")
85
+ end
86
+
87
+ it "returns nil" do
88
+ @result.should be_nil
89
+ end
90
+ end
91
+ end
92
+
93
+ describe "finding all the rows matching a query" do
94
+ before do
95
+ @results = [row(:id => 42), row(:id => 43), row(:id => 44)]
96
+ @query = query(:name => "x")
97
+ @datastore = stub(:all => @results)
98
+ @index = Friendly::Index.new(@klass, [:name], @datastore)
99
+ @documents = stub
100
+ @klass.stubs(:all).with(:id => [42, 43, 44],
101
+ :preserve_order! => false).returns(@documents)
102
+ @result = @index.all(@query)
103
+ end
104
+
105
+ it "queries the datastore with the conditions" do
106
+ @datastore.should have_received(:all).once
107
+ @datastore.should have_received(:all).with(@index, @query)
108
+ end
109
+
110
+ it "then queries the klass for the ids it found in the index" do
111
+ @klass.should have_received(:all).with(:id => [42, 43, 44],
112
+ :preserve_order! => false)
113
+ end
114
+
115
+ it "returns the result from the klass.all call" do
116
+ @result.should == @documents
117
+ end
118
+ end
119
+
120
+ describe "finding all the rows matching a query in order" do
121
+ before do
122
+ @results = [row(:id => 42), row(:id => 43), row(:id => 44)]
123
+ @query = query(:name => "x", :order! => :created_at.desc)
124
+ @datastore = stub(:all => @results)
125
+ @index = Friendly::Index.new(@klass, [:name], @datastore)
126
+ @documents = stub
127
+ @klass.stubs(:all).with(:id => [42, 43, 44],
128
+ :preserve_order! => true).returns(@documents)
129
+ @result = @index.all(@query)
130
+ end
131
+
132
+ it "queries the klass with preserve_order! => true" do
133
+ @klass.should have_received(:all).with(:id => [42, 43, 44],
134
+ :preserve_order! => true)
135
+ end
136
+ end
137
+
138
+ describe "updating the indexes" do
139
+ before do
140
+ @datastore = stub(:insert => nil, :update => nil)
141
+ @index = Friendly::Index.new(stub, [:name], @datastore)
142
+ @document = stub(:name => "Stewie",
143
+ :indexes => [@index],
144
+ :id => 42)
145
+ @index_record = {:name => "Stewie", :id => 42}
146
+ end
147
+
148
+ describe "indexing a new document" do
149
+ before do
150
+ @index.create(@document)
151
+ end
152
+
153
+ it "inserts a record in to the datastore with the indexed vals and id" do
154
+ @datastore.should have_received(:insert).with(@index, @index_record)
155
+ end
156
+ end
157
+
158
+ describe "indexing an existing document" do
159
+ before do
160
+ @index.update(@document)
161
+ end
162
+
163
+ it "updates the index records in the database" do
164
+ @datastore.should have_received(:update).with(@index, 42, @index_record)
165
+ end
166
+ end
167
+ end
168
+
169
+ describe "destroying the index rows" do
170
+ before do
171
+ @datastore = stub(:delete => nil)
172
+ @index = Friendly::Index.new(stub, [:name], @datastore)
173
+ @document = stub(:name => "Stewie",
174
+ :indexes => [@index],
175
+ :id => 42)
176
+ @index.destroy(@document)
177
+ end
178
+
179
+ it "deletes the records in the index" do
180
+ @datastore.should have_received(:delete).with(@index, 42)
181
+ end
182
+ end
183
+
184
+ describe "counting rows matching a query" do
185
+ before do
186
+ @datastore = stub
187
+ @query = query(:name => "Stewie")
188
+ @index = Friendly::Index.new(@klass, [:name], @datastore)
189
+ @datastore.stubs(:count).with(@index, @query).returns(10)
190
+ end
191
+
192
+ it "delegates to the datastore" do
193
+ @index.count(@query).should == 10
194
+ end
195
+ end
196
+ end
@@ -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,16 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "Friendly::NamedScope" do
4
+ before do
5
+ @klass = stub
6
+ @scope = stub
7
+ @scope_klass = stub
8
+ @parameters = {:name => "James"}
9
+ @scope_klass.stubs(:new).with(@klass, @parameters).returns(@scope)
10
+ @named_scope = Friendly::NamedScope.new(@klass, @parameters, @scope_klass)
11
+ end
12
+
13
+ it "provides scope instances with the given parameters" do
14
+ @named_scope.scope.should == @scope
15
+ end
16
+ 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