crate_ruby 0.0.9 → 0.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.
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
-