cassandra_object 0.6.0.pre

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 (113) hide show
  1. data/lib/cassandra_object/associations/one_to_many.rb +136 -0
  2. data/lib/cassandra_object/associations/one_to_one.rb +77 -0
  3. data/lib/cassandra_object/associations.rb +35 -0
  4. data/lib/cassandra_object/attributes.rb +93 -0
  5. data/lib/cassandra_object/base.rb +104 -0
  6. data/lib/cassandra_object/callbacks.rb +10 -0
  7. data/lib/cassandra_object/collection.rb +8 -0
  8. data/lib/cassandra_object/cursor.rb +86 -0
  9. data/lib/cassandra_object/dirty.rb +27 -0
  10. data/lib/cassandra_object/identity/abstract_key_factory.rb +36 -0
  11. data/lib/cassandra_object/identity/key.rb +20 -0
  12. data/lib/cassandra_object/identity/natural_key_factory.rb +51 -0
  13. data/lib/cassandra_object/identity/uuid_key_factory.rb +37 -0
  14. data/lib/cassandra_object/identity.rb +61 -0
  15. data/lib/cassandra_object/indexes.rb +129 -0
  16. data/lib/cassandra_object/legacy_callbacks.rb +33 -0
  17. data/lib/cassandra_object/migrations.rb +72 -0
  18. data/lib/cassandra_object/mocking.rb +15 -0
  19. data/lib/cassandra_object/persistence.rb +193 -0
  20. data/lib/cassandra_object/serialization.rb +6 -0
  21. data/lib/cassandra_object/type_registration.rb +7 -0
  22. data/lib/cassandra_object/types.rb +128 -0
  23. data/lib/cassandra_object/validation.rb +58 -0
  24. data/lib/cassandra_object.rb +30 -0
  25. data/vendor/active_support_shims.rb +4 -0
  26. data/vendor/activemodel/CHANGELOG +13 -0
  27. data/vendor/activemodel/CHANGES +12 -0
  28. data/vendor/activemodel/MIT-LICENSE +21 -0
  29. data/vendor/activemodel/README +21 -0
  30. data/vendor/activemodel/Rakefile +52 -0
  31. data/vendor/activemodel/activemodel.gemspec +19 -0
  32. data/vendor/activemodel/examples/validations.rb +29 -0
  33. data/vendor/activemodel/lib/active_model/attribute_methods.rb +291 -0
  34. data/vendor/activemodel/lib/active_model/callbacks.rb +91 -0
  35. data/vendor/activemodel/lib/active_model/conversion.rb +8 -0
  36. data/vendor/activemodel/lib/active_model/deprecated_error_methods.rb +33 -0
  37. data/vendor/activemodel/lib/active_model/dirty.rb +126 -0
  38. data/vendor/activemodel/lib/active_model/errors.rb +162 -0
  39. data/vendor/activemodel/lib/active_model/lint.rb +91 -0
  40. data/vendor/activemodel/lib/active_model/locale/en.yml +27 -0
  41. data/vendor/activemodel/lib/active_model/naming.rb +45 -0
  42. data/vendor/activemodel/lib/active_model/observing.rb +191 -0
  43. data/vendor/activemodel/lib/active_model/railtie.rb +2 -0
  44. data/vendor/activemodel/lib/active_model/serialization.rb +30 -0
  45. data/vendor/activemodel/lib/active_model/serializers/json.rb +96 -0
  46. data/vendor/activemodel/lib/active_model/serializers/xml.rb +204 -0
  47. data/vendor/activemodel/lib/active_model/state_machine/event.rb +62 -0
  48. data/vendor/activemodel/lib/active_model/state_machine/machine.rb +75 -0
  49. data/vendor/activemodel/lib/active_model/state_machine/state.rb +47 -0
  50. data/vendor/activemodel/lib/active_model/state_machine/state_transition.rb +40 -0
  51. data/vendor/activemodel/lib/active_model/state_machine.rb +70 -0
  52. data/vendor/activemodel/lib/active_model/test_case.rb +18 -0
  53. data/vendor/activemodel/lib/active_model/translation.rb +44 -0
  54. data/vendor/activemodel/lib/active_model/validations/acceptance.rb +55 -0
  55. data/vendor/activemodel/lib/active_model/validations/confirmation.rb +47 -0
  56. data/vendor/activemodel/lib/active_model/validations/exclusion.rb +42 -0
  57. data/vendor/activemodel/lib/active_model/validations/format.rb +64 -0
  58. data/vendor/activemodel/lib/active_model/validations/inclusion.rb +42 -0
  59. data/vendor/activemodel/lib/active_model/validations/length.rb +117 -0
  60. data/vendor/activemodel/lib/active_model/validations/numericality.rb +111 -0
  61. data/vendor/activemodel/lib/active_model/validations/presence.rb +42 -0
  62. data/vendor/activemodel/lib/active_model/validations/with.rb +59 -0
  63. data/vendor/activemodel/lib/active_model/validations.rb +120 -0
  64. data/vendor/activemodel/lib/active_model/validator.rb +110 -0
  65. data/vendor/activemodel/lib/active_model/version.rb +9 -0
  66. data/vendor/activemodel/lib/active_model.rb +61 -0
  67. data/vendor/activemodel/test/cases/attribute_methods_test.rb +46 -0
  68. data/vendor/activemodel/test/cases/callbacks_test.rb +70 -0
  69. data/vendor/activemodel/test/cases/helper.rb +23 -0
  70. data/vendor/activemodel/test/cases/lint_test.rb +28 -0
  71. data/vendor/activemodel/test/cases/naming_test.rb +28 -0
  72. data/vendor/activemodel/test/cases/observing_test.rb +133 -0
  73. data/vendor/activemodel/test/cases/serializeration/json_serialization_test.rb +83 -0
  74. data/vendor/activemodel/test/cases/serializeration/xml_serialization_test.rb +110 -0
  75. data/vendor/activemodel/test/cases/state_machine/event_test.rb +49 -0
  76. data/vendor/activemodel/test/cases/state_machine/machine_test.rb +43 -0
  77. data/vendor/activemodel/test/cases/state_machine/state_test.rb +72 -0
  78. data/vendor/activemodel/test/cases/state_machine/state_transition_test.rb +84 -0
  79. data/vendor/activemodel/test/cases/state_machine_test.rb +312 -0
  80. data/vendor/activemodel/test/cases/tests_database.rb +37 -0
  81. data/vendor/activemodel/test/cases/translation_test.rb +45 -0
  82. data/vendor/activemodel/test/cases/validations/acceptance_validation_test.rb +71 -0
  83. data/vendor/activemodel/test/cases/validations/conditional_validation_test.rb +141 -0
  84. data/vendor/activemodel/test/cases/validations/confirmation_validation_test.rb +58 -0
  85. data/vendor/activemodel/test/cases/validations/exclusion_validation_test.rb +47 -0
  86. data/vendor/activemodel/test/cases/validations/format_validation_test.rb +118 -0
  87. data/vendor/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb +175 -0
  88. data/vendor/activemodel/test/cases/validations/i18n_validation_test.rb +527 -0
  89. data/vendor/activemodel/test/cases/validations/inclusion_validation_test.rb +71 -0
  90. data/vendor/activemodel/test/cases/validations/length_validation_test.rb +437 -0
  91. data/vendor/activemodel/test/cases/validations/numericality_validation_test.rb +180 -0
  92. data/vendor/activemodel/test/cases/validations/presence_validation_test.rb +70 -0
  93. data/vendor/activemodel/test/cases/validations/with_validation_test.rb +166 -0
  94. data/vendor/activemodel/test/cases/validations_test.rb +215 -0
  95. data/vendor/activemodel/test/config.rb +3 -0
  96. data/vendor/activemodel/test/fixtures/topics.yml +41 -0
  97. data/vendor/activemodel/test/models/contact.rb +7 -0
  98. data/vendor/activemodel/test/models/custom_reader.rb +17 -0
  99. data/vendor/activemodel/test/models/developer.rb +6 -0
  100. data/vendor/activemodel/test/models/person.rb +9 -0
  101. data/vendor/activemodel/test/models/reply.rb +34 -0
  102. data/vendor/activemodel/test/models/topic.rb +9 -0
  103. data/vendor/activemodel/test/models/track_back.rb +4 -0
  104. data/vendor/activemodel/test/schema.rb +14 -0
  105. data/vendor/activesupport/lib/active_support/autoload.rb +48 -0
  106. data/vendor/activesupport/lib/active_support/concern.rb +25 -0
  107. data/vendor/activesupport/lib/active_support/core_ext/array/wrap.rb +20 -0
  108. data/vendor/activesupport/lib/active_support/core_ext/object/blank.rb +58 -0
  109. data/vendor/activesupport/lib/active_support/core_ext/object/tap.rb +6 -0
  110. data/vendor/activesupport/lib/active_support/dependency_module.rb +17 -0
  111. data/vendor/activesupport/lib/active_support/i18n.rb +2 -0
  112. data/vendor/activesupport/lib/active_support/locale/en.yml +33 -0
  113. metadata +230 -0
@@ -0,0 +1,37 @@
1
+ module CassandraObject
2
+ module Identity
3
+ # Key factories need to support 3 operations
4
+ class UUIDKeyFactory < AbstractKeyFactory
5
+ class UUID < SimpleUUID::UUID
6
+ include Key
7
+
8
+ def to_param
9
+ to_guid
10
+ end
11
+
12
+ def to_s
13
+ # FIXME - this should probably write the raw bytes
14
+ # but it's very hard to debug without this for now.
15
+ to_guid
16
+ end
17
+ end
18
+
19
+ # Next key takes an object and returns the key object it should use.
20
+ # object will be ignored with synthetic keys but could be useful with natural ones
21
+ def next_key(object)
22
+ UUID.new
23
+ end
24
+
25
+ # Parse should create a new key object from the 'to_param' format
26
+ def parse(string)
27
+ UUID.new(string)
28
+ end
29
+
30
+ # create should create a new key object from the cassandra format.
31
+ def create(string)
32
+ UUID.new(string)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
@@ -0,0 +1,61 @@
1
+ require 'cassandra_object/identity/abstract_key_factory'
2
+ require 'cassandra_object/identity/key'
3
+ require 'cassandra_object/identity/uuid_key_factory'
4
+ require 'cassandra_object/identity/natural_key_factory'
5
+
6
+ module CassandraObject
7
+ # Some docs will be needed here but the gist of this is simple. Instead of returning a string, Base#key now returns a key object.
8
+ # There are corresponding key factories which generate them
9
+ module Identity
10
+ extend ActiveSupport::Concern
11
+ module ClassMethods
12
+ # Indicate what kind of key the model will have: uuid or natural
13
+ #
14
+ # @param [:uuid, :natural] the type of key
15
+ # @param the options you want to pass along to the key factory (like :attributes => :name, for a natural key).
16
+ #
17
+ def key(name_or_factory = :uuid, *options)
18
+ @key_factory = case name_or_factory
19
+ when :uuid
20
+ UUIDKeyFactory.new
21
+ when :natural
22
+ NaturalKeyFactory.new(*options)
23
+ else
24
+ name_or_factory
25
+ end
26
+ end
27
+
28
+ def next_key(object = nil)
29
+ returning(@key_factory.next_key(object)) do |key|
30
+ raise "Keys may not be nil" if key.nil?
31
+ end
32
+ end
33
+
34
+ def parse_key(string)
35
+ @key_factory.parse(string)
36
+ end
37
+ end
38
+
39
+ module InstanceMethods
40
+
41
+ def ==(comparison_object)
42
+ comparison_object.equal?(self) ||
43
+ (comparison_object.instance_of?(self.class) &&
44
+ comparison_object.key == key &&
45
+ !comparison_object.new_record?)
46
+ end
47
+
48
+ def eql?(comparison_object)
49
+ self == (comparison_object)
50
+ end
51
+
52
+ def hash
53
+ key.to_s.hash
54
+ end
55
+
56
+ def to_param
57
+ key.to_param
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,129 @@
1
+ module CassandraObject
2
+ module Indexes
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_inheritable_accessor :indexes
7
+ end
8
+
9
+ class UniqueIndex
10
+ def initialize(attribute_name, model_class, options)
11
+ @attribute_name = attribute_name
12
+ @model_class = model_class
13
+ end
14
+
15
+ def find(attribute_value)
16
+ # first find the key value
17
+ key = @model_class.connection.get(column_family, attribute_value.to_s, 'key')
18
+ # then pass to get
19
+ if key
20
+ @model_class.get(key.to_s)
21
+ else
22
+ @model_class.connection.remove(column_family, attribute_value.to_s)
23
+ nil
24
+ end
25
+ end
26
+
27
+ def write(record)
28
+ @model_class.connection.insert(column_family, record.send(@attribute_name).to_s, {'key'=>record.key.to_s})
29
+ end
30
+
31
+ def remove(record)
32
+ @model_class.connection.remove(column_family, record.send(@attribute_name).to_s)
33
+ end
34
+
35
+ def column_family
36
+ @model_class.column_family + "By" + @attribute_name.to_s.camelize
37
+ end
38
+
39
+ def column_family_configuration
40
+ {:Name=>column_family, :CompareWith=>"UTF8Type"}
41
+ end
42
+ end
43
+
44
+ class Index
45
+ def initialize(attribute_name, model_class, options)
46
+ @attribute_name = attribute_name
47
+ @model_class = model_class
48
+ @reversed = options[:reversed]
49
+ end
50
+
51
+ def find(attribute_value, options = {})
52
+ cursor = CassandraObject::Cursor.new(@model_class, column_family, attribute_value.to_s, @attribute_name.to_s, :start_after=>options[:start_after], :reversed=>@reversed)
53
+ cursor.validator do |object|
54
+ object.send(@attribute_name) == attribute_value
55
+ end
56
+ cursor.find(options[:limit] || 100)
57
+ end
58
+
59
+ def write(record)
60
+ @model_class.connection.insert(column_family, record.send(@attribute_name).to_s, {@attribute_name.to_s=>{new_key=>record.key.to_s}})
61
+ end
62
+
63
+ def remove(record)
64
+ end
65
+
66
+ def column_family
67
+ @model_class.column_family + "By" + @attribute_name.to_s.camelize
68
+ end
69
+
70
+ def new_key
71
+ SimpleUUID::UUID.new
72
+ end
73
+
74
+ def column_family_configuration
75
+ {:Name=>column_family, :CompareWith=>"UTF8Type", :ColumnType=>"Super", :CompareSubcolumnsWith=>"TimeUUIDType"}
76
+ end
77
+
78
+ end
79
+
80
+ module ClassMethods
81
+ def column_family_configuration
82
+ if indexes
83
+ super + indexes.values.map(&:column_family_configuration)
84
+ else
85
+ super
86
+ end
87
+ end
88
+
89
+ def index(attribute_name, options = {})
90
+ self.indexes ||= {}.with_indifferent_access
91
+ if options.delete(:unique)
92
+ self.indexes[attribute_name] = UniqueIndex.new(attribute_name, self, options)
93
+ class_eval <<-eom
94
+ def self.find_by_#{attribute_name}(value)
95
+ indexes[:#{attribute_name}].find(value)
96
+ end
97
+
98
+ after_save do |record|
99
+ self.indexes[:#{attribute_name}].write(record)
100
+ true
101
+ end
102
+
103
+ after_destroy do |record|
104
+ record.class.indexes[:#{attribute_name}].remove(record)
105
+ true
106
+ end
107
+ eom
108
+ else
109
+ self.indexes[attribute_name] = Index.new(attribute_name, self, options)
110
+ class_eval <<-eom
111
+ def self.find_all_by_#{attribute_name}(value, options = {})
112
+ self.indexes[:#{attribute_name}].find(value, options)
113
+ end
114
+
115
+ after_save do |record|
116
+ record.class.indexes[:#{attribute_name}].write(record)
117
+ true
118
+ end
119
+
120
+ after_destroy do |record|
121
+ record.class.indexes[:#{attribute_name}].remove(record)
122
+ true
123
+ end
124
+ eom
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,33 @@
1
+ module CassandraObject
2
+ module Callbacks
3
+ extend ActiveSupport::Concern
4
+ include ActiveSupport::Callbacks
5
+
6
+ included do
7
+ define_model_callbacks :save, :create, :destroy, :update
8
+ end
9
+
10
+ module ClassMethods
11
+ def define_model_callbacks(*callbacks)
12
+ callbacks.each do |callback|
13
+ define_callbacks "before_#{callback}"
14
+ define_callbacks "after_#{callback}"
15
+ end
16
+ end
17
+ end
18
+
19
+ module InstanceMethods
20
+ def run_callbacks(callback)
21
+ if block_given?
22
+ unless false == super("before_#{callback}")
23
+ yield.tap do
24
+ super("after_#{callback}")
25
+ end
26
+ end
27
+ else
28
+ super
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,72 @@
1
+ module CassandraObject
2
+ module Migrations
3
+ extend ActiveSupport::Concern
4
+ included do
5
+ class_inheritable_array :migrations
6
+ class_inheritable_accessor :current_schema_version
7
+ self.current_schema_version = 0
8
+ end
9
+
10
+ class Migration
11
+ attr_reader :version
12
+ def initialize(version, block)
13
+ @version = version
14
+ @block = block
15
+ end
16
+
17
+ def run(attrs)
18
+ @block.call(attrs)
19
+ end
20
+ end
21
+
22
+ class MigrationNotFoundError < StandardError
23
+ def initialize(record_version, migrations)
24
+ super("Cannot migrate a record from #{record_version.inspect}. Migrations exist for #{migrations.map(&:version)}")
25
+ end
26
+ end
27
+
28
+ module InstanceMethods
29
+ def schema_version
30
+ Integer(@schema_version || self.class.current_schema_version)
31
+ end
32
+ end
33
+
34
+ module ClassMethods
35
+ def migrate(version, &blk)
36
+ write_inheritable_array(:migrations, [Migration.new(version, blk)])
37
+
38
+ if version > self.current_schema_version
39
+ self.current_schema_version = version
40
+ end
41
+ end
42
+
43
+ def instantiate(key, attributes)
44
+ version = attributes.delete('schema_version')
45
+ original_attributes = attributes.dup
46
+ if version == current_schema_version
47
+ return super(key, attributes)
48
+ end
49
+
50
+ versions_to_migrate = ((version.to_i + 1)..current_schema_version)
51
+
52
+ migrations_to_run = versions_to_migrate.map do |v|
53
+ migrations.find {|m| m.version == v}
54
+ end
55
+
56
+ if migrations_to_run.any?(&:nil?)
57
+ raise MigrationNotFoundError.new(version, migrations)
58
+ end
59
+
60
+ migrations_to_run.inject(attributes) do |attrs, migration|
61
+ migration.run(attrs)
62
+ @schema_version = migration.version.to_s
63
+ attrs
64
+ end
65
+
66
+ returning super(key, attributes) do |record|
67
+ record.attributes_changed!(original_attributes.diff(attributes).keys)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,15 @@
1
+ require 'cassandra/mock'
2
+ module CassandraObject
3
+ module Mocking
4
+ extend ActiveSupport::Concern
5
+ module ClassMethods
6
+ def use_mock!(really=true)
7
+ if really
8
+ self.connection_class = Cassandra::Mock
9
+ else
10
+ self.connection_class = Cassandra
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,193 @@
1
+ module CassandraObject
2
+ module Persistence
3
+ extend ActiveSupport::Concern
4
+ included do
5
+ class_inheritable_writer :write_consistency
6
+ class_inheritable_writer :read_consistency
7
+ end
8
+
9
+ VALID_READ_CONSISTENCY_LEVELS = [:one, :quorum, :all]
10
+ VALID_WRITE_CONSISTENCY_LEVELS = VALID_READ_CONSISTENCY_LEVELS + [:zero]
11
+
12
+ module ClassMethods
13
+ def consistency_levels(levels)
14
+ if levels.has_key?(:write)
15
+ unless valid_write_consistency_level?(levels[:write])
16
+ raise ArgumentError, "Invalid write consistency level. Valid levels are: #{VALID_WRITE_CONSISTENCY_LEVELS.inspect}. You gave me #{levels[:write].inspect}"
17
+ end
18
+ self.write_consistency = levels[:write]
19
+ end
20
+
21
+ if levels.has_key?(:read)
22
+ unless valid_read_consistency_level?(levels[:read])
23
+ raise ArgumentError, "Invalid read consistency level. Valid levels are #{VALID_READ_CONSISTENCY_LEVELS.inspect}. You gave me #{levels[:write].inspect}"
24
+ end
25
+ self.read_consistency = levels[:read]
26
+ end
27
+ end
28
+
29
+ def write_consistency
30
+ read_inheritable_attribute(:write_consistency) || :quorum
31
+ end
32
+
33
+ def read_consistency
34
+ read_inheritable_attribute(:read_consistency) || :quorum
35
+ end
36
+
37
+ def get(key, options = {})
38
+ multi_get([key], options).values.first
39
+ end
40
+
41
+ def multi_get(keys, options = {})
42
+ options = {:consistency => self.read_consistency, :limit => 100}.merge(options)
43
+ unless valid_read_consistency_level?(options[:consistency])
44
+ raise ArgumentError, "Invalid read consistency level: '#{options[:consistency]}'. Valid options are [:quorum, :one]"
45
+ end
46
+
47
+ attribute_results = connection.multi_get(column_family, keys.map(&:to_s), :count=>options[:limit], :consistency=>consistency_for_thrift(options[:consistency]))
48
+
49
+ attribute_results.inject(ActiveSupport::OrderedHash.new) do |memo, (key, attributes)|
50
+ if attributes.empty?
51
+ memo[key] = nil
52
+ else
53
+ memo[parse_key(key)] = instantiate(key, attributes)
54
+ end
55
+ memo
56
+ end
57
+ end
58
+
59
+ def remove(key)
60
+ connection.remove(column_family, key.to_s, :consistency => write_consistency_for_thrift)
61
+ end
62
+
63
+ def all(keyrange = ''..'', options = {})
64
+ results = connection.get_range(column_family, :start => keyrange.first, :finish => keyrange.last, :count=>(options[:limit] || 100))
65
+ keys = results.map(&:key)
66
+ keys.map {|key| get(key) }
67
+ end
68
+
69
+ def first(keyrange = ''..'', options = {})
70
+ all(keyrange, options.merge(:limit=>1)).first
71
+ end
72
+
73
+ def create(attributes)
74
+ returning new(attributes) do |object|
75
+ object.save
76
+ end
77
+ end
78
+
79
+ def write(key, attributes, schema_version)
80
+ returning(key) do |key|
81
+ connection.insert(column_family, key.to_s, encode_columns_hash(attributes, schema_version), :consistency => write_consistency_for_thrift)
82
+ end
83
+ end
84
+
85
+ def instantiate(key, attributes)
86
+ # remove any attributes we don't know about. we would do this earlier, but we want to make such
87
+ # attributes available to migrations
88
+ attributes.delete_if{|k,_| !model_attributes.keys.include?(k)}
89
+ returning allocate do |object|
90
+ object.instance_variable_set("@schema_version", attributes.delete('schema_version'))
91
+ object.instance_variable_set("@key", parse_key(key))
92
+ object.instance_variable_set("@attributes", decode_columns_hash(attributes).with_indifferent_access)
93
+ end
94
+ end
95
+
96
+ def encode_columns_hash(attributes, schema_version)
97
+ attributes.inject(Hash.new) do |memo, (column_name, value)|
98
+ memo[column_name.to_s] = model_attributes[column_name].converter.encode(value)
99
+ memo
100
+ end.merge({"schema_version" => schema_version.to_s})
101
+ end
102
+
103
+ def decode_columns_hash(attributes)
104
+ attributes.inject(Hash.new) do |memo, (column_name, value)|
105
+ memo[column_name.to_s] = model_attributes[column_name].converter.decode(value)
106
+ memo
107
+ end
108
+ end
109
+
110
+ def column_family_configuration
111
+ [{:Name=>column_family, :CompareWith=>"UTF8Type"}]
112
+ end
113
+
114
+ protected
115
+ def valid_read_consistency_level?(level)
116
+ !!VALID_READ_CONSISTENCY_LEVELS.include?(level)
117
+ end
118
+
119
+ def valid_write_consistency_level?(level)
120
+ !!VALID_WRITE_CONSISTENCY_LEVELS.include?(level)
121
+ end
122
+
123
+ def write_consistency_for_thrift
124
+ consistency_for_thrift(write_consistency)
125
+ end
126
+
127
+ def read_consistency_for_thrift
128
+ consistency_for_thrift(read_consistency)
129
+ end
130
+
131
+ def consistency_for_thrift(consistency)
132
+ {
133
+ :zero => Cassandra::Consistency::ZERO,
134
+ :one => Cassandra::Consistency::ONE,
135
+ :quorum => Cassandra::Consistency::QUORUM,
136
+ :all => Cassandra::Consistency::ALL
137
+ }[consistency]
138
+ end
139
+ end
140
+
141
+ module InstanceMethods
142
+ def save
143
+ run_callbacks :save do
144
+ create_or_update
145
+ end
146
+ end
147
+
148
+ def create_or_update
149
+ if new_record?
150
+ create
151
+ else
152
+ update
153
+ end
154
+ true
155
+ end
156
+
157
+ def create
158
+ run_callbacks :create do
159
+ @key ||= self.class.next_key(self)
160
+ _write
161
+ @new_record = false
162
+ true
163
+ end
164
+ end
165
+
166
+ def update
167
+ run_callbacks :update do
168
+ _write
169
+ end
170
+ end
171
+
172
+ def _write
173
+ changed_attributes = changed.inject({}) { |h, n| h[n] = read_attribute(n); h }
174
+ self.class.write(key, changed_attributes, schema_version)
175
+ end
176
+
177
+ def new_record?
178
+ @new_record || false
179
+ end
180
+
181
+ def destroy
182
+ run_callbacks :destroy do
183
+ self.class.remove(key)
184
+ end
185
+ end
186
+
187
+ def reload
188
+ self.class.get(self.key)
189
+ end
190
+
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,6 @@
1
+ module CassandraObject
2
+ module Serialization
3
+ extend ActiveSupport::Concern
4
+ include ActiveModel::Serializers::JSON
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ CassandraObject::Base.register_attribute_type(:integer, Integer, CassandraObject::IntegerType)
2
+ CassandraObject::Base.register_attribute_type(:float, Float, CassandraObject::FloatType)
3
+ CassandraObject::Base.register_attribute_type(:date, Date, CassandraObject::DateType)
4
+ CassandraObject::Base.register_attribute_type(:time, Time, CassandraObject::TimeType)
5
+ CassandraObject::Base.register_attribute_type(:time_with_zone, ActiveSupport::TimeWithZone, CassandraObject::TimeWithZoneType)
6
+ CassandraObject::Base.register_attribute_type(:string, String, CassandraObject::StringType)
7
+ CassandraObject::Base.register_attribute_type(:hash, Hash, CassandraObject::HashType)
@@ -0,0 +1,128 @@
1
+ module CassandraObject
2
+ module IntegerType
3
+ REGEX = /\A[-+]?\d+\Z/
4
+ def encode(int)
5
+ return '' if int.nil?
6
+ raise ArgumentError.new("#{self} requires an Integer. You passed #{int.inspect}") unless int.kind_of?(Integer)
7
+ int.to_s
8
+ end
9
+ module_function :encode
10
+
11
+ def decode(str)
12
+ return nil if str.empty?
13
+ raise ArgumentError.new("#{str} isn't a String that looks like a Integer") unless str.kind_of?(String) && str.match(REGEX)
14
+ str.to_i
15
+ end
16
+ module_function :decode
17
+ end
18
+
19
+ module FloatType
20
+ REGEX = /\A[-+]?\d+(\.\d+)\Z/
21
+ def encode(float)
22
+ return '' if float.nil?
23
+ raise ArgumentError.new("#{self} requires a Float") unless float.kind_of?(Float)
24
+ float.to_s
25
+ end
26
+ module_function :encode
27
+
28
+ def decode(str)
29
+ return nil if str == ''
30
+ raise ArgumentError.new("#{str} isn't a String that looks like a Float") unless str.kind_of?(String) && str.match(REGEX)
31
+ str.to_f
32
+ end
33
+ module_function :decode
34
+ end
35
+
36
+ module DateType
37
+ FORMAT = '%Y-%m-%d'
38
+ REGEX = /\A\d{4}-\d{2}-\d{2}\Z/
39
+ def encode(date)
40
+ raise ArgumentError.new("#{self} requires a Date") unless date.kind_of?(Date)
41
+ date.strftime(FORMAT)
42
+ end
43
+ module_function :encode
44
+
45
+ def decode(str)
46
+ raise ArgumentError.new("#{str} isn't a String that looks like a Date") unless str.kind_of?(String) && str.match(REGEX)
47
+ Date.strptime(str, FORMAT)
48
+ end
49
+ module_function :decode
50
+ end
51
+
52
+ module TimeType
53
+ # lifted from the implementation of Time.xmlschema and simplified
54
+ REGEX = /\A\s*
55
+ (-?\d+)-(\d\d)-(\d\d)
56
+ T
57
+ (\d\d):(\d\d):(\d\d)
58
+ (\.\d*)?
59
+ (Z|[+-]\d\d:\d\d)?
60
+ \s*\z/ix
61
+
62
+ def encode(time)
63
+ raise ArgumentError.new("#{self} requires a Time") unless time.kind_of?(Time)
64
+ time.xmlschema(6)
65
+ end
66
+ module_function :encode
67
+
68
+ def decode(str)
69
+ raise ArgumentError.new("#{str} isn't a String that looks like a Time") unless str.kind_of?(String) && str.match(REGEX)
70
+ Time.xmlschema(str)
71
+ end
72
+ module_function :decode
73
+ end
74
+
75
+ module TimeWithZoneType
76
+ def encode(time)
77
+ TimeType.encode(time.utc)
78
+ end
79
+ module_function :encode
80
+
81
+ def decode(str)
82
+ TimeType.decode(str).in_time_zone
83
+ end
84
+ module_function :decode
85
+ end
86
+
87
+ module StringType
88
+ def encode(str)
89
+ raise ArgumentError.new("#{self} requires a String") unless str.kind_of?(String)
90
+ str
91
+ end
92
+ module_function :encode
93
+
94
+ def decode(str)
95
+ str
96
+ end
97
+ module_function :decode
98
+ end
99
+
100
+ module HashType
101
+ def encode(hash)
102
+ raise ArgumentError.new("#{self} requires a Hash") unless hash.kind_of?(Hash)
103
+ ActiveSupport::JSON.encode(hash)
104
+ end
105
+ module_function :encode
106
+
107
+ def decode(str)
108
+ ActiveSupport::JSON.decode(str)
109
+ end
110
+ module_function :decode
111
+ end
112
+
113
+ module BooleanType
114
+ ALLOWED = [true, false, nil]
115
+ def encode(bool)
116
+ unless ALLOWED.any?{ |a| bool == a }
117
+ raise ArgumentError.new("#{self} requires a Boolean or nil")
118
+ end
119
+ bool ? '1' : '0'
120
+ end
121
+ module_function :encode
122
+
123
+ def decode(bool)
124
+ bool == '1'
125
+ end
126
+ module_function :decode
127
+ end
128
+ end