cequel 1.5.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +4 -17
  3. data/CHANGELOG.md +8 -0
  4. data/DRIVER_TODO +5 -0
  5. data/Gemfile +4 -0
  6. data/Gemfile.lock +110 -65
  7. data/README.md +4 -3
  8. data/Rakefile +39 -3
  9. data/Vagrantfile +10 -13
  10. data/cequel.gemspec +42 -0
  11. data/lib/cequel.rb +2 -2
  12. data/lib/cequel/metal/data_set.rb +1 -1
  13. data/lib/cequel/metal/keyspace.rb +23 -15
  14. data/lib/cequel/metal/logging.rb +1 -1
  15. data/lib/cequel/metal/request_logger.rb +1 -1
  16. data/lib/cequel/metal/writer.rb +1 -1
  17. data/lib/cequel/record.rb +1 -1
  18. data/lib/cequel/record/association_collection.rb +1 -1
  19. data/lib/cequel/record/associations.rb +1 -1
  20. data/lib/cequel/record/belongs_to_association.rb +1 -1
  21. data/lib/cequel/record/collection.rb +34 -28
  22. data/lib/cequel/record/data_set_builder.rb +1 -1
  23. data/lib/cequel/record/lazy_record_collection.rb +1 -1
  24. data/lib/cequel/record/persistence.rb +4 -2
  25. data/lib/cequel/record/properties.rb +2 -2
  26. data/lib/cequel/record/record_set.rb +1 -1
  27. data/lib/cequel/record/schema.rb +2 -2
  28. data/lib/cequel/record/scoped.rb +1 -1
  29. data/lib/cequel/schema/create_table_dsl.rb +1 -1
  30. data/lib/cequel/schema/keyspace.rb +17 -2
  31. data/lib/cequel/schema/migration_validator.rb +1 -1
  32. data/lib/cequel/schema/update_table_dsl.rb +1 -1
  33. data/lib/cequel/type.rb +5 -5
  34. data/lib/cequel/util.rb +30 -0
  35. data/lib/cequel/uuids.rb +5 -5
  36. data/lib/cequel/version.rb +1 -1
  37. data/spec/examples/metal/keyspace_spec.rb +1 -1
  38. data/spec/examples/record/list_spec.rb +9 -0
  39. data/spec/examples/record/persistence_spec.rb +2 -2
  40. data/spec/examples/schema/keyspace_spec.rb +1 -2
  41. data/spec/examples/schema/table_reader_spec.rb +1 -1
  42. data/spec/examples/spec_helper.rb +3 -0
  43. data/spec/examples/uuids_spec.rb +1 -1
  44. data/spec/support/helpers.rb +7 -5
  45. data/tags +836 -0
  46. metadata +49 -38
data/Vagrantfile CHANGED
@@ -11,7 +11,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
11
11
  # please see the online documentation at vagrantup.com.
12
12
 
13
13
  # Every Vagrant virtual environment requires a box to build off of.
14
- config.vm.box = "precise64"
14
+ config.vm.box = "ubuntu/trusty64"
15
15
 
16
16
  # The url from where the 'config.vm.box' box will be fetched if it
17
17
  # doesn't already exist on the user's system.
@@ -45,13 +45,9 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
45
45
  # backing providers for Vagrant. These expose provider-specific options.
46
46
  # Example for VirtualBox:
47
47
  #
48
- # config.vm.provider :virtualbox do |vb|
49
- # # Don't boot with headless mode
50
- # vb.gui = true
51
- #
52
- # # Use VBoxManage to customize the VM. For example to change memory:
53
- # vb.customize ["modifyvm", :id, "--memory", "1024"]
54
- # end
48
+ config.vm.provider :virtualbox do |vb|
49
+ vb.memory = 1024
50
+ end
55
51
  #
56
52
  # View the documentation for the provider you're using for more
57
53
  # information on available options.
@@ -131,14 +127,15 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
131
127
  echo "start on runlevel [2345]
132
128
  stop on runlevel [06]
133
129
  exec /opt/apache-cassandra-$1/bin/cassandra" > /etc/init/cassandra.conf
134
- sed -i -e 's/rpc_address:.*/rpc_address: 0.0.0.0/' /opt/apache-cassandra-$1/conf/cassandra.yaml
135
- sed -i -e 's/start_native_transport:.*/start_native_transport: true/' /opt/apache-cassandra-$1/conf/cassandra.yaml
136
- sed -i -e 's/start_rpc:.*/start_rpc: true/' /opt/apache-cassandra-$1/conf/cassandra.yaml
130
+ sed -i -e 's/.*broadcast_rpc_address:.*/broadcast_rpc_address: 127.0.0.1/' /opt/apache-cassandra-$1/conf/cassandra.yaml
131
+ sed -i -e 's/^rpc_address:.*/rpc_address: 0.0.0.0/' /opt/apache-cassandra-$1/conf/cassandra.yaml
132
+ sed -i -e 's/^start_native_transport:.*/start_native_transport: true/' /opt/apache-cassandra-$1/conf/cassandra.yaml
133
+ sed -i -e 's/^start_rpc:.*/start_rpc: true/' /opt/apache-cassandra-$1/conf/cassandra.yaml
137
134
  service cassandra start
138
135
  SH
139
136
 
140
- listing = Net::HTTP.get(URI.parse("http://archive.apache.org/dist/cassandra/"))
141
- versions = listing.scan(%r(href="(\d+\.\d+\.\d+)/")).map(&:first).grep(/^(1\.2\.|2\.)/)
137
+ versions = File.read(File.expand_path('../.cassandra-versions', __FILE__)).each_line
138
+ .map(&:strip).grep(/^(1\.2\.|2\.)/)
142
139
  versions.each do |version|
143
140
  java_version = version =~ /^1/ ? '6' : '7'
144
141
  config.vm.define version do |machine|
data/cequel.gemspec ADDED
@@ -0,0 +1,42 @@
1
+ require File.expand_path('../lib/cequel/version', __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'cequel'
5
+ s.version = Cequel::VERSION
6
+ s.authors = [
7
+ 'Mat Brown', 'Aubrey Holland', 'Keenan Brock', 'Insoo Buzz Jung',
8
+ 'Louis Simoneau', 'Peter Williams', 'Kenneth Hoffman', 'Antti Tapio',
9
+ 'Ilya Bazylchuk', 'Dan Cardamore', 'Kei Kusakari', 'Oleh Novosad',
10
+ 'John Smart', 'Angelo Lakra', 'Olivier Lance', 'Tomohiro Nishimura',
11
+ 'Masaki Takahashi', 'G Gordon Worley III', 'Clark Bremer', 'Tamara Temple',
12
+ 'Long On', 'Lucas Mundim'
13
+ ]
14
+ s.homepage = "https://github.com/cequel/cequel"
15
+ s.email = 'mat.a.brown@gmail.com'
16
+ s.license = 'MIT'
17
+ s.summary = 'Full-featured, ActiveModel-compliant ORM for Cassandra using CQL3'
18
+ s.description = <<DESC
19
+ Cequel is an ActiveRecord-like domain model layer for Cassandra that exposes
20
+ the robust data modeling capabilities of CQL3, including parent-child
21
+ relationships via compound primary keys and in-memory atomic manipulation of
22
+ collection columns.
23
+ DESC
24
+
25
+ s.files = Dir['lib/**/*.rb', 'templates/**/*', 'spec/**/*.rb', '[A-Z]*']
26
+ s.test_files = Dir['spec/examples/**/*.rb']
27
+ s.has_rdoc = true
28
+ s.extra_rdoc_files = 'README.md'
29
+ s.required_ruby_version = '>= 1.9'
30
+ s.add_runtime_dependency 'activemodel', '>= 3.1', '< 5.0'
31
+ s.add_runtime_dependency 'cassandra-driver', '~> 1.0'
32
+ s.add_development_dependency 'appraisal', '~> 1.0'
33
+ s.add_development_dependency 'wwtd', '~> 0.5'
34
+ s.add_development_dependency 'rake', '~> 10.1'
35
+ s.add_development_dependency 'rspec', '~> 3.1'
36
+ s.add_development_dependency 'rspec-its', '~> 1.0'
37
+ s.add_development_dependency 'rubocop', '~> 0.28'
38
+ s.add_development_dependency 'timecop', '~> 0.7'
39
+ s.add_development_dependency 'travis', '~> 1.7'
40
+ s.add_development_dependency 'yard', '~> 0.6'
41
+ s.requirements << 'Cassandra >= 1.2.0'
42
+ end
data/lib/cequel.rb CHANGED
@@ -4,13 +4,13 @@ require 'delegate'
4
4
  require 'active_support'
5
5
  require 'active_support/deprecation'
6
6
  require 'active_support/core_ext'
7
- require 'cql'
7
+ require 'cassandra'
8
8
 
9
9
  require 'cequel/errors'
10
+ require 'cequel/util'
10
11
  require 'cequel/metal'
11
12
  require 'cequel/schema'
12
13
  require 'cequel/type'
13
- require 'cequel/util'
14
14
  require 'cequel/uuids'
15
15
  require 'cequel/instrumentation'
16
16
  require 'cequel/record'
@@ -21,7 +21,7 @@ module Cequel
21
21
  #
22
22
  class DataSet
23
23
  include Enumerable
24
- extend Forwardable
24
+ extend Util::Forwardable
25
25
 
26
26
  # @return [Keyspace] keyspace that this data set's table resides in
27
27
  attr_reader :keyspace
@@ -9,7 +9,7 @@ module Cequel
9
9
  # instance.
10
10
  #
11
11
  class Keyspace
12
- extend Forwardable
12
+ extend Util::Forwardable
13
13
  include Logging
14
14
  include MonitorMixin
15
15
 
@@ -154,9 +154,7 @@ module Cequel
154
154
  #
155
155
  def client
156
156
  synchronize do
157
- @client ||= raw_client.tap do |client|
158
- client.use(name) if name
159
- end
157
+ @client ||= cluster.connect(name)
160
158
  end
161
159
  end
162
160
 
@@ -193,8 +191,9 @@ module Cequel
193
191
  log('CQL', statement, *bind_vars) do
194
192
  begin
195
193
  client.execute(sanitize(statement, bind_vars),
196
- consistency || default_consistency)
197
- rescue Cql::NotConnectedError, Ione::Io::ConnectionError
194
+ consistency: consistency || default_consistency)
195
+ rescue Cassandra::Errors::NoHostsAvailable,
196
+ Ione::Io::ConnectionError => e
198
197
  clear_active_connections!
199
198
  raise if retries == 0
200
199
  retries -= 1
@@ -213,8 +212,11 @@ module Cequel
213
212
  if defined? @client
214
213
  remove_instance_variable(:@client)
215
214
  end
216
- if defined? @raw_client
217
- remove_instance_variable(:@raw_client)
215
+ if defined? @client_without_keyspace
216
+ remove_instance_variable(:@client_without_keyspace)
217
+ end
218
+ if defined? @cluster
219
+ remove_instance_variable(:@cluster)
218
220
  end
219
221
  end
220
222
 
@@ -235,7 +237,7 @@ module Cequel
235
237
  CQL
236
238
 
237
239
  log('CQL', statement, [name]) do
238
- raw_client.execute(sanitize(statement, [name])).any?
240
+ client_without_keyspace.execute(sanitize(statement, [name])).any?
239
241
  end
240
242
  end
241
243
 
@@ -249,15 +251,21 @@ module Cequel
249
251
  def_delegator :lock, :synchronize
250
252
  private :lock
251
253
 
252
- def raw_client
254
+ def cluster
253
255
  synchronize do
254
- @raw_client ||= Cql::Client.connect(client_options)
256
+ @cluster ||= Cassandra.cluster(client_options)
257
+ end
258
+ end
259
+
260
+ def client_without_keyspace
261
+ synchronize do
262
+ @client_without_keyspace ||= cluster.connect
255
263
  end
256
264
  end
257
265
 
258
266
  def client_options
259
267
  {hosts: hosts, port: port}.tap do |options|
260
- options[:credentials] = credentials if credentials
268
+ options.merge!(credentials) if credentials
261
269
  end
262
270
  end
263
271
 
@@ -272,9 +280,9 @@ module Cequel
272
280
  def extract_hosts_and_port(configuration)
273
281
  hosts, ports = [], Set[]
274
282
  ports << configuration[:port] if configuration.key?(:port)
275
- Array.wrap(configuration.fetch(
276
- :host, configuration.fetch(:hosts, '127.0.0.1'))).each do |host_port|
277
-
283
+ host_or_hosts =
284
+ configuration.fetch(:host, configuration.fetch(:hosts, '127.0.0.1'))
285
+ Array.wrap(host_or_hosts).each do |host_port|
278
286
  host, port = host_port.split(':')
279
287
  hosts << host
280
288
  if port
@@ -5,7 +5,7 @@ module Cequel
5
5
  # Methods to handle logging for {Keyspace} instances
6
6
  #
7
7
  module Logging
8
- extend Forwardable
8
+ extend Util::Forwardable
9
9
  def_delegators :request_logger, :logger, :logger=, :slowlog_threshold,
10
10
  :slowlog_threshold=
11
11
 
@@ -7,7 +7,7 @@ module Cequel
7
7
  # @api private
8
8
  #
9
9
  class RequestLogger
10
- extend Forwardable
10
+ extend Util::Forwardable
11
11
  # @return [::Logger] An instance of Logger that responds to methods for
12
12
  # standard severity levels
13
13
  attr_accessor :logger
@@ -11,7 +11,7 @@ module Cequel
11
11
  # @api private
12
12
  #
13
13
  class Writer
14
- extend Forwardable
14
+ extend Util::Forwardable
15
15
 
16
16
  #
17
17
  # @param data_set [DataSet] data set to write to
data/lib/cequel/record.rb CHANGED
@@ -78,7 +78,7 @@ module Cequel
78
78
  #
79
79
  module Record
80
80
  extend ActiveSupport::Concern
81
- extend Forwardable
81
+ extend Util::Forwardable
82
82
 
83
83
  included do
84
84
  include Properties
@@ -12,7 +12,7 @@ module Cequel
12
12
  #
13
13
  class AssociationCollection < DelegateClass(RecordSet)
14
14
  include Enumerable
15
- extend Forwardable
15
+ extend Util::Forwardable
16
16
 
17
17
  #
18
18
  # @yield [Record]
@@ -65,7 +65,7 @@ module Cequel
65
65
  # @see Associations
66
66
  #
67
67
  module ClassMethods
68
- include Forwardable
68
+ include Util::Forwardable
69
69
 
70
70
  # @!attribute parent_association
71
71
  # @return [BelongsToAssociation] association declared by
@@ -9,7 +9,7 @@ module Cequel
9
9
  # @since 1.0.0
10
10
  #
11
11
  class BelongsToAssociation
12
- extend Forwardable
12
+ extend Util::Forwardable
13
13
 
14
14
  # @return [Class] child class that declared `belongs_to`
15
15
  attr_reader :owner_class
@@ -53,7 +53,7 @@ module Cequel
53
53
  #
54
54
  module Collection
55
55
  extend ActiveSupport::Concern
56
- extend Forwardable
56
+ extend Util::Forwardable
57
57
 
58
58
  #
59
59
  # @!method loaded?
@@ -137,12 +137,16 @@ module Cequel
137
137
  def to_modify(&block)
138
138
  if loaded?
139
139
  model.__send__("#{column_name}_will_change!")
140
- block.call()
140
+ block.call
141
141
  else modifications << block
142
142
  end
143
143
  self
144
144
  end
145
145
 
146
+ def to_update
147
+ yield unless model.new_record?
148
+ end
149
+
146
150
  def modifications
147
151
  @modifications ||= []
148
152
  end
@@ -231,15 +235,17 @@ module Cequel
231
235
  "indices"
232
236
  end
233
237
 
234
- if count.nil?
235
- updater.list_replace(column_name, first, element)
236
- else
237
- element = Array.wrap(element)
238
- count.times do |i|
239
- if i < element.length
240
- updater.list_replace(column_name, first+i, element[i])
241
- else
242
- deleter.list_remove_at(column_name, first+i)
238
+ to_update do
239
+ if count.nil?
240
+ updater.list_replace(column_name, first, element)
241
+ else
242
+ element = Array.wrap(element)
243
+ count.times do |i|
244
+ if i < element.length
245
+ updater.list_replace(column_name, first+i, element[i])
246
+ else
247
+ deleter.list_remove_at(column_name, first+i)
248
+ end
243
249
  end
244
250
  end
245
251
  end
@@ -253,7 +259,7 @@ module Cequel
253
259
  # @return [List] self
254
260
  #
255
261
  def clear
256
- deleter.delete_columns(column_name)
262
+ to_update { deleter.delete_columns(column_name) }
257
263
  to_modify { super }
258
264
  end
259
265
 
@@ -265,7 +271,7 @@ module Cequel
265
271
  #
266
272
  def concat(array)
267
273
  array = cast_collection(array)
268
- updater.list_append(column_name, array)
274
+ to_update { updater.list_append(column_name, array) }
269
275
  to_modify { super }
270
276
  end
271
277
 
@@ -277,7 +283,7 @@ module Cequel
277
283
  #
278
284
  def delete(object)
279
285
  object = cast_element(object)
280
- updater.list_remove(column_name, object)
286
+ to_update { updater.list_remove(column_name, object) }
281
287
  to_modify { super }
282
288
  end
283
289
 
@@ -288,7 +294,7 @@ module Cequel
288
294
  # @return [List] self
289
295
  #
290
296
  def delete_at(index)
291
- deleter.list_remove_at(column_name, index)
297
+ to_update { deleter.list_remove_at(column_name, index) }
292
298
  to_modify { super }
293
299
  end
294
300
 
@@ -300,7 +306,7 @@ module Cequel
300
306
  #
301
307
  def push(*objects)
302
308
  objects.map! { |object| cast_element(object) }
303
- updater.list_append(column_name, objects)
309
+ to_update { updater.list_append(column_name, objects) }
304
310
  to_modify { super }
305
311
  end
306
312
  alias_method :<<, :push
@@ -314,7 +320,7 @@ module Cequel
314
320
  #
315
321
  def replace(array)
316
322
  array = cast_collection(array)
317
- updater.set(column_name => array)
323
+ to_update { updater.set(column_name => array) }
318
324
  to_modify { super }
319
325
  end
320
326
 
@@ -326,7 +332,7 @@ module Cequel
326
332
  #
327
333
  def unshift(*objects)
328
334
  objects.map!(&method(:cast_element))
329
- updater.list_prepend(column_name, objects.reverse)
335
+ to_update { updater.list_prepend(column_name, objects.reverse) }
330
336
  to_modify { super }
331
337
  end
332
338
  alias_method :prepend, :unshift
@@ -368,7 +374,7 @@ module Cequel
368
374
  #
369
375
  def add(object)
370
376
  object = cast_element(object)
371
- updater.set_add(column_name, object)
377
+ to_update { updater.set_add(column_name, object) }
372
378
  to_modify { super }
373
379
  end
374
380
  alias_method :<<, :add
@@ -380,7 +386,7 @@ module Cequel
380
386
  # @return [Set] self
381
387
  #
382
388
  def clear
383
- deleter.delete_columns(column_name)
389
+ to_update { deleter.delete_columns(column_name) }
384
390
  to_modify { super }
385
391
  end
386
392
 
@@ -392,7 +398,7 @@ module Cequel
392
398
  #
393
399
  def delete(object)
394
400
  object = cast_element(object)
395
- updater.set_remove(column_name, object)
401
+ to_update { updater.set_remove(column_name, object) }
396
402
  to_modify { super }
397
403
  end
398
404
 
@@ -404,7 +410,7 @@ module Cequel
404
410
  #
405
411
  def replace(set)
406
412
  set = cast_collection(set)
407
- updater.set(column_name => set)
413
+ to_update { updater.set(column_name => set) }
408
414
  to_modify { super }
409
415
  end
410
416
  end
@@ -419,7 +425,7 @@ module Cequel
419
425
  #
420
426
  class Map < DelegateClass(::Hash)
421
427
  include Collection
422
- extend Forwardable
428
+ extend Util::Forwardable
423
429
 
424
430
  # These methods involve mutation that cannot be expressed as a CQL
425
431
  # operation, so are not implemented.
@@ -456,7 +462,7 @@ module Cequel
456
462
  #
457
463
  def []=(key, value)
458
464
  key = cast_key(key)
459
- updater.map_update(column_name, key => value)
465
+ to_update { updater.map_update(column_name, key => value) }
460
466
  to_modify { super }
461
467
  end
462
468
  alias_method :store, :[]=
@@ -468,7 +474,7 @@ module Cequel
468
474
  # @return [Map] self
469
475
  #
470
476
  def clear
471
- deleter.delete_columns(column_name)
477
+ to_update { deleter.delete_columns(column_name) }
472
478
  to_modify { super }
473
479
  end
474
480
 
@@ -480,7 +486,7 @@ module Cequel
480
486
  #
481
487
  def delete(key)
482
488
  key = cast_key(key)
483
- deleter.map_remove(column_name, key)
489
+ to_update { deleter.map_remove(column_name, key) }
484
490
  to_modify { super }
485
491
  end
486
492
 
@@ -492,7 +498,7 @@ module Cequel
492
498
  #
493
499
  def merge!(hash)
494
500
  hash = cast_collection(hash)
495
- updater.map_update(column_name, hash)
501
+ to_update { updater.map_update(column_name, hash) }
496
502
  to_modify { super }
497
503
  end
498
504
  alias_method :update, :merge!
@@ -505,7 +511,7 @@ module Cequel
505
511
  #
506
512
  def replace(hash)
507
513
  hash = cast_collection(hash)
508
- updater.set(column_name => hash)
514
+ to_update { updater.set(column_name => hash) }
509
515
  to_modify { super }
510
516
  end
511
517