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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/lib/cequel.rb +18 -0
  3. data/lib/cequel/errors.rb +8 -4
  4. data/lib/cequel/metal.rb +14 -0
  5. data/lib/cequel/metal/batch.rb +21 -11
  6. data/lib/cequel/metal/batch_manager.rb +74 -0
  7. data/lib/cequel/metal/cql_row_specification.rb +19 -6
  8. data/lib/cequel/metal/data_set.rb +400 -163
  9. data/lib/cequel/metal/deleter.rb +45 -11
  10. data/lib/cequel/metal/incrementer.rb +23 -10
  11. data/lib/cequel/metal/inserter.rb +19 -6
  12. data/lib/cequel/metal/keyspace.rb +82 -159
  13. data/lib/cequel/metal/logger.rb +71 -0
  14. data/lib/cequel/metal/logging.rb +47 -0
  15. data/lib/cequel/metal/new_relic_instrumentation.rb +26 -0
  16. data/lib/cequel/metal/row.rb +36 -10
  17. data/lib/cequel/metal/row_specification.rb +21 -8
  18. data/lib/cequel/metal/statement.rb +30 -6
  19. data/lib/cequel/metal/updater.rb +89 -12
  20. data/lib/cequel/metal/writer.rb +23 -14
  21. data/lib/cequel/record.rb +52 -6
  22. data/lib/cequel/record/association_collection.rb +13 -6
  23. data/lib/cequel/record/associations.rb +146 -54
  24. data/lib/cequel/record/belongs_to_association.rb +34 -7
  25. data/lib/cequel/record/bound.rb +69 -12
  26. data/lib/cequel/record/bulk_writes.rb +29 -1
  27. data/lib/cequel/record/callbacks.rb +22 -6
  28. data/lib/cequel/record/collection.rb +273 -36
  29. data/lib/cequel/record/configuration_generator.rb +5 -0
  30. data/lib/cequel/record/data_set_builder.rb +86 -0
  31. data/lib/cequel/record/dirty.rb +11 -8
  32. data/lib/cequel/record/errors.rb +38 -4
  33. data/lib/cequel/record/has_many_association.rb +42 -9
  34. data/lib/cequel/record/lazy_record_collection.rb +39 -10
  35. data/lib/cequel/record/mass_assignment.rb +14 -6
  36. data/lib/cequel/record/persistence.rb +157 -20
  37. data/lib/cequel/record/properties.rb +147 -24
  38. data/lib/cequel/record/railtie.rb +15 -2
  39. data/lib/cequel/record/record_set.rb +504 -75
  40. data/lib/cequel/record/schema.rb +77 -13
  41. data/lib/cequel/record/scoped.rb +16 -11
  42. data/lib/cequel/record/secondary_indexes.rb +42 -6
  43. data/lib/cequel/record/tasks.rb +2 -1
  44. data/lib/cequel/record/validations.rb +51 -11
  45. data/lib/cequel/schema.rb +9 -0
  46. data/lib/cequel/schema/column.rb +172 -33
  47. data/lib/cequel/schema/create_table_dsl.rb +62 -31
  48. data/lib/cequel/schema/keyspace.rb +106 -7
  49. data/lib/cequel/schema/migration_validator.rb +128 -0
  50. data/lib/cequel/schema/table.rb +183 -20
  51. data/lib/cequel/schema/table_property.rb +92 -34
  52. data/lib/cequel/schema/table_reader.rb +45 -15
  53. data/lib/cequel/schema/table_synchronizer.rb +101 -43
  54. data/lib/cequel/schema/table_updater.rb +114 -19
  55. data/lib/cequel/schema/table_writer.rb +31 -13
  56. data/lib/cequel/schema/update_table_dsl.rb +71 -40
  57. data/lib/cequel/type.rb +214 -53
  58. data/lib/cequel/util.rb +6 -9
  59. data/lib/cequel/version.rb +2 -1
  60. data/spec/examples/record/associations_spec.rb +12 -12
  61. data/spec/examples/record/persistence_spec.rb +5 -5
  62. data/spec/examples/record/record_set_spec.rb +62 -50
  63. data/spec/examples/schema/table_synchronizer_spec.rb +37 -11
  64. data/spec/examples/schema/table_updater_spec.rb +3 -3
  65. data/spec/examples/spec_helper.rb +2 -11
  66. data/spec/examples/type_spec.rb +3 -3
  67. metadata +23 -4
  68. 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
- attr_reader :owner_class, :name, :association_class_name
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
@@ -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
- attr_reader :column, :value
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?(Type::Timeuuid) && !value.is_a?(CassandraCQL::UUID)
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
- class TimeuuidBound < Bound
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(:unlogged => true) do
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
- raise "Attempted to call __setobj__ on read-only delegate!"
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 Range === position then first, count = position.first, position.count
102
- else first, count = position, args[-2]
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
- raise ArgumentError,
112
- "Bad index #{position}: CQL lists do not support negative indices"
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
- def push(object)
153
- object = cast_element(object)
154
- updater.list_append(column_name, object)
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
- def unshift(*objs)
166
- objs.map!(&method(:cast_element))
167
- updater.list_prepend(column_name, objs.reverse)
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