cequel 1.0.0.rc1 → 1.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cequel.rb +18 -0
- data/lib/cequel/errors.rb +8 -4
- data/lib/cequel/metal.rb +14 -0
- data/lib/cequel/metal/batch.rb +21 -11
- data/lib/cequel/metal/batch_manager.rb +74 -0
- data/lib/cequel/metal/cql_row_specification.rb +19 -6
- data/lib/cequel/metal/data_set.rb +400 -163
- data/lib/cequel/metal/deleter.rb +45 -11
- data/lib/cequel/metal/incrementer.rb +23 -10
- data/lib/cequel/metal/inserter.rb +19 -6
- data/lib/cequel/metal/keyspace.rb +82 -159
- data/lib/cequel/metal/logger.rb +71 -0
- data/lib/cequel/metal/logging.rb +47 -0
- data/lib/cequel/metal/new_relic_instrumentation.rb +26 -0
- data/lib/cequel/metal/row.rb +36 -10
- data/lib/cequel/metal/row_specification.rb +21 -8
- data/lib/cequel/metal/statement.rb +30 -6
- data/lib/cequel/metal/updater.rb +89 -12
- data/lib/cequel/metal/writer.rb +23 -14
- data/lib/cequel/record.rb +52 -6
- data/lib/cequel/record/association_collection.rb +13 -6
- data/lib/cequel/record/associations.rb +146 -54
- data/lib/cequel/record/belongs_to_association.rb +34 -7
- data/lib/cequel/record/bound.rb +69 -12
- data/lib/cequel/record/bulk_writes.rb +29 -1
- data/lib/cequel/record/callbacks.rb +22 -6
- data/lib/cequel/record/collection.rb +273 -36
- data/lib/cequel/record/configuration_generator.rb +5 -0
- data/lib/cequel/record/data_set_builder.rb +86 -0
- data/lib/cequel/record/dirty.rb +11 -8
- data/lib/cequel/record/errors.rb +38 -4
- data/lib/cequel/record/has_many_association.rb +42 -9
- data/lib/cequel/record/lazy_record_collection.rb +39 -10
- data/lib/cequel/record/mass_assignment.rb +14 -6
- data/lib/cequel/record/persistence.rb +157 -20
- data/lib/cequel/record/properties.rb +147 -24
- data/lib/cequel/record/railtie.rb +15 -2
- data/lib/cequel/record/record_set.rb +504 -75
- data/lib/cequel/record/schema.rb +77 -13
- data/lib/cequel/record/scoped.rb +16 -11
- data/lib/cequel/record/secondary_indexes.rb +42 -6
- data/lib/cequel/record/tasks.rb +2 -1
- data/lib/cequel/record/validations.rb +51 -11
- data/lib/cequel/schema.rb +9 -0
- data/lib/cequel/schema/column.rb +172 -33
- data/lib/cequel/schema/create_table_dsl.rb +62 -31
- data/lib/cequel/schema/keyspace.rb +106 -7
- data/lib/cequel/schema/migration_validator.rb +128 -0
- data/lib/cequel/schema/table.rb +183 -20
- data/lib/cequel/schema/table_property.rb +92 -34
- data/lib/cequel/schema/table_reader.rb +45 -15
- data/lib/cequel/schema/table_synchronizer.rb +101 -43
- data/lib/cequel/schema/table_updater.rb +114 -19
- data/lib/cequel/schema/table_writer.rb +31 -13
- data/lib/cequel/schema/update_table_dsl.rb +71 -40
- data/lib/cequel/type.rb +214 -53
- data/lib/cequel/util.rb +6 -9
- data/lib/cequel/version.rb +2 -1
- data/spec/examples/record/associations_spec.rb +12 -12
- data/spec/examples/record/persistence_spec.rb +5 -5
- data/spec/examples/record/record_set_spec.rb +62 -50
- data/spec/examples/schema/table_synchronizer_spec.rb +37 -11
- data/spec/examples/schema/table_updater_spec.rb +3 -3
- data/spec/examples/spec_helper.rb +2 -11
- data/spec/examples/type_spec.rb +3 -3
- metadata +23 -4
- data/lib/cequel/new_relic_instrumentation.rb +0 -22
data/lib/cequel/record/schema.rb
CHANGED
@@ -1,33 +1,96 @@
|
|
1
1
|
module Cequel
|
2
|
-
|
3
2
|
module Record
|
4
|
-
|
3
|
+
#
|
4
|
+
# `Cequel::Record` implementations define their own schema in their class
|
5
|
+
# definitions. As well as defining attributes on record instances, the
|
6
|
+
# column definitions in {Properties} allow a `Cequel::Record` to have a
|
7
|
+
# precise internal represntation of its representation as a CQL3 table
|
8
|
+
# schema. Further, it is able to check this representation against the
|
9
|
+
# actual table defined in Cassandra (if any), and create or modify the
|
10
|
+
# schema in Cassandra to match what's defined in code.
|
11
|
+
#
|
12
|
+
# All the interesting stuff is in the {ClassMethods}.
|
13
|
+
#
|
14
|
+
# @since 1.0.0
|
15
|
+
#
|
5
16
|
module Schema
|
6
|
-
|
7
17
|
extend ActiveSupport::Concern
|
8
18
|
extend Forwardable
|
9
19
|
|
10
20
|
included do
|
11
|
-
class_attribute :table_name, :
|
21
|
+
class_attribute :table_name, instance_writer: false
|
12
22
|
self.table_name = name.tableize.to_sym unless name.nil?
|
13
23
|
end
|
14
24
|
|
25
|
+
#
|
26
|
+
# Methods available on {Record} class singletons to introspect and modify
|
27
|
+
# the schema defined in the database
|
28
|
+
#
|
15
29
|
module ClassMethods
|
30
|
+
#
|
31
|
+
# @!attr table_name
|
32
|
+
# @return [Symbol] name of the CQL table that backs this record class
|
33
|
+
#
|
34
|
+
|
16
35
|
extend Forwardable
|
17
36
|
|
18
|
-
|
19
|
-
|
37
|
+
#
|
38
|
+
# @!attribute [r] columns
|
39
|
+
# (see Cequel::Schema::Table#columns)
|
40
|
+
#
|
41
|
+
# @!attribute [r] key_columns
|
42
|
+
# (see Cequel::Schema::Table#key_columns)
|
43
|
+
#
|
44
|
+
# @!attribute [r] key_column_names
|
45
|
+
# (see Cequel::Schema::Table#key_column_names)
|
46
|
+
#
|
47
|
+
# @!attribute [r] partition_key_columns
|
48
|
+
# (see Cequel::Schema::Table#partition_key_columns)
|
49
|
+
#
|
50
|
+
# @!attribute [r] clustering_columns
|
51
|
+
# (see Cequel::Schema::Table#clustering_columns)
|
52
|
+
#
|
53
|
+
# @!method compact_storage?
|
54
|
+
# (see Cequel::Schema::Table#compact_storage?)
|
55
|
+
#
|
56
|
+
def_delegators :table_schema, :columns, :key_columns,
|
57
|
+
:key_column_names, :partition_key_columns,
|
58
|
+
:clustering_columns, :compact_storage?
|
59
|
+
#
|
60
|
+
# @!method reflect_on_column(name)
|
61
|
+
# (see Cequel::Schema::Table#column)
|
62
|
+
#
|
20
63
|
def_delegator :table_schema, :column, :reflect_on_column
|
21
64
|
|
65
|
+
#
|
66
|
+
# Read the current schema assigned to this record's table from
|
67
|
+
# Cassandra, and make any necessary modifications (including creating
|
68
|
+
# the table for the first time) so that it matches the schema defined
|
69
|
+
# in the record definition
|
70
|
+
#
|
71
|
+
# @raise (see Schema::TableSynchronizer.apply)
|
72
|
+
# @return [void]
|
73
|
+
#
|
22
74
|
def synchronize_schema
|
23
|
-
Cequel::Schema::TableSynchronizer
|
24
|
-
apply(connection, read_schema, table_schema)
|
75
|
+
Cequel::Schema::TableSynchronizer
|
76
|
+
.apply(connection, read_schema, table_schema)
|
25
77
|
end
|
26
78
|
|
79
|
+
#
|
80
|
+
# Read the current state of this record's table in Cassandra from the
|
81
|
+
# database.
|
82
|
+
#
|
83
|
+
# @return [Schema::Table] the current schema assigned to this record's
|
84
|
+
# table in the database
|
85
|
+
#
|
27
86
|
def read_schema
|
28
87
|
connection.schema.read_table(table_name)
|
29
88
|
end
|
30
89
|
|
90
|
+
#
|
91
|
+
# @return [Schema::Table] the schema as defined by the columns
|
92
|
+
# specified in the class definition
|
93
|
+
#
|
31
94
|
def table_schema
|
32
95
|
@table_schema ||= Cequel::Schema::Table.new(table_name)
|
33
96
|
end
|
@@ -36,7 +99,11 @@ module Cequel
|
|
36
99
|
|
37
100
|
def key(name, type, options = {})
|
38
101
|
super
|
39
|
-
|
102
|
+
if options[:partition]
|
103
|
+
table_schema.add_partition_key(name, type)
|
104
|
+
else
|
105
|
+
table_schema.add_key(name, type)
|
106
|
+
end
|
40
107
|
end
|
41
108
|
|
42
109
|
def column(name, type, options = {})
|
@@ -66,15 +133,12 @@ module Cequel
|
|
66
133
|
def compact_storage
|
67
134
|
table_schema.compact_storage = true
|
68
135
|
end
|
69
|
-
|
70
136
|
end
|
71
137
|
|
72
138
|
protected
|
139
|
+
|
73
140
|
def_delegator 'self.class', :table_schema
|
74
141
|
protected :table_schema
|
75
|
-
|
76
142
|
end
|
77
|
-
|
78
143
|
end
|
79
|
-
|
80
144
|
end
|
data/lib/cequel/record/scoped.rb
CHANGED
@@ -1,24 +1,31 @@
|
|
1
1
|
module Cequel
|
2
|
-
|
3
2
|
module Record
|
4
|
-
|
3
|
+
#
|
4
|
+
# All of the instance methods of {RecordSet} are also available as class
|
5
|
+
# methods on {Record} implementations.
|
6
|
+
#
|
7
|
+
# @since 0.1.0
|
8
|
+
#
|
5
9
|
module Scoped
|
6
|
-
|
7
10
|
extend ActiveSupport::Concern
|
8
11
|
|
12
|
+
#
|
13
|
+
# Scoping-related methods for {Record} classes
|
14
|
+
#
|
9
15
|
module ClassMethods
|
10
|
-
|
11
16
|
extend Forwardable
|
12
17
|
|
13
18
|
def_delegators :current_scope,
|
14
|
-
|
15
|
-
|
16
|
-
|
19
|
+
*(RecordSet.public_instance_methods(false) +
|
20
|
+
BulkWrites.public_instance_methods -
|
21
|
+
Object.instance_methods)
|
17
22
|
|
23
|
+
# @private
|
18
24
|
def current_scope
|
19
25
|
delegating_scope || RecordSet.new(self)
|
20
26
|
end
|
21
27
|
|
28
|
+
# @private
|
22
29
|
def with_scope(record_set)
|
23
30
|
previous_scope = delegating_scope
|
24
31
|
self.delegating_scope = record_set
|
@@ -40,16 +47,14 @@ module Cequel
|
|
40
47
|
def delegating_scope_key
|
41
48
|
@delegating_scope_key ||= :"#{name}::delegating_scope"
|
42
49
|
end
|
43
|
-
|
44
50
|
end
|
45
51
|
|
52
|
+
private
|
53
|
+
|
46
54
|
def initialize_new_record(*)
|
47
55
|
super
|
48
56
|
@attributes.merge!(self.class.current_scope.scoped_key_attributes)
|
49
57
|
end
|
50
|
-
|
51
58
|
end
|
52
|
-
|
53
59
|
end
|
54
|
-
|
55
60
|
end
|
@@ -1,9 +1,48 @@
|
|
1
1
|
module Cequel
|
2
|
-
|
3
2
|
module Record
|
4
|
-
|
3
|
+
#
|
4
|
+
# Data columns may be given secondary indexes. This allows you to query the
|
5
|
+
# table for records where the indexed column contains a single value.
|
6
|
+
# Secondary indexes are not nearly as flexible as primary keys: you cannot
|
7
|
+
# query for multiple values or for ranges of values. You also cannot
|
8
|
+
# combine a secondary index restriction with a primary key restriction in
|
9
|
+
# the same query, nor can you combine more than one secondary index
|
10
|
+
# restrictions in the same query.
|
11
|
+
#
|
12
|
+
# If a column is given a secondary index, magic finder methods `find_by_*`,
|
13
|
+
# `find_all_by_*`, and `with_*` are added to the class singleton. See below
|
14
|
+
# for an example.
|
15
|
+
#
|
16
|
+
# Secondary indexes are fairly expensive for Cassandra and should only be
|
17
|
+
# defined where needed.
|
18
|
+
#
|
19
|
+
# @example Defining a secondary index
|
20
|
+
# class Post
|
21
|
+
# belongs_to :blog
|
22
|
+
# key :id, :timeuuid, auto: true
|
23
|
+
# column :title, :text
|
24
|
+
# column :body, :text
|
25
|
+
# column :author_id, :uuid, index: true # this column has a secondary
|
26
|
+
# # index
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# @example Using a secondary index
|
30
|
+
# # return the first record with the author_id
|
31
|
+
# Post.find_by_author_id(id)
|
32
|
+
#
|
33
|
+
# # return an Array of all records with the author_id
|
34
|
+
# Post.find_all_by_author_id(id)
|
35
|
+
#
|
36
|
+
# # return a RecordSet scoped to the author_id
|
37
|
+
# Post.with_author_id(id)
|
38
|
+
#
|
39
|
+
# # same as with_author_id
|
40
|
+
# Post.where(:author_id, id)
|
41
|
+
#
|
42
|
+
# @since 1.0.0
|
43
|
+
#
|
5
44
|
module SecondaryIndexes
|
6
|
-
|
45
|
+
# @private
|
7
46
|
def column(name, type, options = {})
|
8
47
|
super
|
9
48
|
name = name.to_sym
|
@@ -23,9 +62,6 @@ module Cequel
|
|
23
62
|
RUBY
|
24
63
|
end
|
25
64
|
end
|
26
|
-
|
27
65
|
end
|
28
|
-
|
29
66
|
end
|
30
|
-
|
31
67
|
end
|
data/lib/cequel/record/tasks.rb
CHANGED
@@ -13,7 +13,8 @@ namespace :cequel do
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
desc "Synchronize all models defined in `app/models' with Cassandra
|
16
|
+
desc "Synchronize all models defined in `app/models' with Cassandra " \
|
17
|
+
"database schema"
|
17
18
|
task :migrate => :environment do
|
18
19
|
watch_stack = ActiveSupport::Dependencies::WatchStack.new
|
19
20
|
|
@@ -1,9 +1,25 @@
|
|
1
1
|
module Cequel
|
2
|
-
|
3
2
|
module Record
|
4
|
-
|
3
|
+
#
|
4
|
+
# {Record} classes can define validations that are run before saving a
|
5
|
+
# record instance.
|
6
|
+
#
|
7
|
+
# @example Validations
|
8
|
+
# class User
|
9
|
+
# include Cequel::Record
|
10
|
+
#
|
11
|
+
# key :login, :text
|
12
|
+
# column :email, :text
|
13
|
+
#
|
14
|
+
# validates :email, presence: true, format: RFC822::EMAIL
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# @see http://api.rubyonrails.org/classes/ActiveModel/Validations.html
|
18
|
+
# ActiveModel::Validations
|
19
|
+
#
|
20
|
+
# @since 0.1.0
|
21
|
+
#
|
5
22
|
module Validations
|
6
|
-
|
7
23
|
extend ActiveSupport::Concern
|
8
24
|
|
9
25
|
included do
|
@@ -12,39 +28,63 @@ module Cequel
|
|
12
28
|
alias_method_chain :valid?, :callbacks
|
13
29
|
end
|
14
30
|
|
31
|
+
#
|
32
|
+
# Validation-related methods exposed on Record class singletons
|
33
|
+
#
|
15
34
|
module ClassMethods
|
16
|
-
|
35
|
+
#
|
36
|
+
# Attempt to create a new record, or raise an exception otherwise
|
37
|
+
#
|
38
|
+
# @param (see Persistence::ClassMethods#create)
|
39
|
+
# @yieldparam (see Persistence::ClassMethods#create)
|
40
|
+
# @return (see Persistence::ClassMethods#create)
|
41
|
+
# @raise (see Validations#save!)
|
42
|
+
#
|
17
43
|
def create!(attributes = {}, &block)
|
18
44
|
new(attributes, &block).save!
|
19
45
|
end
|
20
|
-
|
21
46
|
end
|
22
47
|
|
48
|
+
# @private
|
23
49
|
def save(options = {})
|
24
50
|
validate = options.fetch(:validate, true)
|
25
51
|
options.delete(:validate)
|
26
52
|
(!validate || valid?) && super
|
27
53
|
end
|
28
54
|
|
29
|
-
|
55
|
+
#
|
56
|
+
# Attempt to save the record, or raise an exception if there is a
|
57
|
+
# validation error
|
58
|
+
#
|
59
|
+
# @param (see Persistence#save)
|
60
|
+
# @return [Record] self
|
61
|
+
# @raise [RecordInvalid] if there are validation errors
|
62
|
+
#
|
63
|
+
def save!(attributes = {})
|
30
64
|
tap do
|
31
|
-
unless save(
|
32
|
-
|
65
|
+
unless save(attributes)
|
66
|
+
fail RecordInvalid, errors.full_messages.join("; ")
|
33
67
|
end
|
34
68
|
end
|
35
69
|
end
|
36
70
|
|
71
|
+
#
|
72
|
+
# Set the given attributes and attempt to save, raising an exception if
|
73
|
+
# there is a validation error
|
74
|
+
#
|
75
|
+
# @param (see Persistence#update_attributes)
|
76
|
+
# @return (see #save!)
|
77
|
+
# @raise (see #save!)
|
37
78
|
def update_attributes!(attributes)
|
38
79
|
self.attributes = attributes
|
39
80
|
save!
|
40
81
|
end
|
41
82
|
|
83
|
+
private
|
84
|
+
|
42
85
|
def valid_with_callbacks?
|
43
86
|
run_callbacks(:validation) { valid_without_callbacks? }
|
44
87
|
end
|
45
|
-
|
46
88
|
end
|
47
|
-
|
48
89
|
end
|
49
|
-
|
50
90
|
end
|
data/lib/cequel/schema.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'cequel/schema/column'
|
2
2
|
require 'cequel/schema/create_table_dsl'
|
3
3
|
require 'cequel/schema/keyspace'
|
4
|
+
require 'cequel/schema/migration_validator'
|
4
5
|
require 'cequel/schema/table'
|
5
6
|
require 'cequel/schema/table_property'
|
6
7
|
require 'cequel/schema/table_reader'
|
@@ -10,6 +11,14 @@ require 'cequel/schema/table_writer'
|
|
10
11
|
require 'cequel/schema/update_table_dsl'
|
11
12
|
|
12
13
|
module Cequel
|
14
|
+
#
|
15
|
+
# The Schema module provides full read/write access to keyspace and table
|
16
|
+
# schemas defined in Cassandra.
|
17
|
+
#
|
18
|
+
# @see Schema::Keyspace
|
19
|
+
#
|
20
|
+
# @since 1.0.0
|
21
|
+
#
|
13
22
|
module Schema
|
14
23
|
end
|
15
24
|
end
|
data/lib/cequel/schema/column.rb
CHANGED
@@ -1,155 +1,294 @@
|
|
1
1
|
module Cequel
|
2
|
-
|
3
2
|
module Schema
|
4
|
-
|
3
|
+
#
|
4
|
+
# Represents a column definition in a table schema.
|
5
|
+
#
|
6
|
+
# @abstract
|
7
|
+
#
|
5
8
|
class Column
|
6
|
-
|
7
|
-
attr_reader :name
|
8
|
-
|
9
|
+
# @return [Symbol] the name of the column
|
10
|
+
attr_reader :name
|
11
|
+
# @return [Type] the type of the column
|
12
|
+
attr_reader :type
|
13
|
+
|
14
|
+
#
|
15
|
+
# @param name [Symbol] the name of the column
|
16
|
+
# @param type [Type] the type of the column
|
17
|
+
#
|
9
18
|
def initialize(name, type)
|
10
19
|
@name, @type = name, type
|
11
20
|
end
|
12
21
|
|
22
|
+
#
|
23
|
+
# @return [Boolean] true if this is a key column
|
24
|
+
#
|
25
|
+
# @see
|
26
|
+
# http://www.datastax.com/documentation/cql/3.0/webhelp/index.html#cql/ddl/../../cassandra/glossary/gloss_glossary.html
|
27
|
+
# The CQL3 glossary
|
28
|
+
#
|
13
29
|
def key?
|
14
30
|
partition_key? || clustering_column?
|
15
31
|
end
|
16
32
|
|
33
|
+
#
|
34
|
+
# @return [Boolean] true if this is a partition key column
|
35
|
+
#
|
36
|
+
# @see
|
37
|
+
# http://www.datastax.com/documentation/cql/3.0/webhelp/index.html#cql/ddl/../../cassandra/glossary/gloss_glossary.html
|
38
|
+
# The CQL3 glossary
|
39
|
+
#
|
17
40
|
def partition_key?
|
18
41
|
false
|
19
42
|
end
|
20
43
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
44
|
+
#
|
45
|
+
# @return [Boolean] true if this is a clustering column
|
46
|
+
#
|
47
|
+
# @see
|
48
|
+
# http://www.datastax.com/documentation/cql/3.0/webhelp/index.html#cql/ddl/../../cassandra/glossary/gloss_glossary.html
|
49
|
+
# The CQL3 glossary
|
50
|
+
#
|
25
51
|
def clustering_column?
|
26
52
|
false
|
27
53
|
end
|
28
54
|
|
55
|
+
#
|
56
|
+
# @return [Boolean] true if this is a data column
|
57
|
+
#
|
58
|
+
# @see
|
59
|
+
# http://www.datastax.com/documentation/cql/3.0/webhelp/index.html#cql/ddl/../../cassandra/glossary/gloss_glossary.html
|
60
|
+
# The CQL3 glossary
|
61
|
+
#
|
29
62
|
def data_column?
|
30
63
|
!key?
|
31
64
|
end
|
32
65
|
|
33
|
-
|
34
|
-
|
66
|
+
#
|
67
|
+
# @return [Boolean] true if this is a collection column
|
68
|
+
#
|
69
|
+
def collection_column?
|
70
|
+
false
|
35
71
|
end
|
36
72
|
|
73
|
+
#
|
74
|
+
# @param type_in [Symbol,Type] type to check against
|
75
|
+
# @return [Boolean] true if this column has the type given by `type_in`
|
76
|
+
#
|
77
|
+
def type?(type_in)
|
78
|
+
type == Type[type_in]
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# @param value the value to cast
|
83
|
+
# @return the value cast to the appropriate type for this column
|
84
|
+
#
|
85
|
+
# @api private
|
86
|
+
#
|
37
87
|
def cast(value)
|
38
88
|
@type.cast(value)
|
39
89
|
end
|
40
90
|
|
91
|
+
#
|
92
|
+
# @return [String] a CQL fragment representing this column in a table
|
93
|
+
# definition
|
94
|
+
#
|
95
|
+
# @api private
|
96
|
+
#
|
97
|
+
def to_cql
|
98
|
+
"#{@name} #{@type}"
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# @param other [Column] a column object
|
103
|
+
# @return [Boolean] true if this column has the same CQL representation
|
104
|
+
# as `other` column
|
105
|
+
#
|
41
106
|
def ==(other)
|
42
107
|
to_cql == other.to_cql
|
43
108
|
end
|
44
109
|
|
110
|
+
#
|
111
|
+
# @return [String] the column's name
|
112
|
+
#
|
45
113
|
def to_s
|
46
114
|
name
|
47
115
|
end
|
48
116
|
|
117
|
+
#
|
118
|
+
# @return [String] human-readable representation of this column
|
119
|
+
#
|
49
120
|
def inspect
|
50
121
|
%Q(#<#{self.class.name}: #{to_cql}>)
|
51
122
|
end
|
52
|
-
|
53
123
|
end
|
54
124
|
|
125
|
+
#
|
126
|
+
# A partition key column
|
127
|
+
#
|
55
128
|
class PartitionKey < Column
|
56
|
-
|
129
|
+
#
|
130
|
+
# (see Column#partition_key?)
|
131
|
+
#
|
57
132
|
def partition_key?
|
58
133
|
true
|
59
134
|
end
|
60
|
-
|
61
135
|
end
|
62
136
|
|
137
|
+
#
|
138
|
+
# A clustering column
|
139
|
+
#
|
63
140
|
class ClusteringColumn < Column
|
64
|
-
|
141
|
+
#
|
142
|
+
# @return [:asc,:desc] whether rows are ordered by ascending or
|
143
|
+
# descending values in this column
|
144
|
+
#
|
65
145
|
attr_reader :clustering_order
|
66
146
|
|
147
|
+
#
|
148
|
+
# @param (see Column#initialize)
|
149
|
+
# @param clustering_order [:asc,:desc] ascending or descending order for
|
150
|
+
# this column
|
151
|
+
#
|
67
152
|
def initialize(name, type, clustering_order = nil)
|
68
153
|
super(name, type)
|
69
154
|
@clustering_order = (clustering_order || :asc).to_sym
|
70
155
|
end
|
71
156
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
157
|
+
#
|
158
|
+
# (see Column#clustering_column?)
|
159
|
+
#
|
76
160
|
def clustering_column?
|
77
161
|
true
|
78
162
|
end
|
79
163
|
|
164
|
+
# @private
|
165
|
+
def clustering_order_cql
|
166
|
+
"#{@name} #{@clustering_order}"
|
167
|
+
end
|
80
168
|
end
|
81
169
|
|
170
|
+
#
|
171
|
+
# A scalar data column
|
172
|
+
#
|
82
173
|
class DataColumn < Column
|
83
|
-
|
174
|
+
#
|
175
|
+
# @return [Symbol] name of the secondary index applied to this column, if
|
176
|
+
# any
|
177
|
+
#
|
84
178
|
attr_reader :index_name
|
85
179
|
|
180
|
+
#
|
181
|
+
# @param (see Column#initialize)
|
182
|
+
# @param index_name [Symbol] name this column's secondary index
|
183
|
+
#
|
86
184
|
def initialize(name, type, index_name = nil)
|
87
185
|
super(name, type)
|
88
186
|
@index_name = index_name
|
89
187
|
end
|
90
188
|
|
189
|
+
#
|
190
|
+
# @return [Boolean] true if this column has a secondary index
|
191
|
+
#
|
91
192
|
def indexed?
|
92
193
|
!!@index_name
|
93
194
|
end
|
94
|
-
|
95
195
|
end
|
96
196
|
|
197
|
+
#
|
198
|
+
# A collection column (list, set, or map)
|
199
|
+
#
|
200
|
+
# @abstract
|
201
|
+
#
|
97
202
|
class CollectionColumn < Column
|
203
|
+
# (see Column#collection_column?)
|
204
|
+
def collection_column?
|
205
|
+
true
|
206
|
+
end
|
98
207
|
|
208
|
+
# (see DataColumn#indexed?)
|
99
209
|
def indexed?
|
100
210
|
false
|
101
211
|
end
|
102
|
-
|
103
212
|
end
|
104
213
|
|
214
|
+
#
|
215
|
+
# A List column
|
216
|
+
#
|
217
|
+
# @see http://www.datastax.com/documentation/cql/3.0/webhelp/index.html#cql/cql_using/use_collections_c.html#task_ds_lqp_krj_zj CQL documentation for the list type
|
218
|
+
#
|
105
219
|
class List < CollectionColumn
|
106
|
-
|
220
|
+
# (see Column#to_cql)
|
107
221
|
def to_cql
|
108
222
|
"#{@name} LIST <#{@type}>"
|
109
223
|
end
|
110
224
|
|
225
|
+
#
|
226
|
+
# @return [Array] array with elements cast to correct type for column
|
227
|
+
#
|
111
228
|
def cast(value)
|
112
229
|
value.map { |element| @type.cast(element) }
|
113
230
|
end
|
114
|
-
|
115
231
|
end
|
116
232
|
|
233
|
+
#
|
234
|
+
# A Set column
|
235
|
+
#
|
236
|
+
# @see
|
237
|
+
# http://www.datastax.com/documentation/cql/3.0/webhelp/index.html#cql/cql_using/use_collections_c.html#task_ds_agt_3kj_zj
|
238
|
+
# CQL documentation for set columns
|
239
|
+
#
|
117
240
|
class Set < CollectionColumn
|
118
|
-
|
241
|
+
# (see Column#to_cql)
|
119
242
|
def to_cql
|
120
243
|
"#{@name} SET <#{@type}>"
|
121
244
|
end
|
122
245
|
|
246
|
+
#
|
247
|
+
# @param (see Column#cast)
|
248
|
+
# @return [::Set] set with elements cast to correct type for column
|
249
|
+
#
|
123
250
|
def cast(value)
|
124
|
-
value.
|
125
|
-
set << @type.cast(element)
|
126
|
-
end
|
251
|
+
value.to_set { |element| @type.cast(element) }
|
127
252
|
end
|
128
|
-
|
129
253
|
end
|
130
254
|
|
255
|
+
#
|
256
|
+
# A Map column
|
257
|
+
#
|
258
|
+
# @see
|
259
|
+
# http://www.datastax.com/documentation/cql/3.0/webhelp/index.html#cql/cql_using/use_collections_c.html#task_ds_cvq_kcl_zj
|
260
|
+
# CQL documentation for map columns
|
261
|
+
#
|
131
262
|
class Map < CollectionColumn
|
132
|
-
|
263
|
+
# @return [Type] the type of keys in this map
|
133
264
|
attr_reader :key_type
|
134
265
|
alias_method :value_type, :type
|
135
266
|
|
267
|
+
#
|
268
|
+
# @param name [Symbol] name of this column
|
269
|
+
# @param key_type [Type] type of the keys in the map
|
270
|
+
# @param value_type [Type] type of the values in the map
|
271
|
+
#
|
136
272
|
def initialize(name, key_type, value_type)
|
137
273
|
super(name, value_type)
|
138
274
|
@key_type = key_type
|
139
275
|
end
|
140
276
|
|
277
|
+
# (see Column#to_cql)
|
141
278
|
def to_cql
|
142
279
|
"#{@name} MAP <#{@key_type}, #{@type}>"
|
143
280
|
end
|
144
281
|
|
282
|
+
#
|
283
|
+
# @param (see Column#cast)
|
284
|
+
# @return [Hash] hash with keys and values cast to correct type for
|
285
|
+
# column
|
286
|
+
#
|
145
287
|
def cast(value)
|
146
288
|
value.each_with_object({}) do |(key, element), hash|
|
147
289
|
hash[@key_type.cast(key)] = @type.cast(element)
|
148
290
|
end
|
149
291
|
end
|
150
|
-
|
151
292
|
end
|
152
|
-
|
153
293
|
end
|
154
|
-
|
155
294
|
end
|