cequel 0.5.6 → 1.0.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
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