cassilds 0.9.1

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