cequel 0.5.6 → 1.0.0.pre.1

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.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/lib/cequel.rb +5 -8
  3. data/lib/cequel/errors.rb +1 -0
  4. data/lib/cequel/metal.rb +17 -0
  5. data/lib/cequel/metal/batch.rb +62 -0
  6. data/lib/cequel/metal/cql_row_specification.rb +26 -0
  7. data/lib/cequel/metal/data_set.rb +461 -0
  8. data/lib/cequel/metal/deleter.rb +47 -0
  9. data/lib/cequel/metal/incrementer.rb +35 -0
  10. data/lib/cequel/metal/inserter.rb +53 -0
  11. data/lib/cequel/metal/keyspace.rb +213 -0
  12. data/lib/cequel/metal/row.rb +48 -0
  13. data/lib/cequel/metal/row_specification.rb +37 -0
  14. data/lib/cequel/metal/statement.rb +30 -0
  15. data/lib/cequel/metal/updater.rb +65 -0
  16. data/lib/cequel/metal/writer.rb +73 -0
  17. data/lib/cequel/model.rb +12 -84
  18. data/lib/cequel/model/association_collection.rb +23 -0
  19. data/lib/cequel/model/associations.rb +84 -80
  20. data/lib/cequel/model/base.rb +74 -0
  21. data/lib/cequel/model/belongs_to_association.rb +31 -0
  22. data/lib/cequel/model/callbacks.rb +14 -10
  23. data/lib/cequel/model/collection.rb +255 -0
  24. data/lib/cequel/model/errors.rb +6 -6
  25. data/lib/cequel/model/has_many_association.rb +26 -0
  26. data/lib/cequel/model/mass_assignment.rb +31 -0
  27. data/lib/cequel/model/persistence.rb +119 -115
  28. data/lib/cequel/model/properties.rb +89 -87
  29. data/lib/cequel/model/railtie.rb +21 -14
  30. data/lib/cequel/model/record_set.rb +285 -0
  31. data/lib/cequel/model/schema.rb +33 -0
  32. data/lib/cequel/model/scoped.rb +5 -48
  33. data/lib/cequel/model/validations.rb +18 -18
  34. data/lib/cequel/schema.rb +15 -0
  35. data/lib/cequel/schema/column.rb +135 -0
  36. data/lib/cequel/schema/create_table_dsl.rb +56 -0
  37. data/lib/cequel/schema/keyspace.rb +50 -0
  38. data/lib/cequel/schema/table.rb +120 -0
  39. data/lib/cequel/schema/table_property.rb +67 -0
  40. data/lib/cequel/schema/table_reader.rb +139 -0
  41. data/lib/cequel/schema/table_synchronizer.rb +114 -0
  42. data/lib/cequel/schema/table_updater.rb +83 -0
  43. data/lib/cequel/schema/table_writer.rb +80 -0
  44. data/lib/cequel/schema/update_table_dsl.rb +60 -0
  45. data/lib/cequel/type.rb +232 -0
  46. data/lib/cequel/version.rb +1 -1
  47. data/spec/environment.rb +5 -1
  48. data/spec/examples/metal/data_set_spec.rb +608 -0
  49. data/spec/examples/model/associations_spec.rb +84 -74
  50. data/spec/examples/model/callbacks_spec.rb +66 -59
  51. data/spec/examples/model/list_spec.rb +393 -0
  52. data/spec/examples/model/map_spec.rb +229 -0
  53. data/spec/examples/model/mass_assignment_spec.rb +55 -0
  54. data/spec/examples/model/naming_spec.rb +11 -4
  55. data/spec/examples/model/persistence_spec.rb +140 -150
  56. data/spec/examples/model/properties_spec.rb +122 -75
  57. data/spec/examples/model/record_set_spec.rb +285 -0
  58. data/spec/examples/model/schema_spec.rb +44 -0
  59. data/spec/examples/model/serialization_spec.rb +20 -14
  60. data/spec/examples/model/set_spec.rb +133 -0
  61. data/spec/examples/model/spec_helper.rb +0 -10
  62. data/spec/examples/model/validations_spec.rb +51 -38
  63. data/spec/examples/schema/table_reader_spec.rb +328 -0
  64. data/spec/examples/schema/table_synchronizer_spec.rb +172 -0
  65. data/spec/examples/schema/table_updater_spec.rb +157 -0
  66. data/spec/examples/schema/table_writer_spec.rb +225 -0
  67. data/spec/examples/spec_helper.rb +29 -0
  68. data/spec/examples/type_spec.rb +204 -0
  69. data/spec/support/helpers.rb +67 -8
  70. metadata +121 -152
  71. data/lib/cequel/batch.rb +0 -58
  72. data/lib/cequel/cql_row_specification.rb +0 -22
  73. data/lib/cequel/data_set.rb +0 -371
  74. data/lib/cequel/keyspace.rb +0 -205
  75. data/lib/cequel/model/class_internals.rb +0 -49
  76. data/lib/cequel/model/column.rb +0 -20
  77. data/lib/cequel/model/counter.rb +0 -35
  78. data/lib/cequel/model/dictionary.rb +0 -126
  79. data/lib/cequel/model/dirty.rb +0 -53
  80. data/lib/cequel/model/dynamic.rb +0 -31
  81. data/lib/cequel/model/inheritable.rb +0 -48
  82. data/lib/cequel/model/instance_internals.rb +0 -23
  83. data/lib/cequel/model/local_association.rb +0 -42
  84. data/lib/cequel/model/magic.rb +0 -79
  85. data/lib/cequel/model/mass_assignment_security.rb +0 -21
  86. data/lib/cequel/model/naming.rb +0 -17
  87. data/lib/cequel/model/observer.rb +0 -42
  88. data/lib/cequel/model/readable_dictionary.rb +0 -182
  89. data/lib/cequel/model/remote_association.rb +0 -40
  90. data/lib/cequel/model/scope.rb +0 -362
  91. data/lib/cequel/model/subclass_internals.rb +0 -45
  92. data/lib/cequel/model/timestamps.rb +0 -52
  93. data/lib/cequel/model/translation.rb +0 -17
  94. data/lib/cequel/row_specification.rb +0 -63
  95. data/lib/cequel/statement.rb +0 -23
  96. data/spec/examples/data_set_spec.rb +0 -444
  97. data/spec/examples/keyspace_spec.rb +0 -84
  98. data/spec/examples/model/counter_spec.rb +0 -94
  99. data/spec/examples/model/dictionary_spec.rb +0 -301
  100. data/spec/examples/model/dirty_spec.rb +0 -39
  101. data/spec/examples/model/dynamic_spec.rb +0 -41
  102. data/spec/examples/model/inheritable_spec.rb +0 -45
  103. data/spec/examples/model/magic_spec.rb +0 -199
  104. data/spec/examples/model/mass_assignment_security_spec.rb +0 -13
  105. data/spec/examples/model/observer_spec.rb +0 -86
  106. data/spec/examples/model/scope_spec.rb +0 -677
  107. data/spec/examples/model/timestamps_spec.rb +0 -52
  108. data/spec/examples/model/translation_spec.rb +0 -23
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1b840b678aaff8377efff196f7e011a37939fbbc
4
+ data.tar.gz: b0726b2298165f250f382cf13177c425dd819fcd
5
+ SHA512:
6
+ metadata.gz: 5606d970c9051fa1e45168b0f249c1e2ebe0be2009fb8ab0270af1d75151c96ee990978c73affe468d85394190dfc2d1abf3516c57c86a4edf20266882e4b47f
7
+ data.tar.gz: 98d44471f0dfc64ee16bd31168d76793647a22afb6c17d7fe91b85e38bf65ea88f7017486a91d04a5d8b6804153fe6dd7cda133943eb4514e894b046c98e26bb
data/lib/cequel.rb CHANGED
@@ -1,17 +1,14 @@
1
1
  require 'active_support/core_ext'
2
- require 'cassandra-cql'
2
+ require 'cassandra-cql/1.2'
3
3
  require 'connection_pool'
4
4
 
5
- require 'cequel/batch'
6
5
  require 'cequel/errors'
7
- require 'cequel/cql_row_specification'
8
- require 'cequel/data_set'
9
- require 'cequel/keyspace'
10
- require 'cequel/row_specification'
11
- require 'cequel/statement'
6
+ require 'cequel/metal'
7
+ require 'cequel/schema'
8
+ require 'cequel/type'
12
9
 
13
10
  module Cequel
14
11
  def self.connect(configuration = nil)
15
- Keyspace.new(configuration || {})
12
+ Metal::Keyspace.new(configuration || {})
16
13
  end
17
14
  end
data/lib/cequel/errors.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  module Cequel
2
2
  Error = Class.new(StandardError)
3
3
  EmptySubquery = Class.new(Error)
4
+ InvalidSchemaMigration = Class.new(Error)
4
5
  end
@@ -0,0 +1,17 @@
1
+ require 'cequel/metal/batch'
2
+ require 'cequel/metal/cql_row_specification'
3
+ require 'cequel/metal/data_set'
4
+ require 'cequel/metal/keyspace'
5
+ require 'cequel/metal/row'
6
+ require 'cequel/metal/row_specification'
7
+ require 'cequel/metal/statement'
8
+ require 'cequel/metal/writer'
9
+ require 'cequel/metal/deleter'
10
+ require 'cequel/metal/incrementer'
11
+ require 'cequel/metal/inserter'
12
+ require 'cequel/metal/updater'
13
+
14
+ module Cequel
15
+ module Metal
16
+ end
17
+ end
@@ -0,0 +1,62 @@
1
+ require 'stringio'
2
+
3
+ module Cequel
4
+
5
+ module Metal
6
+
7
+ #
8
+ # Encapsulates a batch operation
9
+ #
10
+ # @see Keyspace::batch
11
+ #
12
+ class Batch
13
+
14
+ #
15
+ # @param keyspace [Keyspace] the keyspace that this batch will be executed on
16
+ # @param options [Hash]
17
+ # @option options (see Keyspace#batch)
18
+ # @see Keyspace#batch
19
+ # @todo support batch-level consistency options
20
+ #
21
+ def initialize(keyspace, options = {})
22
+ @keyspace = keyspace
23
+ @auto_apply = options[:auto_apply]
24
+ reset
25
+ end
26
+
27
+ #
28
+ # Add a statement to the batch.
29
+ #
30
+ # @param (see Keyspace#execute)
31
+ #
32
+ def execute(cql, *bind_vars)
33
+ @statement.append("#{cql}\n", *bind_vars)
34
+ @statement_count += 1
35
+ if @auto_apply && @statement_count >= @auto_apply
36
+ apply
37
+ reset
38
+ end
39
+ end
40
+
41
+ #
42
+ # Send the batch to Cassandra
43
+ #
44
+ def apply
45
+ return if @statement_count.zero?
46
+ @statement.append("APPLY BATCH\n")
47
+ @keyspace.execute(*@statement.args)
48
+ end
49
+
50
+ private
51
+
52
+ def reset
53
+ @statement = Statement.new
54
+ @statement.append("BEGIN BATCH\n")
55
+ @statement_count = 0
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,26 @@
1
+ module Cequel
2
+
3
+ module Metal
4
+
5
+ #
6
+ # @api private
7
+ #
8
+ class CqlRowSpecification
9
+
10
+ def self.build(condition, bind_vars)
11
+ [new(condition, bind_vars)]
12
+ end
13
+
14
+ def initialize(condition, bind_vars)
15
+ @condition, @bind_vars = condition, bind_vars
16
+ end
17
+
18
+ def cql
19
+ [@condition, *@bind_vars]
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,461 @@
1
+ module Cequel
2
+
3
+ module Metal
4
+
5
+ #
6
+ # Encapsulates a data set, specified as a column family and optionally
7
+ # various query elements.
8
+ #
9
+ # @todo Support ALTER, CREATE, CREATE INDEX, DROP
10
+ #
11
+ class DataSet
12
+
13
+ include Enumerable
14
+ extend Forwardable
15
+
16
+ attr_reader :keyspace, :table_name, :select_columns, :ttl_columns,
17
+ :writetime_columns, :row_specifications, :sort_order, :row_limit
18
+
19
+ def_delegator :keyspace, :execute, :execute_cql
20
+ def_delegator :keyspace, :write
21
+
22
+ #
23
+ # @param table_name [Symbol] column family for this data set
24
+ # @param keyspace [Keyspace] keyspace this data set's column family lives in
25
+ #
26
+ # @see Keyspace#[]
27
+ #
28
+ def initialize(table_name, keyspace)
29
+ @table_name, @keyspace = table_name, keyspace
30
+ @select_columns, @ttl_columns, @writetime_columns, @row_specifications,
31
+ @sort_order = [], [], [], [], {}
32
+ end
33
+
34
+ #
35
+ # Insert a row into the column family.
36
+ #
37
+ # @param [Hash] data column-value pairs. The first entry *must* be the key column.
38
+ # @param [Options] options options for persisting the row
39
+ # @option (see #generate_upsert_options)
40
+ # @note if a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.
41
+ #
42
+ def insert(data, options = {})
43
+ inserter(options) { insert(data) }.execute
44
+ end
45
+
46
+ #
47
+ # Update rows
48
+ #
49
+ # @param [Hash] data column-value pairs
50
+ # @param [Options] options options for persisting the column data
51
+ # @option (see #generate_upsert_options)
52
+ # @note if a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.
53
+ #
54
+ def update(*args, &block)
55
+ if block
56
+ updater(args.extract_options!, &block).execute
57
+ else
58
+ data = args.shift
59
+ updater(args.extract_options!) { set(data) }.execute
60
+ end
61
+ end
62
+
63
+ def increment(data, options = {})
64
+ incrementer(options) { increment(data) }.execute
65
+ end
66
+
67
+ def decrement(data, options = {})
68
+ incrementer(options) { decrement(data) }.execute
69
+ end
70
+ alias_method :decr, :decrement
71
+
72
+ #
73
+ # Prepend element(s) to a list in the row(s) matched by this data set.
74
+ #
75
+ # @param [Symbol] column name of list column to prepend to
76
+ # @param [Object,Array] elements one element or an array of elements to prepend
77
+ # @param [Options] options options for persisting the column data
78
+ # @option (see #generate_upsert_options)
79
+ # @note If multiple elements are passed, they will appear in the list in reverse order.
80
+ # @note if a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.
81
+ #
82
+ def list_prepend(column, elements, options = {})
83
+ updater(options) { list_prepend(column, elements) }.execute
84
+ end
85
+
86
+ #
87
+ # Append element(s) to a list in the row(s) matched by this data set.
88
+ #
89
+ # @param [Symbol] column name of list column to append to
90
+ # @param [Object,Array] elements one element or an array of elements to append
91
+ # @param [Options] options options for persisting the column data
92
+ # @option (see #generate_upsert_options)
93
+ # @note if a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.
94
+ #
95
+ def list_append(column, elements, options = {})
96
+ updater(options) { list_append(column, elements) }.execute
97
+ end
98
+
99
+ #
100
+ # Replace a list element at a specified index with a new value
101
+ #
102
+ # @param [Symbol] column name of list column
103
+ # @param [Integer] index which element to replace
104
+ # @param [Object] value new value at this index
105
+ # @param [Options] options options for persisting the data
106
+ # @option (see #generate_upsert_options)
107
+ # @note if a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.
108
+ #
109
+ def list_replace(column, index, value, options = {})
110
+ updater(options) { list_replace(column, index, value) }.execute
111
+ end
112
+
113
+ #
114
+ # Remove all occurrences of a given value from a list column
115
+ #
116
+ # @param [Symbol] column name of list column
117
+ # @param [Object] value value to remove
118
+ # @param [Options] options for persisting the data
119
+ # @option (see #generate_upsert_options)
120
+ # @note if a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.
121
+ #
122
+ def list_remove(column, value, options = {})
123
+ updater(options) { list_remove(column, value) }.execute
124
+ end
125
+
126
+ #
127
+ # Remove all occurrences of a given value from a list column
128
+ #
129
+ # @param [Symbol] column name of list column
130
+ # @param [Object] position position in list to remove value from
131
+ # @param [Options] options for persisting the data
132
+ # @option (see #generate_upsert_options)
133
+ # @note if a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.
134
+ #
135
+ def list_remove_at(column, *positions)
136
+ options = positions.extract_options!
137
+ deleter(options) { list_remove_at(column, *positions) }.execute
138
+ end
139
+
140
+ #
141
+ # Remove a given key from a map column
142
+ #
143
+ # @param [Symbol] column name of map column
144
+ # @param [Object] key map key to remove
145
+ # @param [Options] options for persisting the data
146
+ # @option (see #generate_upsert_options)
147
+ # @note if a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.
148
+ #
149
+ def map_remove(column, *keys)
150
+ options = keys.extract_options!
151
+ deleter(options) { map_remove(column, *keys) }.execute
152
+ end
153
+
154
+ #
155
+ # Add one or more elements to a set
156
+ #
157
+ # @param [Symbol] column name of set column
158
+ # @param [Object,Set] value value or values to add
159
+ # @param [Options] options for persisting the data
160
+ # @option (see #generate_upsert_options)
161
+ # @note if a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.
162
+ #
163
+ def set_add(column, values, options = {})
164
+ updater(options) { set_add(column, values) }.execute
165
+ end
166
+
167
+ #
168
+ # Remove one or more elements from a set
169
+ #
170
+ # @param [Symbol] column name of set column
171
+ # @param [Object,Set] value value or values to add
172
+ # @param [Options] options for persisting the data
173
+ # @option (see #generate_upsert_options)
174
+ # @note if a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.
175
+ #
176
+ def set_remove(column, value, options = {})
177
+ updater(options) { set_remove(column, value) }.execute
178
+ end
179
+
180
+ #
181
+ # Update one or more map elements
182
+ #
183
+ # @param [Symbol] column name of set column
184
+ # @param [Hash] map updates
185
+ # @param [Options] options for persisting the data
186
+ # @option (see #generate_upsert_options)
187
+ # @note if a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.
188
+ #
189
+ def map_update(column, updates, options = {})
190
+ updater(options) { map_update(column, updates) }.execute
191
+ end
192
+
193
+ #
194
+ # Delete data from the column family
195
+ #
196
+ # @param columns zero or more columns to delete. Deletes the entire row if none specified.
197
+ # @param options persistence options
198
+ # @note if a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.
199
+ #
200
+ def delete(*columns, &block)
201
+ options = columns.extract_options!
202
+ if block
203
+ deleter(options, &block).execute
204
+ elsif columns.empty?
205
+ deleter(options) { delete_row }.execute
206
+ else
207
+ deleter(options) { delete_columns(*columns) }.execute
208
+ end
209
+ end
210
+
211
+ #
212
+ # Select specified columns from this data set.
213
+ #
214
+ # @param *columns [Symbol,Array] columns to select
215
+ # @return [DataSet] new data set scoped to specified columns
216
+ #
217
+ def select(*columns)
218
+ clone.tap do |data_set|
219
+ data_set.select_columns.concat(columns.flatten)
220
+ end
221
+ end
222
+
223
+ #
224
+ # Return the remaining TTL for the specified columns from this data set.
225
+ #
226
+ # @param *columns [Symbol,Array] columns to select
227
+ # @return [DataSet] new data set scoped to specified columns
228
+ #
229
+ def select_ttl(*columns)
230
+ clone.tap do |data_set|
231
+ data_set.ttl_columns.concat(columns.flatten)
232
+ end
233
+ end
234
+
235
+ #
236
+ # Return the write time for the specified columns in the data set
237
+ #
238
+ # @param *columns [Symbol,Array] columns to select
239
+ # @return [DataSet] new data set scoped to specified columns
240
+ #
241
+ def select_writetime(*columns)
242
+ clone.tap do |data_set|
243
+ data_set.writetime_columns.concat(columns.flatten)
244
+ end
245
+ end
246
+
247
+ #
248
+ # Select specified columns from this data set, overriding chained scope.
249
+ #
250
+ # @param *columns [Symbol,Array] columns to select
251
+ # @return [DataSet] new data set scoped to specified columns
252
+ #
253
+ def select!(*columns)
254
+ clone.tap do |data_set|
255
+ data_set.select_columns.replace(columns.flatten)
256
+ end
257
+ end
258
+
259
+ #
260
+ # Add a row_specification to this data set
261
+ #
262
+ # @param row_specification [Hash, String] row_specification statement
263
+ # @param *bind_vars bind variables, only if using a CQL string row_specification
264
+ # @return [DataSet] new data set scoped to this row_specification
265
+ # @example Using a simple hash
266
+ # DB[:posts].where(:title => 'Hey')
267
+ # @example Using a CQL string
268
+ # DB[:posts].where("title = 'Hey'")
269
+ # @example Using a CQL string with bind variables
270
+ # DB[:posts].where('title = ?', 'Hey')
271
+ # @example Use another data set as an input -- inner data set must return a single column per row!
272
+ # DB[:blogs].where(:id => DB[:posts].select(:blog_id).where(:title => 'Hey'))
273
+ #
274
+ def where(row_specification, *bind_vars)
275
+ clone.tap do |data_set|
276
+ data_set.row_specifications.
277
+ concat(build_row_specifications(row_specification, bind_vars))
278
+ end
279
+ end
280
+
281
+ def where!(row_specification, *bind_vars)
282
+ clone.tap do |data_set|
283
+ data_set.row_specifications.
284
+ replace(build_row_specifications(row_specification, bind_vars))
285
+ end
286
+ end
287
+
288
+ #
289
+ # Limit the number of rows returned by this data set
290
+ #
291
+ # @param limit [Integer] maximum number of rows to return
292
+ # @return [DataSet] new data set scoped with given limit
293
+ #
294
+ def limit(limit)
295
+ clone.tap { |data_set| data_set.row_limit = limit }
296
+ end
297
+
298
+ #
299
+ # Control how the result rows are sorted. Note that you can only sort by
300
+ # clustering keys, and in the case of multiple clustering keys you can only
301
+ # sort by the schema's clustering order or the reverse of the clustering
302
+ # order for all keys.
303
+ #
304
+ def order(pairs)
305
+ clone.tap do |data_set|
306
+ data_set.sort_order.merge!(pairs.symbolize_keys)
307
+ end
308
+ end
309
+
310
+ #
311
+ # Enumerate over rows in this data set. Along with #each, all other
312
+ # Enumerable methods are implemented.
313
+ #
314
+ # @yield [Hash] result rows
315
+ # @return [Enumerator] enumerator for rows, if no block given
316
+ #
317
+ def each
318
+ if block_given?
319
+ begin
320
+ keyspace.execute(*cql).fetch do |row|
321
+ yield Row.from_result_row(row)
322
+ end
323
+ rescue EmptySubquery
324
+ # Noop -- yield no results
325
+ end
326
+ else
327
+ enum_for(:each)
328
+ end
329
+ end
330
+
331
+ #
332
+ # @return [Hash] the first row in this data set
333
+ #
334
+ def first
335
+ row = keyspace.execute(*limit(1).cql).fetch_row
336
+ Row.from_result_row(row)
337
+ rescue EmptySubquery
338
+ nil
339
+ end
340
+
341
+ #
342
+ # @return [Fixnum] the number of rows in this data set
343
+ #
344
+ def count
345
+ keyspace.execute(*count_cql).fetch_row['count']
346
+ rescue EmptySubquery
347
+ 0
348
+ end
349
+
350
+ #
351
+ # @return [String] CQL select statement encoding this data set's scope.
352
+ #
353
+ def cql
354
+ statement = Statement.new.
355
+ append(select_cql).
356
+ append(" FROM #{table_name}").
357
+ append(*row_specifications_cql).
358
+ append(sort_order_cql).
359
+ append(limit_cql).
360
+ args
361
+ end
362
+
363
+ #
364
+ # @return [String] CQL statement to get count of rows in this data set
365
+ #
366
+ def count_cql
367
+ Statement.new.
368
+ append("SELECT COUNT(*) FROM #{table_name}").
369
+ append(*row_specifications_cql).
370
+ append(limit_cql).args
371
+ end
372
+
373
+ def inspect
374
+ "#<#{self.class.name}: #{CassandraCQL::Statement.sanitize(cql.first, cql[1..-1])}>"
375
+ end
376
+
377
+ def ==(other)
378
+ cql == other.cql
379
+ end
380
+
381
+ def inserter(options = {}, &block)
382
+ Inserter.new(self, options, &block)
383
+ end
384
+
385
+ def updater(options = {}, &block)
386
+ Updater.new(self, options, &block)
387
+ end
388
+
389
+ def incrementer(options = {}, &block)
390
+ Incrementer.new(self, options, &block)
391
+ end
392
+
393
+ def deleter(options = {}, &block)
394
+ Deleter.new(self, options, &block)
395
+ end
396
+
397
+ def row_specifications_cql
398
+ if row_specifications.any?
399
+ cql_fragments, bind_vars = [], []
400
+ row_specifications.each do |spec|
401
+ cql_with_vars = spec.cql
402
+ cql_fragments << cql_with_vars.shift
403
+ bind_vars.concat(cql_with_vars)
404
+ end
405
+ [" WHERE #{cql_fragments.join(' AND ')}", *bind_vars]
406
+ else ['']
407
+ end
408
+ end
409
+
410
+ protected
411
+ attr_writer :row_limit
412
+
413
+ private
414
+
415
+ def initialize_copy(source)
416
+ super
417
+ @select_columns = source.select_columns.clone
418
+ @ttl_columns = source.ttl_columns.clone
419
+ @writetime_columns = source.writetime_columns.clone
420
+ @row_specifications = source.row_specifications.clone
421
+ @sort_order = source.sort_order.clone
422
+ end
423
+
424
+ def select_cql
425
+ all_columns = select_columns +
426
+ ttl_columns.map { |column| "TTL(#{column})" } +
427
+ writetime_columns.map { |column| "WRITETIME(#{column})" }
428
+
429
+ if all_columns.any?
430
+ "SELECT #{all_columns.join(',')}"
431
+ else
432
+ 'SELECT *'
433
+ end
434
+ end
435
+
436
+ def limit_cql
437
+ row_limit ? " LIMIT #{row_limit}" : ''
438
+ end
439
+
440
+ def sort_order_cql
441
+ if sort_order.any?
442
+ order = sort_order.
443
+ map { |column, direction| "#{column} #{direction.to_s.upcase}" }.
444
+ join(', ')
445
+ " ORDER BY #{order}"
446
+ end
447
+ end
448
+
449
+ def build_row_specifications(row_specification, bind_vars)
450
+ case row_specification
451
+ when Hash then RowSpecification.build(row_specification)
452
+ when String then CqlRowSpecification.build(row_specification, bind_vars)
453
+ else raise ArgumentError, "Invalid argument #{row_specification.inspect}; expected Hash or String"
454
+ end
455
+ end
456
+
457
+ end
458
+
459
+ end
460
+
461
+ end