ruby-hbase 0.0.4

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.
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2007-10-25
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 FIXME full name
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ config/hoe.rb
7
+ config/requirements.rb
8
+ lib/ruby-hbase.rb
9
+ lib/ruby-hbase/version.rb
10
+ lib/ruby-hbase/xml_decoder.rb
11
+ lib/ruby-hbase/hbase_table.rb
12
+ lib/ruby-hbase/scanner.rb
13
+ log/debug.log
14
+ script/destroy
15
+ script/generate
16
+ script/txt2html
17
+ setup.rb
18
+ tasks/deployment.rake
19
+ tasks/environment.rake
20
+ tasks/website.rake
21
+ website/index.html
22
+ website/index.txt
23
+ website/javascripts/rounded_corners_lite.inc.js
24
+ website/stylesheets/screen.css
25
+ website/template.rhtml
26
+ spec/spec_helper.rb
27
+ spec/htable_spec.rb
28
+ spec/scanner_spec.rb
29
+ spec/spec_helper.rb
@@ -0,0 +1 @@
1
+ README
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
@@ -0,0 +1,70 @@
1
+ require 'ruby-hbase/version'
2
+
3
+ AUTHOR = 'Bryan Duxbury' # can also be an array of Authors
4
+ EMAIL = "bryan@rapleaf.com"
5
+ DESCRIPTION = "Client library for interacting with HBase the Hadoop database."
6
+ GEM_NAME = 'ruby-hbase' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'rapleaf' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+
11
+ @config_file = "~/.rubyforge/user-config.yml"
12
+ @config = nil
13
+ RUBYFORGE_USERNAME = "unknown"
14
+ def rubyforge_username
15
+ unless @config
16
+ begin
17
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
18
+ rescue
19
+ puts <<-EOS
20
+ ERROR: No rubyforge config file found: #{@config_file}
21
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
22
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
23
+ EOS
24
+ exit
25
+ end
26
+ end
27
+ RUBYFORGE_USERNAME.replace @config["username"]
28
+ end
29
+
30
+
31
+ REV = nil
32
+ # UNCOMMENT IF REQUIRED:
33
+ # REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
34
+ VERS = RubyHbase::VERSION::STRING + (REV ? ".#{REV}" : "")
35
+ RDOC_OPTS = ['--quiet', '--title', 'ruby-hbase documentation',
36
+ "--opname", "index.html",
37
+ "--line-numbers",
38
+ "--main", "README",
39
+ "--inline-source"]
40
+
41
+ class Hoe
42
+ def extra_deps
43
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
44
+ @extra_deps
45
+ end
46
+ end
47
+
48
+ # Generate all the Rake tasks
49
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
50
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
51
+ p.author = AUTHOR
52
+ p.description = DESCRIPTION
53
+ p.email = EMAIL
54
+ p.summary = DESCRIPTION
55
+ p.url = HOMEPATH
56
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
57
+ p.test_globs = ["test/**/test_*.rb"]
58
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
59
+
60
+ # == Optional
61
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\\n\\n")
62
+ #p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
63
+
64
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
65
+
66
+ end
67
+
68
+ CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
69
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
70
+ hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
@@ -0,0 +1,17 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
16
+
17
+ require 'ruby-hbase'
@@ -0,0 +1,10 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require "rubygems"
4
+ require "net/http"
5
+ require "erb"
6
+ require "xml/libxml"
7
+
8
+ require "ruby-hbase/xml_decoder"
9
+ require "ruby-hbase/hbase_table"
10
+ require "ruby-hbase/scanner"
@@ -0,0 +1,167 @@
1
+ module HBase
2
+ class RowNotFound < Exception
3
+ def initialize(msg=nil)
4
+ super
5
+ end
6
+ end
7
+
8
+ class HTable
9
+ include XmlDecoder
10
+
11
+ def initialize(table_uri)
12
+ @table_uri = table_uri
13
+
14
+ @uri = URI.parse(table_uri)
15
+
16
+ @host, @table_name = @uri.host, @uri.path.split("/").last
17
+ end
18
+
19
+ def name
20
+ @table_name
21
+ end
22
+
23
+ ######################
24
+ # Meta-type requests
25
+
26
+ def start_keys
27
+ raise NotImplementedError
28
+ end
29
+
30
+
31
+ def column_descriptors
32
+ column_families = []
33
+
34
+ # get the xml for the column descriptors
35
+ response = Net::HTTP.get_response(@uri.host, "/api/#{@table_name}", @uri.port)
36
+ body = response.body
37
+
38
+ # parse the xml into a document
39
+ doc = XML::Parser.string(body).parse
40
+
41
+ doc.find("/table/columnfamilies/columnfamily").each do |node|
42
+ colfam = {}
43
+ colfam[:name] = node.find_first("name").content.strip.chop
44
+ column_families << colfam
45
+ end
46
+ column_families
47
+ end
48
+
49
+
50
+ #####################
51
+ # Standard CRUD ops
52
+
53
+ DEFAULT_GET_OPTIONS = {:timestamp => nil, :columns => nil}
54
+
55
+ def get(key, options = {})
56
+ opts = DEFAULT_GET_OPTIONS.merge(options)
57
+
58
+ columns = Array(opts.delete(:columns)).compact
59
+ timestamp = opts.delete(:timestamp)
60
+ timestamp = (timestamp.to_f * 1000).to_i.to_s if timestamp
61
+
62
+ Net::HTTP.start(@uri.host, @uri.port) do |session|
63
+ columns_query = columns.map{ |name| "column=#{name}" }.join("&")
64
+
65
+ ts_section = timestamp ? "/#{timestamp}" : ""
66
+
67
+ query_string = "?" + columns_query
68
+
69
+ query = "/api/#{@table_name}/row/#{url_encode(key)}#{ts_section}#{query_string}"
70
+ response = session.get(query, {"Accept" => "*/*"})
71
+
72
+ case response.code.to_i
73
+ when 200 #success!
74
+ body = response.body
75
+
76
+ parse_row_result(body).last
77
+ when 204 #no data - probably an incorrect colname
78
+ raise "Didn't get any data back - check your column names!"
79
+ when 404
80
+ raise RowNotFound, "Could not find row '#{key}'"
81
+ else
82
+ nil
83
+ end
84
+ end
85
+ end
86
+
87
+ def put(key, keys_and_values, timestamp = nil)
88
+ Net::HTTP.start(@uri.host, @uri.port) do |session|
89
+ xml = "<columns>"
90
+
91
+ ts_section = timestamp ? "/#{(timestamp.to_f * 1000).to_i}" : ""
92
+
93
+ keys_and_values.each do |name, value|
94
+ xml << "<column><name>#{name}</name><value>#{[value.to_s].pack("m")}</value></column>"
95
+ end
96
+
97
+ xml << "</columns>"
98
+
99
+ query = "/api/#{@table_name}/row/#{url_encode(key)}#{ts_section}"
100
+ response = session.post(query, xml, {"Content-type" => "text/xml"})
101
+
102
+ case response.code.to_i
103
+ when 200
104
+ true
105
+ else
106
+ unexpected_response(response)
107
+ end
108
+ end
109
+ end
110
+
111
+ def delete(row, columns = nil, timestamp = nil)
112
+ Net::HTTP.start(@uri.host, @uri.port) do |session|
113
+ columns_query = Array(columns).compact.map{ |name| "column=#{name}" }.join("&")
114
+
115
+ response = session.delete("/api/#{@table_name}/row/#{row}?#{columns_query}")
116
+ case response.code.to_i
117
+ when 202
118
+ return true
119
+ else
120
+ unexpected_response(response)
121
+ end
122
+
123
+ end
124
+ end
125
+
126
+ #######################
127
+ # Scanning interface
128
+
129
+ def get_scanner(start_row, end_row, timestamp = nil, columns = nil)
130
+ start_row_query = start_row ? "start_row=#{start_row}" : nil
131
+ end_row_query = end_row ? "end_row=#{end_row}" : nil
132
+ timestamp_section = timestamp ? "/#{(timestamp.to_f * 1000).to_i}" : nil
133
+ columns_section = columns ? columns.map{ |col| "column=#{col}" }.join("&") : nil
134
+
135
+ query_string = [start_row_query, end_row_query,
136
+ timestamp_section, columns_section].compact.join("&")
137
+
138
+ path = ""
139
+
140
+ # open the scanner
141
+ Net::HTTP.start(@uri.host, @uri.port) do |session|
142
+ response = session.post("/api/#{@table_name}/scanner?#{query_string}",
143
+ "", {"Accept" => "text/xml"}
144
+ )
145
+
146
+ case response.code.to_i
147
+ when 201
148
+ # redirect - grab the path and send
149
+ Scanner.new(self, "http://#{@uri.host}:#{@uri.port}" + response["Location"])
150
+ else
151
+ unexpected_response(response)
152
+ end
153
+ end
154
+ end
155
+
156
+
157
+ private
158
+
159
+ def url_encode(str)
160
+ ERB::Util.url_encode(str)
161
+ end
162
+
163
+ def unexpected_response(response)
164
+ raise "Unexpected response code #{response.code.to_i}:\n#{response.body}"
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,55 @@
1
+ module HBase
2
+ class Scanner
3
+ include XmlDecoder
4
+
5
+ def initialize(table, scanner_uri)
6
+ @table, @scanner_uri = table, scanner_uri
7
+ end
8
+
9
+ def close
10
+
11
+ end
12
+
13
+ def next
14
+
15
+ end
16
+
17
+ def each
18
+ parsed_uri = URI.parse(@scanner_uri)
19
+ Net::HTTP.start(parsed_uri.host, parsed_uri.port) do |session|
20
+ while true
21
+ response = session.post(@scanner_uri, "")
22
+
23
+ case response.code.to_i
24
+ when 404
25
+ # over
26
+ break
27
+ when 200
28
+ # item
29
+ yield *parse_row_result(response.body)
30
+ else
31
+ # error
32
+ raise "Unexpected response code #{response.code}, body:\n#{response.body}"
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ # def parse_row(xml)
41
+ # doc = REXML::Document.new(xml)
42
+ #
43
+ # result = {}
44
+ #
45
+ # doc.root.each_element("/row/column") do |column|
46
+ # name = column.get_elements("name")[0].text.strip
47
+ # value = column.get_elements("value")[0].text.strip.unpack("m").first
48
+ # result[name] = value
49
+ # end
50
+ #
51
+ # [doc.root.get_elements("name")[0].text.strip, result]
52
+ # end
53
+
54
+ end
55
+ end
@@ -0,0 +1,9 @@
1
+ module RubyHbase #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 4
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ module HBase
2
+ module XmlDecoder
3
+ def parse_row_result(xml)
4
+ doc = XML::Parser.string(xml).parse
5
+
6
+ name_node = doc.root.find_first("/row/name")
7
+ name = name_node ? name_node.content.strip : nil
8
+
9
+ values = {}
10
+
11
+ doc.find("/row/column").each do |node|
12
+ values[node.find_first("name").content.strip] = node.find_first("value").content.strip.unpack("m").first
13
+ end
14
+
15
+ [name, values]
16
+ end
17
+ end
18
+ end
File without changes
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)