superstore 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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +11 -0
- data/CHANGELOG +0 -0
- data/Gemfile +19 -0
- data/LICENSE +13 -0
- data/MIT-LICENSE +20 -0
- data/README.md +100 -0
- data/Rakefile +12 -0
- data/lib/superstore/adapters/abstract_adapter.rb +49 -0
- data/lib/superstore/adapters/cassandra_adapter.rb +181 -0
- data/lib/superstore/adapters/hstore_adapter.rb +163 -0
- data/lib/superstore/attribute_methods/definition.rb +22 -0
- data/lib/superstore/attribute_methods/dirty.rb +36 -0
- data/lib/superstore/attribute_methods/primary_key.rb +25 -0
- data/lib/superstore/attribute_methods/typecasting.rb +59 -0
- data/lib/superstore/attribute_methods.rb +96 -0
- data/lib/superstore/base.rb +33 -0
- data/lib/superstore/belongs_to/association.rb +48 -0
- data/lib/superstore/belongs_to/builder.rb +40 -0
- data/lib/superstore/belongs_to/reflection.rb +30 -0
- data/lib/superstore/belongs_to.rb +63 -0
- data/lib/superstore/callbacks.rb +29 -0
- data/lib/superstore/cassandra_schema/statements.rb +52 -0
- data/lib/superstore/cassandra_schema/tasks.rb +47 -0
- data/lib/superstore/cassandra_schema.rb +9 -0
- data/lib/superstore/connection.rb +39 -0
- data/lib/superstore/core.rb +59 -0
- data/lib/superstore/errors.rb +10 -0
- data/lib/superstore/identity.rb +26 -0
- data/lib/superstore/inspect.rb +25 -0
- data/lib/superstore/log_subscriber.rb +44 -0
- data/lib/superstore/model.rb +37 -0
- data/lib/superstore/persistence.rb +153 -0
- data/lib/superstore/railtie.rb +30 -0
- data/lib/superstore/railties/controller_runtime.rb +45 -0
- data/lib/superstore/schema.rb +20 -0
- data/lib/superstore/scope/batches.rb +32 -0
- data/lib/superstore/scope/finder_methods.rb +48 -0
- data/lib/superstore/scope/query_methods.rb +49 -0
- data/lib/superstore/scope.rb +49 -0
- data/lib/superstore/scoping.rb +27 -0
- data/lib/superstore/tasks/ks.rake +54 -0
- data/lib/superstore/timestamps.rb +19 -0
- data/lib/superstore/type.rb +16 -0
- data/lib/superstore/types/array_type.rb +20 -0
- data/lib/superstore/types/base_type.rb +26 -0
- data/lib/superstore/types/boolean_type.rb +20 -0
- data/lib/superstore/types/date_type.rb +22 -0
- data/lib/superstore/types/float_type.rb +16 -0
- data/lib/superstore/types/integer_type.rb +20 -0
- data/lib/superstore/types/json_type.rb +13 -0
- data/lib/superstore/types/string_type.rb +19 -0
- data/lib/superstore/types/time_type.rb +16 -0
- data/lib/superstore/types.rb +8 -0
- data/lib/superstore/validations.rb +44 -0
- data/lib/superstore.rb +69 -0
- data/superstore.gemspec +23 -0
- data/test/support/cassandra.rb +44 -0
- data/test/support/hstore.rb +40 -0
- data/test/support/issue.rb +10 -0
- data/test/test_helper.rb +42 -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 +118 -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 +62 -0
- data/test/unit/callbacks_test.rb +46 -0
- data/test/unit/cassandra_schema/statements_test.rb +47 -0
- data/test/unit/cassandra_schema/tasks_test.rb +31 -0
- data/test/unit/connection_test.rb +10 -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 +26 -0
- data/test/unit/persistence_test.rb +213 -0
- data/test/unit/railties/controller_runtime_test.rb +48 -0
- data/test/unit/schema_test.rb +27 -0
- data/test/unit/scope/batches_test.rb +30 -0
- data/test/unit/scope/finder_methods_test.rb +51 -0
- data/test/unit/scope/query_methods_test.rb +27 -0
- data/test/unit/scoping_test.rb +7 -0
- data/test/unit/serialization_test.rb +10 -0
- data/test/unit/timestamps_test.rb +27 -0
- data/test/unit/types/array_type_test.rb +21 -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 +30 -0
- data/test/unit/types/time_type_test.rb +19 -0
- data/test/unit/validations_test.rb +27 -0
- metadata +170 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Superstore
|
|
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 = Superstore::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,96 @@
|
|
|
1
|
+
module Superstore
|
|
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,33 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
|
|
3
|
+
require 'superstore/log_subscriber'
|
|
4
|
+
require 'superstore/types'
|
|
5
|
+
|
|
6
|
+
module Superstore
|
|
7
|
+
class Base
|
|
8
|
+
|
|
9
|
+
extend ActiveModel::Naming
|
|
10
|
+
include ActiveModel::Conversion
|
|
11
|
+
extend ActiveSupport::DescendantsTracker
|
|
12
|
+
include ActiveModel::Serializers::JSON
|
|
13
|
+
|
|
14
|
+
include Connection
|
|
15
|
+
include Identity
|
|
16
|
+
include Inspect
|
|
17
|
+
include Persistence
|
|
18
|
+
include AttributeMethods
|
|
19
|
+
include Validations
|
|
20
|
+
include AttributeMethods::Dirty
|
|
21
|
+
include AttributeMethods::PrimaryKey
|
|
22
|
+
include AttributeMethods::Typecasting
|
|
23
|
+
include BelongsTo
|
|
24
|
+
include Callbacks
|
|
25
|
+
include Timestamps
|
|
26
|
+
include Scoping
|
|
27
|
+
include Core
|
|
28
|
+
extend Model
|
|
29
|
+
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
ActiveSupport.run_load_hooks(:superstore, Superstore::Base)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Superstore
|
|
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
|
+
@owner = owner
|
|
10
|
+
@reflection = reflection
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def reader
|
|
14
|
+
unless loaded?
|
|
15
|
+
if record_id = owner.send(reflection.foreign_key).presence
|
|
16
|
+
self.record_variable = association_class.find_by_id(record_id)
|
|
17
|
+
else
|
|
18
|
+
self.record_variable = nil
|
|
19
|
+
end
|
|
20
|
+
@loaded = true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
record_variable
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def writer(record)
|
|
27
|
+
self.record_variable = record
|
|
28
|
+
@loaded = true
|
|
29
|
+
owner.send("#{reflection.foreign_key}=", record.try(:id))
|
|
30
|
+
if reflection.polymorphic?
|
|
31
|
+
owner.send("#{reflection.polymorphic_column}=", record.class.name)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def association_class
|
|
36
|
+
association_class_name.constantize
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def association_class_name
|
|
40
|
+
reflection.polymorphic? ? owner.send(reflection.polymorphic_column) : reflection.class_name
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def loaded?
|
|
44
|
+
@loaded
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Superstore
|
|
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 = Superstore::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
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Superstore
|
|
2
|
+
module BelongsTo
|
|
3
|
+
class Reflection
|
|
4
|
+
attr_reader :model, :name, :options
|
|
5
|
+
def initialize(model, name, options)
|
|
6
|
+
@model, @name, @options = model, name, options
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def instance_variable_name
|
|
10
|
+
"@#{name}"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def foreign_key
|
|
14
|
+
"#{name}_id"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def polymorphic_column
|
|
18
|
+
"#{name}_type"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def polymorphic?
|
|
22
|
+
options[:polymorphic]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def class_name
|
|
26
|
+
options[:class_name] || name.to_s.camelize
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module Superstore
|
|
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 < Superstore::Base
|
|
18
|
+
# end
|
|
19
|
+
# class Truck < Superstore::Base
|
|
20
|
+
# end
|
|
21
|
+
def belongs_to(name, options = {})
|
|
22
|
+
Superstore::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 = Superstore::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,29 @@
|
|
|
1
|
+
module Superstore
|
|
2
|
+
module Callbacks
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
extend ActiveModel::Callbacks
|
|
7
|
+
include ActiveModel::Validations::Callbacks
|
|
8
|
+
|
|
9
|
+
define_model_callbacks :save, :create, :update, :destroy
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def destroy #:nodoc:
|
|
13
|
+
run_callbacks(:destroy) { super }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
def write(*args) #:nodoc:
|
|
18
|
+
run_callbacks(:save) { super }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def create #:nodoc:
|
|
22
|
+
run_callbacks(:create) { super }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def update(*) #:nodoc:
|
|
26
|
+
run_callbacks(:update) { super }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Superstore
|
|
2
|
+
module Statements
|
|
3
|
+
DEFAULT_CREATE_KEYSPACE = {
|
|
4
|
+
'strategy_class' => 'SimpleStrategy',
|
|
5
|
+
'strategy_options:replication_factor' => 1
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
def create_keyspace(keyspace, options = nil)
|
|
9
|
+
stmt = "CREATE KEYSPACE #{keyspace}"
|
|
10
|
+
|
|
11
|
+
options ||= DEFAULT_CREATE_KEYSPACE
|
|
12
|
+
|
|
13
|
+
system_execute adapter.statement_with_options(stmt, options)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def drop_keyspace(keyspace)
|
|
17
|
+
system_execute "DROP KEYSPACE #{keyspace}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def create_column_family(column_family, options = {})
|
|
21
|
+
create_table column_family, options
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def alter_column_family(column_family, instruction, options = {})
|
|
25
|
+
stmt = "ALTER TABLE #{column_family} #{instruction}"
|
|
26
|
+
keyspace_execute adapter.statement_with_options(stmt, options)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def drop_column_family(column_family)
|
|
30
|
+
drop_table column_family
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def add_index(column_family, column, index_name = nil)
|
|
34
|
+
stmt = "CREATE INDEX #{index_name.nil? ? '' : index_name} ON #{column_family} (#{column})"
|
|
35
|
+
keyspace_execute stmt
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# If the index was not given a name during creation, the index name is <columnfamily_name>_<column_name>_idx.
|
|
39
|
+
def drop_index(index_name)
|
|
40
|
+
keyspace_execute "DROP INDEX #{index_name}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def keyspace_execute(cql)
|
|
45
|
+
adapter.schema_execute cql, Superstore::Base.config[:keyspace]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def system_execute(cql)
|
|
49
|
+
adapter.schema_execute cql, 'system'
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Superstore
|
|
2
|
+
module Tasks
|
|
3
|
+
def dump(io)
|
|
4
|
+
table_names.each do |column_family|
|
|
5
|
+
io.puts run_command("DESCRIBE COLUMNFAMILY #{column_family}")
|
|
6
|
+
io.puts
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def load(io)
|
|
11
|
+
current_cql = ''
|
|
12
|
+
|
|
13
|
+
io.each_line do |line|
|
|
14
|
+
next if line.blank?
|
|
15
|
+
|
|
16
|
+
current_cql << line.rstrip
|
|
17
|
+
|
|
18
|
+
if current_cql =~ /;$/
|
|
19
|
+
keyspace_execute current_cql
|
|
20
|
+
current_cql = ''
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def table_names
|
|
26
|
+
run_command('DESCRIBE COLUMNFAMILIES').split.sort
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def run_command(command)
|
|
32
|
+
`echo "#{command};" | #{cqlsh} -2 -k #{keyspace} #{server}`.sub(/^(.*)$/, '').strip
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def cqlsh
|
|
36
|
+
ENV['CQLSH'] || 'cqlsh'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def keyspace
|
|
40
|
+
Superstore::Base.config[:keyspace]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def server
|
|
44
|
+
Superstore::Base.adapter.servers.first.gsub(/:.*/, '')
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module CassandraCQL
|
|
2
|
+
class Statement
|
|
3
|
+
def self.sanitize(statement, bind_vars=[])
|
|
4
|
+
return statement if bind_vars.empty?
|
|
5
|
+
|
|
6
|
+
bind_vars = bind_vars.dup
|
|
7
|
+
expected_bind_vars = statement.count("?")
|
|
8
|
+
|
|
9
|
+
raise Error::InvalidBindVariable, "Wrong number of bound variables (statement expected #{expected_bind_vars}, was #{bind_vars.size})" if expected_bind_vars != bind_vars.size
|
|
10
|
+
|
|
11
|
+
statement.gsub(/\?/) do
|
|
12
|
+
quote(cast_to_cql(bind_vars.shift))
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module Superstore
|
|
19
|
+
module Connection
|
|
20
|
+
extend ActiveSupport::Concern
|
|
21
|
+
|
|
22
|
+
module ClassMethods
|
|
23
|
+
def adapter
|
|
24
|
+
@@adapter ||= adapter_class.new(config)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def adapter_class
|
|
28
|
+
case config[:adapter]
|
|
29
|
+
when 'hstore'
|
|
30
|
+
Superstore::Adapters::HstoreAdapter
|
|
31
|
+
when nil, 'cassandra'
|
|
32
|
+
Superstore::Adapters::CassandraAdapter
|
|
33
|
+
else
|
|
34
|
+
raise "Unknown adapter #{config[:adapter]}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Superstore
|
|
2
|
+
module Core
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
def initialize(attributes=nil)
|
|
6
|
+
@new_record = true
|
|
7
|
+
@destroyed = false
|
|
8
|
+
@attributes = {}
|
|
9
|
+
self.attributes = attributes || {}
|
|
10
|
+
attribute_definitions.each_value do |definition|
|
|
11
|
+
unless definition.default.nil? || attribute_exists?(definition.name)
|
|
12
|
+
@attributes[definition.name] = definition.default
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
yield self if block_given?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize_dup(other)
|
|
20
|
+
@attributes = other.attributes
|
|
21
|
+
@attributes['created_at'] = nil
|
|
22
|
+
@attributes['updated_at'] = nil
|
|
23
|
+
@attributes.delete(self.class.primary_key)
|
|
24
|
+
@id = nil
|
|
25
|
+
@new_record = true
|
|
26
|
+
@destroyed = false
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_param
|
|
31
|
+
id
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def hash
|
|
35
|
+
id.hash
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module ClassMethods
|
|
39
|
+
def inspect
|
|
40
|
+
if self == Base
|
|
41
|
+
super
|
|
42
|
+
else
|
|
43
|
+
attr_list = @attributes.map do |col, definition| "#{col}: #{definition.type}" end * ', '
|
|
44
|
+
"#{super}(#{attr_list.truncate(140 * 1.7337)})"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def ==(comparison_object)
|
|
50
|
+
comparison_object.equal?(self) ||
|
|
51
|
+
(comparison_object.instance_of?(self.class) &&
|
|
52
|
+
comparison_object.id == id)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def eql?(comparison_object)
|
|
56
|
+
self == (comparison_object)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'securerandom'
|
|
2
|
+
|
|
3
|
+
module Superstore
|
|
4
|
+
module Identity
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
class_attribute :key_generator
|
|
9
|
+
|
|
10
|
+
key do
|
|
11
|
+
SecureRandom.uuid.tr('-','')
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module ClassMethods
|
|
16
|
+
# Define a key generator. Default is UUID.
|
|
17
|
+
def key(&block)
|
|
18
|
+
self.key_generator = block
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def _generate_key(object)
|
|
22
|
+
object.instance_eval(&key_generator)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Superstore
|
|
2
|
+
module Inspect
|
|
3
|
+
def inspect
|
|
4
|
+
inspection = ["#{self.class.primary_key}: #{id.inspect}"]
|
|
5
|
+
|
|
6
|
+
@attributes.each do |attribute, value|
|
|
7
|
+
if value.present?
|
|
8
|
+
inspection << "#{attribute}: #{attribute_for_inspect(value)}"
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
"#<#{self.class} #{inspection * ', '}>"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def attribute_for_inspect(value)
|
|
16
|
+
if value.is_a?(String) && value.length > 50
|
|
17
|
+
"#{value[0..50]}...".inspect
|
|
18
|
+
elsif value.is_a?(Date) || value.is_a?(Time)
|
|
19
|
+
%("#{value.to_s(:db)}")
|
|
20
|
+
else
|
|
21
|
+
value.inspect
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|