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
data/lib/cequel/batch.rb DELETED
@@ -1,58 +0,0 @@
1
- require 'stringio'
2
-
3
- module Cequel
4
-
5
- #
6
- # Encapsulates a batch operation
7
- #
8
- # @see Keyspace::batch
9
- #
10
- class Batch
11
-
12
- #
13
- # @param keyspace [Keyspace] the keyspace that this batch will be executed on
14
- # @param options [Hash]
15
- # @option options (see Keyspace#batch)
16
- # @see Keyspace#batch
17
- # @todo support batch-level consistency options
18
- #
19
- def initialize(keyspace, options = {})
20
- @keyspace = keyspace
21
- @auto_apply = options[:auto_apply]
22
- reset
23
- end
24
-
25
- #
26
- # Add a statement to the batch.
27
- #
28
- # @param (see Keyspace#execute)
29
- #
30
- def execute(cql, *bind_vars)
31
- @statement.append("#{cql}\n", *bind_vars)
32
- @statement_count += 1
33
- if @auto_apply && @statement_count >= @auto_apply
34
- apply
35
- reset
36
- end
37
- end
38
-
39
- #
40
- # Send the batch to Cassandra
41
- #
42
- def apply
43
- return if @statement_count.zero?
44
- @statement.append("APPLY BATCH\n")
45
- @keyspace.execute(*@statement.args)
46
- end
47
-
48
- private
49
-
50
- def reset
51
- @statement = Statement.new
52
- @statement.append("BEGIN BATCH\n")
53
- @statement_count = 0
54
- end
55
-
56
- end
57
-
58
- end
@@ -1,22 +0,0 @@
1
- module Cequel
2
-
3
- #
4
- # @api private
5
- #
6
- class CqlRowSpecification
7
-
8
- def self.build(condition, bind_vars)
9
- [new(condition, bind_vars)]
10
- end
11
-
12
- def initialize(condition, bind_vars)
13
- @condition, @bind_vars = condition, bind_vars
14
- end
15
-
16
- def cql
17
- [@condition, *@bind_vars]
18
- end
19
-
20
- end
21
-
22
- end
@@ -1,371 +0,0 @@
1
- module Cequel
2
-
3
- #
4
- # Encapsulates a data set, specified as a column family and optionally
5
- # various query elements.
6
- #
7
- # @todo Support ALTER, CREATE, CREATE INDEX, DROP
8
- #
9
- class DataSet
10
-
11
- include Enumerable
12
-
13
- # @return [Keyspace] the keyspace this data set lives in
14
- attr_reader :keyspace
15
-
16
- # @return [Symbol] the name of the column family this data set draws from
17
- attr_reader :column_family
18
-
19
- #
20
- # @param column_family [Symbol] column family for this data set
21
- # @param keyspace [Keyspace] keyspace this data set's column family lives in
22
- #
23
- # @see Keyspace#[]
24
- #
25
- def initialize(column_family, keyspace)
26
- @column_family, @keyspace = column_family, keyspace
27
- @select_columns, @select_options, @row_specifications = [], {}, []
28
- end
29
-
30
- #
31
- # Insert a row into the column family.
32
- #
33
- # @param [Hash] data column-value pairs. The first entry *must* be the key column.
34
- # @param [Options] options options for persisting the row
35
- # @option (see #generate_upsert_options)
36
- # @note if a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.
37
- #
38
- def insert(data, options = {})
39
- options.symbolize_keys!
40
- cql = "INSERT INTO #{@column_family}" <<
41
- " (?) VALUES (?)" <<
42
- generate_upsert_options(options)
43
-
44
- @keyspace.write(cql, data.keys, data.values)
45
- end
46
-
47
- #
48
- # Update rows
49
- #
50
- # @param [Hash] data column-value pairs
51
- # @param [Options] options options for persisting the column data
52
- # @option (see #generate_upsert_options)
53
- # @note if a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.
54
- #
55
- # @todo support counter columns
56
- #
57
- def update(data, options = {})
58
- statement = Statement.new.
59
- append("UPDATE #{@column_family}").
60
- append(generate_upsert_options(options)).
61
- append(" SET " << data.keys.map { |k| "? = ?" }.join(', '), *data.to_a.flatten).
62
- append(*row_specifications_cql)
63
- @keyspace.write(*statement.args)
64
- rescue EmptySubquery
65
- # Noop -- no rows to update
66
- end
67
-
68
- def increment(data, options = {})
69
- operations = data.map do |key, value|
70
- operator = value < 0 ? '-' : '+'
71
- "? = ? #{operator} ?"
72
- end
73
- statement = Statement.new.
74
- append("UPDATE #{@column_family}").
75
- append(generate_upsert_options(options)).
76
- append(
77
- " SET " << operations.join(', '),
78
- *data.flat_map { |column, count| [column, column, count.abs] }
79
- ).append(*row_specifications_cql)
80
-
81
- @keyspace.write(*statement.args)
82
- end
83
- alias_method :incr, :increment
84
-
85
- def decrement(data, options = {})
86
- increment(Hash[data.map { |column, count| [column, -count] }], options)
87
- end
88
- alias_method :decr, :decrement
89
-
90
- #
91
- # Delete data from the column family
92
- #
93
- # @param columns zero or more columns to delete. Deletes the entire row if none specified.
94
- # @param options persistence options
95
- # @note if a enclosed in a Keyspace#batch block, this method will be executed as part of the batch.
96
- #
97
- def delete(*columns)
98
- options = columns.extract_options!
99
- column_aliases = columns.empty? ? '' : " #{columns.join(', ')}"
100
- statement = Statement.new.append('DELETE')
101
- statement = statement.append(' ?', columns) if columns.any?
102
- statement = statement.
103
- append(" FROM #{@column_family}").
104
- append(generate_upsert_options(options)).
105
- append(*row_specifications_cql)
106
-
107
- @keyspace.write(*statement.args)
108
- rescue EmptySubquery
109
- # Noop -- no rows to delete
110
- end
111
-
112
- #
113
- # Remove all data from the column family.
114
- #
115
- # @note This method always executes immediately, even if called within a batch block. This method does not respect scoped row specifications.
116
- # @see #delete
117
- #
118
- def truncate
119
- @keyspace.execute("TRUNCATE #{@column_family}")
120
- end
121
-
122
- #
123
- # Select specified columns from this data set.
124
- #
125
- # @param *columns [Symbol,Array] columns to select
126
- # @return [DataSet] new data set scoped to specified columns
127
- #
128
- def select(*columns)
129
- options = columns.extract_options!.symbolize_keys
130
- clone.tap do |data_set|
131
- if columns.length == 1 && Range === columns.first
132
- range = columns.first
133
- options[:from] = range.first
134
- options[:to] = range.last
135
- else
136
- data_set.select_columns.concat(columns.flatten)
137
- end
138
- data_set.select_options.merge!(options)
139
- end
140
- end
141
-
142
- #
143
- # Select specified columns from this data set, overriding chained scope.
144
- #
145
- # @param *columns [Symbol,Array] columns to select
146
- # @return [DataSet] new data set scoped to specified columns
147
- #
148
- def select!(*columns)
149
- clone.tap do |data_set|
150
- data_set.select_columns.replace(columns.flatten)
151
- end
152
- end
153
-
154
- #
155
- # Add consistency option for data set retrieval
156
- #
157
- # @param consistency [:one,:quorum,:local_quorum,:each_quorum]
158
- # @return [DataSet] new data set with specified consistency
159
- #
160
- def consistency(consistency)
161
- clone.tap { |data_set| data_set.consistency = consistency.to_sym }
162
- end
163
-
164
- #
165
- # Add a row_specification to this data set
166
- #
167
- # @param row_specification [Hash, String] row_specification statement
168
- # @param *bind_vars bind variables, only if using a CQL string row_specification
169
- # @return [DataSet] new data set scoped to this row_specification
170
- # @example Using a simple hash
171
- # DB[:posts].where(:title => 'Hey')
172
- # @example Using a CQL string
173
- # DB[:posts].where("title = 'Hey'")
174
- # @example Using a CQL string with bind variables
175
- # DB[:posts].where('title = ?', 'Hey')
176
- # @example Use another data set as an input -- inner data set must return a single column per row!
177
- # DB[:blogs].where(:id => DB[:posts].select(:blog_id).where(:title => 'Hey'))
178
- #
179
- def where(row_specification, *bind_vars)
180
- clone.tap do |data_set|
181
- data_set.row_specifications.
182
- concat(build_row_specifications(row_specification, bind_vars))
183
- end
184
- end
185
-
186
- def where!(row_specification, *bind_vars)
187
- clone.tap do |data_set|
188
- data_set.row_specifications.
189
- replace(build_row_specifications(row_specification, bind_vars))
190
- end
191
- end
192
-
193
- #
194
- # Limit the number of rows returned by this data set
195
- #
196
- # @param limit [Integer] maximum number of rows to return
197
- # @return [DataSet] new data set scoped with given limit
198
- #
199
- def limit(limit)
200
- clone.tap { |data_set| data_set.limit = limit }
201
- end
202
-
203
- #
204
- # Enumerate over rows in this data set. Along with #each, all other
205
- # Enumerable methods are implemented.
206
- #
207
- # @yield [Hash] result rows
208
- # @return [Enumerator] enumerator for rows, if no block given
209
- #
210
- def each
211
- if block_given?
212
- begin
213
- @keyspace.execute(*cql).fetch do |row|
214
- yield row.to_hash.with_indifferent_access
215
- end
216
- rescue EmptySubquery
217
- # Noop -- yield no results
218
- end
219
- else
220
- enum_for(:each)
221
- end
222
- end
223
-
224
- #
225
- # @return [Hash] the first row in this data set
226
- #
227
- def first
228
- row = @keyspace.execute(*limit(1).cql).fetch_row
229
- row.to_hash.with_indifferent_access if row
230
- rescue EmptySubquery
231
- nil
232
- end
233
-
234
- #
235
- # @return [Fixnum] the number of rows in this data set
236
- #
237
- def count
238
- @keyspace.execute(*count_cql).fetch_row['count']
239
- rescue EmptySubquery
240
- 0
241
- end
242
-
243
- #
244
- # @raise [EmptySubquery] if row specifications use a subquery that returns no results
245
- # @return [String] CQL select statement encoding this data set's scope.
246
- #
247
- def cql
248
- statement = Statement.new.
249
- append(*select_cql).
250
- append(" FROM #{@column_family}").
251
- append(consistency_cql).
252
- append(*row_specifications_cql).
253
- append(limit_cql).
254
- args
255
- end
256
-
257
- #
258
- # @return [String] CQL statement to get count of rows in this data set
259
- #
260
- def count_cql
261
- Statement.new.
262
- append("SELECT COUNT(*) FROM #{@column_family}").
263
- append(consistency_cql).
264
- append(*row_specifications_cql).
265
- append(limit_cql).args
266
- end
267
-
268
- def inspect
269
- "#<#{self.class.name}: #{CassandraCQL::Statement.sanitize(cql.first, cql[1..-1])}>"
270
- end
271
-
272
- def ==(other)
273
- cql == other.cql
274
- end
275
-
276
- attr_reader :select_columns, :select_options, :row_specifications
277
- attr_writer :consistency, :limit
278
-
279
- private
280
-
281
- def initialize_copy(source)
282
- super
283
- @select_columns = source.select_columns.clone
284
- @select_options = source.select_options.clone
285
- @row_specifications = source.row_specifications.clone
286
- end
287
-
288
- #
289
- # Generate CQL option statement for inserts and updates
290
- #
291
- # @param [Hash] options options for insert
292
- # @option options [Symbol,String] :consistency required consistency for the write
293
- # @option options [Integer] :ttl time-to-live in seconds for the written data
294
- # @option options [Time,Integer] :timestamp the timestamp associated with the column values
295
- #
296
- def generate_upsert_options(options)
297
- if keyspace.default_consistency
298
- options[:consistency] ||= keyspace.default_consistency
299
- end
300
- if options.empty?
301
- ''
302
- else
303
- ' USING ' <<
304
- options.map do |key, value|
305
- serialized_value =
306
- case key
307
- when :consistency then value.to_s.upcase
308
- when :timestamp then value.to_i
309
- else value
310
- end
311
- "#{key.to_s.upcase} #{serialized_value}"
312
- end.join(' AND ')
313
- end
314
- end
315
-
316
- def select_cql
317
- ['SELECT '].tap do |args|
318
- cql = args.first
319
- if @select_options[:first]
320
- cql << "FIRST #{@select_options[:first]} "
321
- elsif @select_options[:last]
322
- cql << "FIRST #{@select_options[:last]} REVERSED "
323
- end
324
- if @select_options[:from] || @select_options[:to]
325
- cql << '?..?'
326
- args << (@select_options[:from] || '') << (@select_options[:to] || '')
327
- elsif @select_columns.any?
328
- cql << '?'
329
- args << @select_columns
330
- else
331
- cql << '*'
332
- end
333
- end
334
- end
335
-
336
- def consistency_cql
337
- consistency = @consistency || keyspace.default_consistency
338
- if consistency
339
- " USING CONSISTENCY #{consistency.upcase}"
340
- else ''
341
- end
342
- end
343
-
344
- def row_specifications_cql
345
- if @row_specifications.any?
346
- cql_fragments, bind_vars = [], []
347
- @row_specifications.each do |spec|
348
- cql_with_vars = spec.cql
349
- cql_fragments << cql_with_vars.shift
350
- bind_vars.concat(cql_with_vars)
351
- end
352
- [" WHERE #{cql_fragments.join(' AND ')}", *bind_vars]
353
- else ['']
354
- end
355
- end
356
-
357
- def limit_cql
358
- @limit ? " LIMIT #{@limit}" : ''
359
- end
360
-
361
- def build_row_specifications(row_specification, bind_vars)
362
- case row_specification
363
- when Hash then RowSpecification.build(row_specification)
364
- when String then CqlRowSpecification.build(row_specification, bind_vars)
365
- else raise ArgumentError, "Invalid argument #{row_specification.inspect}; expected Hash or String"
366
- end
367
- end
368
-
369
- end
370
-
371
- end