hbase-ruby 1.1.0

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 (54) hide show
  1. data/History.txt +11 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.textile +81 -0
  4. data/Rakefile +24 -0
  5. data/VERSION +1 -0
  6. data/lib/hbase.rb +44 -0
  7. data/lib/hbase/client.rb +80 -0
  8. data/lib/hbase/exception.rb +19 -0
  9. data/lib/hbase/model.rb +19 -0
  10. data/lib/hbase/model/column.rb +9 -0
  11. data/lib/hbase/model/column_descriptor.rb +32 -0
  12. data/lib/hbase/model/region_descriptor.rb +9 -0
  13. data/lib/hbase/model/row.rb +11 -0
  14. data/lib/hbase/model/scanner.rb +13 -0
  15. data/lib/hbase/model/table_descriptor.rb +8 -0
  16. data/lib/hbase/operation/meta_operation.rb +10 -0
  17. data/lib/hbase/operation/row_operation.rb +83 -0
  18. data/lib/hbase/operation/scanner_operation.rb +82 -0
  19. data/lib/hbase/operation/table_operation.rb +95 -0
  20. data/lib/hbase/request.rb +7 -0
  21. data/lib/hbase/request/basic_request.rb +27 -0
  22. data/lib/hbase/request/meta_request.rb +17 -0
  23. data/lib/hbase/request/row_request.rb +34 -0
  24. data/lib/hbase/request/scanner_request.rb +25 -0
  25. data/lib/hbase/request/table_request.rb +43 -0
  26. data/lib/hbase/response.rb +7 -0
  27. data/lib/hbase/response/basic_response.rb +16 -0
  28. data/lib/hbase/response/meta_response.rb +35 -0
  29. data/lib/hbase/response/row_response.rb +31 -0
  30. data/lib/hbase/response/scanner_response.rb +37 -0
  31. data/lib/hbase/response/table_response.rb +26 -0
  32. data/spec/hbase/model/column_descriptor_spec.rb +23 -0
  33. data/spec/hbase/model/column_spec.rb +12 -0
  34. data/spec/hbase/model/region_descriptor_spec.rb +4 -0
  35. data/spec/hbase/model/row_spec.rb +12 -0
  36. data/spec/hbase/model/scanner.rb +7 -0
  37. data/spec/hbase/model/table_descriptor_spec.rb +12 -0
  38. data/spec/hbase/operation/meta_operation_spec.rb +18 -0
  39. data/spec/hbase/operation/row_operation_spec.rb +39 -0
  40. data/spec/hbase/operation/scanner_operation_spec.rb +81 -0
  41. data/spec/hbase/operation/table_operation_spec.rb +57 -0
  42. data/spec/hbase/record_spec.rb +25 -0
  43. data/spec/hbase/request/meta_request_spec.rb +10 -0
  44. data/spec/hbase/request/row_request_spec.rb +5 -0
  45. data/spec/hbase/request/scanner_request_spec.rb +5 -0
  46. data/spec/hbase/request/table_request_spec.rb +4 -0
  47. data/spec/hbase/response/meta_response_spec.rb +4 -0
  48. data/spec/hbase/response/row_response_spec.rb +4 -0
  49. data/spec/hbase/response/scanner_response_spec.rb +4 -0
  50. data/spec/hbase/response/table_response_spec.rb +4 -0
  51. data/spec/spec.opts +5 -0
  52. data/spec/spec_helper.rb +7 -0
  53. data/tasks/rspec.rake +7 -0
  54. metadata +147 -0
@@ -0,0 +1,82 @@
1
+ module HBase
2
+ module Operation
3
+ module ScannerOperation
4
+ # Trying to maintain some API stability for now
5
+ def open_scanner(table_name, columns, start_row, stop_row = nil, timestamp = nil)
6
+ warn "[DEPRECATION] This method is deprecated. Use #open_scanner(table_name, options) instead."
7
+
8
+ open_scanner(table_name, {:columns => columns, :start_row => start_row, :stop_row => stop_row, :timestamp => timestamp})
9
+ end
10
+
11
+ def open_scanner(table_name, options = {})
12
+ raise ArgumentError, "options should be given as a Hash" unless options.instance_of? Hash
13
+ columns = options.delete(:columns)
14
+
15
+ begin
16
+ request = Request::ScannerRequest.new(table_name)
17
+
18
+ xml_data = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><Scanner "
19
+ options.each do |key,value|
20
+ if Model::Scanner::AVAILABLE_OPTS.include? key
21
+ xml_data << "#{Model::Scanner::AVAILABLE_OPTS[key]}='"
22
+ xml_data << ( (key == :batch) ? value.to_s : [value.to_s].flatten.pack('m') )
23
+ xml_data << "' "
24
+ else
25
+ warn "[open_scanner] Received invalid option key :#{key}"
26
+ end
27
+ end
28
+ if columns
29
+ xml_data << ">"
30
+ [columns].flatten.each do |col|
31
+ xml_data << "<column>#{[col].flatten.pack('m')}</column>"
32
+ end
33
+ xml_data << "</Scanner>"
34
+ else
35
+ xml_data << "/>"
36
+ end
37
+
38
+ scanner = Response::ScannerResponse.new(post_response(request.open, xml_data), :open_scanner).parse
39
+ scanner.table_name = table_name
40
+ scanner
41
+ rescue Net::ProtocolError => e
42
+ if e.to_s.include?("TableNotFoundException")
43
+ raise TableNotFoundError, "Table #{table_name} Not Found!"
44
+ else
45
+ raise StandardError, e.to_s
46
+ end
47
+ end
48
+ end
49
+
50
+ def get_rows(scanner, limit = nil)
51
+ warn "[DEPRECATION] Use of 'limit' here is deprecated. Instead, define the batch size when creating the scanner." if limit
52
+ begin
53
+ request = Request::ScannerRequest.new(scanner.table_name)
54
+ rows = Response::ScannerResponse.new(get(request.get_rows(scanner)), :get_rows).parse
55
+ rows.each do |row|
56
+ row.table_name = scanner.table_name
57
+ end
58
+ rows
59
+ rescue StandardError => e
60
+ if e.to_s.include?("TableNotFoundException")
61
+ raise TableNotFoundError, "Table #{table_name} Not Found!"
62
+ else
63
+ raise StandardError, e.to_s
64
+ end
65
+ end
66
+ end
67
+
68
+ def close_scanner(scanner)
69
+ begin
70
+ request = Request::ScannerRequest.new(scanner.table_name)
71
+ Response::ScannerResponse.new(delete_response(request.close(scanner)), :close_scanner).parse
72
+ rescue StandardError => e
73
+ if e.to_s.include?("TableNotFoundException")
74
+ raise TableNotFoundError, "Table #{table_name} Not Found!"
75
+ else
76
+ raise StandardError, e.to_s
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,95 @@
1
+ module HBase
2
+ module Operation
3
+ module TableOperation
4
+ def show_table(name)
5
+ begin
6
+ request = Request::TableRequest.new(name)
7
+ table_descriptor = Response::TableResponse.new(get(request.show)).parse
8
+ rescue Net::ProtocolError => e
9
+ raise TableNotFoundError, "Table '#{name}' Not found"
10
+ end
11
+ end
12
+
13
+ def create_table(name, *args)
14
+ request = Request::TableRequest.new(name)
15
+
16
+ raise StandardError, "Table name must be of type String" unless name.instance_of? String
17
+
18
+ begin
19
+ xml_data = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><TableSchema name='#{name}' IS_META='false' IS_ROOT='false'>"
20
+ for arg in args
21
+ if arg.instance_of? String
22
+ xml_data << "<ColumnSchema name='#{arg}' />"
23
+ elsif arg.instance_of? Hash
24
+ xml_data << "<ColumnSchema "
25
+
26
+ arg.each do |k,v|
27
+ if Model::ColumnDescriptor::AVAILABLE_OPTS.include? k
28
+ xml_data << "#{Model::ColumnDescriptor::AVAILABLE_OPTS[k]}='#{v}' "
29
+ end
30
+ end
31
+
32
+ xml_data << "/>"
33
+ else
34
+ raise StandardError, "#{arg.class.to_s} of #{arg.to_s} is not of Hash Type"
35
+ end
36
+ end
37
+ xml_data << "</TableSchema>"
38
+ Response::TableResponse.new(post(request.create, xml_data))
39
+ rescue Net::ProtocolError => e
40
+ if e.to_s.include?("TableExistsException")
41
+ raise TableExistsError, "Table '#{name}' already exists"
42
+ else
43
+ raise TableFailCreateError, e.message
44
+ end
45
+ end
46
+ end
47
+
48
+ def alter_table(name, *args)
49
+ raise StandardError, "Table name must be of type String" unless name.instance_of? String
50
+
51
+ request = Request::TableRequest.new(name)
52
+
53
+ begin
54
+ xml_data = construct_xml_stream(name, *args)
55
+ Response::TableResponse.new(put(request.update, xml_data))
56
+ rescue Net::ProtocolError => e
57
+ if e.to_s.include?("TableNotFoundException")
58
+ raise TableNotFoundError, "Table '#{name}' not exists"
59
+ else
60
+ raise TableFailCreateError, e.message
61
+ end
62
+ end
63
+ end
64
+
65
+ def delete_table(name, columns = nil)
66
+ begin
67
+ request = Request::TableRequest.new(name)
68
+ Response::TableResponse.new(delete(request.delete(columns)))
69
+ rescue Net::ProtocolError => e
70
+ if e.to_s.include?("TableNotFoundException")
71
+ raise TableNotFoundError, "Table '#{name}' not exists"
72
+ elsif e.to_s.include?("TableNotDisabledException")
73
+ raise TableNotDisabledError, "Table '#{name}' not disabled"
74
+ end
75
+ end
76
+ end
77
+
78
+ def destroy_table(name, columns = nil)
79
+ delete_table(name, columns)
80
+ end
81
+
82
+ def enable_table(name)
83
+ warn "[DEPRECATION] Explicitly enabling tables isn't required anymore. HBase Stargate will enable/disable as needed."
84
+ end
85
+
86
+ def disable_table(name)
87
+ warn "[DEPRECATION] Explicitly disabling tables isn't required anymore. HBase Stargate will enable/disable as needed."
88
+ end
89
+
90
+ def table_regions(name, start_row = nil, end_row = nil)
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,7 @@
1
+ module HBase; module Request; end; end
2
+
3
+ require 'hbase/request/basic_request'
4
+ require 'hbase/request/meta_request'
5
+ require 'hbase/request/table_request'
6
+ require 'hbase/request/row_request'
7
+ require 'hbase/request/scanner_request'
@@ -0,0 +1,27 @@
1
+ require 'cgi'
2
+
3
+ module HBase
4
+ module Request
5
+ class BasicRequest
6
+ attr_reader :path
7
+
8
+ def initialize(path)
9
+ @path = path
10
+ end
11
+
12
+
13
+ protected
14
+
15
+ def pack_params columns
16
+ if columns.is_a? String
17
+ columns = [columns]
18
+ elsif columns.is_a? Array
19
+ else
20
+ raise StandardError, "Only String or Array type allows for columns"
21
+ end
22
+
23
+ columns.join(',')
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ module HBase
2
+ module Request
3
+ class MetaRequest < BasicRequest
4
+ def initialize
5
+ super("")
6
+ end
7
+
8
+ def list_tables
9
+ @path << "/"
10
+ end
11
+
12
+ def create_table
13
+ @path << "/tables"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,34 @@
1
+ module HBase
2
+ module Request
3
+ class RowRequest < BasicRequest
4
+ attr_reader :table_name
5
+ attr_reader :name
6
+ attr_reader :timestamp
7
+
8
+ def initialize(table_name, name, timestamp=nil)
9
+ @table_name, @name, @timestamp = CGI.escape(table_name), CGI.escape(name), timestamp
10
+ path = "/#{@table_name}/#{@name}"
11
+ super(path)
12
+ end
13
+
14
+ def show(columns = nil, options = { })
15
+ @path << "/#{pack_params(columns)}" if columns
16
+ @path << "/#{@timestamp}" if @timestamp
17
+ @path << "?v=#{options[:version]}" if options[:version]
18
+ @path
19
+ end
20
+
21
+ def create(columns = nil)
22
+ @path << "/#{pack_params(columns)}" if columns
23
+ @path << "/#{@timestamp}" if @timestamp
24
+ @path
25
+ end
26
+
27
+ def delete(columns = nil)
28
+ @path << "/#{pack_params(columns)}" if columns
29
+ @path << "/#{@timestamp}" if @timestamp
30
+ @path
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,25 @@
1
+ module HBase
2
+ module Request
3
+ class ScannerRequest < BasicRequest
4
+ attr_reader :table_name
5
+
6
+ def initialize(table_name)
7
+ @table_name = CGI.escape(table_name)
8
+ path = "/#{@table_name}/scanner"
9
+ super(path)
10
+ end
11
+
12
+ def open
13
+ @path
14
+ end
15
+
16
+ def get_rows(scanner)
17
+ @path = URI.parse(scanner.scanner_url).path
18
+ end
19
+
20
+ def close(scanner)
21
+ @path = URI.parse(scanner.scanner_url).path
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,43 @@
1
+ module HBase
2
+ module Request
3
+ class TableRequest < BasicRequest
4
+ attr_reader :name
5
+ attr_reader :body
6
+
7
+ def initialize(name)
8
+ super("")
9
+ @name = CGI.escape(name) if name
10
+ end
11
+
12
+ def show
13
+ @path << "/#{name}/schema"
14
+ end
15
+
16
+ def regions(start_row = nil, end_row = nil)
17
+ #TODO no handle the args!
18
+ @path << "/#{name}/regions"
19
+ end
20
+
21
+ def create
22
+ @path << "/#{name}/schema"
23
+ end
24
+
25
+ def update
26
+ @path << "/#{name}"
27
+ end
28
+
29
+ def enable
30
+ @path << "/#{name}/enable"
31
+ end
32
+
33
+ def disable
34
+ @path << "/#{name}/disable"
35
+ end
36
+
37
+ def delete(columns = nil)
38
+ warn "[DEPRECATION] the use of the 'columns' argument is deprecated. Please use the delete method without any arguments." if columns
39
+ @path << "/#{name}/schema"
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,7 @@
1
+ module HBase; module Response; end; end
2
+
3
+ require 'hbase/response/basic_response'
4
+ require 'hbase/response/meta_response'
5
+ require 'hbase/response/table_response'
6
+ require 'hbase/response/row_response'
7
+ require 'hbase/response/scanner_response'
@@ -0,0 +1,16 @@
1
+ require 'json'
2
+
3
+ module HBase
4
+ module Response
5
+ class BasicResponse
6
+
7
+ def initialize(raw_data)
8
+ @raw_data = raw_data
9
+ end
10
+
11
+ def parse
12
+ parse_content @raw_data
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,35 @@
1
+ module HBase
2
+ module Response
3
+ class MetaResponse < BasicResponse
4
+ attr_reader :method
5
+
6
+ def initialize(raw_data, method)
7
+ @method = method
8
+ super(raw_data)
9
+ end
10
+
11
+ def parse_content(raw_data)
12
+ case @method
13
+ when :list_tables
14
+ if raw_data.blank? || raw_data == "null" # "null" from json
15
+ puts "There are no available tables in the HBase"
16
+ return []
17
+ end
18
+
19
+ tables = []
20
+ doc = JSON.parse(raw_data)
21
+
22
+ doc["table"].each do |table|
23
+ name = table["name"].strip rescue nil
24
+ t = Model::TableDescriptor.new(:name => name)
25
+ tables << t
26
+ end
27
+
28
+ tables
29
+ else
30
+ puts "method '#{@method}' not supported yet"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ module HBase
2
+ module Response
3
+ class RowResponse < BasicResponse
4
+ def parse_content(raw_data)
5
+ doc = JSON.parse(raw_data)
6
+ rows = doc["Row"]
7
+
8
+ model_rows = []
9
+ rows.each do |row|
10
+ rname = row["key"].strip.unpack("m").first
11
+ count = row["Cell"].size
12
+ columns = []
13
+
14
+ row["Cell"].each do |col|
15
+ name = col["column"].strip.unpack('m').first
16
+ value = col["$"].strip.unpack('m').first rescue nil
17
+ timestamp = col["timestamp"].to_i
18
+
19
+ columns << HBase::Model::Column.new( :name => name,
20
+ :value => value,
21
+ :timestamp => timestamp)
22
+ end
23
+
24
+ model_rows << HBase::Model::Row.new(:name => rname, :total_count => count, :columns => columns)
25
+ end
26
+
27
+ model_rows
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+ module HBase
2
+ module Response
3
+ class ScannerResponse < BasicResponse
4
+ attr_reader :method
5
+
6
+ def initialize(raw_data, method)
7
+ @method = method
8
+ super(raw_data)
9
+ end
10
+
11
+ def parse_content(raw_data)
12
+ case @method
13
+ when :open_scanner
14
+ case raw_data
15
+ when Net::HTTPCreated
16
+ HBase::Model::Scanner.new(:scanner_url => raw_data["Location"])
17
+ else
18
+ raise StandardError, "Unable to open scanner. Received the following message: #{raw_data.message}"
19
+ end
20
+ when :get_rows
21
+ # Dispatch it to RowResponse, since that method is made
22
+ # to deal with rows already.
23
+ RowResponse.new(raw_data).parse
24
+ when :close_scanner
25
+ case raw_data
26
+ when Net::HTTPOK
27
+ return true
28
+ else
29
+ raise StandardError, "Unable to close scanner. Received the following message: #{raw_data.message}"
30
+ end
31
+ else
32
+ puts "method '#{@method}' not supported yet"
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end