gotime-cassandra_object 0.6.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 (54) hide show
  1. data/CHANGELOG +3 -0
  2. data/Gemfile +14 -0
  3. data/Gemfile.lock +42 -0
  4. data/LICENSE +13 -0
  5. data/README.markdown +79 -0
  6. data/Rakefile +74 -0
  7. data/TODO +2 -0
  8. data/VERSION +1 -0
  9. data/gotime-cassandra_object.gemspec +134 -0
  10. data/lib/cassandra_object.rb +13 -0
  11. data/lib/cassandra_object/associations.rb +35 -0
  12. data/lib/cassandra_object/associations/one_to_many.rb +136 -0
  13. data/lib/cassandra_object/associations/one_to_one.rb +77 -0
  14. data/lib/cassandra_object/attributes.rb +93 -0
  15. data/lib/cassandra_object/base.rb +97 -0
  16. data/lib/cassandra_object/callbacks.rb +10 -0
  17. data/lib/cassandra_object/collection.rb +8 -0
  18. data/lib/cassandra_object/cursor.rb +86 -0
  19. data/lib/cassandra_object/dirty.rb +27 -0
  20. data/lib/cassandra_object/identity.rb +61 -0
  21. data/lib/cassandra_object/identity/abstract_key_factory.rb +36 -0
  22. data/lib/cassandra_object/identity/key.rb +20 -0
  23. data/lib/cassandra_object/identity/natural_key_factory.rb +51 -0
  24. data/lib/cassandra_object/identity/uuid_key_factory.rb +37 -0
  25. data/lib/cassandra_object/indexes.rb +129 -0
  26. data/lib/cassandra_object/log_subscriber.rb +17 -0
  27. data/lib/cassandra_object/migrations.rb +72 -0
  28. data/lib/cassandra_object/mocking.rb +15 -0
  29. data/lib/cassandra_object/persistence.rb +195 -0
  30. data/lib/cassandra_object/serialization.rb +6 -0
  31. data/lib/cassandra_object/type_registration.rb +7 -0
  32. data/lib/cassandra_object/types.rb +128 -0
  33. data/lib/cassandra_object/validation.rb +49 -0
  34. data/test/basic_scenarios_test.rb +243 -0
  35. data/test/callbacks_test.rb +19 -0
  36. data/test/config/cassandra.in.sh +53 -0
  37. data/test/config/log4j.properties +38 -0
  38. data/test/config/storage-conf.xml +221 -0
  39. data/test/connection.rb +25 -0
  40. data/test/cursor_test.rb +66 -0
  41. data/test/dirty_test.rb +34 -0
  42. data/test/fixture_models.rb +90 -0
  43. data/test/identity/natural_key_factory_test.rb +94 -0
  44. data/test/index_test.rb +69 -0
  45. data/test/legacy/test_helper.rb +18 -0
  46. data/test/migration_test.rb +21 -0
  47. data/test/one_to_many_associations_test.rb +163 -0
  48. data/test/test_case.rb +28 -0
  49. data/test/test_helper.rb +16 -0
  50. data/test/time_test.rb +32 -0
  51. data/test/types_test.rb +252 -0
  52. data/test/validation_test.rb +25 -0
  53. data/test/z_mock_test.rb +36 -0
  54. metadata +243 -0
@@ -0,0 +1,94 @@
1
+ require 'test_helper'
2
+
3
+ module Identity
4
+ class NaturalKeyFactoryTest < CassandraObjectTestCase
5
+ context "With one attribute" do
6
+ setup do
7
+ @key_factory = CassandraObject::Identity::NaturalKeyFactory.new :attributes => :name
8
+ @james = Person.new("name" => "james")
9
+ end
10
+
11
+ should "have a key whose string and param value is the value of that attribute" do
12
+ @key = @key_factory.next_key(@james)
13
+
14
+ assert_equal "james", @key.to_s
15
+ assert_equal "james", @key.to_param
16
+ end
17
+
18
+ should "parse to a key from a param value" do
19
+ @key = @key_factory.parse("james")
20
+
21
+ assert_equal "james", @key.to_s
22
+ assert_equal "james", @key.to_param
23
+ end
24
+
25
+ should "create a key from a value" do
26
+ @key = @key_factory.create("james")
27
+
28
+ assert_equal "james", @key.to_s
29
+ assert_equal "james", @key.to_param
30
+ end
31
+ end
32
+
33
+ context "With multiple attributes" do
34
+ setup do
35
+ @key_factory = CassandraObject::Identity::NaturalKeyFactory.new :attributes => [:name, :age]
36
+ @james = Person.new("name" => "james", "age" => 23)
37
+ end
38
+
39
+ should "create a key whose string value is the two values, joined with a separator" do
40
+ key = @key_factory.next_key(@james)
41
+
42
+ assert_equal "james-23", key.to_s
43
+ assert_equal "james-23", key.to_param
44
+ end
45
+
46
+ should "parse the key" do
47
+ key = @key_factory.parse("james-23")
48
+
49
+ assert_equal "james-23", key.to_s
50
+ assert_equal "james-23", key.to_param
51
+ end
52
+
53
+ should "create the key" do
54
+ key = @key_factory.create("james-23")
55
+
56
+ assert_equal "james-23", key.to_s
57
+ assert_equal "james-23", key.to_param
58
+ end
59
+ end
60
+
61
+ context "With a custom separator" do
62
+ setup do
63
+ @key_factory = CassandraObject::Identity::NaturalKeyFactory.new :attributes => [:name, :age],
64
+ :separator => "#"
65
+ @james = Person.new("name" => "james", "age" => 23)
66
+ end
67
+
68
+ should "join the attributes with the custom separator" do
69
+ key = @key_factory.next_key(@james)
70
+
71
+ assert_equal "james#23", key.to_s
72
+ end
73
+ end
74
+
75
+ context "Natural keys" do
76
+ setup do
77
+ @james = CassandraObject::Identity::NaturalKeyFactory::NaturalKey.new("james")
78
+ @another_james = CassandraObject::Identity::NaturalKeyFactory::NaturalKey.new("james")
79
+ @joe = CassandraObject::Identity::NaturalKeyFactory::NaturalKey.new("joe")
80
+ end
81
+
82
+ should "be equal, if their values are equal" do
83
+ assert @james == @another_james
84
+ assert @james.eql?(@another_james)
85
+ end
86
+
87
+ should "not be equal if their values are different" do
88
+ assert @james != @joe
89
+ assert !@james.eql?(@joe)
90
+ end
91
+ end
92
+ end
93
+ end
94
+
@@ -0,0 +1,69 @@
1
+ require 'test_helper'
2
+
3
+ class IndexTest < CassandraObjectTestCase
4
+ context "A non-unique index" do
5
+ setup do
6
+ @last_name = ActiveSupport::SecureRandom.hex(5)
7
+ @koz = Customer.create :first_name=>"Michael", :last_name=>@last_name, :date_of_birth=>28.years.ago.to_date
8
+ @wife = Customer.create :first_name=>"Anika", :last_name=>@last_name, :date_of_birth=>30.years.ago.to_date
9
+ end
10
+
11
+ should "Return both values" do
12
+ assert_ordered [@wife.key, @koz.key], Customer.find_all_by_last_name(@last_name).map(&:key)
13
+ end
14
+
15
+ should "return the older when the newer is destroyed" do
16
+ @wife.destroy
17
+ assert_equal [@koz.key], Customer.find_all_by_last_name(@last_name).map(&:key)
18
+ end
19
+
20
+ should "return a single value when the original one is changed" do
21
+ @wife.last_name = "WTF"
22
+ @wife.save
23
+ assert_equal [@koz.key], Customer.find_all_by_last_name(@last_name).map(&:key)
24
+ end
25
+ end
26
+
27
+ context "A corrupt non-unique index" do
28
+ setup do
29
+ @last_name = ActiveSupport::SecureRandom.hex(5)
30
+ @koz = Customer.create :first_name=>"Michael", :last_name=>@last_name, :date_of_birth=>28.years.ago.to_date
31
+ connection.insert("CustomersByLastName", @last_name, {"last_name"=>{SimpleUUID::UUID.new=>"ROFLSKATES"}})
32
+ @wife = Customer.create :first_name=>"Anika", :last_name=>@last_name, :date_of_birth=>30.years.ago.to_date
33
+ end
34
+
35
+ should "Return both values and clean up" do
36
+ assert_ordered [@wife.key, @koz.key], Customer.find_all_by_last_name(@last_name).map(&:key)
37
+ assert_ordered [@wife.key, @koz.key], connection.get("CustomersByLastName", @last_name, "last_name", :reversed=>true).values
38
+ end
39
+
40
+ end
41
+
42
+ context "A unique index" do
43
+ setup do
44
+ @invoice = mock_invoice
45
+ @number = @invoice.number
46
+ end
47
+
48
+ should "return the right record" do
49
+ assert_equal @invoice, Invoice.find_by_number(@number)
50
+ end
51
+
52
+ should "return nil after destroy" do
53
+ @invoice.destroy
54
+ assert_nil Invoice.find_by_number(@number)
55
+ end
56
+ end
57
+
58
+ context " A corrupt unique index" do
59
+ setup do
60
+ connection.insert("InvoicesByNumber", '15' , {"key"=>"HAHAHAHA"})
61
+ end
62
+
63
+ should "return nil on fetch and cleanup" do
64
+ assert_nil Invoice.find_by_number(15)
65
+ assert connection.get("InvoicesByNumber", "15", "number").blank?
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+
3
+ gem 'activesupport', "~> 2.3.5"
4
+
5
+ puts "LEGACY TEST"
6
+
7
+ require 'cassandra_object'
8
+ require 'connection'
9
+
10
+ require 'test/unit'
11
+ require 'active_support/test_case'
12
+ require 'shoulda'
13
+
14
+ require 'fixture_models'
15
+ require 'pp'
16
+
17
+ require 'test_case'
18
+
@@ -0,0 +1,21 @@
1
+ require 'test_helper'
2
+
3
+ class MigrationTest < CassandraObjectTestCase
4
+ test "a new invoice should have the right schema version" do
5
+ i = mock_invoice
6
+ assert_equal 2, i.schema_version
7
+ end
8
+
9
+ test " a new invoice should have an empty gst_number" do
10
+ assert_equal nil, mock_invoice.gst_number
11
+ end
12
+
13
+ test "an old invoice should get fetched and updated" do
14
+ key = Invoice.next_key.to_s
15
+ connection.insert(Invoice.column_family, key, {"schema_version"=>"1", "number"=>"200", "total"=>"150.35"})
16
+
17
+ invoice = Invoice.get(key)
18
+ assert_equal 2, invoice.schema_version
19
+ assert_equal 150.35, invoice.total
20
+ end
21
+ end
@@ -0,0 +1,163 @@
1
+ require 'test_helper'
2
+
3
+ class OneToManyAssociationsTest < CassandraObjectTestCase
4
+
5
+ context "A customer with no invoices added to its invoice association" do
6
+ setup do
7
+ @customer = Customer.create :first_name => "Michael",
8
+ :last_name => "Koziarski",
9
+ :date_of_birth => Date.parse("1980/08/15")
10
+
11
+ assert @customer.valid?, @customer.errors
12
+ end
13
+
14
+ should "return an empty array for the association" do
15
+ assert_equal [], @customer.invoices.to_a
16
+ end
17
+ end
18
+
19
+ context "A customer with an invoice added to its invoice association" do
20
+ setup do
21
+ @customer = Customer.create :first_name => "Michael",
22
+ :last_name => "Koziarski",
23
+ :date_of_birth => Date.parse("1980/08/15")
24
+
25
+ assert @customer.valid?, @customer.errors
26
+
27
+ @invoice = mock_invoice
28
+ assert @invoice.valid?, @invoice.errors
29
+
30
+ @customer.invoices << @invoice
31
+ end
32
+
33
+ should "have set the inverse" do
34
+ assert_equal @customer, @invoice.customer
35
+ end
36
+
37
+ should "have also written to cassandra" do
38
+ assert_equal @invoice, @customer.invoices.to_a.first
39
+ end
40
+
41
+ context "Simple Read-Repair" do
42
+ setup do
43
+ add_junk_key
44
+ assert_ordered ["SomethingStupid", @invoice.key], association_keys_in_cassandra
45
+ end
46
+
47
+ should "tidy up when fetching" do
48
+ assert_equal [@invoice], @customer.invoices.all
49
+ assert_equal [@invoice.key.to_s], association_keys_in_cassandra
50
+ end
51
+ end
52
+
53
+ context "More complicated Read-Repair" do
54
+ setup do
55
+ # Now add a second legit invoice
56
+ @second_invoice = mock_invoice
57
+ @customer.invoices << @second_invoice
58
+
59
+ add_junk_key
60
+
61
+ @third_invoice = mock_invoice
62
+ @customer.invoices << @third_invoice
63
+
64
+ #
65
+
66
+ assert_ordered [@third_invoice.key,"SomethingStupid", @second_invoice.key, @invoice.key],
67
+ association_keys_in_cassandra
68
+ end
69
+
70
+ should "return the last one when passed a limit of one, and not touch the keys" do
71
+ assert_equal [@third_invoice], @customer.invoices.all(:limit=>1)
72
+ assert_ordered [@third_invoice.key,"SomethingStupid", @second_invoice.key, @invoice.key],
73
+ association_keys_in_cassandra
74
+ end
75
+
76
+ should "return them all when passed a limit of 3, and clean up the keys" do
77
+ assert_ordered [@third_invoice, @second_invoice, @invoice], @customer.invoices.all(:limit=>3), false
78
+ assert_ordered [@third_invoice.key, @second_invoice.key, @invoice.key],
79
+ association_keys_in_cassandra
80
+
81
+ end
82
+
83
+ should "return the first invoice when told to start after the second" do
84
+ assert_ordered [@invoice.key], @customer.invoices.all(:limit=>1, :start_after=>index_key_for(@second_invoice)).map(&:key)
85
+ assert_ordered [@third_invoice.key,"SomethingStupid", @second_invoice.key, @invoice.key],
86
+ association_keys_in_cassandra
87
+ end
88
+ end
89
+ end
90
+
91
+ context "A customer with an invoice added to its paid invoices association (which has no explicit reversed option)" do
92
+ setup do
93
+ @customer = Customer.create :first_name => "Michael",
94
+ :last_name => "Koziarski",
95
+ :date_of_birth => Date.parse("1980/08/15")
96
+
97
+ assert @customer.valid?, @customer.errors
98
+
99
+ @invoice = mock_invoice
100
+ assert @invoice.valid?, @invoice.errors
101
+
102
+ @customer.paid_invoices << @invoice
103
+ end
104
+
105
+ should "return invoice from association" do
106
+ assert_equal @invoice, @customer.paid_invoices.to_a.first
107
+ end
108
+ end
109
+
110
+ context "Association proxy create" do
111
+ setup do
112
+ @customer = Customer.create! :first_name => "Michael",
113
+ :last_name => "Koziarski",
114
+ :date_of_birth => Date.parse("1980/08/15")
115
+ @invoice = @customer.invoices.create :number=>50, :total=>25.0
116
+ end
117
+
118
+ should "return the invoice" do
119
+ assert_kind_of Invoice, @invoice
120
+ end
121
+
122
+ should "have set the attributes" do
123
+ assert_equal 50, @invoice.number
124
+ assert_equal 25.0, @invoice.total
125
+ end
126
+
127
+ should "have set the inverse" do
128
+ assert_equal @customer, @invoice.customer
129
+ end
130
+ end
131
+
132
+ context "Association proxy all" do
133
+ setup do
134
+ @customer = Customer.create! :first_name => "Michael",
135
+ :last_name => "Koziarski",
136
+ :date_of_birth => Date.parse("1980/08/15")
137
+ @first = @customer.invoices.create :number => 50, :total => 25.0
138
+ @second = @customer.invoices.create :number => 50, :total => 25.0
139
+ end
140
+
141
+ should "suport overriding :reversed value" do
142
+ assert_ordered [@first.key, @second.key], @customer.invoices.all(:reversed => false).map(&:key)
143
+ end
144
+ end
145
+
146
+ def add_junk_key
147
+ Customer.associations[:invoices].add(@customer, MockRecord.new("SomethingStupid"))
148
+ end
149
+
150
+ def association_keys_in_cassandra
151
+ Customer.connection.get(Customer.associations[:invoices].column_family, @customer.key.to_s, "invoices", :reversed=>true).values
152
+ end
153
+
154
+ def index_key_for(object)
155
+ Customer.connection.get(Customer.associations[:invoices].column_family, @customer.key.to_s, "invoices").each do |(key, value)|
156
+ if value == object.key.to_s
157
+ return key
158
+ end
159
+ end
160
+ raise "Not found"
161
+ end
162
+
163
+ end
data/test/test_case.rb ADDED
@@ -0,0 +1,28 @@
1
+
2
+ class CassandraObjectTestCase < ActiveSupport::TestCase
3
+ def teardown
4
+ CassandraObject::Base.connection.clear_keyspace!
5
+ end
6
+
7
+ def mock_invoice
8
+ Invoice.create :number=>Time.now.to_i*(rand(5)), :total=>Time.now.to_f
9
+ end
10
+
11
+ def connection
12
+ CassandraObject::Base.connection
13
+ end
14
+
15
+ def assert_ordered(expected_object_order, actual_order, to_s_before_comparing = true)
16
+ # don't use map! so we don't go changing user's arguments
17
+ if to_s_before_comparing
18
+ expected_object_order = expected_object_order.map(&:to_s)
19
+ actual_order = actual_order.map(&:to_s)
20
+ end
21
+
22
+ assert_equal Set.new(expected_object_order), Set.new(actual_order), "Collections weren't equal"
23
+ actual_indexes = actual_order.map do |e|
24
+ expected_object_order.index(e)
25
+ end
26
+ assert_equal expected_object_order, actual_order, "Collection was ordered incorrectly: #{actual_indexes.inspect}"
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.setup
5
+
6
+ require 'cassandra_object'
7
+ require 'connection'
8
+
9
+ require 'test/unit'
10
+ require 'active_support/test_case'
11
+ require 'shoulda'
12
+
13
+ require 'fixture_models'
14
+ require 'pp'
15
+
16
+ require 'test_case'
data/test/time_test.rb ADDED
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+
3
+ class TimeTest < CassandraObjectTestCase
4
+
5
+ test "new raises an error" do
6
+ begin
7
+ appt = Appointment.new :start_time => 1
8
+ flunk "Should have failed to save"
9
+ rescue ArgumentError => e
10
+ assert_equal "CassandraObject::TimeType requires a Time", e.message
11
+ end
12
+ end
13
+
14
+ test "the attribute writer raises an error" do
15
+ begin
16
+ appt = Appointment.new
17
+ appt.start_time = 1
18
+ flunk "Should have failed to save"
19
+ rescue ArgumentError => e
20
+ assert_equal "CassandraObject::TimeType requires a Time", e.message
21
+ end
22
+ end
23
+
24
+ test "Time's should be round-trip-able" do
25
+ appt = Appointment.new :start_time => (t = Time.now.utc), :title => "team meeting"
26
+ appt.save!
27
+ appt2 = Appointment.get(appt.key)
28
+
29
+ assert_equal appt.start_time.class, appt2.start_time.class
30
+ assert_equal appt.start_time, appt2.start_time
31
+ end
32
+ end