cassandra-driver 1.0.0.beta.3 → 1.0.0.rc.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 (53) hide show
  1. checksums.yaml +13 -5
  2. data/README.md +8 -6
  3. data/lib/cassandra.rb +99 -13
  4. data/lib/cassandra/address_resolution.rb +36 -0
  5. data/lib/cassandra/address_resolution/policies.rb +2 -0
  6. data/lib/cassandra/address_resolution/policies/ec2_multi_region.rb +56 -0
  7. data/lib/cassandra/address_resolution/policies/none.rb +35 -0
  8. data/lib/cassandra/auth.rb +1 -1
  9. data/lib/cassandra/auth/providers/password.rb +1 -1
  10. data/lib/cassandra/client.rb +2 -2
  11. data/lib/cassandra/client/batch.rb +3 -3
  12. data/lib/cassandra/client/client.rb +12 -12
  13. data/lib/cassandra/client/connection_manager.rb +2 -2
  14. data/lib/cassandra/client/connector.rb +6 -10
  15. data/lib/cassandra/client/prepared_statement.rb +4 -4
  16. data/lib/cassandra/client/request_runner.rb +1 -2
  17. data/lib/cassandra/cluster.rb +15 -5
  18. data/lib/cassandra/cluster/client.rb +158 -96
  19. data/lib/cassandra/cluster/connector.rb +42 -27
  20. data/lib/cassandra/cluster/control_connection.rb +384 -132
  21. data/lib/cassandra/cluster/options.rb +5 -2
  22. data/lib/cassandra/cluster/registry.rb +19 -9
  23. data/lib/cassandra/compression.rb +1 -1
  24. data/lib/cassandra/compression/compressors/lz4.rb +1 -1
  25. data/lib/cassandra/compression/compressors/snappy.rb +1 -1
  26. data/lib/cassandra/driver.rb +28 -20
  27. data/lib/cassandra/errors.rb +325 -35
  28. data/lib/cassandra/future.rb +3 -3
  29. data/lib/cassandra/load_balancing/policies/dc_aware_round_robin.rb +7 -3
  30. data/lib/cassandra/load_balancing/policies/token_aware.rb +1 -1
  31. data/lib/cassandra/protocol.rb +0 -16
  32. data/lib/cassandra/protocol/cql_byte_buffer.rb +18 -18
  33. data/lib/cassandra/protocol/cql_protocol_handler.rb +74 -8
  34. data/lib/cassandra/protocol/frame_decoder.rb +2 -2
  35. data/lib/cassandra/protocol/frame_encoder.rb +1 -1
  36. data/lib/cassandra/protocol/response.rb +1 -1
  37. data/lib/cassandra/protocol/responses/detailed_error_response.rb +16 -1
  38. data/lib/cassandra/protocol/responses/error_response.rb +17 -0
  39. data/lib/cassandra/protocol/responses/event_response.rb +1 -1
  40. data/lib/cassandra/protocol/responses/raw_rows_result_response.rb +1 -1
  41. data/lib/cassandra/protocol/responses/result_response.rb +1 -1
  42. data/lib/cassandra/protocol/responses/rows_result_response.rb +1 -1
  43. data/lib/cassandra/protocol/type_converter.rb +4 -3
  44. data/lib/cassandra/reconnection.rb +1 -1
  45. data/lib/cassandra/retry.rb +3 -5
  46. data/lib/cassandra/session.rb +11 -5
  47. data/lib/cassandra/table.rb +1 -1
  48. data/lib/cassandra/time_uuid.rb +21 -83
  49. data/lib/cassandra/util.rb +1 -1
  50. data/lib/cassandra/uuid.rb +6 -4
  51. data/lib/cassandra/uuid/generator.rb +207 -0
  52. data/lib/cassandra/version.rb +1 -1
  53. metadata +24 -19
@@ -22,7 +22,7 @@ module Cassandra
22
22
  def self.decode(protocol_version, buffer, length, trace_id=nil)
23
23
  type = buffer.read_string
24
24
  impl = EVENT_TYPES[type]
25
- raise UnsupportedEventTypeError, %(Unsupported event type: "#{type}") unless impl
25
+ raise Errors::DecodingError, %(Unsupported event type: "#{type}") unless impl
26
26
  new_length = length - 4 - type.bytesize
27
27
  impl.decode(protocol_version, buffer, new_length, trace_id)
28
28
  end
@@ -31,7 +31,7 @@ module Cassandra
31
31
  end
32
32
 
33
33
  def rows
34
- raise UnmaterializedRowsError, 'Not materialized!' unless @rows
34
+ raise Errors::DecodingError, 'Not materialized!' unless @rows
35
35
  @rows
36
36
  end
37
37
 
@@ -28,7 +28,7 @@ module Cassandra
28
28
  def self.decode(protocol_version, buffer, length, trace_id=nil)
29
29
  kind = buffer.read_int
30
30
  impl = RESULT_TYPES[kind]
31
- raise UnsupportedResultKindError, %(Unsupported result kind: #{kind}) unless impl
31
+ raise Errors::DecodingError, %(Unsupported result kind: #{kind}) unless impl
32
32
  impl.decode(protocol_version, buffer, length - 4, trace_id)
33
33
  end
34
34
 
@@ -87,7 +87,7 @@ module Cassandra
87
87
  sub_type = read_column_type(buffer)
88
88
  [:set, sub_type]
89
89
  else
90
- raise UnsupportedColumnTypeError, %(Unsupported column type: #{id})
90
+ raise Errors::DecodingError, %(Unsupported column type: #{id})
91
91
  end
92
92
  end
93
93
  type
@@ -46,7 +46,7 @@ module Cassandra
46
46
  case type
47
47
  when Array
48
48
  unless value.nil? || value.is_a?(Enumerable)
49
- raise InvalidValueError, 'Value for collection must be enumerable'
49
+ raise EncodingError, 'Value for collection must be enumerable'
50
50
  end
51
51
  case type.first
52
52
  when :list, :set
@@ -75,12 +75,12 @@ module Cassandra
75
75
  nil_to_bytes(buffer, size_bytes)
76
76
  end
77
77
  else
78
- raise UnsupportedColumnTypeError, %(Unsupported column collection type: #{type.first})
78
+ raise EncodingError, %(Unsupported column collection type: #{type.first})
79
79
  end
80
80
  else
81
81
  converter = @to_bytes_converters[type]
82
82
  unless converter
83
- raise UnsupportedColumnTypeError, %(Unsupported column type: #{type})
83
+ raise EncodingError, %(Unsupported column type: #{type})
84
84
  end
85
85
  converter.call(buffer, value, size_bytes)
86
86
  end
@@ -140,6 +140,7 @@ module Cassandra
140
140
  size = buffer.read_signed_int
141
141
  return nil if size & 0x80000000 == 0x80000000
142
142
  end
143
+ return nil if size.zero?
143
144
  size
144
145
  end
145
146
 
@@ -30,7 +30,7 @@ module Cassandra
30
30
 
31
31
  # A reconnection policy
32
32
  # @abstract Actual reconnection policies supplied as `:reconnection_policy`
33
- # option to {Cassandra.connect} don't need to inherit this class, only
33
+ # option to {Cassandra.cluster} don't need to inherit this class, only
34
34
  # implement its methods. This class exists for documentation purposes
35
35
  # only.
36
36
  class Policy
@@ -19,7 +19,7 @@
19
19
  module Cassandra
20
20
  module Retry
21
21
  # @abstract Actual retry policies supplied as `:retry_policy` option to
22
- # {Cassandra.connect} don't need to inherit this class, only implement
22
+ # {Cassandra.cluster} don't need to inherit this class, only implement
23
23
  # its methods. This class exists for documentation purposes only.
24
24
  module Policy
25
25
  # Decides wether to retry a read and at what consistency level.
@@ -51,8 +51,7 @@ module Cassandra
51
51
  # @param statement [Cassandra::Statement] the original statement that timed out
52
52
  # @param consistency [Symbol] the original consistency level for the
53
53
  # request, one of {Cassandra::CONSISTENCIES}
54
- # @param type [Symbol] One of `:simple`, `:batch`, `:unlogged_batch`,
55
- # `:counter` or `:batch_log`
54
+ # @param type [Symbol] One of {Cassandra::WRITE_TYPES}
56
55
  # @param required [Integer] the number of acks required to achieve
57
56
  # requested consistency level
58
57
  # @param received [Integer] the number of acks received by the time the
@@ -75,8 +74,7 @@ module Cassandra
75
74
  # request, one of {Cassandra::CONSISTENCIES}
76
75
  # @param required [Integer] the number of replicas required to achieve
77
76
  # requested consistency level
78
- # @param alive [Integer] the number of replicas received by the time the
79
- # query timed out
77
+ # @param alive [Integer] the number of replicas available for the request
80
78
  # @param retries [Integer] the number of retries already performed
81
79
  #
82
80
  # @return [Cassandra::Policies::Retry::Decision] a retry decision
@@ -54,14 +54,17 @@ module Cassandra
54
54
  # relevant for conditional updates and specifies a serial consistency to
55
55
  # be used, one of {Cassandra::SERIAL_CONSISTENCIES}
56
56
  #
57
- # @see Cassandra.connect for description of options that can be specified
58
- # on the cluster-level as well as default values chosen.
57
+ # @see Cassandra.cluster Options that can be specified on the cluster-level
58
+ # and their default values.
59
59
  #
60
60
  # @note Last argument will be treated as `options` if it is a {Hash}.
61
61
  # Therefore, make sure to pass empty `options` when executing a statement
62
62
  # with the last parameter required to be a map datatype.
63
63
  #
64
64
  # @return [Cassandra::Future<Cassandra::Result>]
65
+ #
66
+ # @see Cassandra::Session#execute A list of errors this future can be
67
+ # resolved with
65
68
  def execute_async(statement, *args)
66
69
  if args.last.is_a?(::Hash)
67
70
  options = @options.override(args.pop)
@@ -91,8 +94,11 @@ module Cassandra
91
94
  # @see Cassandra::Future#get
92
95
  #
93
96
  # @return [Cassandra::Result] query result
94
- # @raise [Cassandra::Errors::NoHostsAvailable] if none of the hosts can be reached
95
- # @raise [Cassandra::Errors::QueryError] if Cassandra returns an error response
97
+ # @raise [Cassandra::Errors::NoHostsAvailable] if all hosts fail
98
+ # @raise [Cassandra::Errors::ExecutionError] if Cassandra fails to execute
99
+ # @raise [Cassandra::Errors::ValidationError] if Cassandra fails to validate
100
+ # @raise [Cassandra::Errors::TimeoutError] if request has not completed
101
+ # within the `:timeout` given
96
102
  def execute(*args)
97
103
  execute_async(*args).get
98
104
  end
@@ -131,7 +137,7 @@ module Cassandra
131
137
  #
132
138
  # @return [Cassandra::Statements::Prepared] prepared statement
133
139
  # @raise [Cassandra::Errors::NoHostsAvailable] if none of the hosts can be reached
134
- # @raise [Cassandra::Errors::QueryError] if Cassandra returns an error response
140
+ # @raise [Cassandra::Errors::ExecutionError] if Cassandra returns an error response
135
141
  def prepare(*args)
136
142
  prepare_async(*args).get
137
143
  end
@@ -247,7 +247,7 @@ module Cassandra
247
247
  # @private
248
248
  def create_partition_key(values)
249
249
  partition_key = @partition_key
250
- return nil unless partition_key.size == values.size
250
+ return nil if partition_key.size > values.size
251
251
 
252
252
  if partition_key.one?
253
253
  column = partition_key.first
@@ -19,6 +19,7 @@
19
19
  module Cassandra
20
20
  # A variant of UUID which can extract its time component.
21
21
  #
22
+ # You can use {Cassandra::Uuid::Generator} to generate {Cassandra::TimeUuid}s
22
23
  class TimeUuid < Uuid
23
24
  include Comparable
24
25
 
@@ -29,18 +30,37 @@ module Cassandra
29
30
  t = time_bits - GREGORIAN_OFFSET
30
31
  seconds = t/10_000_000
31
32
  microseconds = (t - seconds * 10_000_000)/10.0
33
+
32
34
  Time.at(seconds, microseconds).utc
33
35
  end
34
36
 
37
+ # Returns the date component from this UUID as Date.
38
+ #
39
+ # This just sugar around {#to_time}
40
+ #
41
+ # @return [Date]
42
+ def to_date
43
+ to_time.to_date
44
+ end
45
+
46
+ # Compares this timeuuid with another timeuuid
47
+ #
48
+ # @param other [Cassandra::TimeUuid] another timeuuid to compare
49
+ # @see Comparable
50
+ #
51
+ # @return [nil] when other is not a {Cassandra::Uuid}
52
+ # @return [Integer] `-1` when less than `other`, `0` when equal to `other`
53
+ # and `1` when greater than `other`
35
54
  def <=>(other)
36
55
  return nil unless other.kind_of?(Cassandra::Uuid)
37
56
  c = self.value <=> other.value
38
- return c if c == 0
57
+ return c if c == 0 || !other.is_a?(Cassandra::TimeUuid)
39
58
  self.time_bits <=> other.time_bits
40
59
  end
41
60
 
42
61
  protected
43
62
 
63
+ # @private
44
64
  def time_bits
45
65
  n = (value >> 64)
46
66
  t = 0
@@ -54,88 +74,6 @@ module Cassandra
54
74
 
55
75
  # @private
56
76
  LOWER_HALF_MASK = 0xffffffff_ffffffff
57
-
58
- public
59
-
60
- # A UUID version 1, variant 1 generator. It can generate a sequence of UUIDs
61
- # with reasonable uniqueness guarantees:
62
- #
63
- # * The clock ID and node ID components are set to random numbers when the
64
- # generator is created.
65
- # * If two calls to {#next} happen within the time afforded by the system
66
- # clock resolution a counter is incremented and added to the time
67
- # component.
68
- # * If the clock moves backwards the clock ID is reset to a new random
69
- # number.
70
- #
71
- # Instances of this class are absolutely not threadsafe. You should
72
- # never share instances between threads.
73
- #
74
- class Generator
75
- # Create a new UUID generator.
76
- #
77
- # @param [Integer] node_id an alternate node ID (defaults to a random number)
78
- # @param [Integer] clock_id an alternate clock ID (defaults to a random number)
79
- #
80
- def initialize(node_id=nil, clock_id=nil, clock=Time)
81
- @node_id = node_id || (rand(2**47) | 0x010000000000)
82
- @clock_id = clock_id || rand(2**16)
83
- @clock = clock
84
- end
85
-
86
- # Returns a new UUID with a time component that is the current time.
87
- #
88
- # @return [Cassandra::TimeUuid] a new UUID
89
- #
90
- def next
91
- now = @clock.now
92
- usecs = now.to_i * 1_000_000 + now.usec
93
- if @last_usecs && @last_usecs - @sequence <= usecs && usecs <= @last_usecs
94
- @sequence += 1
95
- elsif @last_usecs && @last_usecs > usecs
96
- @sequence = 0
97
- @clock_id = rand(2**16)
98
- else
99
- @sequence = 0
100
- end
101
- @last_usecs = usecs + @sequence
102
- from_usecs(@last_usecs)
103
- end
104
-
105
- # Returns a new UUID with a time component based on the specified Time.
106
- # A piece of jitter is added to ensure that multiple calls with the same
107
- # time do not generate the same UUID (if you want determinism you can set
108
- # the second parameter to zero).
109
- #
110
- # @param [Time] time the time to encode into the UUID
111
- # @param [Integer] jitter a number of microseconds to add to the time to make it unique
112
- # @return [Cassandra::TimeUuid] a new UUID
113
- #
114
- def from_time(time, jitter=rand(2**16))
115
- usecs = time.to_i * 1_000_000 + time.usec + jitter
116
- from_usecs(usecs)
117
- end
118
-
119
- # @private
120
- def from_usecs(usecs)
121
- t = GREGORIAN_OFFSET + usecs * 10
122
- time_hi = t & 0x0fff000000000000
123
- time_mid = t & 0x0000ffff00000000
124
- time_low = t & 0x00000000ffffffff
125
- version = 1
126
- clock_id = @clock_id & 0x3fff
127
- node_id = @node_id & 0xffffffffffff
128
- variant = 0x8000
129
- n = (time_low << 96) | (time_mid << 48) | (time_hi << 16)
130
- n |= version << 76
131
- n |= (clock_id | variant) << 48
132
- n |= node_id
133
- TimeUuid.new(n)
134
- end
135
- end
136
-
137
- private
138
-
139
77
  # @private
140
78
  GREGORIAN_OFFSET = 122192928000000000
141
79
  end
@@ -103,7 +103,7 @@ module Cassandra
103
103
  when false then io.print(FALSE_STR)
104
104
  when true then io.print(TRUE_STR)
105
105
  else
106
- raise "unsupported type: #{object.inspect}"
106
+ raise ::ArgumentError, "unsupported type: #{object.inspect}"
107
107
  end
108
108
 
109
109
  io.string
@@ -22,7 +22,7 @@ module Cassandra
22
22
  # This is a very basic implementation of UUIDs and exists more or less just
23
23
  # to encode and decode UUIDs from and to Cassandra.
24
24
  #
25
- # If you want to generate UUIDs see {Cassandra::TimeUuid::Generator}.
25
+ # If you want to generate UUIDs see {Cassandra::Uuid::Generator}.
26
26
  #
27
27
  class Uuid
28
28
  # Creates a new UUID either from a string (expected to be on the standard 8-4-4-4-12 form, or just 32 characters without hyphens), or from a 128 bit number.
@@ -88,17 +88,19 @@ module Cassandra
88
88
  # @private
89
89
  def from_s(str)
90
90
  str = str.gsub(HYPHEN, EMPTY_STRING)
91
- raise ArgumentError, "Expected 32 hexadecimal digits but got #{str.length}" unless str.length == 32
92
- raise ArgumentError, "invalid value for Integer(): \"#{str}\"" unless str =~ HEX_RE
91
+ raise ::ArgumentError, "Expected 32 hexadecimal digits but got #{str.length}" unless str.length == 32
92
+ raise ::ArgumentError, "invalid value for Integer(): \"#{str}\"" unless str =~ HEX_RE
93
93
  Integer(str, 16)
94
94
  end
95
95
  else
96
96
  # @private
97
97
  def from_s(str)
98
98
  str = str.gsub(HYPHEN, EMPTY_STRING)
99
- raise ArgumentError, "Expected 32 hexadecimal digits but got #{str.length}" unless str.length == 32
99
+ raise ::ArgumentError, "Expected 32 hexadecimal digits but got #{str.length}" unless str.length == 32
100
100
  Integer(str, 16)
101
101
  end
102
102
  end
103
103
  end
104
104
  end
105
+
106
+ require 'cassandra/uuid/generator'
@@ -0,0 +1,207 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright 2013-2014 DataStax, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #++
18
+
19
+ module Cassandra
20
+ class Uuid
21
+ # A UUID generator.
22
+ #
23
+ # This class can be used to genereate Apache Cassandra timeuuid and uuid
24
+ # values.
25
+ #
26
+ # @see Cassandra::Uuid::Generator#now Generating a sequence time UUIDs
27
+ # with reasonable uniqueness guarantees.
28
+ # @see Cassandra::Uuid::Generator#at Generating a time UUID for a given
29
+ # time object or unix timestamp.
30
+ # @see Cassandra::Uuid::Generator#uuid Generating completely random v4
31
+ # UUIDs.
32
+ #
33
+ # @note Instances of this class are absolutely not threadsafe. You should
34
+ # never share instances between threads.
35
+ #
36
+ class Generator
37
+ # Create a new UUID generator.
38
+ #
39
+ # The clock ID and node ID components are set to random numbers when the
40
+ # generator is created. These are used for generation of time UUIDs only.
41
+ #
42
+ # @param [Integer] node_id an alternate node ID
43
+ # @param [Integer] clock_id an alternate clock ID
44
+ # @param [Object<#now>] clock used to generate timeuuid from current time
45
+ #
46
+ # @raise [ArgumentError] if clock doesn't respond to `now`
47
+ def initialize(node_id = (SecureRandom.random_number(2**47) | 0x010000000000), clock_id = SecureRandom.random_number(65536), clock = Time)
48
+ raise ::ArgumentError, "invalid clock" unless clock.respond_to?(:now)
49
+
50
+ @node_id = Integer(node_id)
51
+ @clock_id = Integer(clock_id)
52
+ @clock = clock
53
+ end
54
+
55
+ # Returns a new UUID with a time component that is the current time.
56
+ #
57
+ # If two calls to {#now} happen within the time afforded by the system
58
+ # clock resolution a counter is incremented and added to the time
59
+ # component.
60
+ #
61
+ # If the clock moves backwards the clock ID is reset to a new random
62
+ # number.
63
+ #
64
+ # @example Creating a sequential TimeUuids for the current time
65
+ # generator = Cassandra::Uuid::Generator.new
66
+ # timeuuids = 5.times.map { generator.now }
67
+ #
68
+ # puts timeuuids.zip(timeuuids.map(&:to_time)).map(&:inspect)
69
+ #
70
+ # # Outputs:
71
+ # # [8614b7d0-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
72
+ # # [8614b91a-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
73
+ # # [8614b960-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
74
+ # # [8614b99c-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
75
+ # # [8614b9ce-5646-11e4-8e54-6761d3995ef3, 2014-10-17 21:42:42 UTC]
76
+ #
77
+ # @see Time.now
78
+ #
79
+ # @return [Cassandra::TimeUuid] a new UUID
80
+ def now
81
+ now = @clock.now
82
+ usecs = now.to_i * 1_000_000 + now.usec
83
+ if @last_usecs && @last_usecs - @sequence <= usecs && usecs <= @last_usecs
84
+ @sequence += 1
85
+ elsif @last_usecs && @last_usecs > usecs
86
+ @sequence = 0
87
+ @clock_id = SecureRandom.random_number(65536)
88
+ else
89
+ @sequence = 0
90
+ end
91
+ @last_usecs = usecs + @sequence
92
+ from_usecs(@last_usecs)
93
+ end
94
+
95
+ # Returns a new UUID with a time component based on the specified Time.
96
+ # A piece of jitter is added to ensure that multiple calls with the same
97
+ # time do not generate the same UUID (if you want determinism you can set
98
+ # the second parameter to zero).
99
+ #
100
+ # @overload at(time, jitter = SecureRandom.random_number(65536))
101
+ # @param [Time] time a Time instance
102
+ # @param [Integer] jitter a number of microseconds to add to the time
103
+ # @return [Cassandra::TimeUuid] a new UUID
104
+ # @overload at(seconds_with_frac, jitter = SecureRandom.random_number(65536))
105
+ # @param [Numeric] seconds_with_frac can be {Integer}, {Float},
106
+ # {Rational}, or other {Numeric}
107
+ # @param [Integer] jitter a number of microseconds to add to the time
108
+ # @return [Cassandra::TimeUuid] a new UUID
109
+ # @overload at(seconds, microseconds_with_frac, jitter = SecureRandom.random_number(65536))
110
+ # @param [Integer] seconds
111
+ # @param [Numeric] microseconds_with_frac can be {Integer}, {Float},
112
+ # {Rational}, or other {Numeric}
113
+ # @param [Integer] jitter a number of microseconds to add to the time
114
+ # @return [Cassandra::TimeUuid] a new UUID
115
+ #
116
+ # @note the `jitter` argument accepted by all variants of this method is
117
+ # required to add randomness to generated {Cassandra::TimeUuid} and
118
+ # might affect the order of generated timestamps. You should set
119
+ # `jitter` to 0 when the source time(stamp)s are unique.
120
+ #
121
+ # @example Creating a TimeUuid from a Time instance
122
+ # generator = Cassandra::Uuid::Generator.new
123
+ # timeuuid = generator.at(Time.at(1413582460))
124
+ #
125
+ # puts timeuuid.to_time
126
+ #
127
+ # # Outputs:
128
+ # # 2014-10-17 21:47:40 UTC
129
+ #
130
+ # @example Creating a TimeUuid from a timestamp
131
+ # generator = Cassandra::Uuid::Generator.new
132
+ # timeuuid = generator.at(1413582423)
133
+ #
134
+ # puts timeuuid.to_time
135
+ #
136
+ # # Outputs:
137
+ # # 2014-10-17 21:47:03 UTC
138
+ #
139
+ # @example Avoid jitter in generated TimeUuid
140
+ # timestamp = 1413582418
141
+ # generator = Cassandra::Uuid::Generator.new
142
+ # timeuuid = generator.at(timestamp, 0)
143
+ #
144
+ # puts timeuuid.to_time.to_i
145
+ #
146
+ # # Outputs:
147
+ # # 1413582418
148
+ #
149
+ # @raise [ArgumentError] when given no arguments or more than 3 arguments
150
+ #
151
+ # @see Time.at
152
+ def at(*args)
153
+ raise ::ArgumentError, "not enough arguments" if args.empty?
154
+ raise ::ArgumentError, "too many arguments" if args.size > 3
155
+
156
+ if args.first.is_a?(Time)
157
+ time = args.shift
158
+ jitter = args.empty? ? SecureRandom.random_number(65536) : Integer(args.shift)
159
+ else
160
+ jitter = args.size > 2 ? Integer(args.pop) : SecureRandom.random_number(65536)
161
+ time = Time.at(*args)
162
+ end
163
+
164
+ from_usecs(time.to_i * 1_000_000 + time.usec + jitter)
165
+ end
166
+
167
+ # Returns a completely random version 4 UUID.
168
+ #
169
+ # @example Generating a random Uuid
170
+ # generator = Cassandra::Uuid::Generator.new
171
+ # uuid = generator.uuid
172
+ #
173
+ # puts uuid
174
+ #
175
+ # # Outputs:
176
+ # # 664dedae-e162-4bc0-9066-b9f1968252aa
177
+ #
178
+ # @see SecureRandom.uuid
179
+ #
180
+ # @return [Cassandra::Uuid] a new UUID
181
+ def uuid
182
+ Uuid.new(SecureRandom.uuid)
183
+ end
184
+
185
+ private
186
+
187
+ # @private
188
+ def from_usecs(usecs)
189
+ t = TimeUuid::GREGORIAN_OFFSET + usecs * 10
190
+ time_hi = t & 0x0fff000000000000
191
+ time_mid = t & 0x0000ffff00000000
192
+ time_low = t & 0x00000000ffffffff
193
+ version = 1
194
+ clock_id = @clock_id & 0x3fff
195
+ node_id = @node_id & 0xffffffffffff
196
+ variant = 0x8000
197
+
198
+ n = (time_low << 96) | (time_mid << 48) | (time_hi << 16)
199
+ n |= version << 76
200
+ n |= (clock_id | variant) << 48
201
+ n |= node_id
202
+
203
+ TimeUuid.new(n)
204
+ end
205
+ end
206
+ end
207
+ end