eurydice 1.2.3-java → 1.2.4-java
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.
- data/.rvmrc +1 -1
- data/.travis.yml +18 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +10 -10
- data/README.mdown +2 -2
- data/Rakefile +5 -0
- data/lib/cassandra.rb +20 -8
- data/lib/eurydice.rb +3 -1
- data/lib/eurydice/column_enumerator.rb +110 -0
- data/lib/eurydice/column_page.rb +109 -0
- data/lib/eurydice/pelops.rb +6 -0
- data/lib/eurydice/pelops/column_family.rb +9 -19
- data/lib/eurydice/pelops/keyspace.rb +1 -1
- data/lib/eurydice/version.rb +1 -1
- data/spec/eurydice/column_enumerator_spec.rb +197 -0
- data/spec/eurydice/column_page_spec.rb +139 -0
- data/spec/eurydice/pelops/cluster_spec.rb +1 -1
- data/spec/eurydice/pelops/column_family_spec.rb +1 -1
- data/spec/eurydice/pelops/keyspace_spec.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- metadata +87 -77
data/.rvmrc
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
rvm --create use jruby-1.
|
|
1
|
+
rvm --create use jruby-1.7.2@eurydice
|
data/.travis.yml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
language: ruby
|
|
2
|
+
script: 'rspec spec'
|
|
3
|
+
rvm:
|
|
4
|
+
# - jruby-19mode
|
|
5
|
+
- jruby-head
|
|
6
|
+
jdk:
|
|
7
|
+
- openjdk7
|
|
8
|
+
# - oraclejdk7
|
|
9
|
+
# - openjdk6
|
|
10
|
+
services:
|
|
11
|
+
- cassandra
|
|
12
|
+
before_script:
|
|
13
|
+
- 'cat /etc/cassandra/cassandra.yaml'
|
|
14
|
+
- 'cat /etc/init.d/cassandra'
|
|
15
|
+
- 'cat /usr/local/cassandra/conf/log4j-server.properties'
|
|
16
|
+
- 'cat /var/log/cassandra/system.log'
|
|
17
|
+
- '/etc/init.d/cassandra status'
|
|
18
|
+
- 'CASSANDRA_CONF=/etc/cassandra /usr/local/cassandra/bin/nodetool status'
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
eurydice (1.2.
|
|
4
|
+
eurydice (1.2.4-java)
|
|
5
5
|
pelops-jars (>= 1.3.0)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
@@ -15,14 +15,14 @@ GEM
|
|
|
15
15
|
pelops-jars (1.3.0-java)
|
|
16
16
|
cassandra-jars (~> 1.0.0)
|
|
17
17
|
rake (0.9.2)
|
|
18
|
-
rspec (2.
|
|
19
|
-
rspec-core (~> 2.
|
|
20
|
-
rspec-expectations (~> 2.
|
|
21
|
-
rspec-mocks (~> 2.
|
|
22
|
-
rspec-core (2.
|
|
23
|
-
rspec-expectations (2.
|
|
24
|
-
diff-lcs (~> 1.1.
|
|
25
|
-
rspec-mocks (2.
|
|
18
|
+
rspec (2.12.0)
|
|
19
|
+
rspec-core (~> 2.12.0)
|
|
20
|
+
rspec-expectations (~> 2.12.0)
|
|
21
|
+
rspec-mocks (~> 2.12.0)
|
|
22
|
+
rspec-core (2.12.2)
|
|
23
|
+
rspec-expectations (2.12.1)
|
|
24
|
+
diff-lcs (~> 1.1.3)
|
|
25
|
+
rspec-mocks (2.12.1)
|
|
26
26
|
|
|
27
27
|
PLATFORMS
|
|
28
28
|
java
|
|
@@ -31,4 +31,4 @@ DEPENDENCIES
|
|
|
31
31
|
eurydice!
|
|
32
32
|
jruby-openssl
|
|
33
33
|
rake
|
|
34
|
-
rspec
|
|
34
|
+
rspec
|
data/README.mdown
CHANGED
|
@@ -6,10 +6,10 @@ See the `examples` directory and the specs for usage.
|
|
|
6
6
|
|
|
7
7
|
## Installation & requirements
|
|
8
8
|
|
|
9
|
-
Tested with
|
|
9
|
+
Tested with the latest 1.6.x and 1.7.x versions of JRuby, against the current Datastax Community Edition (1.2.x) [on Travis](https://travis-ci.org/iconara/eurydice).
|
|
10
10
|
|
|
11
11
|
gem install eurydice
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
This will also install two dependencies: `pelops-jars` and `cassandra-jars` which contain the Pelops and Cassandra JAR files.
|
|
14
14
|
|
|
15
15
|
## Contributors
|
data/Rakefile
CHANGED
data/lib/cassandra.rb
CHANGED
|
@@ -28,14 +28,26 @@ module Cassandra
|
|
|
28
28
|
}.freeze
|
|
29
29
|
|
|
30
30
|
MARSHAL_TYPES = {
|
|
31
|
-
:
|
|
32
|
-
:
|
|
33
|
-
:
|
|
34
|
-
:
|
|
35
|
-
:
|
|
36
|
-
:
|
|
37
|
-
:
|
|
38
|
-
:
|
|
31
|
+
:ascii => 'org.apache.cassandra.db.marshal.AsciiType'.freeze,
|
|
32
|
+
:boolean => 'org.apache.cassandra.db.marshal.BooleanType'.freeze,
|
|
33
|
+
:bytes => 'org.apache.cassandra.db.marshal.BytesType'.freeze,
|
|
34
|
+
:composite => 'org.apache.cassandra.db.marshal.CompositeType'.freeze,
|
|
35
|
+
:counter => 'org.apache.cassandra.db.marshal.CounterColumnType'.freeze,
|
|
36
|
+
:counter_column => 'org.apache.cassandra.db.marshal.CounterColumnType'.freeze,
|
|
37
|
+
:date => 'org.apache.cassandra.db.marshal.DateType'.freeze,
|
|
38
|
+
:decimal => 'org.apache.cassandra.db.marshal.DecimalType'.freeze,
|
|
39
|
+
:double => 'org.apache.cassandra.db.marshal.DoubleType'.freeze,
|
|
40
|
+
:dynamic_composite => 'org.apache.cassandra.db.marshal.DynamicCompositeType'.freeze,
|
|
41
|
+
:float => 'org.apache.cassandra.db.marshal.FloatType'.freeze,
|
|
42
|
+
:int32 => 'org.apache.cassandra.db.marshal.Int32Type'.freeze,
|
|
43
|
+
:int => 'org.apache.cassandra.db.marshal.IntegerType'.freeze,
|
|
44
|
+
:integer => 'org.apache.cassandra.db.marshal.IntegerType'.freeze,
|
|
45
|
+
:lexical_uuid => 'org.apache.cassandra.db.marshal.LexicalUUIDType'.freeze,
|
|
46
|
+
:long => 'org.apache.cassandra.db.marshal.LongType'.freeze,
|
|
47
|
+
:reversed => 'org.apache.cassandra.db.marshal.ReversedType'.freeze,
|
|
48
|
+
:time_uuid => 'org.apache.cassandra.db.marshal.TimeUUIDType'.freeze,
|
|
49
|
+
:utf8 => 'org.apache.cassandra.db.marshal.UTF8Type'.freeze,
|
|
50
|
+
:uuid => 'org.apache.cassandra.db.marshal.UUIDType'.freeze
|
|
39
51
|
}.freeze
|
|
40
52
|
|
|
41
53
|
INDEX_OPERATORS = {
|
data/lib/eurydice.rb
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
|
|
3
|
-
require '
|
|
3
|
+
require 'eurydice/column_enumerator'
|
|
4
|
+
require 'eurydice/column_page'
|
|
4
5
|
require 'eurydice/pelops'
|
|
5
6
|
|
|
6
7
|
module Eurydice
|
|
7
8
|
class EurydiceError < StandardError; end
|
|
9
|
+
class ConnectionError < EurydiceError; end
|
|
8
10
|
class InvalidRequestError < EurydiceError; end
|
|
9
11
|
class KeyspaceExistsError < InvalidRequestError; end
|
|
10
12
|
class NotFoundError < EurydiceError; end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Eurydice
|
|
4
|
+
class ColumnEnumeratorBase
|
|
5
|
+
include Enumerable
|
|
6
|
+
|
|
7
|
+
def initialize(column_family, row_key, options={})
|
|
8
|
+
@column_family, @row_key, @options = column_family, row_key, DEFAULT_OPTIONS.merge(options)
|
|
9
|
+
@max_retries = @options.delete(:max_retries)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def each
|
|
13
|
+
loop do
|
|
14
|
+
yield self.next
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def rewind
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
FIRST_KEY = "\0".freeze
|
|
24
|
+
LAST_KEY = ''.freeze
|
|
25
|
+
DEFAULT_OPTIONS = {:max_column_count => 10_000, :max_retries => 3}.freeze
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class ColumnEnumerator < ColumnEnumeratorBase
|
|
29
|
+
def initialize(*args)
|
|
30
|
+
super
|
|
31
|
+
rewind
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def next
|
|
35
|
+
if @buffer.empty? && @exhausted
|
|
36
|
+
raise StopIteration
|
|
37
|
+
elsif @buffer.empty?
|
|
38
|
+
result = @column_family.get(@row_key, @options.merge(from_column: @offset))
|
|
39
|
+
keys = result.keys if result
|
|
40
|
+
if result.nil? || result.empty? || keys.last == @offset
|
|
41
|
+
@exhausted = true
|
|
42
|
+
raise StopIteration
|
|
43
|
+
end
|
|
44
|
+
result.shift if keys.first == @offset
|
|
45
|
+
result.each do |pair|
|
|
46
|
+
@buffer << pair
|
|
47
|
+
end
|
|
48
|
+
@offset = keys.last
|
|
49
|
+
end
|
|
50
|
+
@buffer.shift
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def rewind
|
|
54
|
+
@offset = @options[:from_column] || (@options[:reversed] ? LAST_KEY : FIRST_KEY)
|
|
55
|
+
@reversed = @options[:reversed]
|
|
56
|
+
@buffer = []
|
|
57
|
+
@exhausted = false
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class ConcurrentColumnEnumerator < ColumnEnumeratorBase
|
|
62
|
+
java_import 'java.util.concurrent.Executors'
|
|
63
|
+
java_import 'java.util.concurrent.ArrayBlockingQueue'
|
|
64
|
+
java_import 'java.util.concurrent.atomic.AtomicBoolean'
|
|
65
|
+
java_import 'org.jruby.threading.DaemonThreadFactory'
|
|
66
|
+
|
|
67
|
+
def initialize(*args)
|
|
68
|
+
super
|
|
69
|
+
@standard_enumerator = ColumnEnumerator.new(*args)
|
|
70
|
+
@queue = ArrayBlockingQueue.new(@options[:max_column_count] * 2)
|
|
71
|
+
@fetch_pool = Executors.new_single_thread_executor(DaemonThreadFactory.new(self.class.name))
|
|
72
|
+
@exhausted = true
|
|
73
|
+
rewind
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def next
|
|
77
|
+
unless @running.get_and_set(true)
|
|
78
|
+
@fetch_pool.execute do
|
|
79
|
+
begin
|
|
80
|
+
while @running.get
|
|
81
|
+
pair = @standard_enumerator.next
|
|
82
|
+
@queue.put(pair)
|
|
83
|
+
end
|
|
84
|
+
rescue StopIteration
|
|
85
|
+
end
|
|
86
|
+
@queue.put(:stop_iteration)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
raise StopIteration if @exhausted
|
|
91
|
+
|
|
92
|
+
value = @queue.take
|
|
93
|
+
if value == :stop_iteration
|
|
94
|
+
@exhausted = true
|
|
95
|
+
raise StopIteration
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
value
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def rewind
|
|
102
|
+
@running = AtomicBoolean.new(false)
|
|
103
|
+
unless @exhausted
|
|
104
|
+
until @queue.take == :stop_iteration; end
|
|
105
|
+
end
|
|
106
|
+
@standard_enumerator.rewind
|
|
107
|
+
@exhausted = false
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Eurydice
|
|
4
|
+
class ColumnPage
|
|
5
|
+
include Enumerable
|
|
6
|
+
|
|
7
|
+
attr_reader :page_size
|
|
8
|
+
|
|
9
|
+
def initialize(column_family, row_key, page_size, offset=FIRST_OFFSET)
|
|
10
|
+
@column_family = column_family
|
|
11
|
+
@row_key = row_key
|
|
12
|
+
@offset = offset
|
|
13
|
+
@page_size = page_size
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def offset
|
|
17
|
+
if @offset == FIRST_OFFSET
|
|
18
|
+
slice.keys.first
|
|
19
|
+
else
|
|
20
|
+
@offset
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def next_page
|
|
25
|
+
return nil if last?
|
|
26
|
+
ForwardColumnPage.new(@column_family, @row_key, @page_size, next_page_offset)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def prev_page
|
|
30
|
+
return nil if first?
|
|
31
|
+
ReverseColumnPage.new(@column_family, @row_key, @page_size, prev_page_offset)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def first?
|
|
35
|
+
@offset == FIRST_OFFSET
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def last?
|
|
39
|
+
slice.size <= @page_size
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def reverse?
|
|
43
|
+
false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def each_column(&block)
|
|
47
|
+
return self unless block_given?
|
|
48
|
+
slice.each.with_index do |item,i|
|
|
49
|
+
yield item if i < @page_size
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
alias_method :each, :each_column
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def next_page_offset
|
|
57
|
+
slice.keys[-2]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def prev_page_offset
|
|
61
|
+
@offset
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
FIRST_OFFSET = "\0".freeze
|
|
65
|
+
|
|
66
|
+
def slice
|
|
67
|
+
@slice ||= @column_family.get(@row_key, from_column: @offset, max_column_count: @page_size + 2, reversed: reverse?)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
class ForwardColumnPage < ColumnPage
|
|
72
|
+
def first?
|
|
73
|
+
false
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class ReverseColumnPage < ColumnPage
|
|
78
|
+
def last?
|
|
79
|
+
false
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def first?
|
|
83
|
+
slice.size <= @page_size + 1
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def reverse?
|
|
87
|
+
true
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def each_column(&block)
|
|
91
|
+
return self unless block_given?
|
|
92
|
+
first = slice.size == @page_size + 2 ? 1 : 0
|
|
93
|
+
last = slice.size - 2
|
|
94
|
+
slice.reverse_each.with_index do |item,i|
|
|
95
|
+
yield item if first <= i && i <= last
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
def next_page_offset
|
|
102
|
+
@offset
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def prev_page_offset
|
|
106
|
+
slice.keys[-2]
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
data/lib/eurydice/pelops.rb
CHANGED
|
@@ -108,8 +108,14 @@ module Eurydice
|
|
|
108
108
|
raise EurydiceError, e.cause.message, e.backtrace
|
|
109
109
|
when Thrift::TTransportException, ::Pelops::TimedOutException
|
|
110
110
|
raise TimeoutError, e.cause.message, e.backtrace
|
|
111
|
+
when java.net.ConnectException
|
|
112
|
+
raise ConnectionError, e.cause.message, e.backtrace
|
|
111
113
|
end
|
|
112
114
|
end
|
|
115
|
+
case e
|
|
116
|
+
when ::Pelops::NotFoundException
|
|
117
|
+
raise NotFoundError, e.message, e.backtrace
|
|
118
|
+
end
|
|
113
119
|
raise e
|
|
114
120
|
end
|
|
115
121
|
|
|
@@ -113,25 +113,15 @@ module Eurydice
|
|
|
113
113
|
nil
|
|
114
114
|
end
|
|
115
115
|
|
|
116
|
-
def each_column(row_key, options={})
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
iterator.each do |column|
|
|
126
|
-
yield column_to_kv(column, options)
|
|
127
|
-
end
|
|
128
|
-
else
|
|
129
|
-
Enumerator.new do |y|
|
|
130
|
-
iterator.each do |column|
|
|
131
|
-
y << column_to_kv(column, options)
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
end
|
|
116
|
+
def each_column(row_key, options={}, &block)
|
|
117
|
+
new_options = options.dup
|
|
118
|
+
new_options[:from_column] = options.delete(:start_beyond) if options.key?(:start_beyond)
|
|
119
|
+
new_options[:max_column_count] = options.delete(:batch_size) if options.key?(:batch_size)
|
|
120
|
+
enum = ColumnEnumerator.new(self, row_key, new_options)
|
|
121
|
+
if block_given?
|
|
122
|
+
enum.each(&block)
|
|
123
|
+
else
|
|
124
|
+
enum
|
|
135
125
|
end
|
|
136
126
|
end
|
|
137
127
|
|
data/lib/eurydice/version.rb
CHANGED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require_relative '../spec_helper'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
module Eurydice
|
|
7
|
+
class FakeColumnFamily
|
|
8
|
+
def initialize
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def get(row_key, options)
|
|
12
|
+
if row_key == 'the_row'
|
|
13
|
+
if options[:max_column_count] == 3
|
|
14
|
+
if options[:reversed]
|
|
15
|
+
case options[:from_column]
|
|
16
|
+
when '' then {'g' => 7, 'f' => 6, 'e' => 5}
|
|
17
|
+
when 'e' then {'e' => 5, 'd' => 4, 'c' => 3, 'b' => 2}
|
|
18
|
+
when 'b' then {'b' => 2, 'a' => 1}
|
|
19
|
+
when 'a' then {'a' => 1}
|
|
20
|
+
end
|
|
21
|
+
else
|
|
22
|
+
case options[:from_column]
|
|
23
|
+
when "\0" then {'a' => 1, 'b' => 2, 'c' => 3}
|
|
24
|
+
when 'c' then {'c' => 3, 'd' => 4, 'e' => 5, 'f' => 6}
|
|
25
|
+
when 'f' then {'f' => 6, 'g' => 7}
|
|
26
|
+
when 'g' then {'g' => 7}
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
shared_examples_for 'a column enumerator' do
|
|
35
|
+
let :column_family do
|
|
36
|
+
double(:column_family)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context 'as an Enumerator' do
|
|
40
|
+
describe '#next' do
|
|
41
|
+
context 'in the basic case' do
|
|
42
|
+
let :enumerator do
|
|
43
|
+
described_class.new(FakeColumnFamily.new, 'the_row', max_column_count: 3)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'returns the first column of the row' do
|
|
47
|
+
k, v = enumerator.next
|
|
48
|
+
k.should == 'a'
|
|
49
|
+
v.should == 1
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'returns the second column of the row' do
|
|
53
|
+
enumerator.next
|
|
54
|
+
k, v = enumerator.next
|
|
55
|
+
k.should == 'b'
|
|
56
|
+
v.should == 2
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'returns each column of the row in order' do
|
|
60
|
+
columns = []
|
|
61
|
+
7.times do
|
|
62
|
+
columns << enumerator.next
|
|
63
|
+
end
|
|
64
|
+
columns = [['a', 1], ['b', 2], ['c', 3], ['d', 4], ['e', 5], ['f', 6], ['g', 7]]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'raises StopIteration when all columns have been returned' do
|
|
68
|
+
7.times { enumerator.next }
|
|
69
|
+
expect { enumerator.next }.to raise_error(StopIteration)
|
|
70
|
+
expect { enumerator.next }.to raise_error(StopIteration)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
context 'when enumerating in reverse' do
|
|
75
|
+
let :enumerator do
|
|
76
|
+
described_class.new(FakeColumnFamily.new, 'the_row', max_column_count: 3, reversed: true)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it 'returns the last column of the row' do
|
|
80
|
+
enumerator.next.should == ['g', 7]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'returns the next to last column of the row' do
|
|
84
|
+
enumerator.next
|
|
85
|
+
enumerator.next.should == ['f', 6]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'returns each column of the row in reverse order' do
|
|
89
|
+
columns = []
|
|
90
|
+
7.times do
|
|
91
|
+
columns << enumerator.next
|
|
92
|
+
end
|
|
93
|
+
columns = [['g', 7], ['f', 6], ['e', 5], ['d', 4], ['c', 3], ['b', 2], ['a', 1]]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'raises StopIteration when all column have been returned' do
|
|
97
|
+
7.times { enumerator.next }
|
|
98
|
+
expect { enumerator.next }.to raise_error(StopIteration)
|
|
99
|
+
expect { enumerator.next }.to raise_error(StopIteration)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
context 'with special cases' do
|
|
104
|
+
let :enumerator do
|
|
105
|
+
described_class.new(column_family, 'the_row', max_column_count: 3)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
let :slices do
|
|
109
|
+
[
|
|
110
|
+
{'a' => 1, 'b' => 2, 'c' => 3},
|
|
111
|
+
{'c' => 3, 'd' => 4, 'e' => 5, 'f' => 6},
|
|
112
|
+
{'f' => 6, 'g' => 7},
|
|
113
|
+
{'g' => 7},
|
|
114
|
+
]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'raises StopIteration immediately if the row does not exist' do
|
|
118
|
+
column_family.stub(:get).and_return(nil)
|
|
119
|
+
enumerator = described_class.new(column_family, 'another_row')
|
|
120
|
+
expect { enumerator.next }.to raise_error(StopIteration)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it 'raises StopIteration immediately if the row is empty' do
|
|
124
|
+
column_family.stub(:get).and_return({})
|
|
125
|
+
enumerator = described_class.new(column_family, 'another_row')
|
|
126
|
+
expect { enumerator.next }.to raise_error(StopIteration)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it 'correctly handles rows with columns that are exactly the page size' do
|
|
130
|
+
column_family.stub(:get).with('another_row', max_column_count: 3, from_column: "\0").and_return(slices[0].dup)
|
|
131
|
+
column_family.stub(:get).with('another_row', max_column_count: 3, from_column: 'c').and_return({'c' => 3})
|
|
132
|
+
enumerator = described_class.new(column_family, 'another_row', max_column_count: 3)
|
|
133
|
+
3.times { enumerator.next }
|
|
134
|
+
expect { enumerator.next }.to raise_error(StopIteration)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it 'correctly handles rows with columns that are less then one page' do
|
|
138
|
+
column_family.stub(:get).with('another_row', max_column_count: 3, from_column: "\0").and_return({'a' => 1, 'b' => 2})
|
|
139
|
+
column_family.stub(:get).with('another_row', max_column_count: 3, from_column: 'b').and_return({'b' => 2})
|
|
140
|
+
enumerator = described_class.new(column_family, 'another_row', max_column_count: 3)
|
|
141
|
+
2.times { enumerator.next }
|
|
142
|
+
expect { enumerator.next }.to raise_error(StopIteration)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
context 'with a transformer'
|
|
147
|
+
context 'with query options'
|
|
148
|
+
context 'when transport errors occur'
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
describe '#rewind' do
|
|
152
|
+
let :enumerator do
|
|
153
|
+
described_class.new(FakeColumnFamily.new, 'the_row', max_column_count: 3)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it 'resets the enumerator so that it can be used again' do
|
|
157
|
+
first_results = 7.times.map { enumerator.next }
|
|
158
|
+
enumerator.rewind
|
|
159
|
+
second_results = 7.times.map { enumerator.next }
|
|
160
|
+
first_results.should == second_results
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
context 'as an Enumerable' do
|
|
166
|
+
let :enumerable do
|
|
167
|
+
described_class.new(FakeColumnFamily.new, 'the_row', max_column_count: 3)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
describe '#each' do
|
|
171
|
+
it 'returns each column of the row in order' do
|
|
172
|
+
columns = []
|
|
173
|
+
enumerable.each do |k, v|
|
|
174
|
+
columns << [k, v]
|
|
175
|
+
end
|
|
176
|
+
columns = [['a', 1], ['b', 2], ['c', 3], ['d', 4], ['e', 5], ['f', 6], ['g', 7]]
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
describe '#map' do
|
|
181
|
+
it 'returns each column of the row in order' do
|
|
182
|
+
columns = []
|
|
183
|
+
columns = enumerable.map { |k, v| v }
|
|
184
|
+
columns = [1, 2, 3, 4, 5, 6, 7]
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
describe ColumnEnumerator do
|
|
191
|
+
it_behaves_like 'a column enumerator'
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
describe ConcurrentColumnEnumerator do
|
|
195
|
+
it_behaves_like 'a column enumerator'
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require_relative '../spec_helper'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
module Eurydice
|
|
7
|
+
describe ColumnPage do
|
|
8
|
+
before :all do
|
|
9
|
+
@keyspace_name = "eurydice_test_space_#{rand(1000)}"
|
|
10
|
+
@cf_name = "column_family_#{rand(1000)}"
|
|
11
|
+
@cluster = Eurydice.connect(host: ENV['CASSANDRA_HOST'])
|
|
12
|
+
@keyspace = @cluster.keyspace(@keyspace_name, :create => false)
|
|
13
|
+
@keyspace.drop! rescue nil
|
|
14
|
+
@keyspace.create!
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
let :column_family do
|
|
18
|
+
cf = @keyspace.column_family(@cf_name)
|
|
19
|
+
cf.insert('xyz', {'a' => '1', 'b' => '2', 'c' => '3', 'd' => '4', 'e' => '5', 'f' => '6', 'g' => '7', 'h' => '8'})
|
|
20
|
+
cf
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe '#next_page' do
|
|
24
|
+
it 'returns a new page with the correct offset' do
|
|
25
|
+
page = described_class.new(column_family, 'xyz', 3)
|
|
26
|
+
page.next_page.offset.should == 'd'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'returns a new page with the same page size' do
|
|
30
|
+
page = described_class.new(column_family, 'xyz', 3)
|
|
31
|
+
page.next_page.page_size.should == 3
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'returns nil if there is no next page' do
|
|
35
|
+
page = described_class.new(column_family, 'xyz', 6)
|
|
36
|
+
page.next_page.next_page.should be_nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'picks the right offsets when paging through the row' do
|
|
40
|
+
page = described_class.new(column_family, 'xyz', 3)
|
|
41
|
+
offsets = []
|
|
42
|
+
loop do
|
|
43
|
+
offsets << page.offset
|
|
44
|
+
page = page.next_page
|
|
45
|
+
break unless page
|
|
46
|
+
end
|
|
47
|
+
offsets.should == ['a','d','g']
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
describe '#prev_page' do
|
|
52
|
+
it 'returns a reverse page' do
|
|
53
|
+
page = described_class.new(column_family, 'xyz', 3).next_page
|
|
54
|
+
page.prev_page.should be_reverse
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'returns a new page with the correct offset' do
|
|
58
|
+
page = described_class.new(column_family, 'xyz', 3).next_page
|
|
59
|
+
page.prev_page.offset.should == 'd'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'returns a new page with the same page size' do
|
|
63
|
+
page = described_class.new(column_family, 'xyz', 3).next_page
|
|
64
|
+
page.prev_page.page_size.should == 3
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'returns nil if there is no previous page' do
|
|
68
|
+
page = described_class.new(column_family, 'xyz', 3)
|
|
69
|
+
page.prev_page.should be_nil
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'picks the right offsets when paging through the row' do
|
|
73
|
+
page = described_class.new(column_family, 'xyz', 3)
|
|
74
|
+
page = page.next_page until page.last?
|
|
75
|
+
offsets = []
|
|
76
|
+
loop do
|
|
77
|
+
offsets << page.offset
|
|
78
|
+
page = page.prev_page
|
|
79
|
+
break unless page
|
|
80
|
+
end
|
|
81
|
+
offsets.should == ['g','g','d']
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
context 'navigating back and forth' do
|
|
86
|
+
it 'next + prev + next == next' do
|
|
87
|
+
page = described_class.new(column_family, 'xyz', 3)
|
|
88
|
+
page.next_page.prev_page.next_page.offset.should == 'd'
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
context 'with edge cases' do
|
|
93
|
+
it 'handles the case when row size is divisible by page size' do
|
|
94
|
+
[2,4,8].each do |page_size|
|
|
95
|
+
expected_page_count = 8/page_size
|
|
96
|
+
page = described_class.new(column_family, 'xyz', page_size)
|
|
97
|
+
page_count = 1
|
|
98
|
+
until page.last?
|
|
99
|
+
page = page.next_page
|
|
100
|
+
page_count += 1
|
|
101
|
+
page_count.should_not > expected_page_count
|
|
102
|
+
end
|
|
103
|
+
page_count.should == expected_page_count
|
|
104
|
+
|
|
105
|
+
page_count = 1
|
|
106
|
+
until page.first?
|
|
107
|
+
page = page.prev_page
|
|
108
|
+
page_count += 1
|
|
109
|
+
page_count.should_not > expected_page_count
|
|
110
|
+
end
|
|
111
|
+
page_count.should == expected_page_count
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it 'handles the case when previous page is short' do
|
|
116
|
+
items = []
|
|
117
|
+
described_class.new(column_family, 'xyz', 3, 'b').prev_page.each_column do |key,value|
|
|
118
|
+
items << [key,value]
|
|
119
|
+
end
|
|
120
|
+
items.should == [%w[a 1]]
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
describe '#each_column' do
|
|
125
|
+
it 'yields every item in page' do
|
|
126
|
+
items = []
|
|
127
|
+
described_class.new(column_family, 'xyz', 3).each_column do |key,value|
|
|
128
|
+
items << [key,value]
|
|
129
|
+
end
|
|
130
|
+
items.should == [%w[a 1], %w[b 2], %w[c 3]]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it 'returns an enumerable unless block is given' do
|
|
134
|
+
enum = described_class.new(column_family, 'xyz', 3).each_column
|
|
135
|
+
enum.map { |k,v| v }.should == %w[1 2 3]
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -7,7 +7,7 @@ module Eurydice
|
|
|
7
7
|
before :all do
|
|
8
8
|
@keyspace_name = "eurydice_test_space_#{rand(1000)}"
|
|
9
9
|
@cf_name = "column_family_#{rand(1000)}"
|
|
10
|
-
@cluster = Eurydice.connect
|
|
10
|
+
@cluster = Eurydice.connect(host: ENV['CASSANDRA_HOST'])
|
|
11
11
|
@keyspace = @cluster.keyspace(@keyspace_name, :create => false)
|
|
12
12
|
@keyspace.drop! rescue nil
|
|
13
13
|
@keyspace.create!
|
|
@@ -6,7 +6,7 @@ module Eurydice
|
|
|
6
6
|
describe Keyspace do
|
|
7
7
|
before :all do
|
|
8
8
|
@keyspace_name = "eurydice_test_space_#{rand(1000)}"
|
|
9
|
-
@cluster = Eurydice.connect
|
|
9
|
+
@cluster = Eurydice.connect(host: ENV['CASSANDRA_HOST'])
|
|
10
10
|
if @cluster.keyspaces.include?(@keyspace_name)
|
|
11
11
|
@cluster.keyspace(@keyspace_name).drop!
|
|
12
12
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -3,6 +3,8 @@ $: << File.expand_path('../../lib', __FILE__)
|
|
|
3
3
|
require 'bundler/setup'
|
|
4
4
|
require 'eurydice/pelops'
|
|
5
5
|
|
|
6
|
+
ENV['CASSANDRA_HOST'] ||= 'localhost'
|
|
7
|
+
|
|
6
8
|
require_relative 'eurydice/support/cluster'
|
|
7
9
|
require_relative 'eurydice/support/column_family'
|
|
8
10
|
require_relative 'eurydice/support/keyspace'
|
metadata
CHANGED
|
@@ -1,94 +1,104 @@
|
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: eurydice
|
|
3
|
-
version: !ruby/object:Gem::Version
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.2.4
|
|
5
|
+
prerelease:
|
|
6
6
|
platform: java
|
|
7
|
-
authors:
|
|
8
|
-
|
|
9
|
-
autorequire:
|
|
7
|
+
authors:
|
|
8
|
+
- Theo Hultberg
|
|
9
|
+
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
12
|
+
date: 2013-05-13 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: pelops-jars
|
|
16
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
17
|
+
requirements:
|
|
18
|
+
- - ">="
|
|
19
|
+
- !ruby/object:Gem::Version
|
|
20
|
+
version: 1.3.0
|
|
21
|
+
none: false
|
|
22
|
+
requirement: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 1.3.0
|
|
27
|
+
none: false
|
|
28
|
+
prerelease: false
|
|
29
|
+
type: :runtime
|
|
30
|
+
description: ''
|
|
31
|
+
email:
|
|
32
|
+
- theo@burtcorp.com
|
|
29
33
|
executables: []
|
|
30
|
-
|
|
31
34
|
extensions: []
|
|
32
|
-
|
|
33
35
|
extra_rdoc_files: []
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
36
|
+
files:
|
|
37
|
+
- ".gitignore"
|
|
38
|
+
- ".rspec"
|
|
39
|
+
- ".rvmrc"
|
|
40
|
+
- ".travis.yml"
|
|
41
|
+
- Gemfile
|
|
42
|
+
- Gemfile.lock
|
|
43
|
+
- README.mdown
|
|
44
|
+
- Rakefile
|
|
45
|
+
- eurydice.gemspec
|
|
46
|
+
- examples/01_connect.rb
|
|
47
|
+
- examples/02_create_keyspace.rb
|
|
48
|
+
- examples/03_create_column_family.rb
|
|
49
|
+
- examples/04_storing_data.rb
|
|
50
|
+
- examples/05_loading_data.rb
|
|
51
|
+
- examples/06_cluster_info.rb
|
|
52
|
+
- examples/common.rb
|
|
53
|
+
- lib/cassandra.rb
|
|
54
|
+
- lib/eurydice.rb
|
|
55
|
+
- lib/eurydice/column_enumerator.rb
|
|
56
|
+
- lib/eurydice/column_page.rb
|
|
57
|
+
- lib/eurydice/pelops.rb
|
|
58
|
+
- lib/eurydice/pelops/cluster.rb
|
|
59
|
+
- lib/eurydice/pelops/column_family.rb
|
|
60
|
+
- lib/eurydice/pelops/keyspace.rb
|
|
61
|
+
- lib/eurydice/pelops/mutator.rb
|
|
62
|
+
- lib/eurydice/version.rb
|
|
63
|
+
- spec/eurydice/column_enumerator_spec.rb
|
|
64
|
+
- spec/eurydice/column_page_spec.rb
|
|
65
|
+
- spec/eurydice/pelops/cluster_spec.rb
|
|
66
|
+
- spec/eurydice/pelops/column_family_spec.rb
|
|
67
|
+
- spec/eurydice/pelops/keyspace_spec.rb
|
|
68
|
+
- spec/eurydice/support/cluster.rb
|
|
69
|
+
- spec/eurydice/support/column_family.rb
|
|
70
|
+
- spec/eurydice/support/keyspace.rb
|
|
71
|
+
- spec/spec_helper.rb
|
|
66
72
|
homepage: http://github.com/iconara/eurydice
|
|
67
73
|
licenses: []
|
|
68
|
-
|
|
69
|
-
post_install_message:
|
|
74
|
+
post_install_message:
|
|
70
75
|
rdoc_options: []
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
require_paths:
|
|
77
|
+
- lib
|
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
segments:
|
|
83
|
+
- 0
|
|
84
|
+
version: !binary |-
|
|
85
|
+
MA==
|
|
86
|
+
hash: 2
|
|
75
87
|
none: false
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
|
+
requirements:
|
|
90
|
+
- - ">="
|
|
91
|
+
- !ruby/object:Gem::Version
|
|
92
|
+
segments:
|
|
93
|
+
- 0
|
|
94
|
+
version: !binary |-
|
|
95
|
+
MA==
|
|
96
|
+
hash: 2
|
|
81
97
|
none: false
|
|
82
|
-
requirements:
|
|
83
|
-
- - ">="
|
|
84
|
-
- !ruby/object:Gem::Version
|
|
85
|
-
version: "0"
|
|
86
98
|
requirements: []
|
|
87
|
-
|
|
88
99
|
rubyforge_project: eurydice
|
|
89
|
-
rubygems_version: 1.8.
|
|
90
|
-
signing_key:
|
|
100
|
+
rubygems_version: 1.8.24
|
|
101
|
+
signing_key:
|
|
91
102
|
specification_version: 3
|
|
92
103
|
summary: Ruby wrapper for the Pelops library
|
|
93
104
|
test_files: []
|
|
94
|
-
|