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,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.detect { |k,v| k == conditions }.last
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.detect { |k,v| k == conditions }.last
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,12 @@
1
+ class SerializerFake
2
+ attr_writer :generate
3
+
4
+ def initialize(opts = {})
5
+ opts.each { |k,v| send("#{k}=", v) }
6
+ end
7
+
8
+ def generate(args)
9
+ @generate[args]
10
+ end
11
+ end
12
+
@@ -0,0 +1,12 @@
1
+ class TimeFake
2
+ attr_writer :time
3
+
4
+ def initialize(time)
5
+ @time = time
6
+ end
7
+
8
+ def new
9
+ @time
10
+ end
11
+ end
12
+
@@ -0,0 +1,42 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "Querying with an ad-hoc scope" do
4
+ before do
5
+ User.all(:name => "Fred").each { |u| u.destroy }
6
+ @users = (1..10).map { User.create(:name => "Fred") }
7
+ end
8
+
9
+ it "can return all the objects matching the scope" do
10
+ User.scope(:name => "Fred").all.should == @users
11
+ end
12
+
13
+ it "can return the first object matching the scope" do
14
+ User.scope(:name => "Fred").first.should == User.first(:name => "Fred")
15
+ end
16
+
17
+ it "can paginate over the matching objects" do
18
+ found = User.scope(:name => "Fred").paginate(:per_page! => 5)
19
+ found.should == User.paginate(:name => "Fred", :per_page! => 5)
20
+ end
21
+
22
+ it "can build an object at scope" do
23
+ User.scope(:name => "Fred", :limit! => 5).build.name.should == "Fred"
24
+ end
25
+
26
+ it "supports overriding parameters when building" do
27
+ scope = User.scope(:name => "Fred", :limit! => 5)
28
+ scope.build(:name => "Joe").name.should == "Joe"
29
+ end
30
+
31
+ it "can create an object at scope" do
32
+ user = User.scope(:name => "Joe").create
33
+ user.should_not be_new_record
34
+ user.name.should == "Joe"
35
+ end
36
+
37
+ it "supports overriding parameters when creating" do
38
+ user = User.scope(:name => "Joe").create(:name => "Fred")
39
+ user.should_not be_new_record
40
+ user.name.should == "Fred"
41
+ end
42
+ end
@@ -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 => Friendly::UUID.new)
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