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 CHANGED
@@ -1 +1 @@
1
- rvm --create use jruby-1.6.5@eurydice
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
@@ -5,5 +5,5 @@ gemspec
5
5
  group :development do
6
6
  gem 'jruby-openssl'
7
7
  gem 'rake'
8
- gem 'rspec', '2.7.0' # 2.8.0 doesn't work with JRuby because of JRUBY-6324
8
+ gem 'rspec'
9
9
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- eurydice (1.2.3-java)
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.7.0)
19
- rspec-core (~> 2.7.0)
20
- rspec-expectations (~> 2.7.0)
21
- rspec-mocks (~> 2.7.0)
22
- rspec-core (2.7.1)
23
- rspec-expectations (2.7.0)
24
- diff-lcs (~> 1.1.2)
25
- rspec-mocks (2.7.0)
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 (= 2.7.0)
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 JRuby 1.6.2 in 1.9 mode and Cassandra 0.8.1.
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
@@ -1,6 +1,11 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require 'bundler'
4
+ require 'rspec/core/rake_task'
4
5
 
5
6
 
6
7
  Bundler::GemHelper.install_tasks
8
+
9
+ RSpec::Core::RakeTask.new(:spec)
10
+
11
+ task :default => :spec
data/lib/cassandra.rb CHANGED
@@ -28,14 +28,26 @@ module Cassandra
28
28
  }.freeze
29
29
 
30
30
  MARSHAL_TYPES = {
31
- :bytes => 'org.apache.cassandra.db.marshal.BytesType'.freeze,
32
- :ascii => 'org.apache.cassandra.db.marshal.AsciiType'.freeze,
33
- :utf8 => 'org.apache.cassandra.db.marshal.UTF8Type'.freeze,
34
- :long => 'org.apache.cassandra.db.marshal.LongType'.freeze,
35
- :lexical_uuid => 'org.apache.cassandra.db.marshal.LexicalUUIDType'.freeze,
36
- :time_uuid => 'org.apache.cassandra.db.marshal.TimeUUIDType'.freeze,
37
- :counter => 'org.apache.cassandra.db.marshal.CounterColumnType'.freeze,
38
- :counter_column => 'org.apache.cassandra.db.marshal.CounterColumnType'.freeze
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 'java'
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
@@ -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
- thrift_exception_handler do
118
- reversed = options.fetch(:reversed, false)
119
- batch_size = options.fetch(:batch_size, 100)
120
- start_beyond = options.fetch(:start_beyond, nil)
121
- start_beyond = to_pelops_bytes(start_beyond) if start_beyond
122
- selector = @keyspace.create_selector
123
- iterator = selector.iterate_columns_from_row(@name, to_pelops_bytes(row_key), start_beyond, reversed, batch_size, get_cl(options))
124
- if block_given?
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
 
@@ -13,7 +13,7 @@ module Eurydice
13
13
  @cluster = cluster
14
14
  @pool_name = pool_name
15
15
  @driver = driver
16
- @batch_key = "#{@name}-batch"
16
+ @batch_key = "#{@name}-batch-#{object_id}"
17
17
  end
18
18
 
19
19
  def definition(reload=false)
@@ -2,5 +2,5 @@
2
2
 
3
3
 
4
4
  module Eurydice
5
- VERSION = '1.2.3'
5
+ VERSION = '1.2.4'
6
6
  end
@@ -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
@@ -5,7 +5,7 @@ module Eurydice
5
5
  module Pelops
6
6
  describe Cluster do
7
7
  before :all do
8
- @cluster = Eurydice.connect
8
+ @cluster = Eurydice.connect(host: ENV['CASSANDRA_HOST'])
9
9
  end
10
10
 
11
11
  it_behaves_like 'Cluster', @cluster
@@ -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
- prerelease:
5
- version: 1.2.3
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.4
5
+ prerelease:
6
6
  platform: java
7
- authors:
8
- - Theo Hultberg
9
- autorequire:
7
+ authors:
8
+ - Theo Hultberg
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2012-12-31 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: pelops-jars
17
- prerelease: false
18
- requirement: &id001 !ruby/object:Gem::Requirement
19
- none: false
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: 1.3.0
24
- type: :runtime
25
- version_requirements: *id001
26
- description: ""
27
- email:
28
- - theo@burtcorp.com
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
- files:
36
- - .gitignore
37
- - .rspec
38
- - .rvmrc
39
- - Gemfile
40
- - Gemfile.lock
41
- - README.mdown
42
- - Rakefile
43
- - eurydice.gemspec
44
- - examples/01_connect.rb
45
- - examples/02_create_keyspace.rb
46
- - examples/03_create_column_family.rb
47
- - examples/04_storing_data.rb
48
- - examples/05_loading_data.rb
49
- - examples/06_cluster_info.rb
50
- - examples/common.rb
51
- - lib/cassandra.rb
52
- - lib/eurydice.rb
53
- - lib/eurydice/pelops.rb
54
- - lib/eurydice/pelops/cluster.rb
55
- - lib/eurydice/pelops/column_family.rb
56
- - lib/eurydice/pelops/keyspace.rb
57
- - lib/eurydice/pelops/mutator.rb
58
- - lib/eurydice/version.rb
59
- - spec/eurydice/pelops/cluster_spec.rb
60
- - spec/eurydice/pelops/column_family_spec.rb
61
- - spec/eurydice/pelops/keyspace_spec.rb
62
- - spec/eurydice/support/cluster.rb
63
- - spec/eurydice/support/column_family.rb
64
- - spec/eurydice/support/keyspace.rb
65
- - spec/spec_helper.rb
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
- require_paths:
73
- - lib
74
- required_ruby_version: !ruby/object:Gem::Requirement
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
- requirements:
77
- - - ">="
78
- - !ruby/object:Gem::Version
79
- version: "0"
80
- required_rubygems_version: !ruby/object:Gem::Requirement
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.9
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
-