bigrecord 0.0.5

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 (104) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +44 -0
  3. data/Rakefile +17 -0
  4. data/VERSION +1 -0
  5. data/doc/bigrecord_specs.rdoc +36 -0
  6. data/doc/getting_started.rdoc +157 -0
  7. data/examples/bigrecord.yml +25 -0
  8. data/generators/bigrecord/bigrecord_generator.rb +17 -0
  9. data/generators/bigrecord/templates/bigrecord.rake +47 -0
  10. data/generators/bigrecord_migration/bigrecord_migration_generator.rb +13 -0
  11. data/generators/bigrecord_migration/templates/migration.rb +9 -0
  12. data/generators/bigrecord_model/bigrecord_model_generator.rb +28 -0
  13. data/generators/bigrecord_model/templates/migration.rb +13 -0
  14. data/generators/bigrecord_model/templates/model.rb +7 -0
  15. data/generators/bigrecord_model/templates/model_spec.rb +12 -0
  16. data/init.rb +9 -0
  17. data/install.rb +22 -0
  18. data/lib/big_record/abstract_base.rb +1088 -0
  19. data/lib/big_record/action_view_extensions.rb +266 -0
  20. data/lib/big_record/ar_associations/association_collection.rb +194 -0
  21. data/lib/big_record/ar_associations/association_proxy.rb +158 -0
  22. data/lib/big_record/ar_associations/belongs_to_association.rb +57 -0
  23. data/lib/big_record/ar_associations/belongs_to_many_association.rb +57 -0
  24. data/lib/big_record/ar_associations/has_and_belongs_to_many_association.rb +164 -0
  25. data/lib/big_record/ar_associations/has_many_association.rb +191 -0
  26. data/lib/big_record/ar_associations/has_one_association.rb +80 -0
  27. data/lib/big_record/ar_associations.rb +1608 -0
  28. data/lib/big_record/ar_reflection.rb +223 -0
  29. data/lib/big_record/attribute_methods.rb +75 -0
  30. data/lib/big_record/base.rb +618 -0
  31. data/lib/big_record/br_associations/association_collection.rb +194 -0
  32. data/lib/big_record/br_associations/association_proxy.rb +153 -0
  33. data/lib/big_record/br_associations/belongs_to_association.rb +52 -0
  34. data/lib/big_record/br_associations/belongs_to_many_association.rb +293 -0
  35. data/lib/big_record/br_associations/cached_item_proxy.rb +194 -0
  36. data/lib/big_record/br_associations/cached_item_proxy_factory.rb +62 -0
  37. data/lib/big_record/br_associations/has_and_belongs_to_many_association.rb +168 -0
  38. data/lib/big_record/br_associations/has_one_association.rb +80 -0
  39. data/lib/big_record/br_associations.rb +978 -0
  40. data/lib/big_record/br_reflection.rb +151 -0
  41. data/lib/big_record/callbacks.rb +367 -0
  42. data/lib/big_record/connection_adapters/abstract/connection_specification.rb +279 -0
  43. data/lib/big_record/connection_adapters/abstract/database_statements.rb +175 -0
  44. data/lib/big_record/connection_adapters/abstract/quoting.rb +58 -0
  45. data/lib/big_record/connection_adapters/abstract_adapter.rb +190 -0
  46. data/lib/big_record/connection_adapters/column.rb +491 -0
  47. data/lib/big_record/connection_adapters/hbase_adapter.rb +432 -0
  48. data/lib/big_record/connection_adapters/view.rb +27 -0
  49. data/lib/big_record/connection_adapters.rb +10 -0
  50. data/lib/big_record/deletion.rb +73 -0
  51. data/lib/big_record/dynamic_schema.rb +92 -0
  52. data/lib/big_record/embedded.rb +71 -0
  53. data/lib/big_record/embedded_associations/association_proxy.rb +148 -0
  54. data/lib/big_record/family_span_columns.rb +89 -0
  55. data/lib/big_record/fixtures.rb +1025 -0
  56. data/lib/big_record/migration.rb +380 -0
  57. data/lib/big_record/routing_ext.rb +65 -0
  58. data/lib/big_record/timestamp.rb +51 -0
  59. data/lib/big_record/validations.rb +830 -0
  60. data/lib/big_record.rb +125 -0
  61. data/lib/bigrecord.rb +1 -0
  62. data/rails/init.rb +9 -0
  63. data/spec/connections/bigrecord.yml +13 -0
  64. data/spec/connections/cassandra/connection.rb +2 -0
  65. data/spec/connections/hbase/connection.rb +2 -0
  66. data/spec/debug.log +281 -0
  67. data/spec/integration/br_associations_spec.rb +80 -0
  68. data/spec/lib/animal.rb +12 -0
  69. data/spec/lib/book.rb +10 -0
  70. data/spec/lib/broken_migrations/duplicate_name/20090706182535_add_animals_table.rb +14 -0
  71. data/spec/lib/broken_migrations/duplicate_name/20090706193019_add_animals_table.rb +9 -0
  72. data/spec/lib/broken_migrations/duplicate_version/20090706190623_add_books_table.rb +9 -0
  73. data/spec/lib/broken_migrations/duplicate_version/20090706190623_add_companies_table.rb +9 -0
  74. data/spec/lib/company.rb +14 -0
  75. data/spec/lib/embedded/web_link.rb +12 -0
  76. data/spec/lib/employee.rb +33 -0
  77. data/spec/lib/migrations/20090706182535_add_animals_table.rb +13 -0
  78. data/spec/lib/migrations/20090706190623_add_books_table.rb +15 -0
  79. data/spec/lib/migrations/20090706193019_add_companies_table.rb +14 -0
  80. data/spec/lib/migrations/20090706194512_add_employees_table.rb +13 -0
  81. data/spec/lib/migrations/20090706195741_add_zoos_table.rb +13 -0
  82. data/spec/lib/novel.rb +5 -0
  83. data/spec/lib/zoo.rb +17 -0
  84. data/spec/spec.opts +4 -0
  85. data/spec/spec_helper.rb +55 -0
  86. data/spec/unit/abstract_base_spec.rb +287 -0
  87. data/spec/unit/adapters/abstract_adapter_spec.rb +56 -0
  88. data/spec/unit/adapters/adapter_shared_spec.rb +51 -0
  89. data/spec/unit/adapters/hbase_adapter_spec.rb +15 -0
  90. data/spec/unit/ar_associations_spec.rb +8 -0
  91. data/spec/unit/base_spec.rb +6 -0
  92. data/spec/unit/br_associations_spec.rb +58 -0
  93. data/spec/unit/embedded_spec.rb +43 -0
  94. data/spec/unit/find_spec.rb +34 -0
  95. data/spec/unit/hash_helper_spec.rb +44 -0
  96. data/spec/unit/migration_spec.rb +144 -0
  97. data/spec/unit/model_spec.rb +315 -0
  98. data/spec/unit/validations_spec.rb +182 -0
  99. data/tasks/bigrecord_tasks.rake +47 -0
  100. data/tasks/data_store.rb +46 -0
  101. data/tasks/gem.rb +22 -0
  102. data/tasks/rdoc.rb +8 -0
  103. data/tasks/spec.rb +34 -0
  104. metadata +189 -0
@@ -0,0 +1,58 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe BigRecord::BrAssociations do
4
+
5
+ # Clear the tables before and after these tests
6
+ before(:all) do
7
+ Animal.delete_all
8
+ Employee.delete_all
9
+ Zoo.delete_all
10
+ end
11
+
12
+ it "should list associations in #reflections" do
13
+ Animal.reflections.should have_key(:zoo)
14
+ Animal.reflections.should have_key(:books)
15
+
16
+ Animal.reflections[:zoo].macro.should == :belongs_to_big_record
17
+ Animal.reflections[:books].macro.should == :belongs_to_many
18
+
19
+ Employee.reflections.should have_key(:company)
20
+ Employee.reflections[:company].macro.should == :belongs_to_big_record
21
+
22
+ Zoo.reflections.should have_key(:animals)
23
+ Zoo.reflections[:animals].macro.should == :belongs_to_many
24
+ end
25
+
26
+ it "should mixin all the helper methods into the model for :belongs_to associations" do
27
+ animal = Animal.new
28
+
29
+ animal.should respond_to(:zoo)
30
+ animal.should respond_to(:zoo=)
31
+ animal.should respond_to(:build_zoo)
32
+ animal.should respond_to(:create_zoo)
33
+ end
34
+
35
+ it "should mixin all the helper methods into the model for :belongs_to_many associations" do
36
+ zoo = Zoo.new
37
+
38
+ zoo.should respond_to(:animals)
39
+ zoo.should respond_to(:animals=)
40
+ zoo.should respond_to(:animal_ids)
41
+ zoo.should respond_to(:animal_ids=)
42
+ zoo.animals.should respond_to(:<<)
43
+ zoo.animals.should respond_to(:delete)
44
+ zoo.animals.should respond_to(:clear)
45
+ zoo.animals.should respond_to(:empty?)
46
+ zoo.animals.should respond_to(:size)
47
+ end
48
+
49
+ it "should mixin build() and create() methods into collection assocations" do
50
+ pending "This needs to be implemented"
51
+
52
+ zoo = Zoo.new
53
+
54
+ zoo.animals.should respond_to(:build)
55
+ zoo.animals.should respond_to(:create)
56
+ end
57
+
58
+ end
@@ -0,0 +1,43 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), 'abstract_base_spec'))
3
+
4
+ describe BigRecord::Embedded do
5
+
6
+ # Clear the tables before and after these tests
7
+ before(:all) do
8
+ Zoo.delete_all
9
+ end
10
+
11
+ after(:all) do
12
+ Zoo.delete_all
13
+ end
14
+
15
+ it_should_behave_like "BigRecord::AbstractBase"
16
+
17
+ describe "embedded within a BigRecord::Base model" do
18
+
19
+ it "should save successfully" do
20
+ zoo = Zoo.new( :name => "African Lion Safari",
21
+ :address => "RR #1 Cambridge, Ontario Canada\nN1R 5S2",
22
+ :description => "Canada's Original Safari Adventure")
23
+
24
+ zoo.weblink = Embedded::WebLink.new(:title => "African Lion Safari - Wikipedia", :url => "http://en.wikipedia.org/wiki/African_Lion_Safari")
25
+ zoo.save.should be_true
26
+ zoo.reload
27
+
28
+ zoo.weblink.should_not be_nil
29
+ zoo.weblink.should be_a_kind_of(Embedded::WebLink)
30
+ zoo.weblink.title.should == "African Lion Safari - Wikipedia"
31
+ zoo.weblink.url.should == "http://en.wikipedia.org/wiki/African_Lion_Safari"
32
+
33
+ zoo.weblink = Embedded::WebLink.new(:title => "African Lion Safari", :url => "http://www.lionsafari.com/")
34
+ zoo.save.should be_true
35
+ zoo.reload
36
+
37
+ zoo.weblink.title.should == "African Lion Safari"
38
+ zoo.weblink.url.should == "http://www.lionsafari.com/"
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,34 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe BigRecord::Base do
4
+
5
+ describe "#find" do
6
+
7
+ it "should dispatch properly to #find_every when given :first" do
8
+ zoo = Zoo.new
9
+
10
+ Zoo.should_receive(:find_every).with(hash_including(:limit => 1)).and_return([zoo])
11
+
12
+ Zoo.find(:first).should == zoo
13
+ end
14
+
15
+ it "should dispatch properly to #find_every when given :all" do
16
+ zoo = Zoo.new
17
+
18
+ Zoo.should_receive(:find_every).and_return([zoo])
19
+
20
+ Zoo.find(:all).should == [zoo]
21
+ end
22
+
23
+ it "should dispatch properly to #find_from_ids when given anything else" do
24
+ zoo = Zoo.new
25
+ id = "c6e2cf62-332d-44f0-a558-dfdfe2c7ee1e"
26
+
27
+ Zoo.should_receive(:find_from_ids).with([id], an_instance_of(Hash)).and_return([zoo])
28
+
29
+ Zoo.find(id).should == [zoo]
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,44 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ # Not exactly the most comprehensive testing for this function.
4
+ describe Hash do
5
+
6
+ before(:each) do
7
+ @hash1 = { :key1 => "value1", :key2 => "value2", :key3 => "value3" }
8
+ @hash2 = { :key1 => "value1", :key2 => "value2", :key3 => "value3", :key4 => "value4" }
9
+ @hash3 = { :key1 => "value4", :key2 => "value3", :key3 => "value1", :key4 => "value2" }
10
+ @hash4 = { :key2 => "value2", :key3 => "value3" }
11
+ end
12
+
13
+ it "#subset_of? should compare 2 hashes correctly (when one really is a subset of the other)" do
14
+ @hash1.subset_of?(@hash2).should be_true
15
+ @hash2.subset_of?(@hash1).should be_false
16
+ end
17
+
18
+ it "#superset_of? should compare 2 hashes correctly (when one really is a superset of the other)" do
19
+ @hash1.superset_of?(@hash2).should be_false
20
+ @hash2.superset_of?(@hash1).should be_true
21
+ end
22
+
23
+ it "#subset_of? and #superset_of? should be true when comparing the same hash to itself" do
24
+ @hash1.subset_of?(@hash1).should be_true
25
+ @hash1.superset_of?(@hash1).should be_true
26
+
27
+ @hash2.subset_of?(@hash2).should be_true
28
+ @hash2.superset_of?(@hash2).should be_true
29
+ end
30
+
31
+ it "should return false when there's a mismatch of key/value pairs" do
32
+ @hash2.subset_of?(@hash3).should be_false
33
+ @hash2.superset_of?(@hash3).should be_false
34
+ end
35
+
36
+ it "should compare properly with missing keys in one of the hashes (even if all the other pairs match)" do
37
+ # false because @hash4 is missing :key1
38
+ @hash1.subset_of?(@hash4).should be_false
39
+
40
+ # it is a superset because all of the key/value pairs of @hash4 are contained within @hash1
41
+ @hash1.superset_of?(@hash4).should be_true
42
+ end
43
+
44
+ end
@@ -0,0 +1,144 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe BigRecord::Migration do
4
+
5
+ before(:each) do
6
+ @migrations_path = File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "migrations"))
7
+ @migration_files = Dir["#{@migrations_path}/[0-9]*_*.rb"]
8
+
9
+ @broken_migrations_path = File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "broken_migrations"))
10
+ @broken_migration_files = Dir["#{@broken_migrations_path}/[0-9]*_*.rb"]
11
+
12
+ # It doesn't matter whether the adapter works for this spec, so we'll mock it
13
+ @mock_connection = mock(BigRecord::ConnectionAdapters::AbstractAdapter, :null_object => true)
14
+ @mock_connection.stub!(:supports_ddl_transactions?).and_return(false)
15
+
16
+ # Replace BigRecord::Base.connection to always return the mock_connection
17
+ BigRecord::Base.stub!(:connection).and_return(@mock_connection)
18
+
19
+ # We don't want it outputting to stdout
20
+ BigRecord::Migration.verbose = false
21
+ end
22
+
23
+ describe "class methods and initialization" do
24
+
25
+ it "#proper_table_name should return the corresponding table name for the data store (with different arguments types)" do
26
+ # Check that it works with a symbol, BigRecord model, or string.
27
+ BigRecord::Migrator.proper_table_name(:animals).should == "animals"
28
+ BigRecord::Migrator.proper_table_name(Animal).should == "animals"
29
+ BigRecord::Migrator.proper_table_name("animals").should == "animals"
30
+ end
31
+
32
+ it "#get_all_versions should query from the data store" do
33
+ @mock_connection.should_receive(:get_all_schema_versions).and_return(["version1"])
34
+
35
+ BigRecord::Migrator.get_all_versions.should == ["version1"]
36
+ end
37
+
38
+ it "should initialize the migrator given a directory of migration files" do
39
+ # Assuming that the adapter supports migrations
40
+ @mock_connection.should_receive(:supports_migrations?).and_return(true)
41
+
42
+ # Setting up the migrator will initialize the schema migration table
43
+ @mock_connection.should_receive(:initialize_schema_migrations_table)
44
+
45
+ # Initializing the migrator
46
+ migrator = BigRecord::Migrator.new(:up, @migrations_path)
47
+
48
+ # Now assuming that the adapter doesn't supports migrations
49
+ @mock_connection.should_receive(:supports_migrations?).and_return(false)
50
+
51
+ # Setting up the migrator should not initialize the schema migration table
52
+ @mock_connection.should_not_receive(:initialize_schema_migrations_table)
53
+
54
+ lambda{
55
+ migrator = BigRecord::Migrator.new(:up, @migrations_path)
56
+ }.should raise_error(StandardError)
57
+ end
58
+
59
+ end
60
+
61
+ describe "migrator methods" do
62
+
63
+ before(:each) do
64
+ # Setting up the migrator will initialize the schema migration table
65
+ @mock_connection.should_receive(:initialize_schema_migrations_table)
66
+ end
67
+
68
+ it "should show all the pending migrations" do
69
+ # Initializing the migrator
70
+ @migrator = BigRecord::Migrator.new(:up, @migrations_path)
71
+
72
+ # And request all the schema versions currently within the data store
73
+ @mock_connection.should_receive(:get_all_schema_versions).and_return([])
74
+
75
+ # Verify that our migrations are listed as pending
76
+ @migration_files.each do |migration_file|
77
+ @migrator.pending_migrations.map(&:filename).should include(migration_file)
78
+ end
79
+ end
80
+
81
+ it "should run the migrations and update the schema migration table in the data store" do
82
+ # Initializing the migrator
83
+ @migrator = BigRecord::Migrator.new(:up, @migrations_path)
84
+
85
+ # And request all the schema versions currently within the data store
86
+ @mock_connection.should_receive(:get_all_schema_versions).and_return([])
87
+
88
+ # The data store will be updated once for each migration file
89
+ @mock_connection.should_receive(:update).exactly(@migration_files.size).times.and_return(true)
90
+ result = @migrator.migrate
91
+
92
+ # The pending migrations should now be listed in the migrated #migrated method and removed from #pending_migrations
93
+ result.size.should == @migration_files.size
94
+ @migrator.migrated.size.should == @migration_files.size
95
+ @migrator.pending_migrations.should be_empty
96
+ end
97
+
98
+ it "should call the appropriate adapter connection methods when migrating :up" do
99
+ # Initializing the migrator
100
+ @migrator = BigRecord::Migrator.new(:up, @migrations_path, 20090706182535) # Hard-coded target. Don't change the migration!
101
+
102
+ # And request all the schema versions currently within the data store
103
+ @mock_connection.should_receive(:get_all_schema_versions).and_return([])
104
+
105
+ @mock_connection.should_receive(:create_table).with("animals", {:force => true}).once.and_return(true)
106
+ @migrator.migrate
107
+ end
108
+
109
+ it "should call the appropriate adapter connection methods when reverting :down" do
110
+ # Initializing the migrator
111
+ @migrator = BigRecord::Migrator.new(:down, @migrations_path) # Hard-coded target. Don't change the migration!
112
+
113
+ # And request all the schema versions currently within the data store.
114
+ @mock_connection.should_receive(:get_all_schema_versions).and_return([20090706182535]) # Hard-coded target. Don't change the migration!
115
+
116
+ @mock_connection.should_receive(:drop_table).with("animals").once.and_return(true)
117
+ @migrator.migrate
118
+ end
119
+
120
+ end
121
+
122
+ describe "error handling" do
123
+
124
+ it "should raise an error on duplicate version numbers" do
125
+ # Initializing the migrator
126
+ @migrator = BigRecord::Migrator.new(:up, @broken_migrations_path+"/duplicate_version")
127
+
128
+ lambda {
129
+ @migrator.migrations
130
+ }.should raise_error(BigRecord::DuplicateMigrationVersionError)
131
+ end
132
+
133
+ it "should raise an error on duplicate migration names" do
134
+ # Initializing the migrator
135
+ @migrator = BigRecord::Migrator.new(:up, @broken_migrations_path+"/duplicate_name")
136
+
137
+ lambda {
138
+ @migrator.migrations
139
+ }.should raise_error(BigRecord::DuplicateMigrationNameError)
140
+ end
141
+
142
+ end
143
+
144
+ end
@@ -0,0 +1,315 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe BigRecord::Base do
4
+
5
+ # Clear the tables before and after these tests
6
+ before(:all) do
7
+ Book.delete_all
8
+ end
9
+
10
+ after(:all) do
11
+ Book.delete_all
12
+ end
13
+
14
+ it "should provide .id" do
15
+ Book.new.should respond_to(:id)
16
+ end
17
+
18
+ it "should provide hash-like attribute accessors, i.e. [] and []=" do
19
+ Book.new.should respond_to(:[])
20
+ Book.new.should respond_to(:[]=)
21
+ end
22
+
23
+ it 'should provide #create' do
24
+ Book.should respond_to(:create)
25
+ end
26
+
27
+ describe '#create' do
28
+
29
+ it 'should create a new record in the data store' do
30
+ book = Book.create(:title => "I Am Legend", :author => "Richard Matheson")
31
+
32
+ book.should be_a_kind_of(Book)
33
+ book.id.should_not be_nil
34
+ book.new_record?.should be_false
35
+
36
+ book_confirm = Book.find(book.id)
37
+
38
+ book_confirm.title.should == "I Am Legend"
39
+ book_confirm.author.should == "Richard Matheson"
40
+ end
41
+
42
+ it 'should return the unsaved object even if a record could not be created' do
43
+ attributes = {:title => "I Am Legend", :author => "Richard Matheson"}
44
+ book = Book.new(attributes)
45
+
46
+ # Mocking the #create method in Base to return a specific book object we define
47
+ Book.should_receive(:new).and_return(book)
48
+
49
+ # When #save is called on our book object, it'll return false.
50
+ book.should_receive(:save).and_return(false)
51
+
52
+ # Now we'll try the #create method on the Book class
53
+ created_book = Book.create(attributes)
54
+
55
+ # The save shouldn't have succeeded
56
+ created_book.new_record?.should be_true
57
+
58
+ # But we should still receive a book object that's the same
59
+ created_book.should == book
60
+ end
61
+
62
+ end
63
+
64
+ # Protected instance method called by .save (different than Class#create)
65
+ it 'should provide .create' do
66
+ Book.new.should respond_to(:create)
67
+ end
68
+
69
+ # Protected instance method called by .save
70
+ it 'should provide .update' do
71
+ Book.new.should respond_to(:update)
72
+ end
73
+
74
+ it 'should provide .save and .save!' do
75
+ Book.new.should respond_to(:save)
76
+ Book.new.should respond_to(:save!)
77
+ end
78
+
79
+ describe '.save and .save!' do
80
+
81
+ describe 'with a new resource' do
82
+
83
+ it 'should create new entries in the data store' do
84
+ # Create a new object
85
+ book = Book.new( :title => "I Am Legend",
86
+ :author => "Richard Matheson",
87
+ :description => "The most clever and riveting vampire novel since Dracula.")
88
+
89
+ book.new_record?.should be_true
90
+ book.id.should be_nil
91
+ book.save.should be_true
92
+
93
+ book.new_record?.should be_false
94
+ book.id.should_not be_nil
95
+
96
+ # Verify that the object was saved
97
+ book2 = Book.find(book.id)
98
+
99
+ book2.title.should == "I Am Legend"
100
+ book2.author.should == "Richard Matheson"
101
+ book2.description.should == "The most clever and riveting vampire novel since Dracula."
102
+ end
103
+
104
+ it 'should raise an exception with .save! if a record was not saved or true if successful' do
105
+ book = Book.new( :title => "I Am Legend",
106
+ :author => "Richard Matheson",
107
+ :description => "The most clever and riveting vampire novel since Dracula.")
108
+
109
+ # The actual method that's called just before the data store write is #update_big_record, which returns
110
+ # a boolean. We're going to mock this method and have it return false.
111
+ book.should_receive(:update_bigrecord).and_return(false)
112
+
113
+ # Verify that an exception is raised
114
+ lambda { book.save! }.should raise_error(BigRecord::RecordNotSaved)
115
+
116
+ # Verify that true gets returned on success
117
+ book.should_receive(:update_bigrecord).and_return(true)
118
+ book.save.should be_true
119
+ end
120
+
121
+ end
122
+
123
+ describe 'with an existing record' do
124
+
125
+ before(:each) do
126
+ # Just want to verify that the book is created properly everytime
127
+ new_book = Book.new( :title => "I Am Legend",
128
+ :author => "Richard Matheson",
129
+ :description => "The most clever and riveting vampire novel since Dracula.")
130
+
131
+ new_book.save.should be_true
132
+
133
+ # Maybe a little paranoid...
134
+ new_book.new_record?.should be_false
135
+ new_book.id.should_not be_nil
136
+
137
+ # Grab the entry from the data store and verify
138
+ @book = Book.find(new_book)
139
+ @book.title.should == "I Am Legend"
140
+ @book.author.should == "Richard Matheson"
141
+ @book.description.should == "The most clever and riveting vampire novel since Dracula."
142
+ end
143
+
144
+ it 'should update that record' do
145
+ @book.description = "One of the Ten All-Time Best Novels of Vampirism."
146
+ @book.save.should be_true
147
+
148
+ book_verify = Book.find(@book)
149
+ book_verify.description.should == "One of the Ten All-Time Best Novels of Vampirism."
150
+ book_verify.id.should == @book.id
151
+ end
152
+
153
+ end
154
+
155
+ end
156
+
157
+ describe 'modified attribute tracking' do
158
+
159
+ it "should not mark attributes as modified if they are similar" do
160
+ pending "attribute tracking needs to be implemented in BigRecord::Base"
161
+
162
+ attributes = { :title => "I Am Legend",
163
+ :author => "Richard Matheson",
164
+ :description => "The most clever and riveting vampire novel since Dracula." }
165
+
166
+ book = Book.new(attributes)
167
+ book.save.should be_true
168
+
169
+ book.attributes = attributes
170
+
171
+ book.modified_attributes.should be_empty
172
+ end
173
+
174
+ it "should track modified attributes" do
175
+ pending "attribute tracking needs to be implemented in BigRecord::Base"
176
+
177
+ attributes = { :title => "I Am Legend",
178
+ :author => "Richard Matheson",
179
+ :description => "The most clever and riveting vampire novel since Dracula." }
180
+
181
+ book = Book.new(attributes)
182
+ book.save.should be_true
183
+
184
+ book.attributes = {:description => "One of the Ten All-Time Best Novels of Vampirism."}
185
+
186
+ book.modified_attributes.has_key?(:description).should be_true
187
+ end
188
+
189
+ end
190
+
191
+ describe 'attribute functionality' do
192
+
193
+ it "should return a list of attribute names with .attribute_names" do
194
+ (Book.new.attribute_names & ["attribute:author", "attribute:description", "attribute:links", "attribute:title"]).should == ["attribute:author", "attribute:description", "attribute:links", "attribute:title"]
195
+ end
196
+
197
+ it "should provide hash-like attribute accessors, i.e. [] and []=" do
198
+ Book.new.should respond_to(:[])
199
+ Book.new.should respond_to(:[]=)
200
+ end
201
+
202
+ it "should provide attribute accessing with .read_attribute" do
203
+ book = Book.new( :title => "I Am Legend",
204
+ :author => "Richard Matheson",
205
+ :description => "The most clever and riveting vampire novel since Dracula.")
206
+
207
+ book.save.should be_true
208
+
209
+ book.read_attribute("attribute:title").should == "I Am Legend"
210
+ book.read_attribute("attribute:author").should == "Richard Matheson"
211
+ book.read_attribute("attribute:description").should == "The most clever and riveting vampire novel since Dracula."
212
+ end
213
+
214
+ it "should determine whether an attribute is present with .has_attribute?" do
215
+ book = Book.new
216
+
217
+ ["attribute:author", "attribute:description", "attribute:links", "attribute:title"].each do |attr|
218
+ book.has_attribute?(attr).should be_true
219
+ end
220
+ end
221
+
222
+ it "should determine whether an attribute is present (i.e. set either by the user or db) with .attribute_present?" do
223
+ book = Book.new
224
+
225
+ # Initially they should all be false
226
+ ["attribute:author", "attribute:description", "attribute:title"].each do |attr|
227
+ book.attribute_present?(attr).should be_false
228
+ end
229
+
230
+ book.attributes = { :title => "I Am Legend", :author => "Richard Matheson", :description => "The most clever and riveting vampire novel since Dracula." }
231
+
232
+ ["attribute:author", "attribute:description", "attribute:title"].each do |attr|
233
+ book.attribute_present?(attr).should be_true
234
+ end
235
+ end
236
+
237
+ it '.update_attribute(nil) should raise an exception' do
238
+ lambda {
239
+ Book.new.update_attribute(nil)
240
+ }.should raise_error(ArgumentError)
241
+ end
242
+
243
+ it ".update_attribute should update a single attribute of a record" do
244
+ book = Book.new( :title => "I Am Legend",
245
+ :author => "Richard Matheson",
246
+ :description => "The most clever and riveting vampire novel since Dracula.")
247
+
248
+ book.new_record?.should be_true
249
+ book.id.should be_nil
250
+ book.save.should be_true
251
+
252
+ book.update_attribute(:description, "One of the Ten All-Time Best Novels of Vampirism.")
253
+
254
+ book.description.should == "One of the Ten All-Time Best Novels of Vampirism."
255
+ end
256
+
257
+ it ".update_attribute should return false if the attribute could not be updated" do
258
+ book = Book.new( :title => "I Am Legend",
259
+ :author => "Richard Matheson",
260
+ :description => "The most clever and riveting vampire novel since Dracula.")
261
+
262
+ book.new_record?.should be_true
263
+ book.id.should be_nil
264
+ book.save.should be_true
265
+
266
+ book.should_receive(:save).and_return(false)
267
+ book.update_attribute(:description, "One of the Ten All-Time Best Novels of Vampirism.").should be_false
268
+ end
269
+
270
+ describe '' do
271
+
272
+ before(:each) do
273
+ @book = Book.new( :title => "I Am Legend",
274
+ :author => "Richard Matheson",
275
+ :description => "The most clever and riveting vampire novel since Dracula.")
276
+
277
+ @book.new_record?.should be_true
278
+ @book.id.should be_nil
279
+ @book.save.should be_true
280
+
281
+ @new_attributes = {:title => "A Stir of Echoes", :description => "One of the most important writers of the twentieth century."}
282
+ end
283
+
284
+ it ".update_attributes should update all attributes of a record" do
285
+ @book.update_attributes(@new_attributes).should be_true
286
+
287
+ @new_attributes.each do |key, value|
288
+ @book.send(key).should == value
289
+ end
290
+ end
291
+
292
+ it ".update_attributes should return false if the record couldn't be updated with those attributes" do
293
+ @book.should_receive(:save).and_return(false)
294
+ @book.update_attributes(@new_attributes).should be_false
295
+ end
296
+
297
+ it ".update_attributes! should update all attributes of a record" do
298
+ @book.update_attributes(@new_attributes).should be_true
299
+
300
+ @new_attributes.each do |key, value|
301
+ @book.send(key).should == value
302
+ end
303
+ end
304
+
305
+ it ".update_attributes! should raise an error when the record couldn't be updated" do
306
+ @book.should_receive(:save!).and_raise(BigRecord::RecordNotSaved)
307
+
308
+ lambda { @book.update_attributes!(@new_attributes) }.should raise_error(BigRecord::RecordNotSaved)
309
+ end
310
+
311
+ end
312
+
313
+ end
314
+
315
+ end