ok_hbase 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +1 -0
  4. data/Gemfile +17 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +47 -0
  7. data/Rakefile +22 -0
  8. data/examples/README.md +46 -0
  9. data/examples/advanced/README.md +36 -0
  10. data/examples/advanced/perf_read.rb +146 -0
  11. data/examples/advanced/perf_write.rb +143 -0
  12. data/examples/advanced/table_read.rb +115 -0
  13. data/examples/advanced/table_write.rb +128 -0
  14. data/examples/table_scan.rb +97 -0
  15. data/examples/table_write.rb +97 -0
  16. data/lib/ok_hbase/active_model.rb +35 -0
  17. data/lib/ok_hbase/client.rb +42 -0
  18. data/lib/ok_hbase/concerns/custom_row/class_methods.rb +13 -0
  19. data/lib/ok_hbase/concerns/custom_row.rb +40 -0
  20. data/lib/ok_hbase/concerns/indexable/class_methods.rb +13 -0
  21. data/lib/ok_hbase/concerns/indexable.rb +101 -0
  22. data/lib/ok_hbase/concerns/row.rb +85 -0
  23. data/lib/ok_hbase/concerns/table/batch.rb +95 -0
  24. data/lib/ok_hbase/concerns/table/class_methods.rb +13 -0
  25. data/lib/ok_hbase/concerns/table/instrumentation.rb +48 -0
  26. data/lib/ok_hbase/concerns/table.rb +241 -0
  27. data/lib/ok_hbase/concerns.rb +13 -0
  28. data/lib/ok_hbase/connection.rb +157 -0
  29. data/lib/ok_hbase/row.rb +21 -0
  30. data/lib/ok_hbase/table.rb +10 -0
  31. data/lib/ok_hbase/version.rb +3 -0
  32. data/lib/ok_hbase.rb +39 -0
  33. data/lib/thrift/hbase/hbase.rb +2643 -0
  34. data/lib/thrift/hbase/hbase_constants.rb +14 -0
  35. data/lib/thrift/hbase/hbase_types.rb +252 -0
  36. data/ok-hbase.gemspec +23 -0
  37. data/spec/ok_hbase/connection_spec.rb +99 -0
  38. data/spec/ok_hbase/table_spec.rb +149 -0
  39. data/spec/ok_hbase_spec.rb +24 -0
  40. data/spec/spec_helper.rb +20 -0
  41. data/tasks/bump.rb +30 -0
  42. metadata +122 -0
@@ -0,0 +1,14 @@
1
+ #
2
+ # Autogenerated by Thrift Compiler (0.9.0)
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+ module Apache
8
+ module Hadoop
9
+ module Hbase
10
+ module Thrift
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,252 @@
1
+ #
2
+ # Autogenerated by Thrift Compiler (0.9.0)
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+ module Apache
8
+ module Hadoop
9
+ module Hbase
10
+ module Thrift
11
+ # TCell - Used to transport a cell value (byte[]) and the timestamp it was
12
+ # stored with together as a result for get and getRow methods. This promotes
13
+ # the timestamp of a cell to a first-class value, making it easy to take
14
+ # note of temporal data. Cell is used all the way from HStore up to HTable.
15
+ class TCell
16
+ include ::Thrift::Struct, ::Thrift::Struct_Union
17
+ VALUE = 1
18
+ TIMESTAMP = 2
19
+
20
+ FIELDS = {
21
+ VALUE => {:type => ::Thrift::Types::STRING, :name => 'value', :binary => true},
22
+ TIMESTAMP => {:type => ::Thrift::Types::I64, :name => 'timestamp'}
23
+ }
24
+
25
+ def struct_fields; FIELDS; end
26
+
27
+ def validate
28
+ end
29
+
30
+ ::Thrift::Struct.generate_accessors self
31
+ end
32
+
33
+ # An HColumnDescriptor contains information about a column family
34
+ # such as the number of versions, compression settings, etc. It is
35
+ # used as input when creating a table or adding a column.
36
+ class ColumnDescriptor
37
+ include ::Thrift::Struct, ::Thrift::Struct_Union
38
+ NAME = 1
39
+ MAXVERSIONS = 2
40
+ COMPRESSION = 3
41
+ INMEMORY = 4
42
+ BLOOMFILTERTYPE = 5
43
+ BLOOMFILTERVECTORSIZE = 6
44
+ BLOOMFILTERNBHASHES = 7
45
+ BLOCKCACHEENABLED = 8
46
+ TIMETOLIVE = 9
47
+
48
+ FIELDS = {
49
+ NAME => {:type => ::Thrift::Types::STRING, :name => 'name', :binary => true},
50
+ MAXVERSIONS => {:type => ::Thrift::Types::I32, :name => 'maxVersions', :default => 3},
51
+ COMPRESSION => {:type => ::Thrift::Types::STRING, :name => 'compression', :default => %q"NONE"},
52
+ INMEMORY => {:type => ::Thrift::Types::BOOL, :name => 'inMemory', :default => false},
53
+ BLOOMFILTERTYPE => {:type => ::Thrift::Types::STRING, :name => 'bloomFilterType', :default => %q"NONE"},
54
+ BLOOMFILTERVECTORSIZE => {:type => ::Thrift::Types::I32, :name => 'bloomFilterVectorSize', :default => 0},
55
+ BLOOMFILTERNBHASHES => {:type => ::Thrift::Types::I32, :name => 'bloomFilterNbHashes', :default => 0},
56
+ BLOCKCACHEENABLED => {:type => ::Thrift::Types::BOOL, :name => 'blockCacheEnabled', :default => false},
57
+ TIMETOLIVE => {:type => ::Thrift::Types::I32, :name => 'timeToLive', :default => -1}
58
+ }
59
+
60
+ def struct_fields; FIELDS; end
61
+
62
+ def validate
63
+ end
64
+
65
+ ::Thrift::Struct.generate_accessors self
66
+ end
67
+
68
+ # A TRegionInfo contains information about an HTable region.
69
+ class TRegionInfo
70
+ include ::Thrift::Struct, ::Thrift::Struct_Union
71
+ STARTKEY = 1
72
+ ENDKEY = 2
73
+ ID = 3
74
+ NAME = 4
75
+ VERSION = 5
76
+
77
+ FIELDS = {
78
+ STARTKEY => {:type => ::Thrift::Types::STRING, :name => 'startKey', :binary => true},
79
+ ENDKEY => {:type => ::Thrift::Types::STRING, :name => 'endKey', :binary => true},
80
+ ID => {:type => ::Thrift::Types::I64, :name => 'id'},
81
+ NAME => {:type => ::Thrift::Types::STRING, :name => 'name', :binary => true},
82
+ VERSION => {:type => ::Thrift::Types::BYTE, :name => 'version'}
83
+ }
84
+
85
+ def struct_fields; FIELDS; end
86
+
87
+ def validate
88
+ end
89
+
90
+ ::Thrift::Struct.generate_accessors self
91
+ end
92
+
93
+ # A Mutation object is used to either update or delete a column-value.
94
+ class Mutation
95
+ include ::Thrift::Struct, ::Thrift::Struct_Union
96
+ ISDELETE = 1
97
+ COLUMN = 2
98
+ VALUE = 3
99
+
100
+ FIELDS = {
101
+ ISDELETE => {:type => ::Thrift::Types::BOOL, :name => 'isDelete', :default => false},
102
+ COLUMN => {:type => ::Thrift::Types::STRING, :name => 'column', :binary => true},
103
+ VALUE => {:type => ::Thrift::Types::STRING, :name => 'value', :binary => true}
104
+ }
105
+
106
+ def struct_fields; FIELDS; end
107
+
108
+ def validate
109
+ end
110
+
111
+ ::Thrift::Struct.generate_accessors self
112
+ end
113
+
114
+ # A BatchMutation object is used to apply a number of Mutations to a single row.
115
+ class BatchMutation
116
+ include ::Thrift::Struct, ::Thrift::Struct_Union
117
+ ROW = 1
118
+ MUTATIONS = 2
119
+
120
+ FIELDS = {
121
+ ROW => {:type => ::Thrift::Types::STRING, :name => 'row', :binary => true},
122
+ MUTATIONS => {:type => ::Thrift::Types::LIST, :name => 'mutations', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Apache::Hadoop::Hbase::Thrift::Mutation}}
123
+ }
124
+
125
+ def struct_fields; FIELDS; end
126
+
127
+ def validate
128
+ end
129
+
130
+ ::Thrift::Struct.generate_accessors self
131
+ end
132
+
133
+ # Holds row name and then a map of columns to cells.
134
+ class TRowResult
135
+ include ::Thrift::Struct, ::Thrift::Struct_Union
136
+ ROW = 1
137
+ COLUMNS = 2
138
+
139
+ FIELDS = {
140
+ ROW => {:type => ::Thrift::Types::STRING, :name => 'row', :binary => true},
141
+ COLUMNS => {:type => ::Thrift::Types::MAP, :name => 'columns', :key => {:type => ::Thrift::Types::STRING, :binary => true}, :value => {:type => ::Thrift::Types::STRUCT, :class => ::Apache::Hadoop::Hbase::Thrift::TCell}}
142
+ }
143
+
144
+ def struct_fields; FIELDS; end
145
+
146
+ def validate
147
+ end
148
+
149
+ ::Thrift::Struct.generate_accessors self
150
+ end
151
+
152
+ # A Scan object is used to specify scanner parameters when opening a scanner.
153
+ class TScan
154
+ include ::Thrift::Struct, ::Thrift::Struct_Union
155
+ STARTROW = 1
156
+ STOPROW = 2
157
+ TIMESTAMP = 3
158
+ COLUMNS = 4
159
+ CACHING = 5
160
+ FILTERSTRING = 6
161
+
162
+ FIELDS = {
163
+ STARTROW => {:type => ::Thrift::Types::STRING, :name => 'startRow', :binary => true, :optional => true},
164
+ STOPROW => {:type => ::Thrift::Types::STRING, :name => 'stopRow', :binary => true, :optional => true},
165
+ TIMESTAMP => {:type => ::Thrift::Types::I64, :name => 'timestamp', :optional => true},
166
+ COLUMNS => {:type => ::Thrift::Types::LIST, :name => 'columns', :element => {:type => ::Thrift::Types::STRING, :binary => true}, :optional => true},
167
+ CACHING => {:type => ::Thrift::Types::I32, :name => 'caching', :optional => true},
168
+ FILTERSTRING => {:type => ::Thrift::Types::STRING, :name => 'filterString', :binary => true, :optional => true}
169
+ }
170
+
171
+ def struct_fields; FIELDS; end
172
+
173
+ def validate
174
+ end
175
+
176
+ ::Thrift::Struct.generate_accessors self
177
+ end
178
+
179
+ # An IOError exception signals that an error occurred communicating
180
+ # to the Hbase master or an Hbase region server. Also used to return
181
+ # more general Hbase error conditions.
182
+ class IOError < ::Thrift::Exception
183
+ include ::Thrift::Struct, ::Thrift::Struct_Union
184
+ def initialize(message=nil)
185
+ super()
186
+ self.message = message
187
+ end
188
+
189
+ MESSAGE = 1
190
+
191
+ FIELDS = {
192
+ MESSAGE => {:type => ::Thrift::Types::STRING, :name => 'message'}
193
+ }
194
+
195
+ def struct_fields; FIELDS; end
196
+
197
+ def validate
198
+ end
199
+
200
+ ::Thrift::Struct.generate_accessors self
201
+ end
202
+
203
+ # An IllegalArgument exception indicates an illegal or invalid
204
+ # argument was passed into a procedure.
205
+ class IllegalArgument < ::Thrift::Exception
206
+ include ::Thrift::Struct, ::Thrift::Struct_Union
207
+ def initialize(message=nil)
208
+ super()
209
+ self.message = message
210
+ end
211
+
212
+ MESSAGE = 1
213
+
214
+ FIELDS = {
215
+ MESSAGE => {:type => ::Thrift::Types::STRING, :name => 'message'}
216
+ }
217
+
218
+ def struct_fields; FIELDS; end
219
+
220
+ def validate
221
+ end
222
+
223
+ ::Thrift::Struct.generate_accessors self
224
+ end
225
+
226
+ # An AlreadyExists exceptions signals that a table with the specified
227
+ # name already exists
228
+ class AlreadyExists < ::Thrift::Exception
229
+ include ::Thrift::Struct, ::Thrift::Struct_Union
230
+ def initialize(message=nil)
231
+ super()
232
+ self.message = message
233
+ end
234
+
235
+ MESSAGE = 1
236
+
237
+ FIELDS = {
238
+ MESSAGE => {:type => ::Thrift::Types::STRING, :name => 'message'}
239
+ }
240
+
241
+ def struct_fields; FIELDS; end
242
+
243
+ def validate
244
+ end
245
+
246
+ ::Thrift::Struct.generate_accessors self
247
+ end
248
+
249
+ end
250
+ end
251
+ end
252
+ end
data/ok-hbase.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ok_hbase/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "ok_hbase"
8
+ gem.version = OkHbase::VERSION
9
+ gem.authors = ["Nathan Keyes"]
10
+ gem.email = ["keyes@okcupidlabs.com"]
11
+ gem.description = %q{Lightweight Ruby Hbase Client}
12
+ gem.summary = %q{Lightweight Ruby Hbase Client}
13
+ gem.homepage = "http://okcwest.github.io/ok_hbase/"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+
21
+ gem.add_dependency 'thrift', '0.9.0'
22
+ gem.add_dependency 'activesupport'
23
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ module OkHbase
4
+ describe Connection do
5
+ describe ".create_table" do
6
+ let(:conn) { Connection.new auto_connect: true, timeout: 60 }
7
+
8
+ it "should create tables with the right column families" do
9
+ name = "ok_hbase_test_table"
10
+ column_families = {
11
+ 'a' => {
12
+ 'max_versions' => 5,
13
+ 'compression' => 'GZ',
14
+ 'in_memory' => true,
15
+ 'bloom_filter_type' => 'ROW',
16
+ 'block_cache_enabled' => true,
17
+
18
+ #TODO find out if these aren't being set properly or just aren't reported properly. 0 is the default
19
+ 'bloom_filter_vector_size' => 0,
20
+ 'bloom_filter_nb_hashes' => 0,
21
+
22
+ #TODO find out why this doesn't get reported properly. -1 is the default
23
+ 'time_to_live' => -1
24
+ },
25
+ 'b' => {
26
+ 'max_versions' => 15,
27
+ 'compression' => 'NONE',
28
+ 'in_memory' => true,
29
+ 'bloom_filter_type' => 'ROWCOL',
30
+ 'block_cache_enabled' => true,
31
+
32
+ 'bloom_filter_vector_size' => 0,
33
+ 'bloom_filter_nb_hashes' => 0,
34
+
35
+ 'time_to_live' => -1
36
+ }
37
+ }
38
+
39
+ expected_families = Hash[column_families.map { |cf, data| [cf, { 'name' => "#{cf}:" }.merge(data)] }]
40
+
41
+ #sanity check
42
+ conn.tables.should_not include(name)
43
+
44
+ conn.create_table(name, column_families)
45
+
46
+ conn.tables.should include(name)
47
+
48
+ table = conn.table(name)
49
+
50
+ table.families.should == expected_families
51
+
52
+ #cleanup
53
+ conn.delete_table(name, true)
54
+ end
55
+
56
+
57
+ it "should create tables with the right name" do
58
+ name = "ok_hbase_test_table"
59
+ column_families = {
60
+ 'd' => {}
61
+ }
62
+
63
+ #sanity check
64
+ conn.tables.should_not include(name)
65
+
66
+ conn.create_table(name, column_families)
67
+
68
+ conn.tables.should include(name)
69
+
70
+ #cleanup
71
+ conn.delete_table(name, true)
72
+ end
73
+ end
74
+
75
+ describe ".open" do
76
+ let(:conn) { Connection.new }
77
+
78
+ it "should open a connection" do
79
+ expect { conn.open }.to change { conn.open? }.to(true)
80
+ end
81
+ end
82
+
83
+ describe ".close" do
84
+ let(:conn) { Connection.new auto_connect: true }
85
+
86
+ it "should close a connection" do
87
+ expect { conn.close }.to change { conn.open? }.to(false)
88
+ end
89
+ end
90
+
91
+ describe ".tables" do
92
+ let(:conn) { Connection.new auto_connect: true }
93
+
94
+ it "should return an array of table names" do
95
+ conn.tables.should be_an Array
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,149 @@
1
+ require 'spec_helper'
2
+
3
+ module OkHbase
4
+ describe Table do
5
+ let(:row_key1) { 'row1' }
6
+ let(:row_key2) { 'row2' }
7
+ let(:row_data1) { { 'd:foo' => 'Foo1', 'd:bar' => 'Bar1', 'd:baz' => 'Baz1' } }
8
+ let(:row_data2) { { 'd:foo' => 'Foo2', 'd:bar' => 'Bar2', 'd:baz' => 'Baz2' } }
9
+ let!(:timestamp) { (Time.now.to_f * 1000).to_i } # hbase timestamps are in milisecnds
10
+
11
+ test_table_name = 'ok_hbase_test_table'
12
+ conn = Connection.new(auto_connect: true, timeout: 60)
13
+
14
+ before(:all) do
15
+ conn.create_table(test_table_name, d: { max_versions: 3 })
16
+ end
17
+
18
+ after(:all) do
19
+ conn.delete_table(test_table_name, true)
20
+ end
21
+
22
+ subject { Table.new(test_table_name, conn) }
23
+
24
+ describe '#_scanner' do
25
+ let(:scanner_opts) { {
26
+ start_row: 'start',
27
+ stop_row: 'stop',
28
+ timestamp: Time.now,
29
+ columns: ['d:foo', 'd:bar', 'i:baz'],
30
+ caching: 1000,
31
+ filter_string: 'filter',
32
+ } }
33
+
34
+ it 'should set all the options' do
35
+ scanner = subject._scanner(scanner_opts)
36
+
37
+ scanner.startRow.should == scanner_opts[:start_row]
38
+ scanner.stopRow.should == scanner_opts[:stop_row]
39
+ scanner.timestamp.should == scanner_opts[:timestamp]
40
+ scanner.columns.should == scanner_opts[:columns]
41
+ scanner.caching.should == scanner_opts[:caching]
42
+ scanner.filterString.should == scanner_opts[:filter_string]
43
+ end
44
+ end
45
+
46
+ describe '.scan' do
47
+
48
+ it 'should convert a row prefix to a start and stop rows' do
49
+ subject.put('scan_row1', row_data1)
50
+ subject.put('scan_row2', row_data1)
51
+
52
+ opts = { row_prefix: 'scan' }
53
+ subject.should_receive(:_scanner).with(hash_including(start_row: 'scan', stop_row: 'scao')).and_return {
54
+ Apache::Hadoop::Hbase::Thrift::TScan.new(
55
+ startRow: 'scan',
56
+ stopRow: 'scao',
57
+ caching: 1000,
58
+ )
59
+ }
60
+
61
+ subject.scan(opts) do |row_key, cols|
62
+ cols.should == row_data1
63
+ end
64
+ end
65
+ end
66
+
67
+ describe '.regions' do
68
+ it 'should list all region having rows for the table' do
69
+ regions = subject.regions
70
+ regions.should be_an Array
71
+ regions.size.should be >= 1
72
+
73
+ regions.each do |region|
74
+ region.keys.should include('start_key', 'end_key', 'id', 'name', 'version')
75
+ end
76
+
77
+ end
78
+ end
79
+
80
+ describe 'CRUD' do
81
+
82
+ describe '.put' do
83
+
84
+ it 'should write to the row' do
85
+ subject.put(row_key1, row_data1)
86
+
87
+ subject.row(row_key1).should == row_data1
88
+ end
89
+
90
+ it 'should write to the row with a timestamp' do
91
+
92
+ subject.put(row_key1, row_data1, timestamp)
93
+
94
+ subject.row(row_key1, nil, nil, true).should == Hash[row_data1.map { |k, v| [k, [v, timestamp]] }]
95
+ end
96
+ end
97
+
98
+ describe '.cells' do
99
+ it 'should retrieve values' do
100
+
101
+ subject.put(row_key2, row_data2.merge('d:foo' => 'OldFoo1'), timestamp-10)
102
+ subject.put(row_key2, row_data2.merge('d:foo' => 'OldFoo2'), timestamp-5)
103
+ subject.put(row_key2, row_data2, timestamp)
104
+
105
+ subject.cells(row_key2, 'd:foo').should == ['Foo2', 'OldFoo2', 'OldFoo1']
106
+
107
+ subject.cells(row_key2, 'd:foo', nil, timestamp-1).should == ['OldFoo2', 'OldFoo1']
108
+ subject.cells(row_key2, 'd:foo', nil, timestamp-5).should == ['OldFoo1']
109
+
110
+ subject.cells(row_key2, 'd:foo', nil, nil, true).should == [['Foo2', timestamp], ['OldFoo2', timestamp-5], ['OldFoo1', timestamp-10]]
111
+ subject.cells(row_key2, 'd:foo', 2, nil, true).should == [['Foo2', timestamp], ['OldFoo2', timestamp-5]]
112
+ subject.cells(row_key2, 'd:foo', 1, nil, true).should == [['Foo2', timestamp]]
113
+
114
+ subject.cells(row_key2, 'd:foo', nil, nil, false).should == ['Foo2', 'OldFoo2', 'OldFoo1']
115
+ subject.cells(row_key2, 'd:foo', 2, nil, false).should == ['Foo2', 'OldFoo2']
116
+ subject.cells(row_key2, 'd:foo', 1, nil, false).should == ['Foo2']
117
+
118
+ end
119
+ end
120
+
121
+ describe ".rows" do
122
+ it 'should retrieve the specified rows' do
123
+
124
+ subject.put(row_key1, row_data1, timestamp-10)
125
+ subject.put(row_key2, row_data2.merge('d:foo' => 'OldFoo1'), timestamp-10)
126
+ subject.put(row_key2, row_data2.merge('d:foo' => 'OldFoo2'), timestamp-5)
127
+ subject.put(row_key2, row_data2, timestamp)
128
+
129
+ subject.rows([row_key1, row_key2]).should == [row_data1, row_data2]
130
+ subject.rows([row_key1, row_key2], nil, timestamp-9).should == [row_data1, row_data2.merge('d:foo' => 'OldFoo1')]
131
+
132
+ subject.rows([row_key1, row_key2], row_data1.keys - ['d:foo']).should == [row_data1.except('d:foo'), row_data2.except('d:foo')]
133
+ subject.rows([row_key1, row_key2], row_data1.keys - ['d:foo'], timestamp-5).should == [row_data1.except('d:foo'), row_data2.except('d:foo')]
134
+
135
+ end
136
+ end
137
+
138
+ describe ".delete" do
139
+ it 'should retrieve the specified rows' do
140
+ subject.put(row_key1, row_data1)
141
+
142
+ subject.delete(row_key1, ['d:bar', 'd:baz'])
143
+
144
+ subject.row(row_key1).should == row_data1.except('d:bar', 'd:baz')
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # require 'spec_helper'
3
+
4
+ describe OkHbase do
5
+
6
+ describe "#increment_string" do
7
+ let(:normal_string) { "foo baq" }
8
+ let(:incremented_normal_string) { "foo bar" }
9
+
10
+ let(:utf8_string) { "Vulgar fraction one half: ¼".force_encoding(Encoding::UTF_8) }
11
+ let(:incremented_utf8_string) { "Vulgar fraction one half: ½".force_encoding(Encoding::UTF_8) }
12
+ let(:max_byte_string) { [255, 255, 255].pack('c*') }
13
+
14
+ it "should increment the last byte byte < 255" do
15
+ OkHbase::increment_string(normal_string).should == incremented_normal_string
16
+ OkHbase::increment_string(utf8_string).should == incremented_utf8_string
17
+ end
18
+
19
+ it "should return nil when all bytes are 255" do
20
+ OkHbase::increment_string(max_byte_string).should be_nil
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'support'))
4
+
5
+ # needs to be at the top
6
+ require 'simplecov'
7
+ SimpleCov.start do
8
+ add_filter "/thrift/"
9
+ end
10
+
11
+ Bundler.require :default, :test
12
+
13
+
14
+ require 'rubygems'
15
+ require 'bundler'
16
+ require 'awesome_print'
17
+
18
+ require 'ok_hbase'
19
+
20
+
data/tasks/bump.rb ADDED
@@ -0,0 +1,30 @@
1
+ desc 'shortcut for bump:patch'
2
+ task :bump => 'bump:patch'
3
+
4
+ namespace :bump do
5
+ desc 'Bump x.y.Z'
6
+ task :patch
7
+
8
+ desc 'Bump x.Y.z'
9
+ task :minor
10
+
11
+ desc 'Bump X.y.z'
12
+ task :major
13
+ end
14
+
15
+ # extracted and modified from https://github.com/grosser/project_template
16
+ rule /^bump:.*/ do |t|
17
+ sh "git status | grep 'nothing to commit'" # ensure we are not dirty
18
+ index = ['major', 'minor','patch'].index(t.name.split(':').last)
19
+ file = 'lib/ok_hbase/version.rb'
20
+
21
+ version_file = File.read(file)
22
+ old_version, *version_parts = version_file.match(/(\d+)\.(\d+)\.(\d+)/).to_a
23
+ version_parts[index] = version_parts[index].to_i + 1
24
+ version_parts[2] = 0 if index < 2
25
+ version_parts[1] = 0 if index < 1
26
+ new_version = version_parts * '.'
27
+ File.open(file,'w'){|f| f.write(version_file.sub(old_version, new_version)) }
28
+
29
+ sh "bundle && git add #{file} && git commit -m 'bump version to #{new_version}'"
30
+ end