sessionm-cassandra_object 2.2.6

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 (83) hide show
  1. data/.gitignore +2 -0
  2. data/CHANGELOG +3 -0
  3. data/Gemfile +2 -0
  4. data/LICENSE +13 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.markdown +12 -0
  7. data/Rakefile +15 -0
  8. data/lib/cassandra_object/associations/one_to_many.rb +146 -0
  9. data/lib/cassandra_object/associations/one_to_one.rb +85 -0
  10. data/lib/cassandra_object/associations.rb +50 -0
  11. data/lib/cassandra_object/attributes.rb +97 -0
  12. data/lib/cassandra_object/base.rb +97 -0
  13. data/lib/cassandra_object/batches.rb +31 -0
  14. data/lib/cassandra_object/callbacks.rb +27 -0
  15. data/lib/cassandra_object/collection.rb +8 -0
  16. data/lib/cassandra_object/connection.rb +29 -0
  17. data/lib/cassandra_object/consistency.rb +31 -0
  18. data/lib/cassandra_object/cursor.rb +90 -0
  19. data/lib/cassandra_object/dirty.rb +32 -0
  20. data/lib/cassandra_object/errors.rb +10 -0
  21. data/lib/cassandra_object/finder_methods.rb +72 -0
  22. data/lib/cassandra_object/generators/migration_generator.rb +31 -0
  23. data/lib/cassandra_object/generators/templates/migration.rb.erb +11 -0
  24. data/lib/cassandra_object/identity/abstract_key_factory.rb +36 -0
  25. data/lib/cassandra_object/identity/custom_key_factory.rb +50 -0
  26. data/lib/cassandra_object/identity/hashed_natural_key_factory.rb +10 -0
  27. data/lib/cassandra_object/identity/key.rb +20 -0
  28. data/lib/cassandra_object/identity/natural_key_factory.rb +51 -0
  29. data/lib/cassandra_object/identity/uuid_key_factory.rb +39 -0
  30. data/lib/cassandra_object/identity.rb +52 -0
  31. data/lib/cassandra_object/log_subscriber.rb +37 -0
  32. data/lib/cassandra_object/migrations/migration.rb +15 -0
  33. data/lib/cassandra_object/migrations.rb +66 -0
  34. data/lib/cassandra_object/mocking.rb +15 -0
  35. data/lib/cassandra_object/persistence.rb +138 -0
  36. data/lib/cassandra_object/railtie.rb +11 -0
  37. data/lib/cassandra_object/schema/migration.rb +106 -0
  38. data/lib/cassandra_object/schema/migration_proxy.rb +25 -0
  39. data/lib/cassandra_object/schema/migrator.rb +213 -0
  40. data/lib/cassandra_object/schema.rb +37 -0
  41. data/lib/cassandra_object/serialization.rb +6 -0
  42. data/lib/cassandra_object/tasks/column_family.rb +90 -0
  43. data/lib/cassandra_object/tasks/keyspace.rb +89 -0
  44. data/lib/cassandra_object/tasks/ks.rake +121 -0
  45. data/lib/cassandra_object/timestamps.rb +19 -0
  46. data/lib/cassandra_object/type.rb +19 -0
  47. data/lib/cassandra_object/types/array_type.rb +16 -0
  48. data/lib/cassandra_object/types/boolean_type.rb +23 -0
  49. data/lib/cassandra_object/types/date_type.rb +20 -0
  50. data/lib/cassandra_object/types/float_type.rb +19 -0
  51. data/lib/cassandra_object/types/hash_type.rb +16 -0
  52. data/lib/cassandra_object/types/integer_type.rb +19 -0
  53. data/lib/cassandra_object/types/set_type.rb +22 -0
  54. data/lib/cassandra_object/types/string_type.rb +16 -0
  55. data/lib/cassandra_object/types/time_type.rb +27 -0
  56. data/lib/cassandra_object/types/time_with_zone_type.rb +18 -0
  57. data/lib/cassandra_object/types/utf8_string_type.rb +18 -0
  58. data/lib/cassandra_object/types.rb +11 -0
  59. data/lib/cassandra_object/validations.rb +46 -0
  60. data/lib/cassandra_object.rb +49 -0
  61. data/sessionm-cassandra_object.gemspec +26 -0
  62. data/test/active_model_test.rb +9 -0
  63. data/test/base_test.rb +28 -0
  64. data/test/batches_test.rb +30 -0
  65. data/test/connection_test.rb +28 -0
  66. data/test/consistency_test.rb +20 -0
  67. data/test/finder_methods_test.rb +49 -0
  68. data/test/identity_test.rb +30 -0
  69. data/test/persistence_test.rb +84 -0
  70. data/test/test_helper.rb +31 -0
  71. data/test/timestamps_test.rb +27 -0
  72. data/test/types/array_type_test.rb +15 -0
  73. data/test/types/boolean_type_test.rb +23 -0
  74. data/test/types/date_type_test.rb +4 -0
  75. data/test/types/float_type_test.rb +4 -0
  76. data/test/types/hash_type_test.rb +4 -0
  77. data/test/types/integer_type_test.rb +18 -0
  78. data/test/types/set_type_test.rb +17 -0
  79. data/test/types/string_type_test.rb +4 -0
  80. data/test/types/time_type_test.rb +4 -0
  81. data/test/types/utf8_string_type_test.rb +4 -0
  82. data/test/validations_test.rb +15 -0
  83. metadata +183 -0
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ *.gem
data/CHANGELOG ADDED
@@ -0,0 +1,3 @@
1
+ v 0.5.0.pre
2
+ - First release
3
+ - Rough around the corners, especially outside the core attributes/persistence stuff
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2009 Koziarski Software Ltd
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any
4
+ purpose with or without fee is hereby granted, provided that the above
5
+ copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 [Michael Koziarski]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,12 @@
1
+ # Cassandra Object
2
+
3
+ Cassandra Object provides a API for working with [Cassandra](http://incubator.apache.org/cassandra/) in Rails projects.
4
+
5
+ Installation:
6
+
7
+ gem 'gotime-cassandra_object', require: 'cassandra_object'
8
+
9
+ Example:
10
+
11
+ class Customer < CassandraObject::Base
12
+ end
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+
6
+ # require File.expand_path('../lib/cassandra_object', __FILE__)
7
+
8
+ task default: :test
9
+
10
+ Rake::TestTask.new(:test) do |t|
11
+ t.libs << 'lib'
12
+ t.libs << 'test'
13
+ t.pattern = 'test/**/*_test.rb'
14
+ t.verbose = true
15
+ end
@@ -0,0 +1,146 @@
1
+ module CassandraObject
2
+ module Associations
3
+ class OneToMany
4
+ def initialize(association_name, owner_class, options)
5
+ @association_name = association_name.to_s
6
+ @owner_class = owner_class
7
+ @target_class_name = options[:class_name] || association_name.to_s.singularize.camelize
8
+ @options = options
9
+
10
+ define_methods!
11
+ end
12
+
13
+ def find(owner, options = {})
14
+ reversed = options.has_key?(:reversed) ? options[:reversed] : reversed?
15
+ cursor = CassandraObject::Cursor.new(target_class, column_family, owner.key.to_s, @association_name, :start_after => options[:start_after], :reversed => reversed)
16
+ cursor.find(options[:limit] || 100)
17
+ end
18
+
19
+ def add(owner, record, set_inverse = true)
20
+ key = owner.key
21
+ attributes = {@association_name=>{new_key=>record.key.to_s}}
22
+ ActiveSupport::Notifications.instrument("insert.cassandra_object", :column_family => column_family, :key => key, :attributes => attributes) do
23
+ connection.insert(column_family, key.to_s, attributes, :consistency => @owner_class.thrift_write_consistency)
24
+ end
25
+ if has_inverse? && set_inverse
26
+ inverse.set_inverse(record, owner)
27
+ end
28
+ end
29
+
30
+ def new_key
31
+ SimpleUUID::UUID.new
32
+ end
33
+
34
+ def column_family
35
+ @owner_class.relationships_column_family
36
+ end
37
+
38
+ def connection
39
+ @owner_class.connection
40
+ end
41
+
42
+ def target_class
43
+ @target_class ||= @target_class_name.constantize
44
+ end
45
+
46
+ def new_proxy(owner)
47
+ OneToManyAssociationProxy.new(self, owner)
48
+ end
49
+
50
+ def has_inverse?
51
+ @options[:inverse_of]
52
+ end
53
+
54
+ def inverse
55
+ has_inverse? && target_class.associations[@options[:inverse_of]]
56
+ end
57
+
58
+ def set_inverse(owner, record)
59
+ add(owner, record, false)
60
+ end
61
+
62
+ def reversed?
63
+ @options[:reversed] == true
64
+ end
65
+
66
+ def define_methods!
67
+ @owner_class.class_eval <<-eos
68
+ def #{@association_name}
69
+ @_#{@association_name} ||= self.class.associations[:#{@association_name}].new_proxy(self)
70
+ end
71
+ eos
72
+ end
73
+ end
74
+
75
+ class OneToManyAssociationProxy
76
+ def initialize(association, owner)
77
+ @association = association
78
+ @owner = owner
79
+ end
80
+
81
+ include Enumerable
82
+ def each
83
+ target.each do |i|
84
+ yield i
85
+ end
86
+ end
87
+
88
+ def [](index)
89
+ to_a[index]
90
+ end
91
+
92
+ def <<(record)
93
+ @association.add(@owner, record)
94
+ if loaded?
95
+ @target << record
96
+ end
97
+ end
98
+
99
+ # Get the targets of this association proxy
100
+ #
101
+ # @param [Hash] options the options with which to modify this query
102
+ # @option options [String] :start_after the key after which to start returning results
103
+ # @option options [Boolean] :reversed (false or association default) return the results in reverse order
104
+ # @option options [Integer] :limit the max number of results to return
105
+ # @return [Array<CassandraObject::Base>] an array of objects of type self#target_class
106
+ #
107
+ def all(options = {})
108
+ @association.find(@owner, options)
109
+ end
110
+
111
+ # Create a record of the associated type with
112
+ # the supplied attributes and add it to this
113
+ # association
114
+ #
115
+ # @param [Hash] attributes the attributes with which to create the object
116
+ # @return [CassandraObject::Base] the newly created object
117
+ #
118
+ def create(attributes)
119
+ @association.target_class.create(attributes).tap do |record|
120
+ if record.valid?
121
+ self << record
122
+ end
123
+ end
124
+ end
125
+
126
+ def create!(attributes)
127
+ @association.target_class.create!(attributes).tap do |record|
128
+ self << record
129
+ end
130
+ end
131
+
132
+ def target
133
+ @target ||= begin
134
+ @loaded = true
135
+ @association.find(@owner)
136
+ end
137
+ end
138
+
139
+ alias to_a target
140
+
141
+ def loaded?
142
+ defined?(@loaded) && @loaded
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,85 @@
1
+ module CassandraObject
2
+ module Associations
3
+ class OneToOne
4
+ def initialize(association_name, owner_class, options)
5
+ @association_name = association_name.to_s
6
+ @owner_class = owner_class
7
+ @target_class_name = options[:class_name] || association_name.to_s.camelize
8
+ @options = options
9
+
10
+ define_methods!
11
+ end
12
+
13
+ def define_methods!
14
+ @owner_class.class_eval <<-eos
15
+ def #{@association_name}
16
+ @_#{@association_name} ||= self.class.associations[:#{@association_name}].find(self)
17
+ end
18
+
19
+ def #{@association_name}=(record)
20
+ @_#{@association_name} = record
21
+ self.class.associations[:#{@association_name}].set(self, record)
22
+ end
23
+ eos
24
+ end
25
+
26
+ def clear(owner)
27
+ ActiveSupport::Notifications.instrument("remove.cassandra_object", :column_family => column_family, :key => owner.key, :columns => @association_name) do
28
+ connection.remove(column_family, owner.key.to_s, @association_name)
29
+ end
30
+ end
31
+
32
+ def find(owner)
33
+ if key = connection.get(column_family, owner.key.to_s, @association_name.to_s, :count=>1).values.first
34
+ target_class.get(key)
35
+ else
36
+ nil
37
+ end
38
+ end
39
+
40
+ def set(owner, record, set_inverse = true)
41
+ clear(owner)
42
+ key = owner.key
43
+ attributes = {@association_name=>{new_key=>record.key.to_s}}
44
+ ActiveSupport::Notifications.instrument("insert.cassandra_object", :column_family => column_family, :key => key, :attributes => attributes) do
45
+ connection.insert(column_family, key.to_s, attributes, :consistency => @owner_class.thrift_write_consistency)
46
+ end
47
+ if has_inverse? && set_inverse
48
+ inverse.set_inverse(record, owner)
49
+ end
50
+ end
51
+
52
+ def new_key
53
+ SimpleUUID::UUID.new
54
+ end
55
+
56
+ def set_inverse(owner, record)
57
+ set(owner, record, false)
58
+ end
59
+
60
+ def has_inverse?
61
+ @options[:inverse_of]
62
+ end
63
+
64
+ def inverse
65
+ has_inverse? && target_class.associations[@options[:inverse_of]]
66
+ end
67
+
68
+ def column_family
69
+ @owner_class.relationships_column_family
70
+ end
71
+
72
+ def connection
73
+ @owner_class.connection
74
+ end
75
+
76
+ def target_class
77
+ @target_class ||= @target_class_name.constantize
78
+ end
79
+
80
+ def new_proxy(owner)
81
+ # OneToManyAssociationProxy.new(self, owner)
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,50 @@
1
+ module CassandraObject
2
+ module Associations
3
+ extend ActiveSupport::Concern
4
+ extend ActiveSupport::Autoload
5
+
6
+ # TODO: is this the convention?
7
+ include ActiveRecord::Reflection
8
+ include ActiveRecord::Associations
9
+
10
+ autoload :OneToMany
11
+ autoload :OneToOne
12
+
13
+ included do
14
+ class_inheritable_hash :associations
15
+ end
16
+
17
+ module ClassMethods
18
+ def relationships_column_family=(column_family)
19
+ @relationships_column_family = column_family
20
+ end
21
+
22
+ def relationships_column_family
23
+ @relationships_column_family || "#{name}Relationships"
24
+ end
25
+
26
+ def column_family_configuration
27
+ super << {:Name=>relationships_column_family, :CompareWith=>"UTF8Type", :CompareSubcolumnsWith=>"TimeUUIDType", :ColumnType=>"Super"}
28
+ end
29
+
30
+ def association(association_name, options= {})
31
+ if options[:unique]
32
+ write_inheritable_hash(:associations, {association_name => OneToOne.new(association_name, self, options)})
33
+ else
34
+ write_inheritable_hash(:associations, {association_name => OneToMany.new(association_name, self, options)})
35
+ end
36
+ end
37
+
38
+ def remove(key)
39
+ begin
40
+ ActiveSupport::Notifications.instrument("remove.cassandra_object", column_family: relationships_column_family, key: key) do
41
+ connection.remove(relationships_column_family, key.to_s, consistency: thrift_write_consistency)
42
+ end
43
+ rescue Cassandra::AccessError => e
44
+ raise e unless e.message =~ /Invalid column family/
45
+ end
46
+ super
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,97 @@
1
+ module CassandraObject
2
+ class Attribute
3
+ attr_reader :name, :converter, :expected_type
4
+ def initialize(name, converter, expected_type)
5
+ @name = name.to_s
6
+ @converter = converter
7
+ @expected_type = expected_type
8
+ end
9
+
10
+ def check_value!(value)
11
+ return value if value.nil?
12
+ value.kind_of?(expected_type) ? value : converter.decode(value)
13
+ end
14
+ end
15
+
16
+ module Attributes
17
+ extend ActiveSupport::Concern
18
+ include ActiveModel::AttributeMethods
19
+
20
+ module ClassMethods
21
+ def attribute(name, options)
22
+ if model_attributes.empty?
23
+ self.model_attributes = {}.with_indifferent_access
24
+ end
25
+
26
+ if type_mapping = CassandraObject::Type.get_mapping(options[:type])
27
+ converter = type_mapping.converter
28
+ expected_type = type_mapping.expected_type
29
+ elsif options[:converter]
30
+ converter = options[:converter]
31
+ expected_type = options[:type]
32
+ else
33
+ raise "Unknown type #{options[:type]}"
34
+ end
35
+
36
+ model_attributes[name] = Attribute.new(name, converter, expected_type)
37
+ end
38
+
39
+ def define_attribute_methods
40
+ super(model_attributes.keys)
41
+ end
42
+ end
43
+
44
+ included do
45
+ class_attribute :model_attributes
46
+ self.model_attributes = {}.with_indifferent_access
47
+
48
+ attribute_method_suffix("", "=")
49
+ end
50
+
51
+ def write_attribute(name, value)
52
+ if ma = self.class.model_attributes[name]
53
+ @attributes[name.to_s] = ma.check_value!(value)
54
+ else
55
+ raise NoMethodError, "Unknown attribute #{name.inspect}"
56
+ end
57
+ end
58
+
59
+ def read_attribute(name)
60
+ @attributes[name.to_s]
61
+ end
62
+
63
+ def attributes=(attributes)
64
+ attributes.each do |(name, value)|
65
+ send("#{name}=", value)
66
+ end
67
+ end
68
+
69
+ def method_missing(method_id, *args, &block)
70
+ if !self.class.attribute_methods_generated?
71
+ self.class.define_attribute_methods
72
+ send(method_id, *args, &block)
73
+ else
74
+ super
75
+ end
76
+ end
77
+
78
+ def respond_to?(*args)
79
+ self.class.define_attribute_methods unless self.class.attribute_methods_generated?
80
+ super
81
+ end
82
+
83
+ protected
84
+ def attribute_method?(name)
85
+ !!model_attributes[name.to_sym]
86
+ end
87
+
88
+ private
89
+ def attribute(name)
90
+ read_attribute(name)
91
+ end
92
+
93
+ def attribute=(name, value)
94
+ write_attribute(name, value)
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,97 @@
1
+ require 'set'
2
+
3
+ require 'cassandra_object/log_subscriber'
4
+ require 'cassandra_object/types'
5
+ require 'cassandra_object/errors'
6
+
7
+ module CassandraObject
8
+ class Base
9
+ class << self
10
+ def column_family=(column_family)
11
+ @column_family = column_family
12
+ end
13
+
14
+ def column_family
15
+ @column_family || name.pluralize
16
+ end
17
+
18
+ def base_class
19
+ klass = self
20
+ while klass.superclass != Base
21
+ klass = klass.superclass
22
+ end
23
+ klass
24
+ end
25
+
26
+ delegate :compute_type, :to => 'ActiveRecord::Base'
27
+ end
28
+
29
+ extend ActiveModel::Naming
30
+ include ActiveModel::Conversion
31
+ extend ActiveSupport::DescendantsTracker
32
+
33
+ include Connection
34
+ include Consistency
35
+ include Identity
36
+ include Attributes
37
+ include Persistence
38
+ include Callbacks
39
+ include Dirty
40
+ include Validations
41
+ include Associations
42
+ include Batches
43
+ include FinderMethods
44
+ include Timestamps
45
+
46
+ attr_accessor :key
47
+
48
+ include Serialization
49
+ include Migrations
50
+ include Mocking
51
+
52
+ def initialize(attributes={})
53
+ @key = attributes.delete(:key)
54
+ @new_record = true
55
+ @destroyed = false
56
+ @attributes = {}.with_indifferent_access
57
+ self.attributes = attributes
58
+ @schema_version = self.class.current_schema_version
59
+ end
60
+
61
+ def attributes
62
+ @attributes.merge(:id => id)
63
+ end
64
+
65
+ def to_param
66
+ id.to_s if persisted?
67
+ end
68
+
69
+ def hash
70
+ id.hash
71
+ end
72
+
73
+ def ==(comparison_object)
74
+ comparison_object.equal?(self) ||
75
+ (comparison_object.instance_of?(self.class) &&
76
+ comparison_object.key == key &&
77
+ !comparison_object.new_record?)
78
+ end
79
+
80
+ def eql?(comparison_object)
81
+ self == (comparison_object)
82
+ end
83
+
84
+ # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
85
+ # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
86
+ # (Alias for the protected read_attribute method).
87
+ def [](attr_name)
88
+ read_attribute(attr_name)
89
+ end
90
+
91
+ # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
92
+ # (Alias for the protected write_attribute method).
93
+ def []=(attr_name, value)
94
+ write_attribute(attr_name, value)
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,31 @@
1
+ module CassandraObject
2
+ module Batches
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def find_each
7
+ connection.each(column_family) do |k, v|
8
+ yield instantiate(k, v)
9
+ end
10
+ end
11
+
12
+ def find_in_batches(options = {})
13
+ batch_size = options.delete(:batch_size) || 1000
14
+
15
+ batch = []
16
+
17
+ find_each do |record|
18
+ batch << record
19
+ if batch.size == batch_size
20
+ yield(batch)
21
+ batch = []
22
+ end
23
+ end
24
+
25
+ if batch.size > 0
26
+ yield batch
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,27 @@
1
+ module CassandraObject
2
+ module Callbacks
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ extend ActiveModel::Callbacks
7
+ define_model_callbacks :save, :create, :destroy, :update
8
+ end
9
+
10
+ def destroy #:nodoc:
11
+ _run_destroy_callbacks { super }
12
+ end
13
+
14
+ private
15
+ def create_or_update #:nodoc:
16
+ _run_save_callbacks { super }
17
+ end
18
+
19
+ def create #:nodoc:
20
+ _run_create_callbacks { super }
21
+ end
22
+
23
+ def update(*) #:nodoc:
24
+ _run_update_callbacks { super }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,8 @@
1
+ module CassandraObject
2
+ class Collection < Array
3
+ attr_accessor :last_column_name
4
+ def inspect
5
+ "<CassandraObject::Collection##{object_id} contents: #{super} last_column_name: #{last_column_name.inspect}>"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,29 @@
1
+ module CassandraObject
2
+ module Connection
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :connection_spec
7
+ end
8
+
9
+ module ClassMethods
10
+ DEFAULT_OPTIONS = {
11
+ servers: "127.0.0.1:9160",
12
+ thrift: {}
13
+ }
14
+ def establish_connection(spec)
15
+ spec.reverse_merge!(DEFAULT_OPTIONS)
16
+ @connection = Cassandra.new(spec[:keyspace], spec[:servers], spec[:thrift].symbolize_keys!)
17
+ end
18
+
19
+ def connection=(val)
20
+ @connection = val
21
+ end
22
+
23
+ def connection
24
+ establish_connection(connection_spec) if @connection.nil?
25
+ @connection
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ module CassandraObject
2
+ module Consistency
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ cattr_accessor :consistency_levels
7
+ self.consistency_levels = [:one, :quorum, :all]
8
+
9
+ class_attribute :write_consistency
10
+ class_attribute :read_consistency
11
+ self.write_consistency = :quorum
12
+ self.read_consistency = :quorum
13
+ end
14
+
15
+ module ClassMethods
16
+ THRIFT_LEVELS = {
17
+ :one => Cassandra::Consistency::ONE,
18
+ :quorum => Cassandra::Consistency::QUORUM,
19
+ :all => Cassandra::Consistency::ALL
20
+ }
21
+
22
+ def thrift_read_consistency
23
+ THRIFT_LEVELS[read_consistency] || (raise "Invalid consistency level #{read_consistency}")
24
+ end
25
+
26
+ def thrift_write_consistency
27
+ THRIFT_LEVELS[write_consistency] || (raise "Invalid consistency level #{write_consistency}")
28
+ end
29
+ end
30
+ end
31
+ end