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