cassilds 0.9.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 (47) hide show
  1. data/CHANGELOG +53 -0
  2. data/LICENSE +202 -0
  3. data/Manifest +45 -0
  4. data/README.rdoc +83 -0
  5. data/Rakefile +9 -0
  6. data/bin/cassandra_helper +16 -0
  7. data/cassandra.gemspec +46 -0
  8. data/conf/cassandra.in.sh +47 -0
  9. data/conf/cassandra.yaml +113 -0
  10. data/conf/log4j.properties +38 -0
  11. data/conf/storage-conf.xml +342 -0
  12. data/lib/cassandra/0.6/cassandra.rb +68 -0
  13. data/lib/cassandra/0.6/columns.rb +35 -0
  14. data/lib/cassandra/0.6/protocol.rb +92 -0
  15. data/lib/cassandra/0.6.rb +7 -0
  16. data/lib/cassandra/0.7/cassandra.rb +272 -0
  17. data/lib/cassandra/0.7/column_family.rb +3 -0
  18. data/lib/cassandra/0.7/columns.rb +67 -0
  19. data/lib/cassandra/0.7/keyspace.rb +3 -0
  20. data/lib/cassandra/0.7/protocol.rb +139 -0
  21. data/lib/cassandra/0.7.rb +7 -0
  22. data/lib/cassandra/array.rb +8 -0
  23. data/lib/cassandra/cassandra.rb +302 -0
  24. data/lib/cassandra/columns.rb +79 -0
  25. data/lib/cassandra/comparable.rb +28 -0
  26. data/lib/cassandra/constants.rb +11 -0
  27. data/lib/cassandra/debug.rb +9 -0
  28. data/lib/cassandra/helpers.rb +40 -0
  29. data/lib/cassandra/long.rb +58 -0
  30. data/lib/cassandra/mock.rb +326 -0
  31. data/lib/cassandra/ordered_hash.rb +200 -0
  32. data/lib/cassandra/time.rb +11 -0
  33. data/lib/cassandra.rb +39 -0
  34. data/test/cassandra_client_test.rb +20 -0
  35. data/test/cassandra_mock_test.rb +73 -0
  36. data/test/cassandra_test.rb +412 -0
  37. data/test/comparable_types_test.rb +45 -0
  38. data/test/eventmachine_test.rb +42 -0
  39. data/test/ordered_hash_test.rb +380 -0
  40. data/test/test_helper.rb +14 -0
  41. data/vendor/0.6/gen-rb/cassandra.rb +1481 -0
  42. data/vendor/0.6/gen-rb/cassandra_constants.rb +12 -0
  43. data/vendor/0.6/gen-rb/cassandra_types.rb +482 -0
  44. data/vendor/0.7/gen-rb/cassandra.rb +1937 -0
  45. data/vendor/0.7/gen-rb/cassandra_constants.rb +12 -0
  46. data/vendor/0.7/gen-rb/cassandra_types.rb +679 -0
  47. metadata +176 -0
@@ -0,0 +1,302 @@
1
+
2
+ =begin rdoc
3
+ Create a new Cassandra client instance. Accepts a keyspace name, and optional host and port.
4
+
5
+ client = Cassandra.new('twitter', '127.0.0.1:9160')
6
+
7
+ If the server requires authentication, you must authenticate before make calls
8
+
9
+ client.login!('username','password')
10
+
11
+ You can then make calls to the server via the <tt>client</tt> instance.
12
+
13
+ client.insert(:UserRelationships, "5", {"user_timeline" => {SimpleUUID::UUID.new => "1"}})
14
+ client.get(:UserRelationships, "5", "user_timeline")
15
+
16
+ For read methods, valid option parameters are:
17
+
18
+ <tt>:count</tt>:: How many results to return. Defaults to 100.
19
+ <tt>:start</tt>:: Column name token at which to start iterating, inclusive. Defaults to nil, which means the first column in the collation order.
20
+ <tt>:finish</tt>:: Column name token at which to stop iterating, inclusive. Defaults to nil, which means no boundary.
21
+ <tt>:reversed</tt>:: Swap the direction of the collation order.
22
+ <tt>:consistency</tt>:: The consistency level of the request. Defaults to <tt>Cassandra::Consistency::ONE</tt> (one node must respond). Other valid options are <tt>Cassandra::Consistency::ZERO</tt>, <tt>Cassandra::Consistency::QUORUM</tt>, and <tt>Cassandra::Consistency::ALL</tt>.
23
+
24
+ Note that some read options have no relevance in some contexts.
25
+
26
+ For write methods, valid option parameters are:
27
+
28
+ <tt>:timestamp </tt>:: The transaction timestamp. Defaults to the current time in milliseconds. This is used for conflict resolution by the server; you normally never need to change it.
29
+ <tt>:consistency</tt>:: See above.
30
+
31
+ For the initial client instantiation, you may also pass in <tt>:thrift_client<tt> with a ThriftClient subclass attached. On connection, that class will be used instead of the default ThriftClient class, allowing you to add additional behavior to the connection (e.g. query logging).
32
+
33
+ =end rdoc
34
+
35
+ class Cassandra
36
+ include Columns
37
+ include Protocol
38
+ include Helpers
39
+
40
+ class AccessError < StandardError #:nodoc:
41
+ end
42
+
43
+ module Consistency
44
+ include CassandraThrift::ConsistencyLevel
45
+ end
46
+
47
+ WRITE_DEFAULTS = {
48
+ :count => 1000,
49
+ :timestamp => nil,
50
+ :consistency => Consistency::ONE,
51
+ :ttl => nil
52
+ }.freeze
53
+
54
+ READ_DEFAULTS = {
55
+ :count => 100,
56
+ :start => nil,
57
+ :finish => nil,
58
+ :reversed => false,
59
+ :consistency => Consistency::ONE
60
+ }.freeze
61
+
62
+ THRIFT_DEFAULTS = {
63
+ :transport_wrapper => Thrift::BufferedTransport,
64
+ :thrift_client_class => ThriftClient
65
+ }.freeze
66
+
67
+ attr_reader :keyspace, :servers, :schema, :thrift_client_options, :thrift_client_class, :auth_request
68
+
69
+ # Create a new Cassandra instance and open the connection.
70
+ def initialize(keyspace, servers = "127.0.0.1:9160", thrift_client_options = {})
71
+ @is_super = {}
72
+ @column_name_class = {}
73
+ @sub_column_name_class = {}
74
+ @auto_discover_nodes = true
75
+ thrift_client_options[:transport_wrapper] ||= Cassandra.DEFAULT_TRANSPORT_WRAPPER
76
+ @thrift_client_options = THRIFT_DEFAULTS.merge(thrift_client_options)
77
+ @thrift_client_class = @thrift_client_options[:thrift_client_class]
78
+ @keyspace = keyspace
79
+ @servers = Array(servers)
80
+ end
81
+
82
+ def disable_node_auto_discovery!
83
+ @auto_discover_nodes = false
84
+ end
85
+
86
+ def disconnect!
87
+ if @client
88
+ @client.disconnect!
89
+ @client = nil
90
+ end
91
+ end
92
+
93
+ def keyspaces
94
+ @keyspaces ||= client.describe_keyspaces()
95
+ end
96
+
97
+ def login!(username, password)
98
+ @auth_request = CassandraThrift::AuthenticationRequest.new
99
+ @auth_request.credentials = {'username' => username, 'password' => password}
100
+ client.login(@keyspace, @auth_request)
101
+ end
102
+
103
+ def inspect
104
+ "#<Cassandra:#{object_id}, @keyspace=#{keyspace.inspect}, @schema={#{
105
+ schema(false).map {|name, hash| ":#{name} => #{hash['type'].inspect}"}.join(', ')
106
+ }}, @servers=#{servers.inspect}>"
107
+ end
108
+
109
+ ### Write
110
+
111
+ # Insert a row for a key. Pass a flat hash for a regular column family, and
112
+ # a nested hash for a super column family. Supports the <tt>:consistency</tt>,
113
+ # <tt>:timestamp</tt> and <tt>:ttl</tt> options.
114
+ def insert(column_family, key, hash, options = {})
115
+ column_family, _, _, options = extract_and_validate_params(column_family, key, [options], WRITE_DEFAULTS)
116
+
117
+ timestamp = options[:timestamp] || Time.stamp
118
+ mutation_map = if is_super(column_family)
119
+ {
120
+ key => {
121
+ column_family => hash.collect{|k,v| _super_insert_mutation(column_family, k, v, timestamp, options[:ttl]) }
122
+ }
123
+ }
124
+ else
125
+ {
126
+ key => {
127
+ column_family => hash.collect{|k,v| _standard_insert_mutation(column_family, k, v, timestamp, options[:ttl])}
128
+ }
129
+ }
130
+ end
131
+
132
+ @batch ? @batch << [mutation_map, options[:consistency]] : _mutate(mutation_map, options[:consistency])
133
+ end
134
+
135
+
136
+ ## Delete
137
+
138
+ # _mutate the element at the column_family:key:[column]:[sub_column]
139
+ # path you request. Supports the <tt>:consistency</tt> and <tt>:timestamp</tt>
140
+ # options.
141
+ def remove(column_family, key, *columns_and_options)
142
+ column_family, column, sub_column, options = extract_and_validate_params(column_family, key, columns_and_options, WRITE_DEFAULTS)
143
+
144
+ args = {:column_family => column_family}
145
+ columns = is_super(column_family) ? {:super_column => column, :column => sub_column} : {:column => column}
146
+ column_path = CassandraThrift::ColumnPath.new(args.merge(columns))
147
+
148
+ mutation = [:remove, [key, column_path, options[:timestamp] || Time.stamp, options[:consistency]]]
149
+
150
+ @batch ? @batch << mutation : _remove(*mutation[1])
151
+ end
152
+
153
+ ### Read
154
+
155
+ # Count the elements at the column_family:key:[super_column] path you
156
+ # request. Supports the <tt>:consistency</tt> option.
157
+ def count_columns(column_family, key, *columns_and_options)
158
+ column_family, super_column, _, options =
159
+ extract_and_validate_params(column_family, key, columns_and_options, READ_DEFAULTS)
160
+ _count_columns(column_family, key, super_column, options[:consistency])
161
+ end
162
+
163
+ # Multi-key version of Cassandra#count_columns. Supports options <tt>:count</tt>,
164
+ # <tt>:start</tt>, <tt>:finish</tt>, <tt>:reversed</tt>, and <tt>:consistency</tt>.
165
+ # FIXME Not real multi; needs server support
166
+ def multi_count_columns(column_family, keys, *options)
167
+ OrderedHash[*keys.map { |key| [key, count_columns(column_family, key, *options)] }._flatten_once]
168
+ end
169
+
170
+ # Return a list of single values for the elements at the
171
+ # column_family:key:column[s]:[sub_columns] path you request. Supports the
172
+ # <tt>:consistency</tt> option.
173
+ def get_columns(column_family, key, *columns_and_options)
174
+ column_family, columns, sub_columns, options =
175
+ extract_and_validate_params(column_family, key, columns_and_options, READ_DEFAULTS)
176
+ _get_columns(column_family, key, columns, sub_columns, options[:consistency])
177
+ end
178
+
179
+ # Multi-key version of Cassandra#get_columns. Supports the <tt>:consistency</tt>
180
+ # option.
181
+ # FIXME Not real multi; needs to use a Column predicate
182
+ def multi_get_columns(column_family, keys, *options)
183
+ OrderedHash[*keys.map { |key| [key, get_columns(column_family, key, *options)] }._flatten_once]
184
+ end
185
+
186
+ # Return a hash (actually, a Cassandra::OrderedHash) or a single value
187
+ # representing the element at the column_family:key:[column]:[sub_column]
188
+ # path you request. Supports options <tt>:count</tt>, <tt>:start</tt>,
189
+ # <tt>:finish</tt>, <tt>:reversed</tt>, and <tt>:consistency</tt>.
190
+ def get(column_family, key, *columns_and_options)
191
+ multi_get(column_family, [key], *columns_and_options)[key]
192
+ end
193
+
194
+ # Multi-key version of Cassandra#get. Supports options <tt>:count</tt>,
195
+ # <tt>:start</tt>, <tt>:finish</tt>, <tt>:reversed</tt>, and <tt>:consistency</tt>.
196
+ def multi_get(column_family, keys, *columns_and_options)
197
+ column_family, column, sub_column, options =
198
+ extract_and_validate_params(column_family, keys, columns_and_options, READ_DEFAULTS)
199
+
200
+ hash = _multiget(column_family, keys, column, sub_column,
201
+ options[:start], options[:finish], options[:count], options[:reversed],
202
+ options[:consistency])
203
+ # Restore order
204
+ ordered_hash = OrderedHash.new
205
+ keys.each { |key| ordered_hash[key] = hash[key] || (OrderedHash.new if is_super(column_family) and !sub_column) }
206
+ ordered_hash
207
+ end
208
+
209
+ # Return true if the column_family:key:[column]:[sub_column] path you
210
+ # request exists. Supports the <tt>:consistency</tt> option.
211
+ def exists?(column_family, key, *columns_and_options)
212
+ column_family, column, sub_column, options =
213
+ extract_and_validate_params(column_family, key, columns_and_options, READ_DEFAULTS)
214
+ ret = nil
215
+ if column
216
+ ret = _multiget(column_family, [key], column, sub_column, '', '', 1, false, options[:consistency])[key]
217
+ else
218
+ ret = _multiget(column_family, [key], nil, nil, '', '', 1, false, options[:consistency])[key]
219
+ end
220
+ return (!ret.nil? and ret.send(:length) != 0)
221
+ end
222
+
223
+ # Return a list of keys in the column_family you request. Requires the
224
+ # table to be partitioned with OrderPreservingHash. Supports the
225
+ # <tt>:count</tt>, <tt>:start</tt>, <tt>:finish</tt>, and <tt>:consistency</tt>
226
+ # options.
227
+ def get_range(column_family, options = {})
228
+ column_family, _, _, options =
229
+ extract_and_validate_params(column_family, "", [options], READ_DEFAULTS)
230
+ _get_range(column_family, options[:start].to_s, options[:finish].to_s,
231
+ options[:count], options[:consistency])
232
+ end
233
+
234
+ # Return a list of keys in the column_family you request. Requires the
235
+ # table to be partitioned with OrderPreservingHash. Supports the
236
+ # <tt>:count</tt>, <tt>:start</tt>, <tt>:finish</tt>, and <tt>:consistency</tt>
237
+ # options.
238
+ def get_range_hash(column_family, options = {})
239
+ column_family, _, _, options =
240
+ extract_and_validate_params(column_family, "", [options], READ_DEFAULTS)
241
+ _get_range_hash(column_family, options[:start].to_s, options[:finish].to_s,
242
+ options[:count], options[:consistency])
243
+ end
244
+
245
+ # Return a list of keys in the column_family you request. Requires the
246
+ # table to be partitioned with OrderPreservingHash. Supports the
247
+ # <tt>:count</tt>, <tt>:start</tt>, <tt>:finish</tt>, and <tt>:consistency</tt>
248
+ # options.
249
+ def get_range_columns(column_family, *columns_and_options)
250
+ column_family, columns, sub_columns, options =
251
+ extract_and_validate_params(column_family, "", columns_and_options, READ_DEFAULTS)
252
+ _get_range_columns(column_family, columns, sub_columns, options[:start].to_s,
253
+ options[:finish].to_s, options[:count], options[:consistency])
254
+ end
255
+
256
+ # Count all rows in the column_family you request. Requires the table
257
+ # to be partitioned with OrderPreservingHash. Supports the <tt>:start</tt>,
258
+ # <tt>:finish</tt>, and <tt>:consistency</tt> options.
259
+ def count_range(column_family, options = {})
260
+ get_range(column_family, options).select{|r| r.columns.length > 0}.compact.length
261
+ end
262
+
263
+ # Open a batch operation and yield self. Inserts and deletes will be queued
264
+ # until the block closes, and then sent atomically to the server. Supports
265
+ # the <tt>:consistency</tt> option, which overrides the consistency set in
266
+ # the individual commands.
267
+ def batch(options = {})
268
+ _, _, _, options =
269
+ extract_and_validate_params(schema.keys.first, "", [options], WRITE_DEFAULTS)
270
+
271
+ @batch = []
272
+ yield(self)
273
+ compact_mutations!
274
+
275
+ @batch.each do |mutation|
276
+ case mutation.first
277
+ when :remove
278
+ _remove(*mutation[1])
279
+ else
280
+ _mutate(*mutation)
281
+ end
282
+ end
283
+ ensure
284
+ @batch = nil
285
+ end
286
+
287
+ protected
288
+
289
+ def calling_method
290
+ "#{self.class}##{caller[0].split('`').last[0..-3]}"
291
+ end
292
+
293
+ # Roll up queued mutations, to improve atomicity.
294
+ def compact_mutations!
295
+ #TODO re-do this rollup
296
+ end
297
+
298
+ def new_client
299
+ thrift_client_class.new(CassandraThrift::Cassandra::Client, @servers, @thrift_client_options)
300
+ end
301
+
302
+ end
@@ -0,0 +1,79 @@
1
+
2
+ class Cassandra
3
+ # A bunch of crap, mostly related to introspecting on column types
4
+ module Columns #:nodoc:
5
+
6
+ def is_super(column_family)
7
+ @is_super[column_family] ||= column_family_property(column_family, 'Type') == "Super"
8
+ end
9
+
10
+ def column_name_class(column_family)
11
+ @column_name_class[column_family] ||= column_name_class_for_key(column_family, "CompareWith")
12
+ end
13
+
14
+ def sub_column_name_class(column_family)
15
+ @sub_column_name_class[column_family] ||= column_name_class_for_key(column_family, "CompareSubcolumnsWith")
16
+ end
17
+
18
+ private
19
+
20
+ def column_name_class_for_key(column_family, comparator_key)
21
+ property = column_family_property(column_family, comparator_key)
22
+ property =~ /.*\.(.*?)$/
23
+ case $1
24
+ when "LongType" then Long
25
+ when "LexicalUUIDType", "TimeUUIDType" then SimpleUUID::UUID
26
+ else
27
+ String # UTF8, Ascii, Bytes, anything else
28
+ end
29
+ end
30
+
31
+ def column_family_property(column_family, key)
32
+ unless schema[column_family]
33
+ raise AccessError, "Invalid column family \"#{column_family}\""
34
+ end
35
+ schema[column_family][key]
36
+ end
37
+
38
+ def multi_column_to_hash!(hash)
39
+ hash.each do |key, column_or_supercolumn|
40
+ hash[key] = (column_or_supercolumn.column.value if column_or_supercolumn.column)
41
+ end
42
+ end
43
+
44
+ def multi_columns_to_hash!(column_family, hash)
45
+ hash.each do |key, columns|
46
+ hash[key] = columns_to_hash(column_family, columns)
47
+ end
48
+ end
49
+
50
+ def multi_sub_columns_to_hash!(column_family, hash)
51
+ hash.each do |key, sub_columns|
52
+ hash[key] = sub_columns_to_hash(column_family, sub_columns)
53
+ end
54
+ end
55
+
56
+ def columns_to_hash(column_family, columns)
57
+ columns_to_hash_for_classes(columns, column_name_class(column_family), sub_column_name_class(column_family))
58
+ end
59
+
60
+ def sub_columns_to_hash(column_family, columns)
61
+ columns_to_hash_for_classes(columns, sub_column_name_class(column_family))
62
+ end
63
+
64
+ def columns_to_hash_for_classes(columns, column_name_class, sub_column_name_class = nil)
65
+ hash = OrderedHash.new
66
+ Array(columns).each do |c|
67
+ c = c.super_column || c.column if c.is_a?(CassandraThrift::ColumnOrSuperColumn)
68
+ case c
69
+ when CassandraThrift::SuperColumn
70
+ hash.[]=(column_name_class.new(c.name), columns_to_hash_for_classes(c.columns, sub_column_name_class)) # Pop the class stack, and recurse
71
+ when CassandraThrift::Column
72
+ hash.[]=(column_name_class.new(c.name), c.value, c.timestamp)
73
+ end
74
+ end
75
+ hash
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,28 @@
1
+
2
+ class Cassandra
3
+ # Abstract base class for comparable numeric column name types
4
+ class Comparable
5
+ class TypeError < ::TypeError #:nodoc:
6
+ end
7
+
8
+ def <=>(other)
9
+ self.to_i <=> other.to_i
10
+ end
11
+
12
+ def hash
13
+ @bytes.hash
14
+ end
15
+
16
+ def eql?(other)
17
+ other.is_a?(Comparable) and @bytes == other.to_s
18
+ end
19
+
20
+ def ==(other)
21
+ other.respond_to?(:to_i) && self.to_i == other.to_i
22
+ end
23
+
24
+ def to_s
25
+ @bytes
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+
2
+ class Cassandra
3
+ # A helper module you can include in your own class. Makes it easier
4
+ # to work with Cassandra subclasses.
5
+ module Constants
6
+ include Cassandra::Consistency
7
+
8
+ Long = Cassandra::Long
9
+ OrderedHash = Cassandra::OrderedHash
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+
2
+ require 'pp'
3
+
4
+ class CassandraThrift::Cassandra::Client
5
+ def send_message(*args)
6
+ pp args
7
+ super
8
+ end
9
+ end
@@ -0,0 +1,40 @@
1
+ class Cassandra
2
+ module Helpers
3
+ def extract_and_validate_params(column_family, keys, args, options)
4
+ options = options.dup
5
+ column_family = column_family.to_s
6
+ # Keys
7
+ [keys].flatten.each do |key|
8
+ raise ArgumentError, "Key #{key.inspect} must be a String for #{caller[2].inspect}." unless key.is_a?(String)
9
+ end
10
+
11
+ # Options
12
+ if args.last.is_a?(Hash)
13
+ extras = args.last.keys - options.keys
14
+ raise ArgumentError, "Invalid options #{extras.inspect[1..-2]} for #{caller[1]}" if extras.any?
15
+ options.merge!(args.pop)
16
+ end
17
+
18
+ # Ranges
19
+ column, sub_column = args[0], args[1]
20
+ klass, sub_klass = column_name_class(column_family), sub_column_name_class(column_family)
21
+ range_class = column ? sub_klass : klass
22
+
23
+ [:start, :finish].each do |opt|
24
+ options[opt] = options[opt] ? (options[opt] == '' ? '' : range_class.new(options[opt]).to_s) : ''
25
+ end
26
+
27
+ [column_family, s_map(column, klass), s_map(sub_column, sub_klass), options]
28
+ end
29
+
30
+ # Convert stuff to strings.
31
+ def s_map(el, klass)
32
+ case el
33
+ when Array then el.map { |i| s_map(i, klass) }
34
+ when NilClass then nil
35
+ else
36
+ klass.new(el).to_s
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,58 @@
1
+
2
+ class Cassandra
3
+ # A temporally-ordered Long class for use in Cassandra column names
4
+ class Long < Cassandra::Comparable
5
+
6
+ # FIXME Should unify with or subclass Cassandra::UUID
7
+ def initialize(bytes = nil)
8
+ case bytes
9
+ when self.class # Long
10
+ @bytes = bytes.to_s
11
+ when String
12
+ case bytes.size
13
+ when 8 # Raw byte array
14
+ @bytes = bytes
15
+ when 18 # Human-readable UUID-like representation; inverse of #to_guid
16
+ elements = bytes.split("-")
17
+ raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (malformed UUID-like representation)" if elements.size != 3
18
+ @bytes = [elements.join].pack('H32')
19
+ else
20
+ raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (invalid bytecount)"
21
+ end
22
+ when Integer
23
+ raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (integer out of range)" if bytes < 0 or bytes > 2**64
24
+ @bytes = [bytes >> 32, bytes % 2**32].pack("NN")
25
+ when NilClass, Time
26
+ # Time.stamp is 52 bytes, so we have 12 bytes of entropy left over
27
+ int = ((bytes || Time).stamp << 12) + rand(2**12)
28
+ @bytes = [int >> 32, int % 2**32].pack("NN")
29
+ else
30
+ raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (unknown source class)"
31
+ end
32
+ end
33
+
34
+ def to_i
35
+ @to_i ||= begin
36
+ ints = @bytes.unpack("NN")
37
+ (ints[0] << 32) +
38
+ ints[1]
39
+ end
40
+ end
41
+
42
+ def to_guid
43
+ "%08x-%04x-%04x" % @bytes.unpack("Nnn")
44
+ end
45
+
46
+ def inspect
47
+ "<Cassandra::Long##{object_id} time: #{
48
+ Time.at((to_i >> 12) / 1_000_000).utc.inspect
49
+ }, usecs: #{
50
+ (to_i >> 12) % 1_000_000
51
+ }, jitter: #{
52
+ to_i % 2**12
53
+ }, guid: #{
54
+ to_guid
55
+ }>"
56
+ end
57
+ end
58
+ end