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,43 @@
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
+
3
+ describe "Friendly::Associations::Set" do
4
+ before do
5
+ @klass = Class.new
6
+ @association_klass = stub
7
+ @set = Friendly::Associations::Set.new(@klass, @association_klass)
8
+ @assoc = stub
9
+ @association_klass.stubs(:new).
10
+ with(@klass, :my_awesome_association, {}).returns(@assoc)
11
+ end
12
+
13
+ describe "adding an association" do
14
+ before do
15
+ @set.add(:my_awesome_association)
16
+ end
17
+
18
+ it "creates an association and adds it to its hash by name" do
19
+ @set.associations[:my_awesome_association].should == @assoc
20
+ end
21
+
22
+ it "adds an instance method to the klass through which to acces the assoc" do
23
+ @doc = @klass.new
24
+ @scope = stub
25
+ @klass.stubs(:association_set).returns(@set)
26
+ @assoc.stubs(:scope).with(@doc).returns(@scope)
27
+ @doc.my_awesome_association.should == @scope
28
+ end
29
+ end
30
+
31
+ it "can return the association by name" do
32
+ @set.add(:my_awesome_association)
33
+ @set.get(:my_awesome_association).should == @assoc
34
+ end
35
+
36
+ it "provides the scope for an association by name" do
37
+ @doc = stub
38
+ @scope = stub
39
+ @assoc.stubs(:scope).with(@doc).returns(@scope)
40
+ @set.add(:my_awesome_association)
41
+ @set.get_scope(:my_awesome_association, @doc).should == @scope
42
+ end
43
+ end
@@ -0,0 +1,105 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "Friendly::Attribute" do
4
+ before do
5
+ @klass = Class.new
6
+ @name = Friendly::Attribute.new(@klass, :name, String)
7
+ @id = Friendly::Attribute.new(@klass, :id, Friendly::UUID)
8
+ @no_type = Friendly::Attribute.new(@klass, :no_type, nil)
9
+ @default = Friendly::Attribute.new(@klass, :default, String, :default => "asdf")
10
+ @false = Friendly::Attribute.new(@klass, :default, String, :default => false)
11
+ @klass.stubs(:attributes).returns({:name => @name,
12
+ :id => @id,
13
+ :default => @default,
14
+ :false => @false})
15
+ @object = @klass.new
16
+ end
17
+
18
+ it "creates a setter and a getter on klass" do
19
+ @object.name = "Something"
20
+ @object.name.should == "Something"
21
+ end
22
+
23
+ it "typecasts values using the converter function" do
24
+ uuid = Friendly::UUID.new
25
+ @id.typecast(uuid.to_s).should == uuid
26
+ end
27
+
28
+ it "doesn't typecast values if they are of the right type" do
29
+ uuid = Friendly::UUID.new
30
+ @id.typecast(uuid).should == uuid
31
+ end
32
+
33
+ it "raises a useful error if it can't typecast" do
34
+ attribute = Friendly::Attribute.new(@klass, :weird, Class)
35
+ lambda {
36
+ attribute.typecast("ASDF")
37
+ }.should raise_error(Friendly::NoConverterExists)
38
+ end
39
+
40
+ it "creates a getter with a default value" do
41
+ @object.id.should be_instance_of(Friendly::UUID)
42
+ end
43
+
44
+ it "has a default value of type.new" do
45
+ @id.default.should be_instance_of(Friendly::UUID)
46
+ end
47
+
48
+ it "has a default of nil if the type doesn't respond to :new" do
49
+ Friendly::Attribute.new(@klass, :age, Integer).default.should be_nil
50
+ end
51
+
52
+ it "doesn't try to convert when there's no type" do
53
+ @no_type.typecast(true).should == true
54
+ end
55
+
56
+ it "can have a default value" do
57
+ @default.default.should == "asdf"
58
+ @klass.new.default.should == "asdf"
59
+ end
60
+
61
+ it "has a default value even if it's false" do
62
+ @false.default.should be_false
63
+ end
64
+
65
+ describe "registering a type" do
66
+ before do
67
+ @klass = Class.new
68
+ Friendly::Attribute.register_type(@klass, "binary(16)") { |t| t.to_i }
69
+ end
70
+
71
+ after { Friendly::Attribute.deregister_type(@klass) }
72
+
73
+ it "tells Attribute about the sql_type" do
74
+ Friendly::Attribute.sql_type(@klass).should == "binary(16)"
75
+ end
76
+
77
+ it "registers the conversion method" do
78
+ Friendly::Attribute.new(@klass, :something, @klass).convert("1").should == 1
79
+ end
80
+
81
+ it "is custom_type?(@klass)" do
82
+ Friendly::Attribute.should be_custom_type(@klass)
83
+ end
84
+ end
85
+
86
+ describe "deregistering a type" do
87
+ before do
88
+ @klass = Class.new
89
+ Friendly::Attribute.register_type(@klass, "whatever") {}
90
+ Friendly::Attribute.deregister_type(@klass)
91
+ end
92
+
93
+ it "removes the converter method" do
94
+ Friendly::Attribute.converters.should_not be_has_key(@klass)
95
+ end
96
+
97
+ it "removes the sql_type" do
98
+ Friendly::Attribute.sql_types.should_not be_has_key(@klass)
99
+ end
100
+
101
+ it "is not custom_type?(@klass)" do
102
+ Friendly::Attribute.should_not be_custom_type(@klass)
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,102 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "Friendly::Cache::ByID" do
4
+ before do
5
+ @cache = stub(:set => nil)
6
+ @klass = stub(:name => "Product")
7
+ @id_cache = Friendly::Cache::ByID.new(@klass, [:id], {}, @cache)
8
+ @subject = @id_cache
9
+ end
10
+
11
+ it { should be_satisfies(query(:id => "asdf")) }
12
+ it { should be_satisfies(query(:id => ["asdf"])) }
13
+ it { should_not be_satisfies(query(:id => ["asdf"], :name => "asdf")) }
14
+ it { should_not be_satisfies(query(:name => "asdf")) }
15
+
16
+ it "has a default version of 0" do
17
+ @id_cache.version.should == 0
18
+ end
19
+
20
+ it "is possible to override version" do
21
+ Friendly::Cache::ByID.new(@klass, [:id], {:version => 1}).version.should == 1
22
+ end
23
+
24
+ describe "when an object is created" do
25
+ before do
26
+ @uuid = stub(:to_guid => "xxxx-xxx-xxx-xxxx")
27
+ @doc = stub(:id => @uuid)
28
+ @id_cache.create(@doc)
29
+ end
30
+
31
+ it "sets the cache value in the db" do
32
+ @cache.should have_received(:set).with("Product/0/#{@uuid.to_guid}", @doc)
33
+ end
34
+ end
35
+
36
+ describe "when an object is updated" do
37
+ before do
38
+ @uuid = stub(:to_guid => "xxxx-xxx-xxx-xxxx")
39
+ @doc = stub(:id => @uuid)
40
+ @id_cache.update(@doc)
41
+ end
42
+
43
+ it "sets the cache value in the db" do
44
+ @cache.should have_received(:set).with("Product/0/#{@uuid.to_guid}", @doc)
45
+ end
46
+ end
47
+
48
+ describe "when an object is destroyed" do
49
+ before do
50
+ @cache.stubs(:delete)
51
+ @uuid = stub(:to_guid => "xxxx-xxx-xxx-xxxx")
52
+ @doc = stub(:id => @uuid)
53
+ @id_cache.destroy(@doc)
54
+ end
55
+
56
+ it "deletes the object from cache" do
57
+ @cache.should have_received(:delete).with("Product/0/#{@uuid.to_guid}")
58
+ end
59
+ end
60
+
61
+ describe "finding a single object in cache" do
62
+ before do
63
+ @uuid = stub(:to_guid => "xxxx-xxx-xxx-xxxx")
64
+ @doc = stub
65
+ @cache.stubs(:get).with("Product/0/xxxx-xxx-xxx-xxxx").returns(@doc).yields
66
+ @block_called = true
67
+ @returned = @id_cache.first(query(:id => @uuid)) do
68
+ @block_called = true
69
+ end
70
+ end
71
+
72
+ it "returns the document" do
73
+ @returned.should == @doc
74
+ end
75
+
76
+ it "passes along the block to the memcached object" do
77
+ @block_called.should be_true
78
+ end
79
+ end
80
+
81
+ describe "finding many objects in the cache" do
82
+ before do
83
+ @uuid = stub(:to_guid => "xxxx-xxx-xxx-xxxx")
84
+ @doc = stub
85
+ @key = "Product/0/xxxx-xxx-xxx-xxxx"
86
+ @cache.stubs(:multiget).with([@key, @key]).
87
+ returns({@uuid.to_guid => @doc}).yields(@uuid.to_guid)
88
+ @block_called = []
89
+ @returned = @id_cache.all(query(:id => [@uuid, @uuid])) do |id|
90
+ @block_called << id
91
+ end
92
+ end
93
+
94
+ it "returns the values from the hash" do
95
+ @returned.should == [@doc]
96
+ end
97
+
98
+ it "passes the block along to the cache" do
99
+ @block_called.should == [@uuid.to_guid]
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,21 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "Friendly::Cache" do
4
+ describe "getting a cache object for :id" do
5
+ before do
6
+ @cache = Friendly::Cache.cache_for(stub, [:id], {})
7
+ end
8
+
9
+ it "instantiates a Cache::ByID" do
10
+ @cache.should be_instance_of(Friendly::Cache::ByID)
11
+ end
12
+ end
13
+
14
+ describe "for other fields" do
15
+ it "raises NotSupported" do
16
+ lambda {
17
+ Friendly::Cache.cache_for(stub, [:asdf], {})
18
+ }.should raise_error(Friendly::NotSupported)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,4 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "Friendly::Config" do
4
+ end
@@ -0,0 +1,188 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "Friendly::DataStore" do
4
+ before do
5
+ @users = DatasetFake.new(:insert => 42)
6
+ @db = DatabaseFake.new("users" => @users)
7
+ @datastore = Friendly::DataStore.new(@db)
8
+ @klass = stub(:table_name => "users")
9
+ end
10
+
11
+ describe "inserting data" do
12
+ before do
13
+ @return = @datastore.insert(@klass, :name => "Stewie")
14
+ end
15
+
16
+ it "inserts it in to the table in the datastore" do
17
+ @users.inserts.length.should == 1
18
+ @users.inserts.should include(:name => "Stewie")
19
+ end
20
+
21
+ it "returns the id from the dataset" do
22
+ @return.should == 42
23
+ end
24
+ end
25
+
26
+ describe "retrieving all based on a query" do
27
+ before do
28
+ @users.where = {{:name => "Stewie"} => stub(:map => [{:id => 1}])}
29
+ @return = @datastore.all(@klass, query(:name => "Stewie"))
30
+ end
31
+
32
+ it "gets the data from the dataset for the klass and makes it an arary" do
33
+ @return.should == [{:id => 1}]
34
+ end
35
+ end
36
+
37
+ describe "retrieving all with a limit" do
38
+ before do
39
+ @filtered = stub
40
+ @filtered.stubs(:limit).with(10, nil).returns(stub(:map => [{:id => 1}]))
41
+ @users.where = {{:name => "Stewie"} => @filtered}
42
+ @query = query(:name => "Stewie", :limit! => 10)
43
+ @return = @datastore.all(@klass, @query)
44
+ end
45
+
46
+ it "limits the filtered dataset and returns the results" do
47
+ @return.should == [{:id => 1}]
48
+ end
49
+ end
50
+
51
+ describe "retrieving all with a offset" do
52
+ before do
53
+ @filtered = stub
54
+ @filtered.stubs(:limit).with(nil, 10).returns(stub(:map => [{:id => 1}]))
55
+ @users.where = {{:name => "Stewie"} => @filtered}
56
+ @query = query(:name => "Stewie", :offset! => 10)
57
+ @return = @datastore.all(@klass, @query)
58
+ end
59
+
60
+ it "offsets the filtered dataset and returns the results" do
61
+ @return.should == [{:id => 1}]
62
+ end
63
+ end
64
+
65
+ describe "retrieving all with order" do
66
+ before do
67
+ @filtered = stub
68
+ @filtered.stubs(:order).with(:created_at).returns(stub(:map => [{:id => 1}]))
69
+ @users.where = {{:name => "Stewie"} => @filtered}
70
+ @query = query(:name => "Stewie", :order! => :created_at)
71
+ @return = @datastore.all(@klass, @query)
72
+ end
73
+
74
+ it "orders the filtered dataset and returns the results" do
75
+ @return.should == [{:id => 1}]
76
+ end
77
+ end
78
+
79
+ describe "retrieving first with conditions" do
80
+ before do
81
+ @users.first = {{:id => 1} => {:id => 1}}
82
+ @return = @datastore.first(@klass, query(:id => 1))
83
+ end
84
+
85
+ it "gets the first object matching the conditions from the dataset" do
86
+ @return.should == {:id => 1}
87
+ end
88
+ end
89
+
90
+ describe "updating data" do
91
+ before do
92
+ @filtered = DatasetFake.new(:update => true)
93
+ @users.where = {{:id => 1} => @filtered}
94
+ @return = @datastore.update(@klass, 1, :name => "Peter")
95
+ end
96
+
97
+ it "filter the dataset by id and update the filtered row" do
98
+ @filtered.updates.length.should == 1
99
+ @filtered.updates.should include(:name => "Peter")
100
+ end
101
+ end
102
+
103
+ describe "deleting data" do
104
+ before do
105
+ @filtered = stub
106
+ @filtered.stubs(:delete)
107
+ @users.where = {{:id => 1} => @filtered}
108
+ @datastore.delete(@klass, 1)
109
+ end
110
+
111
+ it "filters the dataset by id and deletes" do
112
+ @filtered.should have_received(:delete)
113
+ end
114
+ end
115
+
116
+ describe "when a batch transaction has been started" do
117
+ before do
118
+ @datastore.start_batch
119
+ @persistable = stub(:table_name => "some_table")
120
+ @datastore.insert(@persistable, {:some => "attrs"})
121
+ end
122
+
123
+ after { Thread.current[:friendly_batch] = nil }
124
+
125
+ it "adds the attributes to the batch for that table" do
126
+ inserts = Thread.current[:friendly_batch]["some_table"]
127
+ inserts.length.should == 1
128
+ inserts.should include(:some => "attrs")
129
+ end
130
+ end
131
+
132
+ describe "starting a batch" do
133
+ before do
134
+ @datastore.start_batch
135
+ end
136
+
137
+ after { Thread.current[:friendly_batch] = nil }
138
+
139
+ it "sets Thread.current[:friendly_batch] to empty hash" do
140
+ Thread.current[:friendly_batch].should == {}
141
+ end
142
+ end
143
+
144
+ describe "resetting a batch transaction" do
145
+ before do
146
+ @db.stubs(:from)
147
+ Thread.current[:friendly_batch] = {"users" => [{:a => "b"}]}
148
+ @datastore.reset_batch
149
+ end
150
+ after { Thread.current[:friendly_batch] = nil }
151
+
152
+ it "sets Thread.current[:friendly_batch] to nil without inserting" do
153
+ Thread.current[:friendly_batch].should be_nil
154
+ @db.should have_received(:from).never
155
+ end
156
+ end
157
+
158
+ describe "flushing a batch" do
159
+ before do
160
+ @records = [{:name => "Stewie"}, {:name => "Brian"}]
161
+ Thread.current[:friendly_batch] = {"users" => @records}
162
+ @users.stubs(:multi_insert)
163
+ @datastore.flush_batch
164
+ end
165
+ after { Thread.current[:friendly_batch] = nil }
166
+
167
+ it "performs the multi_insert on each table" do
168
+ @users.should have_received(:multi_insert).
169
+ with(@records, :commit_every => 1000)
170
+ end
171
+
172
+ it "resets the batch" do
173
+ Thread.current[:friendly_batch].should be_nil
174
+ end
175
+ end
176
+
177
+ describe "counting" do
178
+ before do
179
+ @filtered = stub(:count => 10)
180
+ @users.stubs(:where).with(:name => "James").returns(@filtered)
181
+ end
182
+
183
+ it "fitlers and counts in the db" do
184
+ @datastore.count(@klass, query(:name => "James")).should == 10
185
+ end
186
+ end
187
+ end
188
+