crate_ruby 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 7e341cd1fe4282c3dedd29e547b99d0b21c47dff
4
- data.tar.gz: 8bab3933fea54fbaa6026c1226d37842a241803a
2
+ SHA256:
3
+ metadata.gz: 527d9c75e305c5d0bdff4ad77d5a8268ee2e172926dc252e8f2c7cff19e4189c
4
+ data.tar.gz: 99454b1e16d97746418602c7c82fb6feeb56dd9dfaddaa4259635928d9a9ffcf
5
5
  SHA512:
6
- metadata.gz: 4be61df43ff7bfe34d8098523a9096bcb2e6ebdf99913d17834b12a9e372c4ee3a4fc87d772acb052353b87d491a6f6563601b35e0f845346ccdf3bd97bf6b4a
7
- data.tar.gz: 79e1e90745e375d1aa8c4aaa7600d18ed6c0e44e708ffd2391e9eeaa855f8024fb00682025015553bb1d7b9aa8c53ca0f2cbaa802b0355d6c64440eafcb0f9e8
6
+ metadata.gz: 39fc95a25b3efb6630454bb7f6c120f6d8e980d67ab4442a147b48c783ca534fd929e8a0f030c57e22d5a6ca860da9f1ba3dc7fa0471360d48c0c47ad990a8fe
7
+ data.tar.gz: 6075a190c28ed1ff3b4ad455b9db7413abb67f1afc29bffcb1f61d1ae43fd7e177cee54f5c318545cf17c526f2176a77d179e80765733245fdafc41fe7ec0d9d
@@ -4,11 +4,12 @@ rvm:
4
4
  - "2.1.5"
5
5
  - "2.2.0"
6
6
  - "2.4.2"
7
- sudo:
8
- false
7
+
9
8
  before_script:
10
9
  - bundle exec ruby spec/bootstrap.rb
10
+
11
11
  script:
12
12
  bundle exec rspec spec
13
+
13
14
  before_install:
14
15
  - gem update bundler
@@ -24,7 +24,7 @@ To create a new release, you must:
24
24
 
25
25
  - Commit your changes with a message like "prepare release x.y.z"
26
26
 
27
- - Push to origin
27
+ - Push to ``origin/master``
28
28
 
29
29
  - Create a tag by running ``./devtools/create_tag.sh``
30
30
 
data/README.rst CHANGED
@@ -82,6 +82,29 @@ Manipulate BLOBs like so::
82
82
  # deletion
83
83
  client.blob_delete(table_name, digest)
84
84
 
85
+ Schema support
86
+
87
+ A default schema can be set by passing in the schema name::
88
+
89
+ CrateRuby::Client.new(['localhost:44200'], schema: 'my_schema')
90
+
91
+ Authentication
92
+
93
+ Authentication credentials can be passed to the client if needed::
94
+
95
+ CrateRuby::Client.new(['localhost:44200'], username: 'foo', password: 'supersecret')
96
+
97
+
98
+ Version matrix
99
+ ==============
100
+ +--------------+------------+
101
+ | Crate Ruby | CrateDB |
102
+ +==============+============+
103
+ | < 0.9 | < 0.57 |
104
+ +--------------+------------+
105
+ | 0.9 | >= 0.57 |
106
+ +--------------+------------+
107
+
85
108
  Contributing
86
109
  ============
87
110
 
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
31
31
  spec.summary = "A simple interface for the Crate.IO database."
32
32
  spec.description = "A Ruby interface for Crate.IO. Put your data to work. Simply."
33
33
  spec.homepage = "http://crate.io"
34
- spec.license = "Apache License, v2.0"
34
+ spec.license = "Apache-2.0"
35
35
  spec.required_ruby_version = '>= 2.0'
36
36
 
37
37
  spec.files = `git ls-files -z`.split("\x0")
@@ -48,7 +48,7 @@ fi
48
48
 
49
49
  # get version from version.rb
50
50
  VERSION=$(grep "VERSION =" lib/crate_ruby/version.rb | \
51
- cut -d' ' -f5 | tr -d "\'")
51
+ cut -d' ' -f5 | tr -d "\'" | sed -e "s/.freeze//")
52
52
  echo "Version: $VERSION"
53
53
 
54
54
  # check if tag to create has already been created
@@ -1,4 +1,6 @@
1
1
  # coding: UTF-8
2
+ === 0.1.0
3
+ * HTTP Basic Authentication support that comes with 2.3.0
2
4
 
3
5
  === 0.0.9
4
6
 
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8; -*-
2
1
  #
3
2
  # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4
3
  # license agreements. See the NOTICE file distributed with this work for
@@ -19,16 +18,14 @@
19
18
  # with Crate these terms will supersede the license and you may use the
20
19
  # software solely pursuant to the terms of the relevant commercial agreement.
21
20
 
22
- require "crate_ruby/version"
23
- require "crate_ruby/error"
21
+ require 'crate_ruby/version'
22
+ require 'crate_ruby/error'
24
23
  require 'crate_ruby/result_set'
25
24
  require 'crate_ruby/client'
26
- require 'crate_ruby/utils'
27
25
 
28
26
  include CrateRuby
29
27
 
30
28
  module CrateRuby
31
-
32
29
  def self.logger
33
30
  @logger ||= begin
34
31
  require 'logger'
@@ -41,5 +38,4 @@ module CrateRuby
41
38
  def self.logger=(logger)
42
39
  @logger = logger
43
40
  end
44
-
45
41
  end
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8; -*-
2
1
  #
3
2
  # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4
3
  # license agreements. See the NOTICE file distributed with this work for
@@ -21,99 +20,114 @@
21
20
 
22
21
  require 'json'
23
22
  require 'net/http'
23
+ require 'base64'
24
+
24
25
  module CrateRuby
26
+ # Client to interact with Crate.io DB
25
27
  class Client
26
- DEFAULT_HOST = "127.0.0.1"
27
- DEFAULT_PORT = "4200"
28
+ DEFAULT_HOST = '127.0.0.1'.freeze
29
+ DEFAULT_PORT = '4200'.freeze
28
30
 
29
- attr_accessor :logger
31
+ attr_accessor :logger, :schema, :username, :password
30
32
 
31
33
  # Currently only a single server is supported. Fail over will be implemented in upcoming versions
32
34
  # @param [Array] servers An Array of servers including ports [127.0.0.1:4200, 10.0.0.1:4201]
33
- # @param [opts] Optional paramters
35
+ # @param [opts] opts Optional parameters
34
36
  # * logger: Custom Logger
35
37
  # * http_options [Hash]: Net::HTTP options (open_timeout, read_timeout)
38
+ # * schema [String]: Default schema to search in
36
39
  # @return [CrateRuby::Client]
37
40
  def initialize(servers = [], opts = {})
38
41
  @servers = servers
39
42
  @servers << "#{DEFAULT_HOST}:#{DEFAULT_PORT}" if servers.empty?
40
43
  @logger = opts[:logger] || CrateRuby.logger
41
44
  @http_options = opts[:http_options] || { read_timeout: 3600 }
45
+ @schema = opts[:schema] || 'doc'
46
+ @username = opts[:username]
47
+ @password = opts[:password]
42
48
  end
43
49
 
44
50
  def inspect
45
- %Q{#<CrateRuby::Client:#{object_id}>}
51
+ %(#<CrateRuby::Client:#{object_id}>)
46
52
  end
47
53
 
48
54
  # Creates a table
49
55
  # client.create_table "posts", id: [:integer, "primary key"], my_column: :string, my_integer_col: :integer
50
56
  # @param [String] table_name
51
57
  # @param [Hash] column_definition
52
- # @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
58
+ # @option column_definition [String] key sets column name, value sets column type. an array passed as value can
59
+ # be used to set options like primary keys
53
60
  # @return [ResultSet]
54
61
  #
55
- def create_table(table_name, column_definition = {}, blob=false)
62
+ def create_table(table_name, column_definition = {})
56
63
  cols = column_definition.to_a.map { |a| a.join(' ') }.join(', ')
57
- stmt = %Q{CREATE TABLE "#{table_name}" (#{cols})}
64
+ stmt = %{CREATE TABLE "#{table_name}" (#{cols})}
58
65
  execute(stmt)
59
66
  end
60
67
 
61
68
  # Creates a table for storing blobs
62
69
  # @param [String] name Table name
63
- # @param [Integer] shard Shard count, defaults to 5
64
- # @param [Integer] number Number of replicas, defaults to 0
70
+ # @param [Integer] shard_count Shard count, defaults to 5
71
+ # @param [Integer] replicas Number of replicas, defaults to 0
65
72
  # @return [ResultSet]
66
73
  #
67
74
  # client.create_blob_table("blob_table")
68
- def create_blob_table(name, shard_count=5, replicas=0)
69
- stmt = "create blob table #{name} clustered into #{shard_count} shards with (number_of_replicas=#{replicas})"
70
- execute stmt
75
+ def create_blob_table(name, shard_count = 5, replicas = 0)
76
+ stmt = %{CREATE BLOB TABLE "#{name}" CLUSTERED INTO ? SHARDS WITH (number_of_replicas=?)}
77
+ execute stmt, [shard_count, replicas]
71
78
  end
72
79
 
73
80
  # Drop table
74
81
  # @param [String] table_name, Name of table to drop
75
82
  # @param [Boolean] blob Needs to be set to true if table is a blob table
76
83
  # @return [ResultSet]
77
- def drop_table(table_name, blob=false)
78
- tbl = blob ? "BLOB TABLE" : "TABLE"
79
- stmt = %Q{DROP #{tbl} "#{table_name}"}
84
+ def drop_table(table_name, blob = false)
85
+ tbl = blob ? 'BLOB TABLE' : 'TABLE'
86
+ stmt = %(DROP #{tbl} "#{table_name}")
80
87
  execute(stmt)
81
88
  end
82
89
 
83
90
  # List all user tables
84
91
  # @return [ResultSet]
85
92
  def show_tables
86
- execute("select table_name from information_schema.tables where table_schema = 'doc'")
93
+ execute('select table_name from information_schema.tables where table_schema = ?', [schema])
87
94
  end
88
95
 
89
96
  # Returns all tables in schema 'doc'
90
- # @return [Array] Array of table names
97
+ # @return [Array<String>] Array of table names
91
98
  def tables
92
- execute("select table_name from information_schema.tables where table_schema = 'doc'").map(&:first)
99
+ execute('select table_name from information_schema.tables where table_schema = ?', [schema]).map(&:first)
100
+ end
101
+
102
+ # Returns all tables in schema 'blob'
103
+ # @return [Array<String>] Array of blob tables
104
+ def blob_tables
105
+ execute('select table_name from information_schema.tables where table_schema = ?', ['blob']).map(&:first)
93
106
  end
94
107
 
95
108
  # Executes a SQL statement against the Crate HTTP REST endpoint.
96
109
  # @param [String] sql statement to execute
97
110
  # @param [Array] args Array of values used for parameter substitution
98
- # @param [Hash] Net::HTTP options (open_timeout, read_timeout)
111
+ # @param [Array] bulk_args List of lists containing records to be processed
112
+ # @param [Hash] http_options Net::HTTP options (open_timeout, read_timeout)
99
113
  # @return [ResultSet]
100
114
  def execute(sql, args = nil, bulk_args = nil, http_options = {})
101
115
  @logger.debug sql
102
- req = Net::HTTP::Post.new("/_sql", initheader = {'Content-Type' => 'application/json'})
103
- body = {"stmt" => sql}
104
- body.merge!({'args' => args}) if args
105
- body.merge!({'bulk_args' => bulk_args}) if bulk_args
116
+ req = Net::HTTP::Post.new('/_sql', headers)
117
+ body = { 'stmt' => sql }
118
+ body['args'] = args if args
119
+ body['bulk_args'] = bulk_args if bulk_args
106
120
  req.body = body.to_json
107
121
  response = request(req, http_options)
108
122
  @logger.debug response.body
109
- success = case response.code
110
- when /^2\d{2}/
111
- ResultSet.new response.body
112
- else
113
- @logger.info(response.body)
114
- raise CrateRuby::CrateError.new(response.body)
115
- end
116
- success
123
+
124
+ case response.code
125
+ when /^2\d{2}/
126
+ ResultSet.new response.body
127
+ else
128
+ @logger.info(response.body)
129
+ raise CrateRuby::CrateError, response.body
130
+ end
117
131
  end
118
132
 
119
133
  # Upload a File to a blob table
@@ -123,17 +137,16 @@ module CrateRuby
123
137
  def blob_put(table, digest, data)
124
138
  uri = blob_path(table, digest)
125
139
  @logger.debug("BLOB PUT #{uri}")
126
- req = Net::HTTP::Put.new(blob_path(table, digest))
140
+ req = Net::HTTP::Put.new(blob_path(table, digest), headers)
127
141
  req.body = data
128
142
  response = request(req)
129
- success = case response.code
130
- when "201"
131
- true
132
- else
133
- @logger.info("Response #{response.code}: " + response.body)
134
- false
135
- end
136
- success
143
+ case response.code
144
+ when '201'
145
+ true
146
+ else
147
+ @logger.info("Response #{response.code}: " + response.body)
148
+ false
149
+ end
137
150
  end
138
151
 
139
152
  # Download blob
@@ -144,14 +157,14 @@ module CrateRuby
144
157
  def blob_get(table, digest)
145
158
  uri = blob_path(table, digest)
146
159
  @logger.debug("BLOB GET #{uri}")
147
- req = Net::HTTP::Get.new(uri)
160
+ req = Net::HTTP::Get.new(uri, headers)
148
161
  response = request(req)
149
162
  case response.code
150
- when "200"
151
- response.body
152
- else
153
- @logger.info("Response #{response.code}: #{response.body}")
154
- false
163
+ when '200'
164
+ response.body
165
+ else
166
+ @logger.info("Response #{response.code}: #{response.body}")
167
+ false
155
168
  end
156
169
  end
157
170
 
@@ -163,31 +176,30 @@ module CrateRuby
163
176
  def blob_delete(table, digest)
164
177
  uri = blob_path(table, digest)
165
178
  @logger.debug("BLOB DELETE #{uri}")
166
- req = Net::HTTP::Delete.new(uri)
179
+ req = Net::HTTP::Delete.new(uri, headers)
167
180
  response = request(req)
168
- success = case response.code
169
- when "200"
170
- true
171
- else
172
- @logger.info("Response #{response.code}: #{response.body}")
173
- false
174
- end
175
- success
181
+ case response.code
182
+ when '200'
183
+ true
184
+ else
185
+ @logger.info("Response #{response.code}: #{response.body}")
186
+ false
187
+ end
176
188
  end
177
189
 
178
-
179
190
  # Return the table structure
180
191
  # @param [String] table_name Table name to get structure
181
192
  # @param [ResultSet]
182
193
  def table_structure(table_name)
183
- execute("select * from information_schema.columns where table_schema = 'doc' AND table_name = '#{table_name}'")
194
+ execute('select * from information_schema.columns where table_schema = ?' \
195
+ 'AND table_name = ?', [schema, table_name])
184
196
  end
185
197
 
186
-
187
198
  def insert(table_name, attributes)
188
199
  vals = attributes.values
189
- binds = vals.count.times.map {|i| "$#{i+1}"}.join(',')
190
- stmt = %Q{INSERT INTO "#{table_name}" (#{attributes.keys.join(', ')}) VALUES(#{binds})}
200
+ identifiers = attributes.keys.map {|v| %{"#{v}"} }.join(', ')
201
+ binds = vals.count.times.map { |i| "$#{i + 1}" }.join(',')
202
+ stmt = %{INSERT INTO "#{table_name}" (#{identifiers}) VALUES(#{binds})}
191
203
  execute(stmt, vals)
192
204
  end
193
205
 
@@ -207,7 +219,7 @@ module CrateRuby
207
219
  end
208
220
 
209
221
  def connection
210
- host, port = @servers.first.split(':');
222
+ host, port = @servers.first.split(':')
211
223
  Net::HTTP.new(host, port)
212
224
  end
213
225
 
@@ -219,5 +231,16 @@ module CrateRuby
219
231
  end
220
232
  end
221
233
 
234
+ def headers
235
+ header = { 'Content-Type' => 'application/json', 'Accept' => 'application/json' }
236
+ header['Default-Schema'] = schema if schema
237
+ header['Authorization'] = "Basic #{encrypted_credentials}" if username
238
+ header['X-User'] = username if username # for backwards compatibility with Crate 2.2
239
+ header
240
+ end
241
+
242
+ def encrypted_credentials
243
+ @encrypted_credentials ||= Base64.encode64 "#{username}:#{password}"
244
+ end
222
245
  end
223
246
  end
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8; -*-
2
1
  #
3
2
  # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4
3
  # license agreements. See the NOTICE file distributed with this work for
@@ -23,5 +22,4 @@ module CrateRuby
23
22
  # Base Error class
24
23
  class CrateError < StandardError; end
25
24
  class BlobExistsError < CrateError; end
26
-
27
25
  end
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8; -*-
2
1
  #
3
2
  # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4
3
  # license agreements. See the NOTICE file distributed with this work for
@@ -35,7 +34,7 @@ module CrateRuby
35
34
  end
36
35
 
37
36
  def inspect
38
- %Q{#<CrateRuby::ResultSet:#{object_id}>, @rowcount="#{@rowcount}", @duration=#{@duration}>}
37
+ %(#<CrateRuby::ResultSet:#{object_id}>, @rowcount="#{@rowcount}", @duration=#{@duration}>)
39
38
  end
40
39
 
41
40
  def <<(val)
@@ -58,9 +57,8 @@ module CrateRuby
58
57
  # @param [Array] ary Column names to filer on
59
58
  # @return [Array] Filtered rows
60
59
  def select_columns(ary, &block)
61
- indexes = ary.map {|col| @cols.index(col)}.compact
62
- @rows.map{|r| r.values_at(*indexes)}.each(&block)
60
+ indexes = ary.map { |col| @cols.index(col) }.compact
61
+ @rows.map { |r| r.values_at(*indexes) }.each(&block)
63
62
  end
64
-
65
63
  end
66
- end
64
+ end
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8; -*-
2
1
  #
3
2
  # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4
3
  # license agreements. See the NOTICE file distributed with this work for
@@ -20,5 +19,5 @@
20
19
  # software solely pursuant to the terms of the relevant commercial agreement.
21
20
 
22
21
  module CrateRuby
23
- VERSION = '0.0.9'
22
+ VERSION = '0.1.0'.freeze
24
23
  end
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- # -*- coding: utf-8; -*-
3
2
  #
4
3
  # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
5
4
  # license agreements. See the NOTICE file distributed with this work for
@@ -25,8 +24,7 @@ require 'net/http'
25
24
  require 'zlib'
26
25
 
27
26
  class Bootstrap
28
-
29
- VERSION = '2.1.8'
27
+ VERSION = '2.3.0'.freeze
30
28
 
31
29
  def initialize
32
30
  @fname = "crate-#{VERSION}.tar.gz"
@@ -34,10 +32,8 @@ class Bootstrap
34
32
  end
35
33
 
36
34
  def run
37
- if !File.file?(@crate_bin)
38
- if !File.file?(@fname)
39
- download
40
- end
35
+ unless File.file?(@crate_bin)
36
+ download unless File.file?(@fname)
41
37
  extract
42
38
  end
43
39
  end
@@ -45,10 +41,10 @@ class Bootstrap
45
41
  def download
46
42
  uri = URI("https://cdn.crate.io/downloads/releases/#{@fname}")
47
43
  puts "Downloading Crate from #{uri} ..."
48
- Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
44
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
49
45
  request = Net::HTTP::Get.new uri
50
46
  resp = http.request request
51
- open(@fname, "wb") do |file|
47
+ open(@fname, 'wb') do |file|
52
48
  file.write(resp.body)
53
49
  end
54
50
  end
@@ -65,7 +61,7 @@ class Bootstrap
65
61
  else
66
62
  dest_dir = File.dirname(dest_file)
67
63
  FileUtils.mkdir_p dest_dir unless File.directory?(dest_dir)
68
- File.open dest_file, "wb" do |f|
64
+ File.open dest_file, 'wb' do |f|
69
65
  f.print entry.read
70
66
  end
71
67
  end
@@ -75,5 +71,4 @@ class Bootstrap
75
71
  end
76
72
 
77
73
  bootstrap = Bootstrap.new(*ARGV)
78
- bootstrap.run()
79
-
74
+ bootstrap.run
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8; -*-
2
1
  #
3
2
  # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4
3
  # license agreements. See the NOTICE file distributed with this work for
@@ -24,18 +23,9 @@ require 'securerandom'
24
23
 
25
24
  describe CrateRuby::Client do
26
25
  let(:client) { CrateRuby::Client.new(['localhost:44200']) }
27
-
28
- before(:all) do
29
- @cluster = CrateRuby::TestCluster.new(1)
30
- @cluster.start_nodes
31
- end
32
-
33
- after(:all) do
34
- @cluster.stop_nodes
35
- end
26
+ let(:client_w_schema) { CrateRuby::Client.new(['localhost:44200'], schema: 'custom') }
36
27
 
37
28
  describe '#create_table' do
38
-
39
29
  describe 'blob management' do
40
30
  let(:file) { 'logo-crate.png' }
41
31
  let(:path) { File.join(File.dirname(__FILE__), '../uploads/') }
@@ -60,7 +50,7 @@ describe CrateRuby::Client do
60
50
  end
61
51
  end
62
52
  context '#string' do
63
- let(:string) { "my crazy" }
53
+ let(:string) { 'my crazy' }
64
54
  let(:digest) { Digest::SHA1.hexdigest(string) }
65
55
  it 'should upload a string to the blob table' do
66
56
  client.blob_put(@blob_table, digest, string).should be_truthy
@@ -76,9 +66,9 @@ describe CrateRuby::Client do
76
66
  it 'should download a blob' do
77
67
  data = client.blob_get(@blob_table, digest)
78
68
  data.should_not be_falsey
79
- open(store_location, "wb") { |file|
69
+ open(store_location, 'wb') do |file|
80
70
  file.write(data)
81
- }
71
+ end
82
72
  end
83
73
  end
84
74
 
@@ -93,12 +83,12 @@ describe CrateRuby::Client do
93
83
  end
94
84
  end
95
85
 
96
-
97
86
  describe '#execute' do
98
- let(:table_name) { "t_test" }
87
+ let(:table_name) { 't_test' }
99
88
 
100
89
  before do
101
- client.execute("create table #{table_name} (id integer primary key, name string, address object, tags array(string)) ")
90
+ client.execute("create table #{table_name} " \
91
+ '(id integer primary key, name string, address object, tags array(string)) ')
102
92
  end
103
93
 
104
94
  after do
@@ -107,49 +97,70 @@ describe CrateRuby::Client do
107
97
 
108
98
  it 'should allow parameters' do
109
99
  client.execute("insert into #{table_name} (id, name, address, tags) VALUES (?, ?, ?, ?)",
110
- [1, "Post 1", {:street=>'1010 W 2nd Ave', :city=>'Vancouver'}, ['awesome', 'freaky']]).should be_truthy
100
+ [1, 'Post 1', { street: '1010 W 2nd Ave', city: 'Vancouver' },
101
+ %w[awesome freaky]]).should be_truthy
111
102
  client.refresh_table table_name
112
103
  client.execute("select * from #{table_name}").rowcount.should eq(1)
113
104
  end
114
105
 
115
106
  it 'should allow bulk parameters' do
116
107
  bulk_args = [
117
- [1, "Post 1", {:street=>'1010 W 2nd Ave', :city=>'New York'}, ['foo','bar']],
118
- [2, "Post 2", {:street=>'1010 W 2nd Ave', :city=>'San Fran'}, []]
108
+ [1, 'Post 1', { street: '1010 W 2nd Ave', city: 'New York' }, %w[foo bar]],
109
+ [2, 'Post 2', { street: '1010 W 2nd Ave', city: 'San Fran' }, []]
119
110
  ]
120
- client.execute("insert into #{table_name} (id, name, address, tags) VALUES (?, ?, ?, ?)", nil, bulk_args).should be_truthy
111
+ client.execute("insert into #{table_name} (id, name, address, tags) VALUES (?, ?, ?, ?)",
112
+ nil, bulk_args).should be_truthy
121
113
  client.refresh_table table_name
122
114
  client.execute("select * from #{table_name}").rowcount.should eq(2)
123
115
  client.execute("select count(*) from #{table_name}")[0][0].should eq(2)
124
116
  end
125
117
 
126
118
  it 'should accept http options' do
127
- expect { client.execute("select * from #{table_name}", nil, nil, {'read_timeout'=>0}) }.to raise_error Net::ReadTimeout
119
+ expect do
120
+ client.execute("select * from #{table_name}", nil, nil,
121
+ 'read_timeout' => 0)
122
+ end.to raise_error Net::ReadTimeout
128
123
  end
129
- end
130
124
 
125
+ context 'with schema' do
126
+ before do
127
+ client_w_schema.execute("create table #{table_name} \n
128
+ (id integer primary key, name string, address object, tags array(string)) ")
129
+ end
130
+ after { client_w_schema.execute("drop table #{table_name}") }
131
+
132
+ it 'should allow parameters' do
133
+ client_w_schema.execute("insert into #{table_name} (id, name, address, tags) VALUES (?, ?, ?, ?)",
134
+ [1, 'Post 1', { street: '1010 W 2nd Ave', city: 'Vancouver' },
135
+ %w[awesome freaky]]).should be_truthy
136
+ client_w_schema.refresh_table table_name
137
+ client.execute("select * from #{table_name}").rowcount.should_not eq(1)
138
+ client_w_schema.execute("select * from #{table_name}").rowcount.should eq(1)
139
+ end
140
+ end
141
+ end
131
142
 
132
143
  describe '#initialize' do
133
144
  it 'should use host and ports parameters' do
134
- logger = double()
135
- client = CrateRuby::Client.new ["10.0.0.1:4200"], logger: logger
136
- client.instance_variable_get(:@servers).should eq(["10.0.0.1:4200"])
145
+ logger = double
146
+ client = CrateRuby::Client.new ['10.0.0.1:4200'], logger: logger
147
+ client.instance_variable_get(:@servers).should eq(['10.0.0.1:4200'])
137
148
  end
138
149
  it 'should use default request parameters' do
139
150
  client = CrateRuby::Client.new
140
- client.instance_variable_get(:@http_options).should eq({:read_timeout=>3600})
151
+ client.instance_variable_get(:@http_options).should eq(read_timeout: 3600)
141
152
  end
142
153
  it 'should use request parameters' do
143
154
  client = CrateRuby::Client.new ['10.0.0.1:4200'],
144
- http_options: {:read_timeout=>60}
145
- client.instance_variable_get(:@http_options).should eq({:read_timeout=>60})
155
+ http_options: { read_timeout: 60 }
156
+ client.instance_variable_get(:@http_options).should eq(read_timeout: 60)
146
157
  end
147
158
  end
148
159
 
149
160
  describe '#tables' do
150
161
  before do
151
- client.create_table "posts", id: :integer
152
- client.create_table "comments", id: :integer
162
+ client.create_table 'posts', id: :integer
163
+ client.create_table 'comments', id: :integer
153
164
  end
154
165
 
155
166
  after do
@@ -159,26 +170,38 @@ describe CrateRuby::Client do
159
170
  end
160
171
 
161
172
  it 'should return all user tables as an array of string values' do
162
- client.tables.should eq %w(comments posts)
173
+ client.tables.should eq %w[comments posts]
163
174
  end
164
175
  end
165
176
 
177
+ describe '#blob_tables' do
178
+ before do
179
+ client.create_blob_table 'pix'
180
+ end
181
+
182
+ after do
183
+ client.drop_table 'pix', true
184
+ end
185
+
186
+ it 'should return all user tables as an array of string values' do
187
+ client.blob_tables.should eq %w(pix)
188
+ end
189
+ end
166
190
 
167
191
  describe '#insert' do
168
192
  before do
169
- client.create_table("posts", id: [:string, "primary key"],
170
- title: :string,
171
- views: :integer)
193
+ client.create_table('posts', id: [:string, 'primary key'],
194
+ title: :string, views: :integer)
172
195
  end
173
196
 
174
197
  after do
175
- client.drop_table "posts"
198
+ client.drop_table 'posts'
176
199
  end
177
200
 
178
201
  it 'should insert the record' do
179
202
  expect do
180
203
  id = SecureRandom.uuid
181
- client.insert('posts', id: id, title: "Test")
204
+ client.insert('posts', id: id, title: 'Test')
182
205
  client.refresh_table('posts')
183
206
  result_set = client.execute("Select * from posts where id = '#{id}'")
184
207
  result_set.rowcount.should eq 1
@@ -188,10 +211,42 @@ describe CrateRuby::Client do
188
211
 
189
212
  describe '#refresh table' do
190
213
  it 'should issue the proper refresh statment' do
191
- client.should_receive(:execute).with("refresh table posts")
214
+ client.should_receive(:execute).with('refresh table posts')
192
215
  client.refresh_table('posts')
193
216
  end
194
217
  end
218
+ end
219
+
220
+ describe 'authentication' do
221
+ let(:username) { 'matz' }
222
+ let(:password) { 'ruby' }
223
+ let(:encrypted_credentials) { Base64.encode64 "#{username}:#{password}" }
195
224
 
225
+ describe 'with password' do
226
+ let(:auth_client) { CrateRuby::Client.new(['localhost:44200'], username: username, password: password) }
227
+ it 'sets the basic auth header' do
228
+ headers = auth_client.send(:headers)
229
+ expect(headers['Authorization']).to eq "Basic #{encrypted_credentials}"
230
+ end
231
+ end
232
+
233
+ describe 'without password' do
234
+ let(:auth_client) { CrateRuby::Client.new(['localhost:44200'], username: username) }
235
+ let(:enc_creds_wo_pwd) { Base64.encode64 "#{username}:" }
236
+
237
+ it 'sets and encodes auth header even without password' do
238
+ headers = auth_client.send(:headers)
239
+ expect(headers['Authorization']).to eq "Basic #{enc_creds_wo_pwd}"
240
+ end
241
+ end
242
+
243
+ describe 'X-User header' do
244
+ let(:auth_client) { CrateRuby::Client.new(['localhost:44200'], username: username) }
245
+
246
+ it 'sets the X-User header' do
247
+ headers = auth_client.send(:headers)
248
+ expect(headers['X-User']).to eq username
249
+ end
250
+ end
196
251
  end
197
252
  end
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8; -*-
2
1
  #
3
2
  # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4
3
  # license agreements. See the NOTICE file distributed with this work for
@@ -22,18 +21,15 @@
22
21
  require_relative '../spec_helper'
23
22
 
24
23
  describe ResultSet do
25
-
26
- let(:crate_result) { "{\"cols\":[\"my_column\",\"my_integer_col\"],\"rows\":[[\"Foo\",5],[\"Bar\",5]],\"rowcount\":1,\"duration\":4}" }
27
- let(:result_with_array_col) { %Q{{"cols":["id","tags","title"],"rows":[[1,["awesome","freaky"],"My life with crate"]],"rowcount":1,"duration":2}} }
28
- let(:result_with_object) { %Q{{"cols":["address","id","name"],"rows":[[{"street":"1010 W 2nd Ave","city":"Vancouver"},"fb7183ac-d049-462c-85a9-732aca59a1c1","Mad Max"]],"rowcount":1,"duration":3}} }
24
+ let(:crate_result) { '{"cols":["my_column","my_integer_col"],"rows":[["Foo",5],["Bar",5]],"rowcount":1,"duration":4}' }
25
+ let(:result_with_array_col) { %({"cols":["id","tags","title"],"rows":[[1,["awesome","freaky"],"My life with crate"]],"rowcount":1,"duration":2}) }
26
+ let(:result_with_object) { %({"cols":["address","id","name"],"rows":[[{"street":"1010 W 2nd Ave","city":"Vancouver"},"fb7183ac-d049-462c-85a9-732aca59a1c1","Mad Max"]],"rowcount":1,"duration":3}) }
29
27
 
30
28
  let(:result_set) { ResultSet.new(crate_result) }
31
29
 
32
30
  let(:json_result) { JSON.parse crate_result }
33
31
 
34
32
  describe '#initialize' do
35
-
36
-
37
33
  it 'should set rowcount' do
38
34
  result_set.rowcount.should eq 1
39
35
  end
@@ -49,15 +45,14 @@ describe ResultSet do
49
45
  it 'should parse an array column result into an Array' do
50
46
  res = ResultSet.new(result_with_array_col)
51
47
  res[0][1].should be_a(Array)
52
- res[0][1].should eq(["awesome","freaky"])
48
+ res[0][1].should eq(%w[awesome freaky])
53
49
  end
54
50
 
55
51
  it 'should parse an object column result into an Object' do
56
52
  res = ResultSet.new(result_with_object)
57
53
  res[0][0].should be_a(Hash)
58
- res[0][0].should eq({"street" => "1010 W 2nd Ave","city" => "Vancouver"})
54
+ res[0][0].should eq('street' => '1010 W 2nd Ave', 'city' => 'Vancouver')
59
55
  end
60
-
61
56
  end
62
57
 
63
58
  describe '#each' do
@@ -80,12 +75,11 @@ describe ResultSet do
80
75
  result_set.select_columns(['my_column']) do |res|
81
76
  a << res
82
77
  end
83
- a.should eq [["Foo"], ["Bar"]]
84
-
78
+ a.should eq [['Foo'], ['Bar']]
85
79
  end
86
80
  it 'should not raise error on invalid column name' do
87
81
  expect do
88
- result_set.select_columns(['my_column', 'invalid']) do |row|
82
+ result_set.select_columns(%w[my_column invalid]) do |row|
89
83
  end
90
84
  end.to_not raise_error
91
85
  end
@@ -96,7 +90,4 @@ describe ResultSet do
96
90
  result_set.values.should eq json_result['rows']
97
91
  end
98
92
  end
99
-
100
93
  end
101
-
102
-
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8; -*-
2
1
  #
3
2
  # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4
3
  # license agreements. See the NOTICE file distributed with this work for
@@ -21,4 +20,24 @@
21
20
 
22
21
  require_relative '../lib/crate_ruby'
23
22
  require 'net/http'
23
+ require_relative 'support/test_cluster'
24
24
 
25
+ HOST = '127.0.0.1'.freeze
26
+ PORT = 44_200
27
+
28
+ RSpec.configure do |config|
29
+ config.before(:each) do
30
+ end
31
+ config.after(:each) do
32
+ end
33
+ config.before(:suite) do
34
+ @cluster = TestCluster.new(1, PORT)
35
+ @cluster.start_nodes
36
+ end
37
+ config.after(:suite) do
38
+ pid_file = File.join(__dir__, 'support/testnode.pid')
39
+ pid = File.read(pid_file)
40
+ File.delete(pid_file)
41
+ Process.kill('HUP', pid.to_i)
42
+ end
43
+ end
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4
+ # license agreements. See the NOTICE file distributed with this work for
5
+ # additional information regarding copyright ownership. Crate licenses
6
+ # this file to you under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License. You may
8
+ # obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations
16
+ # under the License.
17
+ #
18
+ # However, if you have executed another commercial license agreement
19
+ # with Crate these terms will supersede the license and you may use the
20
+ # software solely pursuant to the terms of the relevant commercial agreement.
21
+
22
+ require 'net/http'
23
+
24
+ class TestCluster
25
+ def initialize(num_nodes = 1, http_port = 44_200)
26
+ @nodes = []
27
+ idx = 0
28
+ while idx < num_nodes
29
+ name = "crate#{idx - 1}"
30
+ port = http_port + idx
31
+ @nodes << TestServer.new(name, port)
32
+ idx += 1
33
+ end
34
+ end
35
+
36
+ def start_nodes
37
+ @nodes.each(&:start)
38
+ end
39
+
40
+ def stop_nodes
41
+ @nodes.each(&:stop)
42
+ end
43
+ end
44
+
45
+ class TestServer
46
+ STARTUP_TIMEOUT = 30
47
+
48
+ def initialize(name, http_port)
49
+ @node_name = name
50
+ @http_port = http_port
51
+
52
+ @crate_bin = File.join('parts', 'crate', 'bin', 'crate')
53
+ unless File.file?(@crate_bin)
54
+ puts "Crate is not available. Please run 'bundle exec ruby spec/bootstrap.rb' first."
55
+ exit 1
56
+ end
57
+ end
58
+
59
+ def start
60
+ cmd = "sh #{@crate_bin} #{start_params}"
61
+ @pid = spawn(cmd)
62
+ wait_for
63
+ Process.detach(@pid)
64
+
65
+ File.write(__dir__ + '/testnode.pid', @pid)
66
+ end
67
+
68
+ def wait_for
69
+ time_slept = 0
70
+ interval = 1
71
+ loop do
72
+ if !alive? && (time_slept > STARTUP_TIMEOUT)
73
+ puts "Crate hasn't started for #{STARTUP_TIMEOUT} seconds. Giving up now..."
74
+ exit 1
75
+ end
76
+ if alive?
77
+ break
78
+ else
79
+ sleep(interval)
80
+ time_slept += interval
81
+ end
82
+ end
83
+ end
84
+
85
+ def stop
86
+ Process.kill('HUP', @pid)
87
+ end
88
+
89
+ private
90
+
91
+ def start_params
92
+ "-Cnode.name=#{@node_name} " \
93
+ "-Chttp.port=#{@http_port} " \
94
+ '-Cnetwork.host=localhost '
95
+ end
96
+
97
+ def alive?
98
+ req = Net::HTTP::Get.new('/')
99
+ resp = Net::HTTP.new('localhost', @http_port)
100
+ begin
101
+ response = resp.start { |http| http.request(req) }
102
+ response.code == '200'
103
+ rescue Errno::ECONNREFUSED
104
+ false
105
+ end
106
+ end
107
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crate_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christoph Klocker
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-10-31 00:00:00.000000000 Z
12
+ date: 2018-01-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -62,19 +62,19 @@ files:
62
62
  - lib/crate_ruby/client.rb
63
63
  - lib/crate_ruby/error.rb
64
64
  - lib/crate_ruby/result_set.rb
65
- - lib/crate_ruby/utils.rb
66
65
  - lib/crate_ruby/version.rb
67
66
  - log/.keep
68
67
  - spec/bootstrap.rb
69
68
  - spec/crate_ruby/client_spec.rb
70
69
  - spec/crate_ruby/result_set_spec.rb
71
70
  - spec/spec_helper.rb
71
+ - spec/support/test_cluster.rb
72
72
  - spec/uploads/get_logo-crate.png
73
73
  - spec/uploads/logo-crate.png
74
74
  - spec/uploads/text.txt
75
75
  homepage: http://crate.io
76
76
  licenses:
77
- - Apache License, v2.0
77
+ - Apache-2.0
78
78
  metadata: {}
79
79
  post_install_message:
80
80
  rdoc_options: []
@@ -92,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
92
  version: '0'
93
93
  requirements: []
94
94
  rubyforge_project:
95
- rubygems_version: 2.6.14
95
+ rubygems_version: 2.7.4
96
96
  signing_key:
97
97
  specification_version: 4
98
98
  summary: A simple interface for the Crate.IO database.
@@ -101,6 +101,7 @@ test_files:
101
101
  - spec/crate_ruby/client_spec.rb
102
102
  - spec/crate_ruby/result_set_spec.rb
103
103
  - spec/spec_helper.rb
104
+ - spec/support/test_cluster.rb
104
105
  - spec/uploads/get_logo-crate.png
105
106
  - spec/uploads/logo-crate.png
106
107
  - spec/uploads/text.txt
@@ -1,117 +0,0 @@
1
- # -*- coding: utf-8; -*-
2
- #
3
- # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4
- # license agreements. See the NOTICE file distributed with this work for
5
- # additional information regarding copyright ownership. Crate licenses
6
- # this file to you under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License. You may
8
- # obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
- # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
- # License for the specific language governing permissions and limitations
16
- # under the License.
17
- #
18
- # However, if you have executed another commercial license agreement
19
- # with Crate these terms will supersede the license and you may use the
20
- # software solely pursuant to the terms of the relevant commercial agreement.
21
-
22
- require 'net/http'
23
-
24
- module CrateRuby
25
-
26
- class TestCluster
27
-
28
- def initialize(num_nodes = 1, http_port=44200)
29
- @nodes = []
30
- idx = 0
31
- while idx < num_nodes do
32
- name = "crate#{idx-1}"
33
- port = http_port + idx
34
- @nodes << TestServer.new(name, port)
35
- idx += 1
36
- end
37
- end
38
-
39
- def start_nodes
40
- @nodes.each do |node|
41
- node.start
42
- end
43
- end
44
-
45
- def stop_nodes
46
- @nodes.each do |node|
47
- node.stop
48
- end
49
- end
50
-
51
- end
52
-
53
- class TestServer
54
-
55
- STARTUP_TIMEOUT = 30
56
-
57
- def initialize(name, http_port)
58
- @node_name = name
59
- @http_port = http_port
60
-
61
- @crate_bin = File.join('parts', 'crate', 'bin', 'crate')
62
- if !File.file?(@crate_bin)
63
- puts "Crate is not available. Please run 'bundle exec ruby spec/bootstrap.rb' first."
64
- exit 1
65
- end
66
- end
67
-
68
- def start
69
- cmd = "sh #{@crate_bin} #{start_params}"
70
- @pid = spawn(cmd)
71
- wait_for
72
- Process.detach(@pid)
73
- end
74
-
75
- def wait_for
76
- time_slept = 0
77
- interval = 1
78
- while true
79
- if !alive? and time_slept > STARTUP_TIMEOUT
80
- puts "Crate hasn't started for #{STARTUP_TIMEOUT} seconds. Giving up now..."
81
- exit 1
82
- end
83
- if alive?
84
- break
85
- else
86
- sleep(interval)
87
- time_slept += interval
88
- end
89
- end
90
- end
91
-
92
- def stop
93
- Process.kill('HUP', @pid)
94
- end
95
-
96
- private
97
-
98
- def start_params
99
- "-Cnode.name=#{@node_name} " +
100
- "-Chttp.port=#{@http_port} " +
101
- "-Cnetwork.host=localhost "
102
- end
103
-
104
- def alive?
105
- req = Net::HTTP::Get.new('/')
106
- resp = Net::HTTP.new('localhost', @http_port)
107
- begin
108
- response = resp.start { |http| http.request(req) }
109
- response.code == "200" ? true : false
110
- rescue Errno::ECONNREFUSED
111
- false
112
- end
113
- end
114
- end
115
-
116
- end
117
-