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
@@ -1,31 +1,58 @@
|
|
1
1
|
module Cequel
|
2
|
-
|
3
2
|
module Record
|
4
|
-
|
3
|
+
#
|
4
|
+
# Represents a parent association declared by
|
5
|
+
# {Associations::ClassMethods#belongs_to belongs_to}
|
6
|
+
#
|
7
|
+
# @see Associations::ClassMethods#parent_association
|
8
|
+
# @since 1.0.0
|
9
|
+
#
|
5
10
|
class BelongsToAssociation
|
6
|
-
|
7
11
|
extend Forwardable
|
8
12
|
|
9
|
-
|
13
|
+
# @return [Class] child class that declared `belongs_to`
|
14
|
+
attr_reader :owner_class
|
15
|
+
# @return [Symbol] name of the association
|
16
|
+
attr_reader :name
|
17
|
+
# @return [String] name of parent class
|
18
|
+
attr_reader :association_class_name
|
10
19
|
|
20
|
+
# @!attribute [r] association_key_columns
|
21
|
+
# @return [Array<Schema::Column>] key columns on the parent class
|
11
22
|
def_delegator :association_class, :key_columns, :association_key_columns
|
12
23
|
|
24
|
+
#
|
25
|
+
# @param owner_class [Class] child class that declared `belongs_to`
|
26
|
+
# @param name [Symbol] name of the association
|
27
|
+
# @param options [Options] options for association
|
28
|
+
# @option options [String] :class_name name of parent class
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
#
|
13
32
|
def initialize(owner_class, name, options = {})
|
33
|
+
options.assert_valid_keys(:class_name)
|
34
|
+
|
14
35
|
@owner_class, @name = owner_class, name.to_sym
|
15
36
|
@association_class_name =
|
16
37
|
options.fetch(:class_name, @name.to_s.classify)
|
17
38
|
end
|
18
39
|
|
40
|
+
#
|
41
|
+
# @return [Class] parent class declared by `belongs_to`
|
42
|
+
#
|
19
43
|
def association_class
|
20
44
|
@association_class ||= association_class_name.constantize
|
21
45
|
end
|
22
46
|
|
47
|
+
#
|
48
|
+
# @return [Symbol] instance variable name to use for storing the parent
|
49
|
+
# instance in a record
|
50
|
+
#
|
51
|
+
# @api private
|
52
|
+
#
|
23
53
|
def instance_variable_name
|
24
54
|
@instance_variable_name ||= :"@#{name}"
|
25
55
|
end
|
26
|
-
|
27
56
|
end
|
28
|
-
|
29
57
|
end
|
30
|
-
|
31
58
|
end
|
data/lib/cequel/record/bound.rb
CHANGED
@@ -1,15 +1,33 @@
|
|
1
1
|
module Cequel
|
2
|
-
|
3
2
|
module Record
|
4
|
-
|
3
|
+
#
|
4
|
+
# An upper or lower bound for a range query.
|
5
|
+
#
|
6
|
+
# @abstract Subclasses must implement the `to_cql` method, and may override
|
7
|
+
# the `operator` and `bind_value` methods.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
# @since 1.0.0
|
11
|
+
#
|
5
12
|
class Bound
|
6
|
-
|
7
|
-
|
13
|
+
# @return [Schema::Column] column bound applies to
|
14
|
+
attr_reader :column
|
15
|
+
# @return value for bound
|
16
|
+
attr_reader :value
|
17
|
+
|
18
|
+
#
|
19
|
+
# Create a bound object for the given column. This method returns an
|
20
|
+
# instance of the appropriate `Bound` subclass given the type of the
|
21
|
+
# column and the class of the value.
|
22
|
+
#
|
23
|
+
# @param (see #initialize)
|
24
|
+
# @return [Bound] instance of appropriate bound implementation
|
25
|
+
#
|
8
26
|
def self.create(column, gt, inclusive, value)
|
9
27
|
implementation =
|
10
28
|
if column.partition_key?
|
11
29
|
PartitionKeyBound
|
12
|
-
elsif column.type?(
|
30
|
+
elsif column.type?(:timeuuid) && !value.is_a?(CassandraCQL::UUID)
|
13
31
|
TimeuuidBound
|
14
32
|
else
|
15
33
|
ClusteringColumnBound
|
@@ -18,27 +36,47 @@ module Cequel
|
|
18
36
|
implementation.new(column, gt, inclusive, value)
|
19
37
|
end
|
20
38
|
|
39
|
+
#
|
40
|
+
# @param column [Schema::Column] column bound applies to
|
41
|
+
# @param gt [Boolean] `true` if this is a lower bound
|
42
|
+
# @param inclusive [Boolean] `true` if this is an inclusive bound
|
43
|
+
# @param value value for bound
|
44
|
+
#
|
21
45
|
def initialize(column, gt, inclusive, value)
|
22
46
|
@column, @gt, @inclusive, @value = column, gt, inclusive, value
|
23
47
|
end
|
24
48
|
|
49
|
+
#
|
50
|
+
# @return [Array] pair containing CQL string and bind value
|
51
|
+
#
|
25
52
|
def to_cql_with_bind_variables
|
26
53
|
[to_cql, bind_value]
|
27
54
|
end
|
28
55
|
|
29
|
-
|
56
|
+
#
|
57
|
+
# @return [Boolean] `true` if this is a lower bound
|
58
|
+
#
|
30
59
|
def gt?
|
31
60
|
!!@gt
|
32
61
|
end
|
33
62
|
|
63
|
+
#
|
64
|
+
# @return [Boolean] `true` if this is an upper bound
|
65
|
+
#
|
34
66
|
def lt?
|
35
67
|
!gt?
|
36
68
|
end
|
37
69
|
|
70
|
+
#
|
71
|
+
# @return [Boolean] `true` if this is an inclusive bound
|
72
|
+
#
|
38
73
|
def inclusive?
|
39
74
|
!!@inclusive
|
40
75
|
end
|
41
76
|
|
77
|
+
#
|
78
|
+
# @return [Boolean] `true` if this is an exclusive bound
|
79
|
+
#
|
42
80
|
def exclusive?
|
43
81
|
!inclusive?
|
44
82
|
end
|
@@ -56,28 +94,49 @@ module Cequel
|
|
56
94
|
def base_operator
|
57
95
|
lt? ? '<' : '>'
|
58
96
|
end
|
59
|
-
|
60
97
|
end
|
61
98
|
|
99
|
+
#
|
100
|
+
# A bound on a partition key.
|
101
|
+
#
|
102
|
+
# @api private
|
103
|
+
# @since 1.0.0
|
104
|
+
#
|
62
105
|
class PartitionKeyBound < Bound
|
106
|
+
protected
|
107
|
+
|
63
108
|
def to_cql
|
64
109
|
"TOKEN(#{column.name}) #{operator} TOKEN(?)"
|
65
110
|
end
|
66
111
|
end
|
67
112
|
|
113
|
+
#
|
114
|
+
# A bound on a clustering column.
|
115
|
+
#
|
116
|
+
# @api private
|
117
|
+
# @since 1.0.0
|
118
|
+
#
|
68
119
|
class ClusteringColumnBound < Bound
|
120
|
+
protected
|
121
|
+
|
69
122
|
def to_cql
|
70
123
|
"#{column.name} #{operator} ?"
|
71
124
|
end
|
72
125
|
end
|
73
126
|
|
74
|
-
|
127
|
+
#
|
128
|
+
# A bound on a column of type `timeuuid` whose bound value is a `timestamp`
|
129
|
+
#
|
130
|
+
# @api private
|
131
|
+
# @since 1.0.0
|
132
|
+
#
|
133
|
+
class TimeuuidBound < ClusteringColumnBound
|
134
|
+
protected
|
135
|
+
|
75
136
|
def to_cql
|
76
137
|
"#{column.name} #{operator} #{function}(?)"
|
77
138
|
end
|
78
139
|
|
79
|
-
protected
|
80
|
-
|
81
140
|
def operator
|
82
141
|
base_operator
|
83
142
|
end
|
@@ -95,7 +154,5 @@ module Cequel
|
|
95
154
|
lt? ^ exclusive? ? 'maxTimeuuid' : 'minTimeuuid'
|
96
155
|
end
|
97
156
|
end
|
98
|
-
|
99
157
|
end
|
100
|
-
|
101
158
|
end
|
@@ -1,14 +1,42 @@
|
|
1
1
|
module Cequel
|
2
2
|
module Record
|
3
|
+
#
|
4
|
+
# This module implements bulk update and delete functionality for classes
|
5
|
+
# that expose a collection of result rows.
|
6
|
+
#
|
7
|
+
# @abstract Including modules must implement `key_attributes_for_each_row`,
|
8
|
+
# which should yield successive fully-specified key attributes for each
|
9
|
+
# result row.
|
10
|
+
#
|
11
|
+
# @since 1.0.0
|
12
|
+
#
|
3
13
|
module BulkWrites
|
14
|
+
#
|
15
|
+
# Update all matched records with the given column values, without
|
16
|
+
# executing callbacks.
|
17
|
+
#
|
18
|
+
# @param attributes [Hash] map of column names to values
|
19
|
+
# @return [void]
|
20
|
+
#
|
4
21
|
def update_all(attributes)
|
5
22
|
each_data_set { |data_set| data_set.update(attributes) }
|
6
23
|
end
|
7
24
|
|
25
|
+
#
|
26
|
+
# Delete all matched records without executing callbacks
|
27
|
+
#
|
28
|
+
# @return [void]
|
29
|
+
#
|
8
30
|
def delete_all
|
9
31
|
each_data_set { |data_set| data_set.delete }
|
10
32
|
end
|
11
33
|
|
34
|
+
#
|
35
|
+
# Destroy all matched records, executing destroy callbacks for each
|
36
|
+
# record.
|
37
|
+
#
|
38
|
+
# @return [void]
|
39
|
+
#
|
12
40
|
def destroy_all
|
13
41
|
each { |record| record.destroy }
|
14
42
|
end
|
@@ -17,7 +45,7 @@ module Cequel
|
|
17
45
|
|
18
46
|
def each_data_set
|
19
47
|
key_attributes_for_each_row.each_slice(100) do |batch|
|
20
|
-
connection.batch(:
|
48
|
+
connection.batch(unlogged: true) do
|
21
49
|
batch.each { |key_attributes| yield table.where(key_attributes) }
|
22
50
|
end
|
23
51
|
end
|
@@ -1,9 +1,26 @@
|
|
1
1
|
module Cequel
|
2
|
-
|
3
2
|
module Record
|
4
|
-
|
3
|
+
#
|
4
|
+
# Cequel::Record models provide lifecycle callbacks for `create`, `update`,
|
5
|
+
# `save`, `destroy`, and `validation`.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# class User
|
9
|
+
# include Cequel::Record
|
10
|
+
#
|
11
|
+
# key :login, :text
|
12
|
+
# column :name, :text
|
13
|
+
#
|
14
|
+
# after_create :send_welcome_email
|
15
|
+
# after_update :reindex_posts_for_search
|
16
|
+
# after_save :reindex_for_search
|
17
|
+
# after_destroy :send_farewell_email
|
18
|
+
# before_validation :set_permalink
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# @since 0.1.0
|
22
|
+
#
|
5
23
|
module Callbacks
|
6
|
-
|
7
24
|
extend ActiveSupport::Concern
|
8
25
|
|
9
26
|
included do
|
@@ -11,10 +28,12 @@ module Cequel
|
|
11
28
|
define_model_callbacks :save, :create, :update, :destroy
|
12
29
|
end
|
13
30
|
|
31
|
+
# (see Persistence#save)
|
14
32
|
def save(options = {})
|
15
33
|
connection.batch { run_callbacks(:save) { super }}
|
16
34
|
end
|
17
35
|
|
36
|
+
# (see Persistence#save)
|
18
37
|
def destroy
|
19
38
|
connection.batch { run_callbacks(:destroy) { super }}
|
20
39
|
end
|
@@ -28,9 +47,6 @@ module Cequel
|
|
28
47
|
def update
|
29
48
|
run_callbacks(:update) { super }
|
30
49
|
end
|
31
|
-
|
32
50
|
end
|
33
|
-
|
34
51
|
end
|
35
|
-
|
36
52
|
end
|
@@ -1,37 +1,117 @@
|
|
1
1
|
require 'delegate'
|
2
2
|
|
3
3
|
module Cequel
|
4
|
-
|
5
4
|
module Record
|
6
|
-
|
5
|
+
#
|
6
|
+
# The value of a collection column in a {Record}. Collections track
|
7
|
+
# modifications that can be expressed as atomic collection mutations in
|
8
|
+
# CQL, and persist those modifications when their owning record is saved.
|
9
|
+
# Such modifications can be done even if the collection has not loaded
|
10
|
+
# data from CQL, in the case of an unloaded record or where the collection
|
11
|
+
# column was not included in the `SELECT` statement.
|
12
|
+
#
|
13
|
+
# Mutation operations that require reading data before writing it are not
|
14
|
+
# supported (e.g. `Array#map!).
|
15
|
+
#
|
16
|
+
# Each collection implementation wraps a built-in Ruby collection type.
|
17
|
+
#
|
18
|
+
# @abstract Including classes must descend from `Delegator` and implement
|
19
|
+
# the `::empty` class method.
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# class Blog
|
23
|
+
# include Cequel::Record
|
24
|
+
#
|
25
|
+
# key :subdomain
|
26
|
+
#
|
27
|
+
# list :categories, :text
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# # Get an unloaded Blog instance; no data read
|
31
|
+
# blog = Blog['cassandra']
|
32
|
+
#
|
33
|
+
# # Stage modification to collection, still no data read
|
34
|
+
# blog.categories << 'Big Data'
|
35
|
+
#
|
36
|
+
# # Issue an UPDATE statement which pushes "Big Data" onto the
|
37
|
+
# # collection. Still no data read
|
38
|
+
# blog.save!
|
39
|
+
#
|
40
|
+
# # Stage another modification to the collection
|
41
|
+
# blog.categories.unshift('Distributed Database')
|
42
|
+
#
|
43
|
+
# # Collection is lazily read from the database, and then staged
|
44
|
+
# # modifications are made to the loaded collection
|
45
|
+
# puts blog.categories.join(', ')
|
46
|
+
#
|
47
|
+
# # Issues an UPDATE statement which prepends "Distributed Data" onto the
|
48
|
+
# # collection
|
49
|
+
# blog.save!
|
50
|
+
#
|
51
|
+
# @since 1.0.0
|
52
|
+
#
|
7
53
|
module Collection
|
8
|
-
|
9
54
|
extend ActiveSupport::Concern
|
10
55
|
extend Forwardable
|
11
56
|
|
57
|
+
#
|
58
|
+
# @!method loaded?
|
59
|
+
# @return [Boolean] `true` if the collection's contents are loaded into
|
60
|
+
# memory
|
61
|
+
#
|
12
62
|
def_delegators :@model, :loaded?, :updater, :deleter
|
63
|
+
private :updater, :deleter
|
64
|
+
|
65
|
+
#
|
66
|
+
# @!method column_name
|
67
|
+
# @return [Symbol] the name of the collection column
|
68
|
+
#
|
13
69
|
def_delegator :@column, :name, :column_name
|
70
|
+
|
14
71
|
def_delegators :__getobj__, :clone, :dup
|
15
72
|
|
16
73
|
included do
|
17
|
-
private
|
18
74
|
define_method(
|
19
75
|
:method_missing,
|
20
76
|
BasicObject.instance_method(:method_missing))
|
77
|
+
private :method_missing
|
21
78
|
end
|
22
79
|
|
80
|
+
#
|
81
|
+
# @param model [Record] record that contains this collection
|
82
|
+
# @param column [Schema::Column] column this collection's data belongs to
|
83
|
+
# @return [Collection] a new collection
|
84
|
+
#
|
23
85
|
def initialize(model, column)
|
24
86
|
@model, @column = model, column
|
25
87
|
end
|
26
88
|
|
89
|
+
#
|
90
|
+
# @return [String] inspected underlying Ruby collection object
|
91
|
+
#
|
27
92
|
def inspect
|
28
93
|
__getobj__.inspect
|
29
94
|
end
|
30
95
|
|
96
|
+
#
|
97
|
+
# Notify the collection that its underlying data is loaded in memory.
|
98
|
+
#
|
99
|
+
# @return [void]
|
100
|
+
#
|
101
|
+
# @api private
|
102
|
+
#
|
31
103
|
def loaded!
|
32
|
-
modifications.each { |modification| modification.() }.clear
|
104
|
+
modifications.each { |modification| modification.call() }.clear
|
33
105
|
end
|
34
106
|
|
107
|
+
#
|
108
|
+
# Notify the collection that its staged changes have been written to the
|
109
|
+
# data store.
|
110
|
+
#
|
111
|
+
# @return [void]
|
112
|
+
#
|
113
|
+
# @api private
|
114
|
+
#
|
35
115
|
def persisted!
|
36
116
|
modifications.clear
|
37
117
|
end
|
@@ -44,10 +124,11 @@ module Cequel
|
|
44
124
|
end
|
45
125
|
|
46
126
|
def __setobj__(obj)
|
47
|
-
|
127
|
+
fail "Attempted to call __setobj__ on read-only delegate!"
|
48
128
|
end
|
49
129
|
|
50
130
|
private
|
131
|
+
|
51
132
|
attr_reader :model, :column
|
52
133
|
def_delegator :column, :cast, :cast_collection
|
53
134
|
def_delegator 'column.type', :cast, :cast_element
|
@@ -56,7 +137,7 @@ module Cequel
|
|
56
137
|
def to_modify(&block)
|
57
138
|
if loaded?
|
58
139
|
model.__send__("#{column_name}_will_change!")
|
59
|
-
block.()
|
140
|
+
block.call()
|
60
141
|
else modifications << block
|
61
142
|
end
|
62
143
|
self
|
@@ -65,13 +146,20 @@ module Cequel
|
|
65
146
|
def modifications
|
66
147
|
@modifications ||= []
|
67
148
|
end
|
68
|
-
|
69
149
|
end
|
70
150
|
|
151
|
+
#
|
152
|
+
# The value of a list column in a {Record} instance. List collections
|
153
|
+
# encapsulate and behave like the built-in `Array` type.
|
154
|
+
#
|
155
|
+
# @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
|
156
|
+
# @since 1.0.0
|
157
|
+
#
|
71
158
|
class List < DelegateClass(Array)
|
72
|
-
|
73
159
|
include Collection
|
74
160
|
|
161
|
+
# These methods are not available on lists because they require reading
|
162
|
+
# collection data before writing it.
|
75
163
|
NON_ATOMIC_MUTATORS = [
|
76
164
|
:collect!,
|
77
165
|
:delete_if,
|
@@ -92,14 +180,50 @@ module Cequel
|
|
92
180
|
:sort_by!,
|
93
181
|
:uniq!
|
94
182
|
]
|
95
|
-
NON_ATOMIC_MUTATORS
|
96
|
-
each { |method| undef_method(method) if method_defined? method }
|
97
|
-
|
183
|
+
NON_ATOMIC_MUTATORS
|
184
|
+
.each { |method| undef_method(method) if method_defined? method }
|
185
|
+
|
186
|
+
#
|
187
|
+
# @return [Array] an empty array
|
188
|
+
#
|
189
|
+
# @api private
|
190
|
+
#
|
98
191
|
def self.empty; []; end
|
99
192
|
|
193
|
+
#
|
194
|
+
# Set the value at a position or range of positions. This modification
|
195
|
+
# will be staged and persisted as an atomic list update when the record
|
196
|
+
# is saved. If the collection data is loaded in memory, it will also be
|
197
|
+
# modified accordingly.
|
198
|
+
#
|
199
|
+
# @return [void]
|
200
|
+
#
|
201
|
+
# @see DataSet#list_replace
|
202
|
+
# @note Negative positions are not supported, as they are not allowed in
|
203
|
+
# CQL list operations.
|
204
|
+
#
|
205
|
+
# @overload []=(position, element)
|
206
|
+
#
|
207
|
+
# @param position [Integer] position at which to set element
|
208
|
+
# @param element element to insert at position in list
|
209
|
+
#
|
210
|
+
# @overload []=(range, elements)
|
211
|
+
#
|
212
|
+
# @param range [Range] range of positions at which to replace elements
|
213
|
+
# @param elements [Array] new elements to replace in this range
|
214
|
+
#
|
215
|
+
# @overload []=(start_position, count, elements)
|
216
|
+
#
|
217
|
+
# @param start_position [Integer] position at which to begin replacing
|
218
|
+
# elements
|
219
|
+
# @param count [Integer] number of elements to replace
|
220
|
+
# @param elements [Array] new elements to replace in this range
|
221
|
+
#
|
100
222
|
def []=(position, *args)
|
101
|
-
if
|
102
|
-
|
223
|
+
if position.is_a?(Range)
|
224
|
+
first, count = position.first, position.count
|
225
|
+
else
|
226
|
+
first, count = position, args[-2]
|
103
227
|
end
|
104
228
|
|
105
229
|
element = args[-1] =
|
@@ -108,8 +232,9 @@ module Cequel
|
|
108
232
|
end
|
109
233
|
|
110
234
|
if first < 0
|
111
|
-
|
112
|
-
|
235
|
+
fail ArgumentError,
|
236
|
+
"Bad index #{position}: CQL lists do not support negative " \
|
237
|
+
"indices"
|
113
238
|
end
|
114
239
|
|
115
240
|
if count.nil?
|
@@ -127,53 +252,105 @@ module Cequel
|
|
127
252
|
to_modify { super }
|
128
253
|
end
|
129
254
|
|
255
|
+
#
|
256
|
+
# Remove all elements from the list. This will propagate to the database
|
257
|
+
# as a DELETE of the list column.
|
258
|
+
#
|
259
|
+
# @return [List] self
|
260
|
+
#
|
130
261
|
def clear
|
131
262
|
deleter.delete_columns(column_name)
|
132
263
|
to_modify { super }
|
133
264
|
end
|
134
265
|
|
266
|
+
#
|
267
|
+
# Concatenate another collection onto this list.
|
268
|
+
#
|
269
|
+
# @param array [Array] elements to concatenate
|
270
|
+
# @return [List] self
|
271
|
+
#
|
135
272
|
def concat(array)
|
136
273
|
array = cast_collection(array)
|
137
274
|
updater.list_append(column_name, array)
|
138
275
|
to_modify { super }
|
139
276
|
end
|
140
277
|
|
278
|
+
#
|
279
|
+
# Remove all instances of a given value from the list.
|
280
|
+
#
|
281
|
+
# @param object value to remove
|
282
|
+
# @return [List] self
|
283
|
+
#
|
141
284
|
def delete(object)
|
142
285
|
object = cast_element(object)
|
143
286
|
updater.list_remove(column_name, object)
|
144
287
|
to_modify { super }
|
145
288
|
end
|
146
289
|
|
290
|
+
#
|
291
|
+
# Remove the element at a given position from the list.
|
292
|
+
#
|
293
|
+
# @param index [Integer] position from which to remove the element
|
294
|
+
# @return [List] self
|
295
|
+
#
|
147
296
|
def delete_at(index)
|
148
297
|
deleter.list_remove_at(column_name, index)
|
149
298
|
to_modify { super }
|
150
299
|
end
|
151
300
|
|
152
|
-
|
153
|
-
|
154
|
-
|
301
|
+
#
|
302
|
+
# Push (append) one or more elements to the end of the list.
|
303
|
+
#
|
304
|
+
# @param objects value(s) to add to the end of the list
|
305
|
+
# @return [List] self
|
306
|
+
#
|
307
|
+
def push(*objects)
|
308
|
+
objects.map! { |object| cast_element(object) }
|
309
|
+
updater.list_append(column_name, objects)
|
155
310
|
to_modify { super }
|
156
311
|
end
|
157
312
|
alias_method :<<, :push
|
158
|
-
|
313
|
+
alias_method :append, :push
|
314
|
+
|
315
|
+
#
|
316
|
+
# Replace the entire contents of this list with a new collection
|
317
|
+
#
|
318
|
+
# @param array [Array] new elements for this list
|
319
|
+
# @return [List] self
|
320
|
+
#
|
159
321
|
def replace(array)
|
160
322
|
array = cast_collection(array)
|
161
323
|
updater.set(column_name => array)
|
162
324
|
to_modify { super }
|
163
325
|
end
|
164
326
|
|
165
|
-
|
166
|
-
|
167
|
-
|
327
|
+
#
|
328
|
+
# Prepend one or more values to the beginning of this list
|
329
|
+
#
|
330
|
+
# @param objects value(s) to add to the beginning of the list
|
331
|
+
# @return [List] self
|
332
|
+
#
|
333
|
+
def unshift(*objects)
|
334
|
+
objects.map!(&method(:cast_element))
|
335
|
+
updater.list_prepend(column_name, objects.reverse)
|
168
336
|
to_modify { super }
|
169
337
|
end
|
170
|
-
|
338
|
+
alias_method :prepend, :unshift
|
171
339
|
end
|
172
340
|
|
341
|
+
#
|
342
|
+
# The value of a set column in a {Record} instance. Contains an unordered,
|
343
|
+
# unique set of elements. Encapsulates and behaves like the `Set` type from
|
344
|
+
# the standard library.
|
345
|
+
#
|
346
|
+
# @see http://www.datastax.com/documentation/cql/3.0/webhelp/index.html#cql/cql_using/use_collections_c.html#task_ds_agt_3kj_zj CQL documentation for set columns
|
347
|
+
# @since 1.0.0
|
348
|
+
#
|
173
349
|
class Set < DelegateClass(::Set)
|
174
|
-
|
175
350
|
include Collection
|
176
351
|
|
352
|
+
# These methods are not implemented because they cannot be expressed as a
|
353
|
+
# single CQL3 write operation.
|
177
354
|
NON_ATOMIC_MUTATORS = [
|
178
355
|
:add?,
|
179
356
|
:collect!,
|
@@ -185,9 +362,15 @@ module Cequel
|
|
185
362
|
:reject!,
|
186
363
|
:select!
|
187
364
|
]
|
188
|
-
NON_ATOMIC_MUTATORS
|
189
|
-
each { |method| undef_method(method) if method_defined? method }
|
190
|
-
|
365
|
+
NON_ATOMIC_MUTATORS
|
366
|
+
.each { |method| undef_method(method) if method_defined? method }
|
367
|
+
|
368
|
+
#
|
369
|
+
# Add an element to the set
|
370
|
+
#
|
371
|
+
# @param object element to add
|
372
|
+
# @return [Set] self
|
373
|
+
#
|
191
374
|
def add(object)
|
192
375
|
object = cast_element(object)
|
193
376
|
updater.set_add(column_name, object)
|
@@ -195,30 +378,55 @@ module Cequel
|
|
195
378
|
end
|
196
379
|
alias_method :<<, :add
|
197
380
|
|
381
|
+
#
|
382
|
+
# Remove everything from the set. Equivalent to deleting the collection
|
383
|
+
# column from the record's row.
|
384
|
+
#
|
385
|
+
# @return [Set] self
|
386
|
+
#
|
198
387
|
def clear
|
199
388
|
deleter.delete_columns(column_name)
|
200
389
|
to_modify { super }
|
201
390
|
end
|
202
391
|
|
392
|
+
#
|
393
|
+
# Remove a single element from the set
|
394
|
+
#
|
395
|
+
# @param object element to remove
|
396
|
+
# @return [Set] self
|
397
|
+
#
|
203
398
|
def delete(object)
|
204
399
|
object = cast_element(object)
|
205
400
|
updater.set_remove(column_name, object)
|
206
401
|
to_modify { super }
|
207
402
|
end
|
208
403
|
|
404
|
+
#
|
405
|
+
# Replace the entire contents of this set with another set
|
406
|
+
#
|
407
|
+
# @param set [::Set] set containing new elements
|
408
|
+
# @return [Set] self
|
409
|
+
#
|
209
410
|
def replace(set)
|
210
411
|
set = cast_collection(set)
|
211
412
|
updater.set(column_name => set)
|
212
413
|
to_modify { super }
|
213
414
|
end
|
214
|
-
|
215
415
|
end
|
216
416
|
|
417
|
+
#
|
418
|
+
# The value of a `map` column in a {Record} instance. Encapsulates and
|
419
|
+
# behaves like a built-in `Hash`.
|
420
|
+
#
|
421
|
+
# @see http://www.datastax.com/documentation/cql/3.0/webhelp/index.html#cql/cql_using/use_collections_c.html#task_ds_cvq_kcl_zj CQL documentation for map columns
|
422
|
+
# @since 1.0.0
|
423
|
+
#
|
217
424
|
class Map < DelegateClass(::Hash)
|
218
|
-
|
219
425
|
include Collection
|
220
426
|
extend Forwardable
|
221
427
|
|
428
|
+
# These methods involve mutation that cannot be expressed as a CQL
|
429
|
+
# operation, so are not implemented.
|
222
430
|
NON_ATOMIC_MUTATORS = [
|
223
431
|
:default,
|
224
432
|
:default=,
|
@@ -240,9 +448,16 @@ module Cequel
|
|
240
448
|
:to_options!,
|
241
449
|
:transform_keys!
|
242
450
|
]
|
243
|
-
NON_ATOMIC_MUTATORS
|
244
|
-
each { |method| undef_method(method) if method_defined? method }
|
245
|
-
|
451
|
+
NON_ATOMIC_MUTATORS
|
452
|
+
.each { |method| undef_method(method) if method_defined? method }
|
453
|
+
|
454
|
+
#
|
455
|
+
# Set the value of a given key
|
456
|
+
#
|
457
|
+
# @param key the key
|
458
|
+
# @param value the value
|
459
|
+
# @return [Map] self
|
460
|
+
#
|
246
461
|
def []=(key, value)
|
247
462
|
key = cast_key(key)
|
248
463
|
updater.map_update(column_name, key => value)
|
@@ -250,17 +465,35 @@ module Cequel
|
|
250
465
|
end
|
251
466
|
alias_method :store, :[]=
|
252
467
|
|
468
|
+
#
|
469
|
+
# Remove all elements from this map. Equivalent to deleting the column
|
470
|
+
# value from the row in CQL
|
471
|
+
#
|
472
|
+
# @return [Map] self
|
473
|
+
#
|
253
474
|
def clear
|
254
475
|
deleter.delete_columns(column_name)
|
255
476
|
to_modify { super }
|
256
477
|
end
|
257
478
|
|
479
|
+
#
|
480
|
+
# Delete one key from the map
|
481
|
+
#
|
482
|
+
# @param key the key to delete
|
483
|
+
# @return [Map] self
|
484
|
+
#
|
258
485
|
def delete(key)
|
259
486
|
key = cast_key(key)
|
260
487
|
deleter.map_remove(column_name, key)
|
261
488
|
to_modify { super }
|
262
489
|
end
|
263
490
|
|
491
|
+
#
|
492
|
+
# Update a collection of keys and values given by a hash
|
493
|
+
#
|
494
|
+
# @param hash [Hash] hash containing keys and values to set
|
495
|
+
# @return [Map] self
|
496
|
+
#
|
264
497
|
def merge!(hash)
|
265
498
|
hash = cast_collection(hash)
|
266
499
|
updater.map_update(column_name, hash)
|
@@ -268,6 +501,12 @@ module Cequel
|
|
268
501
|
end
|
269
502
|
alias_method :update, :merge!
|
270
503
|
|
504
|
+
#
|
505
|
+
# Replace the entire contents of this map with a new one
|
506
|
+
#
|
507
|
+
# @param hash [Hash] hash containing new keys and values
|
508
|
+
# @return [Map] self
|
509
|
+
#
|
271
510
|
def replace(hash)
|
272
511
|
hash = cast_collection(hash)
|
273
512
|
updater.set(column_name => hash)
|
@@ -275,11 +514,9 @@ module Cequel
|
|
275
514
|
end
|
276
515
|
|
277
516
|
private
|
517
|
+
|
278
518
|
def_delegator 'column.key_type', :cast, :cast_key
|
279
519
|
private :cast_key
|
280
|
-
|
281
520
|
end
|
282
|
-
|
283
521
|
end
|
284
|
-
|
285
522
|
end
|