extendi-cassandra_object 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.travis.yml +23 -0
  4. data/CHANGELOG +0 -0
  5. data/Gemfile +17 -0
  6. data/LICENSE +13 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.md +177 -0
  9. data/Rakefile +12 -0
  10. data/extendi-cassandra_object.gemspec +26 -0
  11. data/lib/cassandra_object.rb +73 -0
  12. data/lib/cassandra_object/adapters/abstract_adapter.rb +61 -0
  13. data/lib/cassandra_object/adapters/cassandra_adapter.rb +269 -0
  14. data/lib/cassandra_object/adapters/cassandra_schemaless_adapter.rb +306 -0
  15. data/lib/cassandra_object/attribute_methods.rb +96 -0
  16. data/lib/cassandra_object/attribute_methods/definition.rb +22 -0
  17. data/lib/cassandra_object/attribute_methods/dirty.rb +36 -0
  18. data/lib/cassandra_object/attribute_methods/primary_key.rb +25 -0
  19. data/lib/cassandra_object/attribute_methods/typecasting.rb +59 -0
  20. data/lib/cassandra_object/base.rb +33 -0
  21. data/lib/cassandra_object/base_schema.rb +11 -0
  22. data/lib/cassandra_object/base_schemaless.rb +11 -0
  23. data/lib/cassandra_object/base_schemaless_dynamic.rb +11 -0
  24. data/lib/cassandra_object/belongs_to.rb +63 -0
  25. data/lib/cassandra_object/belongs_to/association.rb +49 -0
  26. data/lib/cassandra_object/belongs_to/builder.rb +40 -0
  27. data/lib/cassandra_object/belongs_to/reflection.rb +30 -0
  28. data/lib/cassandra_object/callbacks.rb +29 -0
  29. data/lib/cassandra_object/core.rb +63 -0
  30. data/lib/cassandra_object/errors.rb +10 -0
  31. data/lib/cassandra_object/identity.rb +26 -0
  32. data/lib/cassandra_object/inspect.rb +25 -0
  33. data/lib/cassandra_object/log_subscriber.rb +44 -0
  34. data/lib/cassandra_object/model.rb +60 -0
  35. data/lib/cassandra_object/persistence.rb +203 -0
  36. data/lib/cassandra_object/railtie.rb +33 -0
  37. data/lib/cassandra_object/railties/controller_runtime.rb +45 -0
  38. data/lib/cassandra_object/schema.rb +83 -0
  39. data/lib/cassandra_object/schemaless.rb +83 -0
  40. data/lib/cassandra_object/scope.rb +86 -0
  41. data/lib/cassandra_object/scope/finder_methods.rb +54 -0
  42. data/lib/cassandra_object/scope/query_methods.rb +69 -0
  43. data/lib/cassandra_object/scoping.rb +27 -0
  44. data/lib/cassandra_object/serialization.rb +6 -0
  45. data/lib/cassandra_object/tasks/ks.rake +54 -0
  46. data/lib/cassandra_object/timestamps.rb +19 -0
  47. data/lib/cassandra_object/type.rb +16 -0
  48. data/lib/cassandra_object/types.rb +8 -0
  49. data/lib/cassandra_object/types/array_type.rb +16 -0
  50. data/lib/cassandra_object/types/base_type.rb +26 -0
  51. data/lib/cassandra_object/types/boolean_type.rb +20 -0
  52. data/lib/cassandra_object/types/date_type.rb +22 -0
  53. data/lib/cassandra_object/types/float_type.rb +16 -0
  54. data/lib/cassandra_object/types/integer_type.rb +20 -0
  55. data/lib/cassandra_object/types/json_type.rb +13 -0
  56. data/lib/cassandra_object/types/string_type.rb +19 -0
  57. data/lib/cassandra_object/types/time_type.rb +16 -0
  58. data/lib/cassandra_object/types/type_helper.rb +39 -0
  59. data/lib/cassandra_object/validations.rb +44 -0
  60. data/test/support/cassandra.rb +63 -0
  61. data/test/support/issue.rb +12 -0
  62. data/test/support/issue_dynamic.rb +12 -0
  63. data/test/support/issue_schema.rb +17 -0
  64. data/test/support/issue_schema_child.rb +17 -0
  65. data/test/support/issue_schema_father.rb +13 -0
  66. data/test/test_helper.rb +41 -0
  67. data/test/unit/active_model_test.rb +18 -0
  68. data/test/unit/adapters/adapter_test.rb +6 -0
  69. data/test/unit/attribute_methods/definition_test.rb +13 -0
  70. data/test/unit/attribute_methods/dirty_test.rb +72 -0
  71. data/test/unit/attribute_methods/primary_key_test.rb +26 -0
  72. data/test/unit/attribute_methods/typecasting_test.rb +119 -0
  73. data/test/unit/attribute_methods_test.rb +51 -0
  74. data/test/unit/base_test.rb +20 -0
  75. data/test/unit/belongs_to/reflection_test.rb +12 -0
  76. data/test/unit/belongs_to_test.rb +63 -0
  77. data/test/unit/callbacks_test.rb +46 -0
  78. data/test/unit/connection_test.rb +6 -0
  79. data/test/unit/connections/connections_test.rb +55 -0
  80. data/test/unit/core_test.rb +55 -0
  81. data/test/unit/identity_test.rb +26 -0
  82. data/test/unit/inspect_test.rb +26 -0
  83. data/test/unit/log_subscriber_test.rb +25 -0
  84. data/test/unit/persistence_schema_test.rb +156 -0
  85. data/test/unit/persistence_test.rb +266 -0
  86. data/test/unit/railties/controller_runtime_test.rb +48 -0
  87. data/test/unit/schema/tasks_test.rb +32 -0
  88. data/test/unit/schema_test.rb +115 -0
  89. data/test/unit/schemaless_test.rb +100 -0
  90. data/test/unit/scope/finder_methods_test.rb +117 -0
  91. data/test/unit/scope/query_methods_test.rb +32 -0
  92. data/test/unit/scoping_test.rb +7 -0
  93. data/test/unit/timestamps_test.rb +27 -0
  94. data/test/unit/types/array_type_test.rb +17 -0
  95. data/test/unit/types/base_type_test.rb +19 -0
  96. data/test/unit/types/boolean_type_test.rb +24 -0
  97. data/test/unit/types/date_type_test.rb +15 -0
  98. data/test/unit/types/float_type_test.rb +17 -0
  99. data/test/unit/types/integer_type_test.rb +19 -0
  100. data/test/unit/types/json_type_test.rb +23 -0
  101. data/test/unit/types/string_type_test.rb +25 -0
  102. data/test/unit/types/time_type_test.rb +14 -0
  103. data/test/unit/validations_test.rb +27 -0
  104. 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,11 @@
1
+ module CassandraObject
2
+ class BaseSchema < Base
3
+ def self.adapter
4
+ @adapter ||= CassandraObject::Adapters::CassandraAdapter.new(Base.config)
5
+ end
6
+
7
+ def self.schema_type
8
+ :standard
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module CassandraObject
2
+ class BaseSchemaless < Base
3
+ def self.adapter
4
+ @adapter ||= CassandraObject::Adapters::CassandraSchemalessAdapter.new(Base.config)
5
+ end
6
+
7
+ def self.schema_type
8
+ :schemaless
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module CassandraObject
2
+ class BaseSchemalessDynamic < Base
3
+ def self.adapter
4
+ @adapter ||= CassandraObject::Adapters::CassandraSchemalessAdapter.new(Base.config)
5
+ end
6
+
7
+ def self.schema_type
8
+ :dynamic_attributes
9
+ end
10
+ end
11
+ end
@@ -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