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