crate_ruby 0.0.1

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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b3ff57320c97fd0469f294c16516f805ac815380
4
+ data.tar.gz: 15b6f0ea26f2d8b64892f706b45a2f23dc753fda
5
+ SHA512:
6
+ metadata.gz: 4b887ed5e8bfa23435bfa9ad3eecbf8deee91fc5c95242ce7086a80bcfb9fbed64fd8403cdcf4a0ea95c97f05e54a5104d6620aa8df32d6b875163a2b47d565d
7
+ data.tar.gz: 338c270195d22fd4fb127cefea310317a2a6b79170f3972f6c597aff6df375ff3e3b6ef4017f0658c2dbb37a019a24e5a13be16c4fc0be1c0296be9d719ffbdf
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea/*
19
+ .rvmrc
20
+ log/crate.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in crate_ruby.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Christoph Klocker
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,58 @@
1
+ # CrateRuby
2
+
3
+ Official ruby library to access a (Crate)[http://crate.io] database.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'crate_ruby'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install crate_ruby
18
+
19
+ ## Usage
20
+
21
+ ### Issueing SQL statements
22
+ require 'crate_ruby'
23
+
24
+ # optional args :host, :port, :logger
25
+ client = CrateRuby::Client.new
26
+ result = client.execute("Select * from posts")
27
+ => #<CrateRuby::ResultSet:0x00000002a9c5e8 @rowcount=1, @duration=5>
28
+ result.each do |row|
29
+ puts row.inspect # [1, "test", 5]
30
+ end
31
+
32
+ result.cols
33
+ => ["id", "my_column", "my_integer_col"]
34
+
35
+ ### Up/Downloading data
36
+ digest = Digest::SHA1.file(file_path).hexdigest
37
+
38
+ #upload
39
+ f = File.read(file_path)
40
+ client.blob_put(table_name, digest, f)
41
+
42
+ #download
43
+ data = client.blob_get(table_name, digest)
44
+ open(file_path, "wb") do |file|
45
+ file.write(data)
46
+ end
47
+
48
+ #deletion
49
+ client.blob_delete(table_name, digest)
50
+
51
+ ## Contributing
52
+
53
+ 1. Fork it ( http://github.com/<my-github-username>/crate_ruby/fork )
54
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
55
+ 3. Add some tests
56
+ 4. Commit your changes (`git commit -am 'Add some feature'`)
57
+ 5. Push to the branch (`git push origin my-new-feature`)
58
+ 6. Create new Pull Request
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ desc "Open an irb session preloaded with this library"
4
+ task :console do
5
+ sh "irb -rubygems -I lib -r ./lib/crate_ruby.rb"
6
+ end
7
+
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'crate_ruby/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "crate_ruby"
8
+ spec.version = CrateRuby::VERSION
9
+ spec.authors = ["Christoph Klocker"]
10
+ spec.email = ["christoph@vedanova.com"]
11
+ spec.summary = "A simple interface to Crate Data."
12
+ spec.description = ""
13
+ spec.source = "https://github.com/crate/crate_ruby"
14
+ spec.homepage = "http://crate.io"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.5"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "rspec", "~> 2.14"
25
+ end
@@ -0,0 +1,22 @@
1
+ require "crate_ruby/version"
2
+ require "crate_ruby/error"
3
+ require 'crate_ruby/result_set'
4
+ require 'crate_ruby/client'
5
+
6
+ include CrateRuby
7
+
8
+ module CrateRuby
9
+ def self.logger
10
+ @logger ||= begin
11
+ require 'logger'
12
+ log = Logger.new(File.join(File.dirname(__FILE__), "../log/crate.log"), 10, 1024000)
13
+ log.level = Logger::DEBUG
14
+ log
15
+ end
16
+ end
17
+
18
+ def self.logger=(logger)
19
+ @logger = logger
20
+ end
21
+
22
+ end
@@ -0,0 +1,155 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ module CrateRuby
4
+ class Client
5
+ DEFAULT_HOST = "127.0.0.1"
6
+ DEFAULT_PORT = "4200"
7
+
8
+ attr_accessor :logger
9
+
10
+ # @param [opts] Optional paramters
11
+ # * host: ip or host name, defaults to 127.0.0.1
12
+ # * port: defaults to 4200
13
+ def initialize(opts = {})
14
+ @host = opts[:host] || DEFAULT_HOST
15
+ @port = opts[:port] || DEFAULT_PORT
16
+ @uri = "http://#{DEFAULT_HOST}:#{DEFAULT_PORT}"
17
+ @logger = opts[:logger] || CrateRuby.logger
18
+ end
19
+
20
+ def inspect
21
+ %Q{#<CrateRuby::Client:#{object_id}, @uri="#{@uri}">}
22
+ end
23
+
24
+ # Creates a table
25
+ # client.create_table "posts", id: [:integer, "primary key"], my_column: :string, my_integer_col: :integer
26
+ # @param [String] table_name
27
+ # @param [Hash] column_definition
28
+ # @option column_definition [String] key sets column name, value sets column type. an array passed as value can be used to set options like primary keys
29
+ # @return [Boolean]
30
+ #
31
+ def create_table(table_name, column_definition = {}, blob=false)
32
+ cols = column_definition.to_a.map { |a| a.join(' ') }.join(', ')
33
+ stmt = %Q{CREATE TABLE "#{table_name}" (#{cols})}
34
+ execute(stmt)
35
+ end
36
+
37
+ # Creates a table for storing blobs
38
+ # @param [String] name Table name
39
+ # @param [Integer] shard Shard count, defaults to 5
40
+ # @param [Integer] number Number of replicas, defaults to 0
41
+ # @return [Boolean]
42
+ #
43
+ # client.create_blob_table("blob_table")
44
+ def create_blob_table(name, shard_count=5, replicas=0)
45
+ stmt = "create blob table #{name} clustered into #{shard_count} shards with (number_of_replicas=#{replicas})"
46
+ execute stmt
47
+ end
48
+
49
+ # Drop table
50
+ # @param [String] table_name, Name of table to drop
51
+ # @param [Boolean] blob Needs to be set to true if table is a blob table
52
+ # @return [Boolean]
53
+ def drop_table(table_name, blob=false)
54
+ tbl = blob ? "BLOB TABLE" : "TABLE"
55
+ stmt = %Q{DROP #{tbl} "#{table_name}"}
56
+ execute(stmt)
57
+ end
58
+
59
+ # List all user tables
60
+ # @return [ResultSet]
61
+ def show_tables
62
+ execute("select * from information_schema.tables where schema_name = 'doc'")
63
+ end
64
+
65
+ # Executes a SQL statement against the Crate HTTP REST endpoint.
66
+ # @param [String] sql statement to execute
67
+ # @return [ResultSet, false]
68
+ def execute(sql)
69
+ req = Net::HTTP::Post.new("/_sql", initheader = {'Content-Type' => 'application/json'})
70
+ req.body = {"stmt" => sql}.to_json
71
+ response = Net::HTTP.new(@host, @port).start { |http| http.request(req) }
72
+ success = case response.code
73
+ when "200"
74
+ ResultSet.new response.body
75
+ when "400"
76
+ @logger.info(response.body)
77
+ false
78
+ else
79
+ @logger.info(response.body)
80
+ false
81
+ end
82
+ success
83
+ end
84
+
85
+ # Upload a File to a blob table
86
+ # @param [String] table
87
+ # @param [String] digest SHA1 hexdigest
88
+ # @param [Boolean] data Can be any payload object that can be sent via HTTP, e.g. STRING, FILE
89
+ def blob_put(table, digest, data)
90
+ uri = blob_path(table, digest)
91
+ @logger.debug("BLOB PUT #{uri}")
92
+ req = Net::HTTP::Put.new(blob_path(table, digest))
93
+ req.body = data
94
+ #req["Content-Type"] = "multipart/form-data, boundary=#{boundary}"
95
+ response = Net::HTTP.new(@host, @port).start { |http| http.request(req) }
96
+ success = case response.code
97
+ when "201"
98
+ true
99
+ else
100
+ @logger.info("Response #{response.code}: " + response.body)
101
+ false
102
+ end
103
+ success
104
+ end
105
+
106
+ # Download blob
107
+ # @param [String] table
108
+ # @param [String] digest SHA1 hexdigest
109
+ #
110
+ # @return [Blob] File data to write to file or use directly
111
+ def blob_get(table, digest)
112
+ uri = blob_path(table, digest)
113
+ @logger.debug("BLOB GET #{uri}")
114
+ req = Net::HTTP::Get.new(uri)
115
+ response = Net::HTTP.new(@host, @port).start do |http|
116
+ http.request(req)
117
+ end
118
+ case response.code
119
+ when "200"
120
+ response.body
121
+ else
122
+ @logger.info("Response #{response.code}: #{response.body}")
123
+ false
124
+ end
125
+ end
126
+
127
+ # Delete blob
128
+ # @param [String] table
129
+ # @param [String] digest SHA1 hexdigest
130
+ #
131
+ # @return [Boolean]
132
+ def blob_delete(table, digest)
133
+ uri = blob_path(table, digest)
134
+ @logger.debug("BLOB DELETE #{uri}")
135
+ req = Net::HTTP::Delete.new(uri)
136
+ response = Net::HTTP.new(@host, @port).start { |http| http.request(req) }
137
+ success = case response.code
138
+ when "200"
139
+ true
140
+ else
141
+ @logger.info("Response #{response.code}: #{response.body}")
142
+ false
143
+ end
144
+ success
145
+ end
146
+
147
+ private
148
+
149
+ def blob_path(table, digest)
150
+ "/_blobs/#{table}/#{digest}"
151
+ end
152
+
153
+
154
+ end
155
+ end
@@ -0,0 +1,6 @@
1
+ module CrateRuby
2
+ # Base Error class
3
+ class CrateError < StandardError; end
4
+ class BlobExistsError < CrateError; end
5
+
6
+ end
@@ -0,0 +1,41 @@
1
+ module CrateRuby
2
+ class ResultSet
3
+ include Enumerable
4
+
5
+ attr_reader :rowcount, :duration, :cols
6
+
7
+ # @param [String] Crate result
8
+ def initialize(result)
9
+ result = JSON.parse(result)
10
+ @cols = result['cols']
11
+ @rows = result['rows']
12
+ @rowcount = result['rowcount']
13
+ @duration = result['duration']
14
+ end
15
+
16
+ def inspect
17
+ %Q{#<CrateRuby::ResultSet:#{object_id}>, @rowcount="#{@rowcount}", @duration=#{@duration}>}
18
+ end
19
+
20
+ def <<(val)
21
+ @rows << val
22
+ end
23
+
24
+ def each(&block)
25
+ @rows.each(&block)
26
+ nil
27
+ end
28
+
29
+ def [](val)
30
+ @rows[val]
31
+ end
32
+
33
+ # @param [Array] ary Column names to filer on
34
+ # @return [Array] Filtered rows
35
+ def select_columns(ary, &block)
36
+ indexes = ary.map {|col| @cols.index(col)}.compact
37
+ @rows.map{|r| r.values_at(*indexes)}.each(&block)
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module CrateRuby
2
+ VERSION = "0.0.1"
3
+ end
File without changes
@@ -0,0 +1,108 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe CrateRuby::Client do
4
+ describe '#create_table' do
5
+ let(:client) { CrateRuby::Client.new }
6
+
7
+ describe 'blob mangament' do
8
+ let(:table_name) { 'blob_table' }
9
+ let(:file) { 'logo-crate.png' }
10
+ #let(:file) { 'text.txt' }
11
+ let(:path) { File.join(File.dirname(__FILE__), '../uploads/') }
12
+ let(:file_path) { File.join(path, file) }
13
+ let(:digest) { Digest::SHA1.file(file_path).hexdigest }
14
+ let(:store_location) { File.join(path, "get_#{file}") }
15
+
16
+ before do
17
+ client.execute("create blob TABLE #{table_name}")
18
+ end
19
+
20
+ after do
21
+ client.execute("drop blog TABLE #{table_name}")
22
+ end
23
+
24
+ describe '#blob_put' do
25
+
26
+ after do
27
+ client.blob_delete(table_name, digest)
28
+ end
29
+
30
+ context 'file' do
31
+
32
+ it 'should upload a file to the blob table' do
33
+ f = File.read(file_path)
34
+ client.blob_put(table_name, digest, f).should be_true
35
+ end
36
+ end
37
+
38
+ context '#string' do
39
+ let(:string) {"my crazy"}
40
+ let(:digest) {Digest::SHA1.hexdigest(string)}
41
+
42
+ it 'should upload a string to the blob table' do
43
+ client.blob_delete(table_name, digest)
44
+ client.blob_put table_name, digest, string
45
+ end
46
+ end
47
+ end
48
+
49
+ describe '#blob_get' do
50
+
51
+ before do
52
+ f = File.read(file_path)
53
+ client.blob_put(table_name, digest, f)
54
+ end
55
+
56
+ it 'should download a blob' do
57
+ data = client.blob_get(table_name, digest)
58
+ data.should_not be_false
59
+ open(store_location, "wb") { |file|
60
+ file.write(data)
61
+ }
62
+ end
63
+
64
+ after do
65
+ client.blob_delete(table_name, digest)
66
+ end
67
+ end
68
+
69
+ describe '#blob_delete' do
70
+ before do
71
+ f = File.read(file_path)
72
+ client.blob_put(table_name, digest, f)
73
+ end
74
+
75
+ it 'should delete a blob' do
76
+ client.blob_delete(table_name, digest)
77
+ end
78
+ end
79
+ end
80
+
81
+ describe '#execute' do
82
+ let(:table_name) { "post" }
83
+
84
+ after do
85
+ client.execute("drop TABLE #{table_name}").should be_true
86
+ end
87
+
88
+ it 'should create a new table' do
89
+ client.execute("CREATE TABLE #{table_name} (id int)").should be_true
90
+ end
91
+
92
+ end
93
+
94
+
95
+ describe '#initialize' do
96
+
97
+ it 'should use host and ports parameters' do
98
+ logger = double()
99
+ client = CrateRuby::Client.new host: "10.0.0.1", port: 5000, logger: logger
100
+ client.instance_variable_get(:@host).should eq("10.0.0.1")
101
+ client.instance_variable_get(:@port).should eq(5000)
102
+ client.instance_variable_get(:@logger).should == logger
103
+ end
104
+
105
+ end
106
+
107
+ end
108
+ end
@@ -0,0 +1,61 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe ResultSet do
4
+
5
+ let(:crate_result) { "{\"cols\":[\"my_column\",\"my_integer_col\"],\"rows\":[[\"Foo\",5],[\"Bar\",5]],\"rowcount\":1,\"duration\":4}" }
6
+
7
+ let(:result_set) { ResultSet.new(crate_result) }
8
+
9
+ let(:json_result) { JSON.parse crate_result }
10
+
11
+ describe '#initialize' do
12
+
13
+
14
+ it 'should set rowcount' do
15
+ result_set.rowcount.should eq 1
16
+ end
17
+
18
+ it 'should set duration' do
19
+ result_set.duration.should eq 4
20
+ end
21
+
22
+ it 'should set cols' do
23
+ result_set.cols.should eq json_result['cols']
24
+ end
25
+
26
+ end
27
+
28
+ describe '#each' do
29
+ it 'should loop over the result rows' do
30
+ result_set.each_with_index do |r, i|
31
+ r.should eq json_result['rows'][i]
32
+ end
33
+ end
34
+ end
35
+
36
+ describe '#[]' do
37
+ it 'should return the row at index' do
38
+ result_set[1][0].should eq('Bar')
39
+ end
40
+ end
41
+
42
+ describe '#values_at' do
43
+ it 'should only return the columns specified' do
44
+ a = []
45
+ result_set.select_columns(['my_column']) do |res|
46
+ a << res
47
+ end
48
+ a.should eq [["Foo"], ["Bar"]]
49
+
50
+ end
51
+ it 'should not raise error on invalid column name' do
52
+ expect do
53
+ result_set.select_columns(['my_column', 'invalid']) do |row|
54
+ end
55
+ end.to_not raise_error
56
+ end
57
+ end
58
+
59
+ end
60
+
61
+
@@ -0,0 +1 @@
1
+ require_relative '../lib/crate_ruby'
@@ -0,0 +1 @@
1
+ TEST
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: crate_ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Christoph Klocker
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.14'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.14'
55
+ description: ''
56
+ email:
57
+ - christoph@vedanova.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - crate_ruby.gemspec
68
+ - lib/crate_ruby.rb
69
+ - lib/crate_ruby/client.rb
70
+ - lib/crate_ruby/error.rb
71
+ - lib/crate_ruby/result_set.rb
72
+ - lib/crate_ruby/version.rb
73
+ - log/.keep
74
+ - spec/crate_ruby/client_spec.rb
75
+ - spec/crate_ruby/result_set_spec.rb
76
+ - spec/spec_helper.rb
77
+ - spec/uploads/get_logo-crate.png
78
+ - spec/uploads/logo-crate.png
79
+ - spec/uploads/text.txt
80
+ homepage: http://crate.io
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 2.2.1
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: A simple interface to Crate Data.
104
+ test_files:
105
+ - spec/crate_ruby/client_spec.rb
106
+ - spec/crate_ruby/result_set_spec.rb
107
+ - spec/spec_helper.rb
108
+ - spec/uploads/get_logo-crate.png
109
+ - spec/uploads/logo-crate.png
110
+ - spec/uploads/text.txt