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.
Files changed (110) hide show
  1. data/.document +5 -0
  2. data/.gitignore +23 -0
  3. data/APACHE-LICENSE +202 -0
  4. data/LICENSE +20 -0
  5. data/README.md +173 -0
  6. data/Rakefile +67 -0
  7. data/VERSION +1 -0
  8. data/examples/friendly.yml +7 -0
  9. data/friendly.gemspec +201 -0
  10. data/lib/friendly/attribute.rb +65 -0
  11. data/lib/friendly/boolean.rb +6 -0
  12. data/lib/friendly/cache/by_id.rb +33 -0
  13. data/lib/friendly/cache.rb +24 -0
  14. data/lib/friendly/config.rb +5 -0
  15. data/lib/friendly/data_store.rb +72 -0
  16. data/lib/friendly/document.rb +165 -0
  17. data/lib/friendly/document_table.rb +56 -0
  18. data/lib/friendly/index.rb +73 -0
  19. data/lib/friendly/memcached.rb +48 -0
  20. data/lib/friendly/newrelic.rb +6 -0
  21. data/lib/friendly/query.rb +42 -0
  22. data/lib/friendly/sequel_monkey_patches.rb +35 -0
  23. data/lib/friendly/storage.rb +31 -0
  24. data/lib/friendly/storage_factory.rb +24 -0
  25. data/lib/friendly/storage_proxy.rb +103 -0
  26. data/lib/friendly/table.rb +15 -0
  27. data/lib/friendly/table_creator.rb +43 -0
  28. data/lib/friendly/time.rb +14 -0
  29. data/lib/friendly/translator.rb +32 -0
  30. data/lib/friendly/uuid.rb +143 -0
  31. data/lib/friendly.rb +49 -0
  32. data/rails/init.rb +3 -0
  33. data/spec/fakes/data_store_fake.rb +29 -0
  34. data/spec/fakes/database_fake.rb +12 -0
  35. data/spec/fakes/dataset_fake.rb +28 -0
  36. data/spec/fakes/document.rb +18 -0
  37. data/spec/fakes/serializer_fake.rb +12 -0
  38. data/spec/fakes/time_fake.rb +12 -0
  39. data/spec/integration/basic_object_lifecycle_spec.rb +114 -0
  40. data/spec/integration/batch_insertion_spec.rb +29 -0
  41. data/spec/integration/convenience_api_spec.rb +25 -0
  42. data/spec/integration/count_spec.rb +12 -0
  43. data/spec/integration/default_value_spec.rb +15 -0
  44. data/spec/integration/find_via_cache_spec.rb +101 -0
  45. data/spec/integration/finder_spec.rb +64 -0
  46. data/spec/integration/index_spec.rb +57 -0
  47. data/spec/integration/pagination_spec.rb +63 -0
  48. data/spec/integration/table_creator_spec.rb +52 -0
  49. data/spec/integration/write_through_cache_spec.rb +53 -0
  50. data/spec/spec.opts +1 -0
  51. data/spec/spec_helper.rb +90 -0
  52. data/spec/unit/attribute_spec.rb +64 -0
  53. data/spec/unit/cache_by_id_spec.rb +102 -0
  54. data/spec/unit/cache_spec.rb +21 -0
  55. data/spec/unit/config_spec.rb +4 -0
  56. data/spec/unit/data_store_spec.rb +188 -0
  57. data/spec/unit/document_spec.rb +311 -0
  58. data/spec/unit/document_table_spec.rb +126 -0
  59. data/spec/unit/friendly_spec.rb +25 -0
  60. data/spec/unit/index_spec.rb +196 -0
  61. data/spec/unit/memcached_spec.rb +114 -0
  62. data/spec/unit/query_spec.rb +104 -0
  63. data/spec/unit/storage_factory_spec.rb +59 -0
  64. data/spec/unit/storage_proxy_spec.rb +218 -0
  65. data/spec/unit/translator_spec.rb +96 -0
  66. data/website/index.html +210 -0
  67. data/website/scripts/clipboard.swf +0 -0
  68. data/website/scripts/shBrushAS3.js +61 -0
  69. data/website/scripts/shBrushBash.js +66 -0
  70. data/website/scripts/shBrushCSharp.js +67 -0
  71. data/website/scripts/shBrushColdFusion.js +102 -0
  72. data/website/scripts/shBrushCpp.js +99 -0
  73. data/website/scripts/shBrushCss.js +93 -0
  74. data/website/scripts/shBrushDelphi.js +57 -0
  75. data/website/scripts/shBrushDiff.js +43 -0
  76. data/website/scripts/shBrushErlang.js +54 -0
  77. data/website/scripts/shBrushGroovy.js +69 -0
  78. data/website/scripts/shBrushJScript.js +52 -0
  79. data/website/scripts/shBrushJava.js +59 -0
  80. data/website/scripts/shBrushJavaFX.js +60 -0
  81. data/website/scripts/shBrushPerl.js +74 -0
  82. data/website/scripts/shBrushPhp.js +91 -0
  83. data/website/scripts/shBrushPlain.js +35 -0
  84. data/website/scripts/shBrushPowerShell.js +76 -0
  85. data/website/scripts/shBrushPython.js +66 -0
  86. data/website/scripts/shBrushRuby.js +57 -0
  87. data/website/scripts/shBrushScala.js +53 -0
  88. data/website/scripts/shBrushSql.js +68 -0
  89. data/website/scripts/shBrushVb.js +58 -0
  90. data/website/scripts/shBrushXml.js +71 -0
  91. data/website/scripts/shCore.js +30 -0
  92. data/website/scripts/shLegacy.js +30 -0
  93. data/website/styles/friendly.css +103 -0
  94. data/website/styles/help.png +0 -0
  95. data/website/styles/ie.css +35 -0
  96. data/website/styles/magnifier.png +0 -0
  97. data/website/styles/page_white_code.png +0 -0
  98. data/website/styles/page_white_copy.png +0 -0
  99. data/website/styles/print.css +29 -0
  100. data/website/styles/printer.png +0 -0
  101. data/website/styles/screen.css +257 -0
  102. data/website/styles/shCore.css +330 -0
  103. data/website/styles/shThemeDefault.css +173 -0
  104. data/website/styles/shThemeDjango.css +176 -0
  105. data/website/styles/shThemeEclipse.css +190 -0
  106. data/website/styles/shThemeEmacs.css +175 -0
  107. data/website/styles/shThemeFadeToGrey.css +177 -0
  108. data/website/styles/shThemeMidnight.css +175 -0
  109. data/website/styles/shThemeRDark.css +175 -0
  110. metadata +264 -0
@@ -0,0 +1,12 @@
1
+ class DatabaseFake
2
+ attr_accessor :from
3
+
4
+ def initialize(from)
5
+ @from = from
6
+ end
7
+
8
+ def from(table)
9
+ @from[table]
10
+ end
11
+ end
12
+
@@ -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,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,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