arunthampi-friendly 0.5.1

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 (137) hide show
  1. data/.document +2 -0
  2. data/.gitignore +26 -0
  3. data/APACHE-LICENSE +202 -0
  4. data/CHANGELOG.md +28 -0
  5. data/CONTRIBUTORS.md +7 -0
  6. data/LICENSE +20 -0
  7. data/README.md +288 -0
  8. data/Rakefile +68 -0
  9. data/TODO.md +5 -0
  10. data/VERSION +1 -0
  11. data/arunthampi-friendly.gemspec +241 -0
  12. data/examples/friendly.yml +7 -0
  13. data/friendly.gemspec +240 -0
  14. data/lib/friendly.rb +53 -0
  15. data/lib/friendly/associations.rb +7 -0
  16. data/lib/friendly/associations/association.rb +34 -0
  17. data/lib/friendly/associations/set.rb +37 -0
  18. data/lib/friendly/attribute.rb +98 -0
  19. data/lib/friendly/boolean.rb +10 -0
  20. data/lib/friendly/cache.rb +24 -0
  21. data/lib/friendly/cache/by_id.rb +33 -0
  22. data/lib/friendly/data_store.rb +73 -0
  23. data/lib/friendly/document.rb +70 -0
  24. data/lib/friendly/document/associations.rb +50 -0
  25. data/lib/friendly/document/attributes.rb +114 -0
  26. data/lib/friendly/document/convenience.rb +41 -0
  27. data/lib/friendly/document/mixin.rb +15 -0
  28. data/lib/friendly/document/scoping.rb +66 -0
  29. data/lib/friendly/document/storage.rb +63 -0
  30. data/lib/friendly/document_table.rb +56 -0
  31. data/lib/friendly/index.rb +73 -0
  32. data/lib/friendly/indexer.rb +50 -0
  33. data/lib/friendly/memcached.rb +48 -0
  34. data/lib/friendly/newrelic.rb +6 -0
  35. data/lib/friendly/query.rb +42 -0
  36. data/lib/friendly/scope.rb +100 -0
  37. data/lib/friendly/scope_proxy.rb +43 -0
  38. data/lib/friendly/sequel_monkey_patches.rb +34 -0
  39. data/lib/friendly/storage.rb +31 -0
  40. data/lib/friendly/storage_factory.rb +24 -0
  41. data/lib/friendly/storage_proxy.rb +111 -0
  42. data/lib/friendly/table.rb +15 -0
  43. data/lib/friendly/table_creator.rb +50 -0
  44. data/lib/friendly/time.rb +14 -0
  45. data/lib/friendly/translator.rb +33 -0
  46. data/lib/friendly/uuid.rb +148 -0
  47. data/lib/tasks/friendly.rake +7 -0
  48. data/rails/init.rb +3 -0
  49. data/spec/config.yml.example +7 -0
  50. data/spec/fakes/data_store_fake.rb +29 -0
  51. data/spec/fakes/database_fake.rb +12 -0
  52. data/spec/fakes/dataset_fake.rb +28 -0
  53. data/spec/fakes/document.rb +18 -0
  54. data/spec/fakes/serializer_fake.rb +12 -0
  55. data/spec/fakes/time_fake.rb +12 -0
  56. data/spec/integration/ad_hoc_scopes_spec.rb +42 -0
  57. data/spec/integration/basic_object_lifecycle_spec.rb +114 -0
  58. data/spec/integration/batch_insertion_spec.rb +29 -0
  59. data/spec/integration/convenience_api_spec.rb +25 -0
  60. data/spec/integration/count_spec.rb +12 -0
  61. data/spec/integration/default_value_spec.rb +30 -0
  62. data/spec/integration/dirty_tracking_spec.rb +43 -0
  63. data/spec/integration/find_via_cache_spec.rb +101 -0
  64. data/spec/integration/finder_spec.rb +71 -0
  65. data/spec/integration/has_many_spec.rb +18 -0
  66. data/spec/integration/index_spec.rb +57 -0
  67. data/spec/integration/named_scope_spec.rb +34 -0
  68. data/spec/integration/offline_indexing_spec.rb +53 -0
  69. data/spec/integration/pagination_spec.rb +63 -0
  70. data/spec/integration/scope_chaining_spec.rb +22 -0
  71. data/spec/integration/table_creator_spec.rb +69 -0
  72. data/spec/integration/write_through_cache_spec.rb +53 -0
  73. data/spec/spec.opts +1 -0
  74. data/spec/spec_helper.rb +105 -0
  75. data/spec/unit/associations/association_spec.rb +57 -0
  76. data/spec/unit/associations/set_spec.rb +43 -0
  77. data/spec/unit/attribute_spec.rb +125 -0
  78. data/spec/unit/cache_by_id_spec.rb +102 -0
  79. data/spec/unit/cache_spec.rb +21 -0
  80. data/spec/unit/data_store_spec.rb +201 -0
  81. data/spec/unit/document/attributes_spec.rb +130 -0
  82. data/spec/unit/document_spec.rb +318 -0
  83. data/spec/unit/document_table_spec.rb +126 -0
  84. data/spec/unit/friendly_spec.rb +25 -0
  85. data/spec/unit/index_spec.rb +196 -0
  86. data/spec/unit/memcached_spec.rb +114 -0
  87. data/spec/unit/query_spec.rb +104 -0
  88. data/spec/unit/scope_proxy_spec.rb +44 -0
  89. data/spec/unit/scope_spec.rb +113 -0
  90. data/spec/unit/storage_factory_spec.rb +59 -0
  91. data/spec/unit/storage_proxy_spec.rb +244 -0
  92. data/spec/unit/translator_spec.rb +91 -0
  93. data/website/index.html +210 -0
  94. data/website/scripts/clipboard.swf +0 -0
  95. data/website/scripts/shBrushAS3.js +61 -0
  96. data/website/scripts/shBrushBash.js +66 -0
  97. data/website/scripts/shBrushCSharp.js +67 -0
  98. data/website/scripts/shBrushColdFusion.js +102 -0
  99. data/website/scripts/shBrushCpp.js +99 -0
  100. data/website/scripts/shBrushCss.js +93 -0
  101. data/website/scripts/shBrushDelphi.js +57 -0
  102. data/website/scripts/shBrushDiff.js +43 -0
  103. data/website/scripts/shBrushErlang.js +54 -0
  104. data/website/scripts/shBrushGroovy.js +69 -0
  105. data/website/scripts/shBrushJScript.js +52 -0
  106. data/website/scripts/shBrushJava.js +59 -0
  107. data/website/scripts/shBrushJavaFX.js +60 -0
  108. data/website/scripts/shBrushPerl.js +74 -0
  109. data/website/scripts/shBrushPhp.js +91 -0
  110. data/website/scripts/shBrushPlain.js +35 -0
  111. data/website/scripts/shBrushPowerShell.js +76 -0
  112. data/website/scripts/shBrushPython.js +66 -0
  113. data/website/scripts/shBrushRuby.js +57 -0
  114. data/website/scripts/shBrushScala.js +53 -0
  115. data/website/scripts/shBrushSql.js +68 -0
  116. data/website/scripts/shBrushVb.js +58 -0
  117. data/website/scripts/shBrushXml.js +71 -0
  118. data/website/scripts/shCore.js +30 -0
  119. data/website/scripts/shLegacy.js +30 -0
  120. data/website/styles/friendly.css +103 -0
  121. data/website/styles/help.png +0 -0
  122. data/website/styles/ie.css +35 -0
  123. data/website/styles/magnifier.png +0 -0
  124. data/website/styles/page_white_code.png +0 -0
  125. data/website/styles/page_white_copy.png +0 -0
  126. data/website/styles/print.css +29 -0
  127. data/website/styles/printer.png +0 -0
  128. data/website/styles/screen.css +257 -0
  129. data/website/styles/shCore.css +330 -0
  130. data/website/styles/shThemeDefault.css +173 -0
  131. data/website/styles/shThemeDjango.css +176 -0
  132. data/website/styles/shThemeEclipse.css +190 -0
  133. data/website/styles/shThemeEmacs.css +175 -0
  134. data/website/styles/shThemeFadeToGrey.css +177 -0
  135. data/website/styles/shThemeMidnight.css +175 -0
  136. data/website/styles/shThemeRDark.css +175 -0
  137. metadata +311 -0
@@ -0,0 +1,57 @@
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
+
3
+ describe "Friendly::Associations::Association" do
4
+ before do
5
+ @owner_klass = stub(:name => "User")
6
+ @klass = stub
7
+ # FIXME: ugh.
8
+ String.any_instance.stubs(:constantize).returns(@klass)
9
+ @assoc_klass = Friendly::Associations::Association
10
+ end
11
+
12
+ describe "with defaults" do
13
+ before do
14
+ @association = @assoc_klass.new(@owner_klass, :addresses)
15
+ end
16
+
17
+ it "has a default klass of name.classify.constantize" do
18
+ @association.klass.should == @klass
19
+ end
20
+
21
+ it "has a foreign_key of owner_klass.name.singularize + '_id'" do
22
+ @association.foreign_key.should == :user_id
23
+ end
24
+
25
+ it "returns a scope on klass of {:foreign_key => document.id}" do
26
+ @scope = stub
27
+ @klass.stubs(:scope).with(:user_id => 42).returns(@scope)
28
+
29
+ @association.scope(stub(:id => 42)).should == @scope
30
+ end
31
+ end
32
+
33
+ describe "with overridden attributes" do
34
+ before do
35
+ @klass = stub
36
+ @class_name = "SomeOtherClass"
37
+ @class_name.stubs(:constantize).returns(@klass)
38
+ @association = @assoc_klass.new @owner_klass, :whatever,
39
+ :class_name => @class_name,
40
+ :foreign_key => :other_id
41
+ end
42
+
43
+ it "uses the overridden class_name to get the class" do
44
+ @association.klass.should == @klass
45
+ end
46
+
47
+ it "uses the overridden foreign_key" do
48
+ @association.foreign_key.should == :other_id
49
+ end
50
+
51
+ it "uses the override foreign_key in the scope" do
52
+ @scope = stub
53
+ @klass.stubs(:scope).with(:other_id => 42).returns(@scope)
54
+ @association.scope(stub(:id => 42)).should == @scope
55
+ end
56
+ end
57
+ end
@@ -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,125 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe "Friendly::Attribute" do
4
+ before do
5
+ @klass = Class.new { def will_change(a); end }
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, :false, 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 getting on klass that notifies it of a change" do
19
+ @object.stubs(:will_change)
20
+ @object.name = "Something"
21
+ @object.should have_received(:will_change).with(:name)
22
+ end
23
+
24
+ it "creates a getter on klass" do
25
+ @object.name = "Something"
26
+ @object.name.should == "Something"
27
+ end
28
+
29
+ it "creates an 'attr_was' getter" do
30
+ @object.instance_variable_set(:@name_was, "Joe the Plumber")
31
+ @object.name_was.should == "Joe the Plumber"
32
+ end
33
+
34
+ it "creates an attr_changed? query method" do
35
+ @object.stubs(:attribute_changed?).with(:name).returns(true)
36
+ @object.should be_name_changed
37
+ end
38
+
39
+ it "typecasts values using the converter function" do
40
+ uuid = Friendly::UUID.new
41
+ @id.typecast(uuid.to_s).should == uuid
42
+ end
43
+
44
+ it "doesn't typecast values if they are of the right type" do
45
+ uuid = Friendly::UUID.new
46
+ @id.typecast(uuid).should == uuid
47
+ end
48
+
49
+ it "raises a useful error if it can't typecast" do
50
+ attribute = Friendly::Attribute.new(@klass, :weird, Class)
51
+ lambda {
52
+ attribute.typecast("ASDF")
53
+ }.should raise_error(Friendly::NoConverterExists)
54
+ end
55
+
56
+ it "has a default value of type.new" do
57
+ @id.default.should be_instance_of(Friendly::UUID)
58
+ end
59
+
60
+ it "has a default of nil if the type doesn't respond to :new" do
61
+ Friendly::Attribute.new(@klass, :age, Integer).default.should be_nil
62
+ end
63
+
64
+ it "doesn't try to convert when there's no type" do
65
+ @no_type.typecast(true).should == true
66
+ end
67
+
68
+ it "can have a default value" do
69
+ @default.default.should == "asdf"
70
+ @obj = @klass.new
71
+ @default.assign_default_value(@obj)
72
+ @obj.default.should == "asdf"
73
+ end
74
+
75
+ it "has a default value even if it's false" do
76
+ @false.default.should be_false
77
+ end
78
+
79
+ it "knows how to assign its own default" do
80
+ @object = stub(:false= => nil)
81
+ @false.assign_default_value(@object)
82
+ @object.should have_received(:false=).with(false)
83
+ end
84
+
85
+ describe "registering a type" do
86
+ before do
87
+ @klass = Class.new
88
+ Friendly::Attribute.register_type(@klass, "binary(16)") { |t| t.to_i }
89
+ end
90
+
91
+ after { Friendly::Attribute.deregister_type(@klass) }
92
+
93
+ it "tells Attribute about the sql_type" do
94
+ Friendly::Attribute.sql_type(@klass).should == "binary(16)"
95
+ end
96
+
97
+ it "registers the conversion method" do
98
+ Friendly::Attribute.new(@klass, :something, @klass).convert("1").should == 1
99
+ end
100
+
101
+ it "is custom_type?(@klass)" do
102
+ Friendly::Attribute.should be_custom_type(@klass)
103
+ end
104
+ end
105
+
106
+ describe "deregistering a type" do
107
+ before do
108
+ @klass = Class.new
109
+ Friendly::Attribute.register_type(@klass, "whatever") {}
110
+ Friendly::Attribute.deregister_type(@klass)
111
+ end
112
+
113
+ it "removes the converter method" do
114
+ Friendly::Attribute.converters.should_not be_has_key(@klass)
115
+ end
116
+
117
+ it "removes the sql_type" do
118
+ Friendly::Attribute.sql_types.should_not be_has_key(@klass)
119
+ end
120
+
121
+ it "is not custom_type?(@klass)" do
122
+ Friendly::Attribute.should_not be_custom_type(@klass)
123
+ end
124
+ end
125
+ 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,201 @@
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 "all without conditions" do
80
+ before do
81
+ @all = stub(:map => [])
82
+ @users.stubs(:order).with(:created_at).returns(@all)
83
+ @query = query(:order! => :created_at)
84
+ @return = @datastore.all(@klass, @query)
85
+ end
86
+
87
+ it "orders the filtered dataset and returns the results" do
88
+ @return.should == []
89
+ end
90
+ end
91
+
92
+ describe "retrieving first with conditions" do
93
+ before do
94
+ @users.first = {{:id => 1} => {:id => 1}}
95
+ @return = @datastore.first(@klass, query(:id => 1))
96
+ end
97
+
98
+ it "gets the first object matching the conditions from the dataset" do
99
+ @return.should == {:id => 1}
100
+ end
101
+ end
102
+
103
+ describe "updating data" do
104
+ before do
105
+ @filtered = DatasetFake.new(:update => true)
106
+ @users.where = {{:id => 1} => @filtered}
107
+ @return = @datastore.update(@klass, 1, :name => "Peter")
108
+ end
109
+
110
+ it "filter the dataset by id and update the filtered row" do
111
+ @filtered.updates.length.should == 1
112
+ @filtered.updates.should include(:name => "Peter")
113
+ end
114
+ end
115
+
116
+ describe "deleting data" do
117
+ before do
118
+ @filtered = stub
119
+ @filtered.stubs(:delete)
120
+ @users.where = {{:id => 1} => @filtered}
121
+ @datastore.delete(@klass, 1)
122
+ end
123
+
124
+ it "filters the dataset by id and deletes" do
125
+ @filtered.should have_received(:delete)
126
+ end
127
+ end
128
+
129
+ describe "when a batch transaction has been started" do
130
+ before do
131
+ @datastore.start_batch
132
+ @persistable = stub(:table_name => "some_table")
133
+ @datastore.insert(@persistable, {:some => "attrs"})
134
+ end
135
+
136
+ after { Thread.current[:friendly_batch] = nil }
137
+
138
+ it "adds the attributes to the batch for that table" do
139
+ inserts = Thread.current[:friendly_batch]["some_table"]
140
+ inserts.length.should == 1
141
+ inserts.should include(:some => "attrs")
142
+ end
143
+ end
144
+
145
+ describe "starting a batch" do
146
+ before do
147
+ @datastore.start_batch
148
+ end
149
+
150
+ after { Thread.current[:friendly_batch] = nil }
151
+
152
+ it "sets Thread.current[:friendly_batch] to empty hash" do
153
+ Thread.current[:friendly_batch].should == {}
154
+ end
155
+ end
156
+
157
+ describe "resetting a batch transaction" do
158
+ before do
159
+ @db.stubs(:from)
160
+ Thread.current[:friendly_batch] = {"users" => [{:a => "b"}]}
161
+ @datastore.reset_batch
162
+ end
163
+ after { Thread.current[:friendly_batch] = nil }
164
+
165
+ it "sets Thread.current[:friendly_batch] to nil without inserting" do
166
+ Thread.current[:friendly_batch].should be_nil
167
+ @db.should have_received(:from).never
168
+ end
169
+ end
170
+
171
+ describe "flushing a batch" do
172
+ before do
173
+ @records = [{:name => "Stewie"}, {:name => "Brian"}]
174
+ Thread.current[:friendly_batch] = {"users" => @records}
175
+ @users.stubs(:multi_insert)
176
+ @datastore.flush_batch
177
+ end
178
+ after { Thread.current[:friendly_batch] = nil }
179
+
180
+ it "performs the multi_insert on each table" do
181
+ @users.should have_received(:multi_insert).
182
+ with(@records, :commit_every => 1000)
183
+ end
184
+
185
+ it "resets the batch" do
186
+ Thread.current[:friendly_batch].should be_nil
187
+ end
188
+ end
189
+
190
+ describe "counting" do
191
+ before do
192
+ @filtered = stub(:count => 10)
193
+ @users.stubs(:where).with(:name => "James").returns(@filtered)
194
+ end
195
+
196
+ it "fitlers and counts in the db" do
197
+ @datastore.count(@klass, query(:name => "James")).should == 10
198
+ end
199
+ end
200
+ end
201
+