hbase-jruby 0.2.1-java → 0.2.2-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/CHANGELOG.md CHANGED
@@ -1,6 +1,14 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ 0.2.2
5
+ -----
6
+ - Added `HBase::Table#delete_row` method
7
+ - Dependency profiles as prefixes
8
+ - Supported prefixes: `cdh4.1`, `cdh3`, `0.94`, `0.92`
9
+ - e.g. `HBase.resolve_dependency! 'cdh4.1.3'`
10
+ - Advanced data access with `Scoped#with_java_scan` and `Scoped#with_java_get`
11
+
4
12
  0.2.1
5
13
  -----
6
14
  - Fix: NameError even when appropriate CLASSPATH is set
data/README.md CHANGED
@@ -1,11 +1,6 @@
1
1
  # hbase-jruby
2
2
 
3
- *hbase-jruby* is a Ruby-esque interface for accessing HBase from JRuby.
4
-
5
- With JRuby, you can of course just use the native Java APIs of HBase,
6
- but doing so requires lots of keystrokes even for the most basic operations and
7
- can lead to having overly verbose code that will be frowned upon by Rubyists.
8
- Anyhow, JRuby is Ruby, not Java, right?
3
+ *hbase-jruby* is a simple JRuby binding for HBase.
9
4
 
10
5
  *hbase-jruby* provides the followings:
11
6
  - Easy, Ruby-esque interface for the fundamental HBase operations
@@ -17,7 +12,7 @@ Anyhow, JRuby is Ruby, not Java, right?
17
12
  ```ruby
18
13
  require 'hbase-jruby'
19
14
 
20
- HBase.resolve_dependency! 'cdh4.1'
15
+ HBase.resolve_dependency! 'cdh4.1.3'
21
16
 
22
17
  hbase = HBase.new
23
18
  table = hbase[:test_table]
@@ -56,7 +51,7 @@ table.delete(:rowkey9)
56
51
  git clone -b devel https://github.com/junegunn/hbase-jruby.git
57
52
  cd hbase-jruby
58
53
  rake build
59
- gem install pkg/hbase-jruby-0.2.1-java.gem
54
+ gem install pkg/hbase-jruby-0.2.2-java.gem
60
55
 
61
56
  ## Setting up
62
57
 
@@ -69,27 +64,29 @@ or by `require`ing relevant JAR files after launching JRuby.
69
64
  ### `HBase.resolve_dependency!`
70
65
 
71
66
  Well, there's an easier way.
72
- You can call `HBase.resolve_dependency!` helper method passing one of the arguments listed below.
67
+ Call `HBase.resolve_dependency!` helper method passing one of the arguments listed below.
73
68
 
74
- | Argument | Description | Required executable |
75
- |------------|----------------------------------------------------------|---------------------|
76
- | 'cdh4.1' | Predefined Maven profile for Cloudera CDH4.1 | mvn |
77
- | 'cdh3' | Predefined Maven profile for Cloudera CDH3 | mvn |
78
- | '0.94' | Predefined Maven profile for Apache HBase 0.94 | mvn |
79
- | '0.92' | Predefined Maven profile for Apache HBase 0.92 | mvn |
80
- | *POM PATH* | Follow dependency described in the given POM file | mvn |
81
- | *:local* | Resolve HBase dependency using `hbase classpath` command | hbase |
69
+ | Argument | Dependency | Required executable |
70
+ |------------|--------------------------|---------------------|
71
+ | cdh4.1[.*] | Cloudera CDH4.1 | mvn |
72
+ | cdh3[u*] | Cloudera CDH3 | mvn |
73
+ | 0.94[.*] | Apache HBase 0.94 | mvn |
74
+ | 0.92[.*] | Apache HBase 0.92 | mvn |
75
+ | *POM PATH* | Custom Maven POM file | mvn |
76
+ | `:local` | Local HBase installation | hbase |
82
77
 
83
- ```ruby
84
- # Examples
78
+ #### Examples
85
79
 
86
- # Load JAR files from CDH4.1 distribution of HBase using Maven
87
- HBase.resolve_dependency! 'cdh4.1'
80
+ ```ruby
81
+ # Load JAR files from CDH4.1.x using Maven
82
+ HBase.resolve_dependency! 'cdh4.1.3'
83
+ HBase.resolve_dependency! 'cdh4.1.1'
88
84
 
89
- # Load JAR files for HBase 0.94 using Maven
90
- HBase.resolve_dependency! '0.94', :verbose => true
85
+ # Load JAR files of HBase 0.94.x using Maven
86
+ HBase.resolve_dependency! '0.94.1'
87
+ HBase.resolve_dependency! '0.94.2', :verbose => true
91
88
 
92
- # Dependency resolution with your own POM file
89
+ # Dependency resolution with custom POM file
93
90
  HBase.resolve_dependency! '/path/to/my/pom.xml'
94
91
  HBase.resolve_dependency! '/path/to/my/pom.xml', :profile => 'trunk'
95
92
 
@@ -375,10 +372,16 @@ table.delete('rowkey1', 'cf1:col1', 1352978648642)
375
372
  table.delete('rowkey1', 'cf1:col1', 1352978648642, 1352978649642)
376
373
 
377
374
  # Batch delete
378
- table.delete(['rowkey1'], ['rowkey2'], ['rowkey3', 'cf1:col1'])
375
+ table.delete(['rowkey1'], ['rowkey2'], ['rowkey3', 'cf1:col1', 1352978648642, 135297864964])
376
+ ```
377
+
378
+ However, the last syntax seems a bit unwieldy when you just wish to delete a few rows.
379
+ In that case, use simpler `delete_row` method.
380
+
381
+ ```ruby
382
+ table.delete_row 'rowkey1'
379
383
 
380
- # Truncate table
381
- table.truncate!
384
+ table.delete_row 'rowkey1', 'rowkey2', 'rowkey3'
382
385
  ```
383
386
 
384
387
  ### Atomic increment of column values
@@ -409,18 +412,20 @@ end
409
412
  You can control how you retrieve data by chaining
410
413
  the following methods of `HBase::Table` (or `HBase::Scoped`).
411
414
 
412
- | Method | Description |
413
- |--------------|-----------------------------------------------------------------|
414
- | `range` | Specifies the rowkey range of scan |
415
- | `project` | To retrieve only a subset of columns |
416
- | `filter` | Filtering conditions of scan |
417
- | `while` | Allows early termination of scan (server-side) |
418
- | `at` | Only retrieve data with the specified timestamp |
419
- | `time_range` | Only retrieve data within the specified time range |
420
- | `limit` | Limits the number of rows |
421
- | `versions` | Limits the number of versions of each column |
422
- | `caching` | Sets the number of rows for caching during scan |
423
- | `batch` | Limits the maximum number of values returned for each iteration |
415
+ | Method | Description |
416
+ |------------------|-----------------------------------------------------------------|
417
+ | `range` | Specifies the rowkey range of scan |
418
+ | `project` | To retrieve only a subset of columns |
419
+ | `filter` | Filtering conditions of scan |
420
+ | `while` | Allows early termination of scan (server-side) |
421
+ | `at` | Only retrieve data with the specified timestamp |
422
+ | `time_range` | Only retrieve data within the specified time range |
423
+ | `limit` | Limits the number of rows |
424
+ | `versions` | Limits the number of versions of each column |
425
+ | `caching` | Sets the number of rows for caching during scan |
426
+ | `batch` | Limits the maximum number of values returned for each iteration |
427
+ | `with_java_scan` | *(ADVANCED)* Access Java Scan object in the given block |
428
+ | `with_java_get` | *(ADVANCED)* Access Java Get object in the given block |
424
429
 
425
430
  Each invocation to these methods returns an `HBase::Scoped` instance with which
426
431
  you can retrieve data with the following methods.
@@ -450,6 +455,9 @@ table.range('A'..'Z'). # Row key range,
450
455
  versions(2). # Only fetches 2 versions for each value
451
456
  batch(100). # Batch size for scan set to 100
452
457
  caching(1000). # Caching 1000 rows
458
+ with_java_scan { |scan| # Directly access Java Scan object
459
+ scan.setCacheBlocks false
460
+ }.
453
461
  to_a # To Array
454
462
  ```
455
463
 
data/hbase-jruby.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |gem|
8
8
  gem.version = HBase::JRuby::VERSION
9
9
  gem.authors = ["Junegunn Choi"]
10
10
  gem.email = ["junegunn.c@gmail.com"]
11
- gem.description = %q{Ruby-esque interface for accessing HBase from JRuby}
12
- gem.summary = %q{Ruby-esque interface for accessing HBase from JRuby}
11
+ gem.description = %q{A JRuby binding for HBase}
12
+ gem.summary = %q{A JRuby binding for HBase}
13
13
  gem.homepage = "https://github.com/junegunn/hbase-jruby"
14
14
  gem.platform = 'java'
15
15
  gem.license = 'MIT'
@@ -3,7 +3,7 @@ class << self
3
3
  # Shortcut method to HBase::ByteArray.new
4
4
  # @param [*Object] values
5
5
  def ByteArray *values
6
- ByteArray.new *values
6
+ ByteArray.new(*values)
7
7
  end
8
8
  end
9
9
  # Boxed class for Java byte arrays
@@ -1,9 +1,20 @@
1
1
  require 'java'
2
2
  require 'open-uri'
3
3
  require 'tempfile'
4
+ require 'erb'
4
5
 
5
6
  # HBase connection
6
7
  class HBase
8
+
9
+ # @private
10
+ SUPPORTED_PROFILES = {
11
+ # Prefix => Latest version
12
+ 'cdh4.1' => 'cdh4.1.3',
13
+ 'cdh3' => 'cdh3u5',
14
+ '0.94' => '0.94.3',
15
+ '0.92' => '0.92.1',
16
+ }
17
+
7
18
  class << self
8
19
  # @overload resolve_dependency!(dist, options)
9
20
  # Resolve Hadoop and HBase dependency with a predefined Maven profile
@@ -40,12 +51,25 @@ class HBase
40
51
  mvn = `which mvn`
41
52
  raise RuntimeError, "Cannot find `mvn` executable" if mvn.empty?
42
53
 
54
+ # POM file path given (with optional profile)
43
55
  if File.exists?(dist)
44
56
  path = dist
45
57
  profile = options[:profile] && "-P #{options[:profile]}"
58
+ # Predefined dependencies
46
59
  else
47
- path = File.expand_path("../pom/pom.xml", __FILE__)
48
- profile = "-P #{dist}"
60
+ matched_profiles = SUPPORTED_PROFILES.keys.select { |pf| dist.start_with? pf }
61
+ if matched_profiles.length != 1
62
+ raise ArgumentError, "Invalid profile: #{dist}"
63
+ end
64
+ matched_profile = matched_profiles.first
65
+ profiles = SUPPORTED_PROFILES.dup
66
+ profiles[matched_profile] = dist if dist != matched_profile
67
+ tempfiles << tf = Tempfile.new('hbase-jruby-pom')
68
+ erb = ERB.new(File.read File.expand_path("../pom/pom.xml.erb", __FILE__))
69
+ tf << erb.result(binding)
70
+ tf.close(false)
71
+ path = tf.path
72
+ profile = "-P #{matched_profile}"
49
73
  end
50
74
 
51
75
  # Download dependent JAR files and build classpath string
@@ -14,8 +14,8 @@
14
14
  <id>cdh4.1</id>
15
15
  <properties>
16
16
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
17
- <hadoop.version>2.0.0-cdh4.1.2</hadoop.version>
18
- <hbase.version>0.92.1-cdh4.1.2</hbase.version>
17
+ <hadoop.version>2.0.0-<%= profiles['cdh4.1'] %></hadoop.version>
18
+ <hbase.version>0.92.1-<%= profiles['cdh4.1'] %></hbase.version>
19
19
  </properties>
20
20
  <repositories>
21
21
  <repository>
@@ -51,8 +51,8 @@
51
51
  <id>cdh3</id>
52
52
  <properties>
53
53
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
54
- <hbase.version>0.90.6-cdh3u5</hbase.version>
55
- <hadoop.version>0.20.2-cdh3u5</hadoop.version>
54
+ <hbase.version>0.90.6-<%= profiles['cdh3'] %></hbase.version>
55
+ <hadoop.version>0.20.2-<%= profiles['cdh3'] %></hadoop.version>
56
56
  </properties>
57
57
 
58
58
  <repositories>
@@ -83,7 +83,7 @@
83
83
  <properties>
84
84
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
85
85
  <hadoop.version>1.1.1</hadoop.version>
86
- <hbase.version>0.92.2</hbase.version>
86
+ <hbase.version><%= profiles['0.92'] %></hbase.version>
87
87
  </properties>
88
88
 
89
89
  <dependencies>
@@ -108,7 +108,7 @@
108
108
  <properties>
109
109
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
110
110
  <hadoop.version>1.1.1</hadoop.version>
111
- <hbase.version>0.94.3</hbase.version>
111
+ <hbase.version><%= profiles['0.94'] %></hbase.version>
112
112
  </properties>
113
113
 
114
114
  <dependencies>
@@ -127,75 +127,6 @@
127
127
  </dependency>
128
128
  </dependencies>
129
129
  </profile>
130
-
131
- <!-- For backward compatibility -->
132
- <profile>
133
- <id>cdh4.1.2</id>
134
- <properties>
135
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
136
- <hadoop.version>2.0.0-cdh4.1.2</hadoop.version>
137
- <hbase.version>0.92.1-cdh4.1.2</hbase.version>
138
- </properties>
139
- <repositories>
140
- <repository>
141
- <id>cloudera-releases</id>
142
- <url>https://repository.cloudera.com/artifactory/cloudera-repos</url>
143
- <releases>
144
- <enabled>true</enabled>
145
- </releases>
146
- <snapshots>
147
- <enabled>false</enabled>
148
- </snapshots>
149
- </repository>
150
- </repositories>
151
-
152
- <dependencies>
153
- <dependency>
154
- <groupId>org.apache.hadoop</groupId>
155
- <artifactId>hadoop-common</artifactId>
156
- <version>${hadoop.version}</version>
157
- <scope>compile</scope>
158
- </dependency>
159
-
160
- <dependency>
161
- <groupId>org.apache.hbase</groupId>
162
- <artifactId>hbase</artifactId>
163
- <version>${hbase.version}</version>
164
- <scope>compile</scope>
165
- </dependency>
166
- </dependencies>
167
- </profile>
168
-
169
- <profile>
170
- <id>cdh3u5</id>
171
- <properties>
172
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
173
- <hbase.version>0.90.6-cdh3u5</hbase.version>
174
- <hadoop.version>0.20.2-cdh3u5</hadoop.version>
175
- </properties>
176
-
177
- <repositories>
178
- <repository>
179
- <id>cloudera-releases</id>
180
- <url>https://repository.cloudera.com/artifactory/cloudera-repos</url>
181
- <releases>
182
- <enabled>true</enabled>
183
- </releases>
184
- <snapshots>
185
- <enabled>false</enabled>
186
- </snapshots>
187
- </repository>
188
- </repositories>
189
-
190
- <dependencies>
191
- <dependency>
192
- <groupId>org.apache.hbase</groupId>
193
- <artifactId>hbase</artifactId>
194
- <version>${hbase.version}</version>
195
- <scope>compile</scope>
196
- </dependency>
197
- </dependencies>
198
- </profile>
199
130
  </profiles>
200
131
  </project>
201
132
 
@@ -217,6 +217,28 @@ class Scoped
217
217
  spawn :@batch, b
218
218
  end
219
219
 
220
+ # Returns an HBase::Scoped object with the Scan-customization block added.
221
+ # The given block will be evaluated just before an actual scan operation.
222
+ # With method-chaining, multiple blocks can be registered to be evaluated sequentially.
223
+ # @return [HBase::Scoped]
224
+ # @yield [org.apache.hadoop.hbase.client.Scan]
225
+ def with_java_scan &block
226
+ raise ArgumentError, "Block not given" if block.nil?
227
+ raise ArgumentError, "Invalid arity: should be 1" unless block.arity == 1
228
+ spawn :@scan_cbs, @scan_cbs + [block]
229
+ end
230
+
231
+ # Returns an HBase::Scoped object with the Get-customization block added
232
+ # The given block will be evaluated just before an actual get operation.
233
+ # With method-chaining, multiple blocks can be registered to be evaluated sequentially.
234
+ # @return [HBase::Scoped]
235
+ # @yield [org.apache.hadoop.hbase.client.Get]
236
+ def with_java_get &block
237
+ raise ArgumentError, "Block not given" if block.nil?
238
+ raise ArgumentError, "Invalid arity: should be 1" unless block.arity == 1
239
+ spawn :@get_cbs, @get_cbs + [block]
240
+ end
241
+
220
242
  private
221
243
  # @param [HBase::Table] table
222
244
  def initialize table
@@ -230,6 +252,8 @@ private
230
252
  @caching = nil
231
253
  @limit = nil
232
254
  @trange = nil
255
+ @scan_cbs = []
256
+ @get_cbs = []
233
257
  end
234
258
 
235
259
  def spawn *args
@@ -365,6 +389,11 @@ private
365
389
  when Time, Fixnum
366
390
  get.setTimeStamp @trange
367
391
  end
392
+
393
+ # Customization
394
+ @get_cbs.each do |prc|
395
+ prc.call get
396
+ end
368
397
  }
369
398
  end
370
399
 
@@ -494,6 +523,11 @@ private
494
523
 
495
524
  # Batch
496
525
  scan.setBatch @batch if @batch
526
+
527
+ # Customization
528
+ @scan_cbs.each do |prc|
529
+ prc.call scan
530
+ end
497
531
  }
498
532
  end
499
533
 
@@ -40,6 +40,14 @@ class Table
40
40
  end
41
41
  end
42
42
 
43
+ def with_java_scan &block
44
+ self.each.with_java_scan(&block)
45
+ end
46
+
47
+ def with_java_get &block
48
+ self.each.with_java_get(&block)
49
+ end
50
+
43
51
  # Performs PUT operations
44
52
  # @overload put(rowkey, data)
45
53
  # Put operation on a rowkey
@@ -119,6 +127,13 @@ class Table
119
127
  }
120
128
  end
121
129
 
130
+ # Delete rows.
131
+ # @param [*Object] rowkeys List of rowkeys of rows to delete
132
+ # @return [nil]
133
+ def delete_row *rowkeys
134
+ htable.delete rowkeys.map { |rk| Delete.new(Util.to_bytes rk) }
135
+ end
136
+
122
137
  # Atomically increase numeric values
123
138
  # @overload increment(rowkey, column, by)
124
139
  # Atomically increase column value by the specified amount
@@ -1,5 +1,5 @@
1
1
  class HBase
2
2
  module JRuby
3
- VERSION = "0.2.1"
3
+ VERSION = "0.2.2"
4
4
  end
5
5
  end
data/test/test_scoped.rb CHANGED
@@ -390,6 +390,8 @@ class TestScoped < TestHBaseJRubyBase
390
390
  @table.put :rowkey3 => { 'cf1:a' => { t3 => 3 } }
391
391
  @table.put :rowkey4 => { 'cf1:a' => { t4 => 4 } }
392
392
 
393
+ assert_equal 4, @table.count
394
+
393
395
  assert_equal 1, @table.time_range(t2, t3).count
394
396
  assert_equal 2, @table.time_range(t2, t3 + 1).count
395
397
  assert_equal 2, @table.time_range(t2, t4).count
@@ -413,5 +415,43 @@ class TestScoped < TestHBaseJRubyBase
413
415
  # according to current hbase impl, later call overrides the previous time ranges. but, why do this?
414
416
  assert_equal 2, @table.time_range(t2, t3).at(t1).count
415
417
  end
418
+
419
+ def test_with_java_scan
420
+ ('a'..'z').each do |rk|
421
+ @table.put rk, 'cf1:a' => 1
422
+ end
423
+
424
+ assert_equal 2, @table.with_java_scan { |scan|
425
+ scan.setStartRow HBase::Util.to_bytes 'a'
426
+ scan.setStopRow HBase::Util.to_bytes 'd'
427
+ }.with_java_scan { |scan|
428
+ scan.setStartRow HBase::Util.to_bytes 'b'
429
+ }.count
430
+ end
431
+
432
+ def test_with_java_get
433
+ t1, t2, t3, t4 =
434
+ Time.now - 4000,
435
+ Time.now - 3000,
436
+ Time.now - 2000,
437
+ Time.now - 1000
438
+ @table.put :r1 => { 'cf1:a' => { t1 => 1 } }
439
+ @table.put :r2 => { 'cf1:a' => { t2 => 2 } }
440
+ @table.put :r3 => { 'cf1:a' => { t3 => 3 } }
441
+ @table.put :r4 => { 'cf1:a' => { t4 => 4 } }
442
+
443
+ assert_equal 4, @table.count
444
+
445
+ rks = [:r1, :r2, :r3, :r4]
446
+ assert_equal 4, @table.get(rks).compact.count
447
+
448
+ scoped = @table.with_java_get { |get|
449
+ get.setTimeRange(t1.to_i * 1000, t4.to_i * 1000)
450
+ }
451
+ assert_equal 3, scoped.get(rks).compact.count
452
+ assert_equal 2, scoped.with_java_get { |get|
453
+ get.setTimeRange(t2.to_i * 1000, t4.to_i * 1000)
454
+ }.get(rks).compact.count
455
+ end
416
456
  end
417
457
 
data/test/test_table.rb CHANGED
@@ -266,5 +266,21 @@ class TestTable < TestHBaseJRubyBase
266
266
  assert_nil @table.get('row3').to_hash['cf1:a']
267
267
  assert_equal 2, @table.get('row3').fixnum('cf1:b')
268
268
  end
269
+
270
+ def test_delete_row
271
+ @table.put(1 => { 'cf1:a' => 1 }, 's' => { 'cf1:a' => 2 }, { :short => 3 } => { 'cf1:a' => 3 })
272
+
273
+ assert_equal 1, @table.get(1).fixnum('cf1:a')
274
+ assert_equal 2, @table.get('s').fixnum('cf1:a')
275
+ assert_equal 3, @table.get({ :short => 3 }).fixnum('cf1:a')
276
+ assert_equal 3, @table.count
277
+
278
+ @table.delete_row 1, { :short => 3 }
279
+
280
+ assert_equal nil, @table.get(1)
281
+ assert_equal 2, @table.get('s').fixnum('cf1:a')
282
+ assert_equal nil, @table.get({ :short => 3 })
283
+ assert_equal 1, @table.count
284
+ end
269
285
  end
270
286
 
metadata CHANGED
@@ -2,14 +2,14 @@
2
2
  name: hbase-jruby
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.2.1
5
+ version: 0.2.2
6
6
  platform: java
7
7
  authors:
8
8
  - Junegunn Choi
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-29 00:00:00.000000000 Z
12
+ date: 2013-02-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: test-unit
@@ -47,7 +47,7 @@ dependencies:
47
47
  none: false
48
48
  prerelease: false
49
49
  type: :development
50
- description: Ruby-esque interface for accessing HBase from JRuby
50
+ description: A JRuby binding for HBase
51
51
  email:
52
52
  - junegunn.c@gmail.com
53
53
  executables: []
@@ -68,7 +68,7 @@ files:
68
68
  - lib/hbase-jruby/column_key.rb
69
69
  - lib/hbase-jruby/dependency.rb
70
70
  - lib/hbase-jruby/hbase.rb
71
- - lib/hbase-jruby/pom/pom.xml
71
+ - lib/hbase-jruby/pom/pom.xml.erb
72
72
  - lib/hbase-jruby/result.rb
73
73
  - lib/hbase-jruby/scoped.rb
74
74
  - lib/hbase-jruby/scoped/aggregation.rb
@@ -113,7 +113,7 @@ rubyforge_project:
113
113
  rubygems_version: 1.8.24
114
114
  signing_key:
115
115
  specification_version: 3
116
- summary: Ruby-esque interface for accessing HBase from JRuby
116
+ summary: A JRuby binding for HBase
117
117
  test_files:
118
118
  - test/helper.rb
119
119
  - test/test_aggregation.rb