cequel 1.0.0.rc1 → 1.0.0.rc2

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 (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
@@ -0,0 +1,71 @@
1
+ module Cequel
2
+ module Metal
3
+ #
4
+ # The Logger class encapsulates logging functionality for {Keyspace}.
5
+ #
6
+ # @api private
7
+ #
8
+ class Logger
9
+ extend Forwardable
10
+ # @return [::Logger] An instance of Logger from the standard library
11
+ attr_reader :out
12
+ # @return [Integer] The severity level for this logger
13
+ attr_reader :severity
14
+ # @return [Integer] Only log queries that take longer than threshold ms
15
+ attr_accessor :threshold
16
+
17
+ #
18
+ # @param out [::Logger] An instance of Logger from the standard library
19
+ # @param severity [Integer] The severity level for this logger
20
+ # @param threshold [Integer] Only log queries that take longer than
21
+ # `threshold` ms
22
+ #
23
+ def initialize(out, severity, threshold = 0)
24
+ @out, @severity, @threshold = out, severity, threshold
25
+ end
26
+
27
+ #
28
+ # Log a CQL statement
29
+ #
30
+ # @param label [String] a logical label for this statement
31
+ # @param timing [Integer] how long this statement took in ms
32
+ # @param statement [String] the CQL statement to log
33
+ # @param bind_vars [Array] bind variables for the CQL statement
34
+ # @return [void]
35
+ #
36
+ def log(label, timing, statement, bind_vars)
37
+ if timing >= threshold
38
+ out.add(severity) do
39
+ sprintf(
40
+ '%s (%dms) %s',
41
+ label, timing, sanitize(statement, bind_vars)
42
+ )
43
+ end
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def_delegator 'CassandraCQL::Statement', :sanitize
50
+ end
51
+
52
+ #
53
+ # Logger for queries that resulted in an exception
54
+ #
55
+ class ExceptionLogger < Logger
56
+ #
57
+ # Log a CQL statement that resulted in an exception
58
+ #
59
+ # @param label [String] a logical label for this statement
60
+ # @param statement [String] the CQL statement to log
61
+ # @param bind_vars [Array] bind variables for the CQL statement
62
+ # @return [void]
63
+ #
64
+ def log(label, statement, bind_vars)
65
+ out.add(severity) do
66
+ sprintf('%s (ERROR) %s', label, sanitize(statement, bind_vars))
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,47 @@
1
+ module Cequel
2
+ module Metal
3
+ #
4
+ # Methods to handle logging for {Keyspace} instances
5
+ #
6
+ module Logging
7
+ def logger=(logger)
8
+ loggers << Logger.new(logger, ::Logger::DEBUG)
9
+ self.exception_logger = ExceptionLogger.new(logger, ::Logger::ERROR)
10
+ end
11
+
12
+ def slowlog=(slowlog)
13
+ warn "#slowlog= is deprecated and will be removed from a future " \
14
+ "version"
15
+ loggers << @slowlog = Logger.new(slowlog, ::Logger::WARN, 2000)
16
+ end
17
+
18
+ def slowlog_threshold=(threshold)
19
+ @slowlog.threshold = threshold
20
+ end
21
+
22
+ protected
23
+
24
+ attr_accessor :exception_logger
25
+
26
+ private
27
+
28
+ def log(label, statement, *bind_vars)
29
+ response = nil
30
+ begin
31
+ time = Benchmark.ms { response = yield }
32
+ loggers.each do |logger|
33
+ logger.log(label, time, statement, bind_vars)
34
+ end
35
+ rescue Exception => e
36
+ exception_logger.log(label, statement, bind_vars) if exception_logger
37
+ raise
38
+ end
39
+ response
40
+ end
41
+
42
+ def loggers
43
+ @loggers ||= []
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,26 @@
1
+ begin
2
+ require 'new_relic/agent/method_tracer'
3
+ rescue LoadError => e
4
+ fail LoadError, "Can't use NewRelic instrumentation without NewRelic gem"
5
+ end
6
+
7
+ module Cequel
8
+ module Metal
9
+ #
10
+ # Provides NewRelic instrumentation for CQL queries.
11
+ #
12
+ module NewRelicInstrumentation
13
+ extend ActiveSupport::Concern
14
+
15
+ included do
16
+ include NewRelic::Agent::MethodTracer
17
+
18
+ add_method_tracer :execute,
19
+ 'Database/Cassandra/#{args[0][/^[A-Z ]*[A-Z]/]' \
20
+ '.sub(/ FROM$/, \'\')}'
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ Cequel::Metal::Keyspace.module_eval { include NewRelicInstrumentation }
@@ -1,13 +1,25 @@
1
1
  module Cequel
2
-
3
2
  module Metal
4
-
5
- class Row < ActiveSupport::HashWithIndifferentAccess
6
-
3
+ #
4
+ # A result row from a CQL query. Acts as a hash of column names to values,
5
+ # but also exposes TTLs and writetimes
6
+ #
7
+ # @since 1.0.0
8
+ #
9
+ class Row < DelegateClass(ActiveSupport::HashWithIndifferentAccess)
10
+ #
11
+ # Encapsulate a row from CassandraCQL
12
+ #
13
+ # @param result_row [CassandraCQL::Row] row from underlying driver
14
+ # @return [Row] encapsulated row
15
+ #
16
+ # @api private
17
+ #
7
18
  def self.from_result_row(result_row)
8
19
  if result_row
9
20
  new.tap do |row|
10
- result_row.column_names.zip(result_row.column_values) do |name, value|
21
+ names, values = result_row.column_names, result_row.column_values
22
+ names.zip(values) do |name, value|
11
23
  if name =~ /^(ttl|writetime)\((.+)\)$/
12
24
  if $1 == 'ttl' then row.set_ttl($2, value)
13
25
  else row.set_writetime($2, value)
@@ -19,30 +31,44 @@ module Cequel
19
31
  end
20
32
  end
21
33
 
22
- def initialize(*_)
23
- super
34
+ #
35
+ # @api private
36
+ #
37
+ def initialize
38
+ super(ActiveSupport::HashWithIndifferentAccess.new)
24
39
  @ttls = ActiveSupport::HashWithIndifferentAccess.new
25
40
  @writetimes = ActiveSupport::HashWithIndifferentAccess.new
26
41
  end
27
42
 
43
+ #
44
+ # Get the TTL (time-to-live) of a column
45
+ #
46
+ # @param column [Symbol] column name
47
+ # @return [Integer] TTL of column in seconds
48
+ #
28
49
  def ttl(column)
29
50
  @ttls[column]
30
51
  end
31
52
 
53
+ #
54
+ # Get the writetime of a column
55
+ #
56
+ # @param column [Symbol] column name
57
+ # @return [Integer] writetime of column in nanoseconds since epoch
58
+ #
32
59
  def writetime(column)
33
60
  @writetimes[column]
34
61
  end
35
62
 
63
+ # @private
36
64
  def set_ttl(column, value)
37
65
  @ttls[column] = value
38
66
  end
39
67
 
68
+ # @private
40
69
  def set_writetime(column, value)
41
70
  @writetimes[column] = value
42
71
  end
43
-
44
72
  end
45
-
46
73
  end
47
-
48
74
  end
@@ -1,22 +1,38 @@
1
1
  module Cequel
2
-
3
2
  module Metal
4
-
5
3
  #
6
- # @private
4
+ # Encapsulates a row specification (`WHERE` clause) constructed from a
5
+ # column ane one or more values to match
6
+ #
7
+ # @api private
7
8
  #
8
9
  class RowSpecification
9
-
10
+ #
11
+ # Build one or more row specifications
12
+ #
13
+ # @param column_values [Hash] map of column name to value or values
14
+ # @return [Array<RowSpecification>] collection of row specifications
15
+ #
10
16
  def self.build(column_values)
11
17
  column_values.map { |column, value| new(column, value) }
12
18
  end
13
19
 
14
- attr_reader :column, :value
20
+ # @return [Symbol] column name
21
+ attr_reader :column
22
+ # @return [Object, Array] value or values to match
23
+ attr_reader :value
15
24
 
25
+ #
26
+ # @param column [Symbol] column name
27
+ # @param value [Object,Array] value or values to match
28
+ #
16
29
  def initialize(column, value)
17
30
  @column, @value = column, value
18
31
  end
19
32
 
33
+ #
34
+ # @return [String] row specification as CQL fragment
35
+ #
20
36
  def cql
21
37
  case @value
22
38
  when Array
@@ -29,9 +45,6 @@ module Cequel
29
45
  ["#{@column} = ?", @value]
30
46
  end
31
47
  end
32
-
33
48
  end
34
-
35
49
  end
36
-
37
50
  end
@@ -1,35 +1,59 @@
1
1
  module Cequel
2
-
3
2
  module Metal
4
-
3
+ #
4
+ # Builder for CQL statements. Contains a CQL string with bind substitutions
5
+ # and a collection of bind variables
6
+ #
7
+ # @api private
8
+ #
5
9
  class Statement
6
- attr_reader :bind_vars, :length
10
+ # @return [Array] bind variables for CQL string
11
+ attr_reader :bind_vars
7
12
 
8
13
  def initialize
9
14
  @cql, @bind_vars = [], []
10
15
  end
11
16
 
17
+ #
18
+ # @return [String] CQL statement
19
+ #
12
20
  def cql
13
21
  @cql.join
14
22
  end
15
23
 
24
+ #
25
+ # Add a CQL fragment with optional bind variables to the beginning of
26
+ # the statement
27
+ #
28
+ # @param (see #append)
29
+ # @return [void]
30
+ #
16
31
  def prepend(cql, *bind_vars)
17
32
  @cql.unshift(cql)
18
33
  @bind_vars.unshift(*bind_vars)
19
34
  end
20
35
 
36
+ #
37
+ # Add a CQL fragment with optional bind variables to the end of the
38
+ # statement
39
+ #
40
+ # @param cql [String] CQL fragment
41
+ # @param bind_vars [Object] zero or more bind variables
42
+ # @return [void]
43
+ #
21
44
  def append(cql, *bind_vars)
22
45
  @cql << cql
23
46
  @bind_vars.concat(bind_vars)
24
47
  self
25
48
  end
26
49
 
50
+ #
51
+ # @return [Array] this statement as an array of arguments to
52
+ # Keyspace#execute (CQL string followed by bind variables)
53
+ #
27
54
  def args
28
55
  [cql, *bind_vars]
29
56
  end
30
-
31
57
  end
32
-
33
58
  end
34
-
35
59
  end
@@ -1,9 +1,25 @@
1
1
  module Cequel
2
-
3
2
  module Metal
4
-
3
+ #
4
+ # Builder for `UPDATE` statement containing heterogeneous operations (set
5
+ # columns, atomically mutate collections)
6
+ #
7
+ # @see DataSet#update
8
+ # @see Deleter
9
+ # @see
10
+ # http://www.datastax.com/documentation/cql/3.0/webhelp/index.html#cql/cql_reference/update_r.html
11
+ # CQL UPDATE documentation
12
+ # @since 1.0.0
13
+ #
5
14
  class Updater < Writer
6
-
15
+ #
16
+ # Directly set column values
17
+ #
18
+ # @param data [Hash] map of column names to values
19
+ # @return [void]
20
+ #
21
+ # @see DataSet#update
22
+ #
7
23
  def set(data)
8
24
  data.each_pair do |column, value|
9
25
  prepare_upsert_value(value) do |binding, *values|
@@ -13,36 +29,100 @@ module Cequel
13
29
  end
14
30
  end
15
31
 
32
+ #
33
+ # Prepend elements to a list column
34
+ #
35
+ # @param column [Symbol] column name
36
+ # @param elements [Array<Object>] elements to prepend
37
+ # @return [void]
38
+ #
39
+ # @see DataSet#list_prepend
40
+ #
16
41
  def list_prepend(column, elements)
17
42
  statements << "#{column} = [?] + #{column}"
18
43
  bind_vars << elements
19
44
  end
20
45
 
46
+ #
47
+ # Append elements to a list column
48
+ #
49
+ # @param column [Symbol] column name
50
+ # @param elements [Array] elements to append
51
+ # @return [void]
52
+ #
53
+ # @see DataSet#list_append
54
+ #
21
55
  def list_append(column, elements)
22
56
  statements << "#{column} = #{column} + [?]"
23
57
  bind_vars << elements
24
58
  end
25
59
 
60
+ #
61
+ # Remove all occurrences of an element from a list
62
+ #
63
+ # @param column [Symbol] column name
64
+ # @param value value to remove
65
+ # @return [void]
66
+ #
67
+ # @see DataSet#list_remove
68
+ #
26
69
  def list_remove(column, value)
27
70
  statements << "#{column} = #{column} - [?]"
28
71
  bind_vars << value
29
72
  end
30
73
 
74
+ #
75
+ # Replace a list item at a given position
76
+ #
77
+ # @param column [Symbol] column name
78
+ # @param index [Integer] index at which to replace value
79
+ # @param value new value for position
80
+ # @return [void]
81
+ #
82
+ # @see DataSet#list_replace
83
+ #
31
84
  def list_replace(column, index, value)
32
85
  statements << "#{column}[#{index}] = ?"
33
86
  bind_vars << value
34
87
  end
35
88
 
89
+ #
90
+ # Add elements to a set
91
+ #
92
+ # @param column [Symbol] column name
93
+ # @param values [Set] elements to add to set
94
+ # @return [void]
95
+ #
96
+ # @see DataSet#set_add
97
+ #
36
98
  def set_add(column, values)
37
99
  statements << "#{column} = #{column} + {?}"
38
100
  bind_vars << values
39
101
  end
40
102
 
41
- def set_remove(column, value)
103
+ #
104
+ # Remove elements from a set
105
+ #
106
+ # @param column [Symbol] column name
107
+ # @param values [Set] elements to remove from set
108
+ # @return [void]
109
+ #
110
+ # @see DataSet#set_remove
111
+ #
112
+ def set_remove(column, values)
42
113
  statements << "#{column} = #{column} - {?}"
43
- bind_vars << ::Kernel.Array(value)
114
+ bind_vars << ::Kernel.Array(values)
44
115
  end
45
116
 
117
+ #
118
+ # Add or update elements in a map
119
+ #
120
+ # @param column [Symbol] column name
121
+ # @param updates [Hash] map of keys to values to update in map
122
+ # @return [void]
123
+ #
124
+ # @see DataSet#map_update
125
+ #
46
126
  def map_update(column, updates)
47
127
  binding_pairs = ::Array.new(updates.length) { '?:?' }.join(',')
48
128
  statements << "#{column} = #{column} + {#{binding_pairs}}"
@@ -52,14 +132,11 @@ module Cequel
52
132
  private
53
133
 
54
134
  def write_to_statement(statement)
55
- statement.append("UPDATE #{table_name}").
56
- append(generate_upsert_options).
57
- append(" SET ").
58
- append(statements.join(', '), *bind_vars)
135
+ statement.append("UPDATE #{table_name}")
136
+ .append(generate_upsert_options)
137
+ .append(" SET ")
138
+ .append(statements.join(', '), *bind_vars)
59
139
  end
60
-
61
140
  end
62
-
63
141
  end
64
-
65
142
  end