extendi-cassandra_object 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.travis.yml +23 -0
- data/CHANGELOG +0 -0
- data/Gemfile +17 -0
- data/LICENSE +13 -0
- data/MIT-LICENSE +20 -0
- data/README.md +177 -0
- data/Rakefile +12 -0
- data/extendi-cassandra_object.gemspec +26 -0
- data/lib/cassandra_object.rb +73 -0
- data/lib/cassandra_object/adapters/abstract_adapter.rb +61 -0
- data/lib/cassandra_object/adapters/cassandra_adapter.rb +269 -0
- data/lib/cassandra_object/adapters/cassandra_schemaless_adapter.rb +306 -0
- data/lib/cassandra_object/attribute_methods.rb +96 -0
- data/lib/cassandra_object/attribute_methods/definition.rb +22 -0
- data/lib/cassandra_object/attribute_methods/dirty.rb +36 -0
- data/lib/cassandra_object/attribute_methods/primary_key.rb +25 -0
- data/lib/cassandra_object/attribute_methods/typecasting.rb +59 -0
- data/lib/cassandra_object/base.rb +33 -0
- data/lib/cassandra_object/base_schema.rb +11 -0
- data/lib/cassandra_object/base_schemaless.rb +11 -0
- data/lib/cassandra_object/base_schemaless_dynamic.rb +11 -0
- data/lib/cassandra_object/belongs_to.rb +63 -0
- data/lib/cassandra_object/belongs_to/association.rb +49 -0
- data/lib/cassandra_object/belongs_to/builder.rb +40 -0
- data/lib/cassandra_object/belongs_to/reflection.rb +30 -0
- data/lib/cassandra_object/callbacks.rb +29 -0
- data/lib/cassandra_object/core.rb +63 -0
- data/lib/cassandra_object/errors.rb +10 -0
- data/lib/cassandra_object/identity.rb +26 -0
- data/lib/cassandra_object/inspect.rb +25 -0
- data/lib/cassandra_object/log_subscriber.rb +44 -0
- data/lib/cassandra_object/model.rb +60 -0
- data/lib/cassandra_object/persistence.rb +203 -0
- data/lib/cassandra_object/railtie.rb +33 -0
- data/lib/cassandra_object/railties/controller_runtime.rb +45 -0
- data/lib/cassandra_object/schema.rb +83 -0
- data/lib/cassandra_object/schemaless.rb +83 -0
- data/lib/cassandra_object/scope.rb +86 -0
- data/lib/cassandra_object/scope/finder_methods.rb +54 -0
- data/lib/cassandra_object/scope/query_methods.rb +69 -0
- data/lib/cassandra_object/scoping.rb +27 -0
- data/lib/cassandra_object/serialization.rb +6 -0
- data/lib/cassandra_object/tasks/ks.rake +54 -0
- data/lib/cassandra_object/timestamps.rb +19 -0
- data/lib/cassandra_object/type.rb +16 -0
- data/lib/cassandra_object/types.rb +8 -0
- data/lib/cassandra_object/types/array_type.rb +16 -0
- data/lib/cassandra_object/types/base_type.rb +26 -0
- data/lib/cassandra_object/types/boolean_type.rb +20 -0
- data/lib/cassandra_object/types/date_type.rb +22 -0
- data/lib/cassandra_object/types/float_type.rb +16 -0
- data/lib/cassandra_object/types/integer_type.rb +20 -0
- data/lib/cassandra_object/types/json_type.rb +13 -0
- data/lib/cassandra_object/types/string_type.rb +19 -0
- data/lib/cassandra_object/types/time_type.rb +16 -0
- data/lib/cassandra_object/types/type_helper.rb +39 -0
- data/lib/cassandra_object/validations.rb +44 -0
- data/test/support/cassandra.rb +63 -0
- data/test/support/issue.rb +12 -0
- data/test/support/issue_dynamic.rb +12 -0
- data/test/support/issue_schema.rb +17 -0
- data/test/support/issue_schema_child.rb +17 -0
- data/test/support/issue_schema_father.rb +13 -0
- data/test/test_helper.rb +41 -0
- data/test/unit/active_model_test.rb +18 -0
- data/test/unit/adapters/adapter_test.rb +6 -0
- data/test/unit/attribute_methods/definition_test.rb +13 -0
- data/test/unit/attribute_methods/dirty_test.rb +72 -0
- data/test/unit/attribute_methods/primary_key_test.rb +26 -0
- data/test/unit/attribute_methods/typecasting_test.rb +119 -0
- data/test/unit/attribute_methods_test.rb +51 -0
- data/test/unit/base_test.rb +20 -0
- data/test/unit/belongs_to/reflection_test.rb +12 -0
- data/test/unit/belongs_to_test.rb +63 -0
- data/test/unit/callbacks_test.rb +46 -0
- data/test/unit/connection_test.rb +6 -0
- data/test/unit/connections/connections_test.rb +55 -0
- data/test/unit/core_test.rb +55 -0
- data/test/unit/identity_test.rb +26 -0
- data/test/unit/inspect_test.rb +26 -0
- data/test/unit/log_subscriber_test.rb +25 -0
- data/test/unit/persistence_schema_test.rb +156 -0
- data/test/unit/persistence_test.rb +266 -0
- data/test/unit/railties/controller_runtime_test.rb +48 -0
- data/test/unit/schema/tasks_test.rb +32 -0
- data/test/unit/schema_test.rb +115 -0
- data/test/unit/schemaless_test.rb +100 -0
- data/test/unit/scope/finder_methods_test.rb +117 -0
- data/test/unit/scope/query_methods_test.rb +32 -0
- data/test/unit/scoping_test.rb +7 -0
- data/test/unit/timestamps_test.rb +27 -0
- data/test/unit/types/array_type_test.rb +17 -0
- data/test/unit/types/base_type_test.rb +19 -0
- data/test/unit/types/boolean_type_test.rb +24 -0
- data/test/unit/types/date_type_test.rb +15 -0
- data/test/unit/types/float_type_test.rb +17 -0
- data/test/unit/types/integer_type_test.rb +19 -0
- data/test/unit/types/json_type_test.rb +23 -0
- data/test/unit/types/string_type_test.rb +25 -0
- data/test/unit/types/time_type_test.rb +14 -0
- data/test/unit/validations_test.rb +27 -0
- metadata +202 -0
@@ -0,0 +1,96 @@
|
|
1
|
+
module CassandraObject
|
2
|
+
module AttributeMethods
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
include ActiveModel::AttributeMethods
|
5
|
+
|
6
|
+
included do
|
7
|
+
if ActiveModel::VERSION::STRING < '3.2'
|
8
|
+
attribute_method_suffix("", "=")
|
9
|
+
else
|
10
|
+
attribute_method_suffix("=")
|
11
|
+
end
|
12
|
+
|
13
|
+
# (Alias for the protected read_attribute method).
|
14
|
+
def [](attr_name)
|
15
|
+
read_attribute(attr_name)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
|
19
|
+
# (Alias for the protected write_attribute method).
|
20
|
+
def []=(attr_name, value)
|
21
|
+
write_attribute(attr_name, value)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module ClassMethods
|
26
|
+
def inherited(child_class)
|
27
|
+
child_class.define_attribute_methods
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
def define_attribute_methods
|
32
|
+
return if attribute_methods_generated?
|
33
|
+
super(attribute_definitions.keys)
|
34
|
+
@attribute_methods_generated = true
|
35
|
+
end
|
36
|
+
|
37
|
+
def attribute_methods_generated?
|
38
|
+
@attribute_methods_generated ||= false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def write_attribute(name, value)
|
43
|
+
@attributes[name.to_s] = self.class.typecast_attribute(self, name, value)
|
44
|
+
end
|
45
|
+
|
46
|
+
def read_attribute(name)
|
47
|
+
@attributes[name.to_s]
|
48
|
+
end
|
49
|
+
|
50
|
+
def attribute_exists?(name)
|
51
|
+
@attributes.key?(name.to_s)
|
52
|
+
end
|
53
|
+
|
54
|
+
def attributes
|
55
|
+
results = {}
|
56
|
+
@attributes.each_key do |key|
|
57
|
+
results[key] = read_attribute(key)
|
58
|
+
end
|
59
|
+
results
|
60
|
+
end
|
61
|
+
|
62
|
+
def attributes=(attributes)
|
63
|
+
attributes.each do |(name, value)|
|
64
|
+
send("#{name}=", value)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def method_missing(method_id, *args, &block)
|
69
|
+
if !self.class.attribute_methods_generated?
|
70
|
+
self.class.define_attribute_methods
|
71
|
+
send(method_id, *args, &block)
|
72
|
+
else
|
73
|
+
super
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def respond_to?(*args)
|
78
|
+
self.class.define_attribute_methods unless self.class.attribute_methods_generated?
|
79
|
+
super
|
80
|
+
end
|
81
|
+
|
82
|
+
protected
|
83
|
+
def attribute_method?(name)
|
84
|
+
!!attribute_definitions[name.to_s]
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
def attribute(name)
|
89
|
+
read_attribute(name)
|
90
|
+
end
|
91
|
+
|
92
|
+
def attribute=(name, value)
|
93
|
+
write_attribute(name, value)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module CassandraObject
|
2
|
+
module AttributeMethods
|
3
|
+
class Definition
|
4
|
+
attr_reader :name, :coder
|
5
|
+
def initialize(name, coder, options)
|
6
|
+
@name = name.to_s
|
7
|
+
@coder = coder.new(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def default
|
11
|
+
coder.default
|
12
|
+
end
|
13
|
+
|
14
|
+
def instantiate(record, value)
|
15
|
+
value = value.nil? ? coder.default : value
|
16
|
+
return if value.nil?
|
17
|
+
|
18
|
+
value.kind_of?(String) ? coder.decode(value) : coder.typecast(value)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module CassandraObject
|
2
|
+
module AttributeMethods
|
3
|
+
module Dirty
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
include ActiveModel::Dirty
|
6
|
+
|
7
|
+
# Attempts to +save+ the record and clears changed attributes if successful.
|
8
|
+
def save(*) #:nodoc:
|
9
|
+
if status = super
|
10
|
+
@previously_changed = changes
|
11
|
+
@changed_attributes = {}
|
12
|
+
end
|
13
|
+
status
|
14
|
+
end
|
15
|
+
|
16
|
+
# <tt>reload</tt> the record and clears changed attributes.
|
17
|
+
def reload
|
18
|
+
super.tap do
|
19
|
+
@previously_changed.try :clear
|
20
|
+
@changed_attributes.try :clear
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def write_attribute(name, value)
|
25
|
+
name = name.to_s
|
26
|
+
old = read_attribute(name)
|
27
|
+
|
28
|
+
super
|
29
|
+
|
30
|
+
unless attribute_changed?(name) || old == read_attribute(name)
|
31
|
+
changed_attributes[name] = old
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module CassandraObject
|
2
|
+
module AttributeMethods
|
3
|
+
module PrimaryKey
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def primary_key
|
8
|
+
'id'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def id
|
13
|
+
@id ||= self.class._generate_key(self)
|
14
|
+
end
|
15
|
+
|
16
|
+
def id=(id)
|
17
|
+
@id = id
|
18
|
+
end
|
19
|
+
|
20
|
+
def attributes
|
21
|
+
super.update(self.class.primary_key => id)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module CassandraObject
|
2
|
+
module AttributeMethods
|
3
|
+
module Typecasting
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
class_attribute :attribute_definitions
|
8
|
+
self.attribute_definitions = {}
|
9
|
+
|
10
|
+
%w(array boolean date float integer json string time).each do |type|
|
11
|
+
instance_eval <<-EOV, __FILE__, __LINE__ + 1
|
12
|
+
def #{type}(*args)
|
13
|
+
options = args.extract_options!
|
14
|
+
args.each do |name|
|
15
|
+
attribute(name, options.merge(:type => :#{type}))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
EOV
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
def inherited(child)
|
24
|
+
super
|
25
|
+
child.attribute_definitions = attribute_definitions.dup
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# attribute :name, type: :string
|
30
|
+
# attribute :ammo, type: Ammo, coder: AmmoCodec
|
31
|
+
#
|
32
|
+
def attribute(name, options)
|
33
|
+
type = options[:type]
|
34
|
+
coder = options[:coder]
|
35
|
+
|
36
|
+
if type.is_a?(Symbol)
|
37
|
+
coder = CassandraObject::Type.get_coder(type) || (raise "Unknown type #{type}")
|
38
|
+
elsif coder.nil?
|
39
|
+
raise "Must supply a :coder for #{name}"
|
40
|
+
end
|
41
|
+
|
42
|
+
attribute_definitions[name.to_s] = AttributeMethods::Definition.new(name, coder, options)
|
43
|
+
end
|
44
|
+
|
45
|
+
def typecast_attribute(record, name, value)
|
46
|
+
if attribute_definition = attribute_definitions[name.to_s]
|
47
|
+
attribute_definition.instantiate(record, value)
|
48
|
+
else
|
49
|
+
raise NoMethodError, "Unknown attribute #{name.inspect}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def coder_for(attribute)
|
54
|
+
attribute_definitions[attribute.to_s].coder
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require 'cassandra_object/log_subscriber'
|
4
|
+
require 'cassandra_object/types'
|
5
|
+
|
6
|
+
module CassandraObject
|
7
|
+
class Base
|
8
|
+
|
9
|
+
extend ActiveModel::Naming
|
10
|
+
include ActiveModel::Conversion
|
11
|
+
extend ActiveSupport::DescendantsTracker
|
12
|
+
|
13
|
+
include Identity
|
14
|
+
include Inspect
|
15
|
+
include Persistence
|
16
|
+
include AttributeMethods
|
17
|
+
include Validations
|
18
|
+
include AttributeMethods::Dirty
|
19
|
+
include AttributeMethods::PrimaryKey
|
20
|
+
include AttributeMethods::Typecasting
|
21
|
+
include BelongsTo
|
22
|
+
include Callbacks
|
23
|
+
include Timestamps
|
24
|
+
include Scoping
|
25
|
+
include Core
|
26
|
+
extend Model
|
27
|
+
|
28
|
+
include Serialization
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
ActiveSupport.run_load_hooks(:cassandra_object, CassandraObject::Base)
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module CassandraObject
|
2
|
+
module BelongsTo
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :belongs_to_reflections
|
7
|
+
self.belongs_to_reflections = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
# === Options
|
12
|
+
# [:class_name]
|
13
|
+
# Use if the class cannot be inferred from the association
|
14
|
+
# [:polymorphic]
|
15
|
+
# Specify if the association is polymorphic
|
16
|
+
# Example:
|
17
|
+
# class Driver < CassandraObject::Base
|
18
|
+
# end
|
19
|
+
# class Truck < CassandraObject::Base
|
20
|
+
# end
|
21
|
+
def belongs_to(name, options = {})
|
22
|
+
CassandraObject::BelongsTo::Builder.build(self, name, options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def generated_belongs_to_methods
|
26
|
+
@generated_belongs_to_methods ||= begin
|
27
|
+
mod = const_set(:GeneratedBelongsToMethods, Module.new)
|
28
|
+
include mod
|
29
|
+
mod
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the belongs_to instance for the given name, instantiating it if it doesn't already exist
|
35
|
+
def belongs_to_association(name)
|
36
|
+
association = belongs_to_instance_get(name)
|
37
|
+
|
38
|
+
if association.nil?
|
39
|
+
association = CassandraObject::BelongsTo::Association.new(self, belongs_to_reflections[name])
|
40
|
+
belongs_to_instance_set(name, association)
|
41
|
+
end
|
42
|
+
|
43
|
+
association
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
def clear_belongs_to_cache
|
48
|
+
belongs_to_cache.clear if persisted?
|
49
|
+
end
|
50
|
+
|
51
|
+
def belongs_to_cache
|
52
|
+
@belongs_to_cache ||= {}
|
53
|
+
end
|
54
|
+
|
55
|
+
def belongs_to_instance_get(name)
|
56
|
+
belongs_to_cache[name.to_sym]
|
57
|
+
end
|
58
|
+
|
59
|
+
def belongs_to_instance_set(name, association)
|
60
|
+
belongs_to_cache[name.to_sym] = association
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module CassandraObject
|
2
|
+
module BelongsTo
|
3
|
+
class Association
|
4
|
+
attr_reader :owner, :reflection
|
5
|
+
attr_accessor :record_variable
|
6
|
+
delegate :options, to: :reflection
|
7
|
+
|
8
|
+
def initialize(owner, reflection)
|
9
|
+
@loaded = false
|
10
|
+
@owner = owner
|
11
|
+
@reflection = reflection
|
12
|
+
end
|
13
|
+
|
14
|
+
def reader
|
15
|
+
unless loaded?
|
16
|
+
if record_id = owner.send(reflection.foreign_key).presence
|
17
|
+
self.record_variable = association_class.find_by_id(record_id)
|
18
|
+
else
|
19
|
+
self.record_variable = nil
|
20
|
+
end
|
21
|
+
@loaded = true
|
22
|
+
end
|
23
|
+
|
24
|
+
record_variable
|
25
|
+
end
|
26
|
+
|
27
|
+
def writer(record)
|
28
|
+
self.record_variable = record
|
29
|
+
@loaded = true
|
30
|
+
owner.send("#{reflection.foreign_key}=", record.try(:id))
|
31
|
+
if reflection.polymorphic?
|
32
|
+
owner.send("#{reflection.polymorphic_column}=", record.class.name)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def association_class
|
37
|
+
association_class_name.constantize
|
38
|
+
end
|
39
|
+
|
40
|
+
def association_class_name
|
41
|
+
reflection.polymorphic? ? owner.send(reflection.polymorphic_column) : reflection.class_name
|
42
|
+
end
|
43
|
+
|
44
|
+
def loaded?
|
45
|
+
@loaded
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module CassandraObject
|
2
|
+
module BelongsTo
|
3
|
+
class Builder
|
4
|
+
def self.build(model, name, options)
|
5
|
+
new(model, name, options).build
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :model, :name, :options
|
9
|
+
def initialize(model, name, options)
|
10
|
+
@model, @name, @options = model, name, options
|
11
|
+
end
|
12
|
+
|
13
|
+
def build
|
14
|
+
define_writer
|
15
|
+
define_reader
|
16
|
+
|
17
|
+
reflection = CassandraObject::BelongsTo::Reflection.new(model, name, options)
|
18
|
+
model.belongs_to_reflections = model.belongs_to_reflections.merge(name => reflection)
|
19
|
+
end
|
20
|
+
|
21
|
+
def mixin
|
22
|
+
model.generated_belongs_to_methods
|
23
|
+
end
|
24
|
+
|
25
|
+
def define_writer
|
26
|
+
name = self.name
|
27
|
+
mixin.redefine_method("#{name}=") do |records|
|
28
|
+
belongs_to_association(name).writer(records)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def define_reader
|
33
|
+
name = self.name
|
34
|
+
mixin.redefine_method(name) do
|
35
|
+
belongs_to_association(name).reader
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|