extendi-cassandra_object 1.0.0

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 (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