cequel 1.0.0.rc1 → 1.0.0.rc2
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 +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
|