tarantool 0.2.5 → 0.3.0.7
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.
- data/Gemfile +14 -12
- data/LICENSE +4 -4
- data/README.md +15 -9
- data/Rakefile +5 -129
- data/lib/tarantool/base_record.rb +288 -0
- data/lib/tarantool/block_db.rb +135 -0
- data/lib/tarantool/callback_db.rb +47 -0
- data/lib/tarantool/core-ext.rb +12 -0
- data/lib/tarantool/em_db.rb +37 -0
- data/lib/tarantool/exceptions.rb +52 -7
- data/lib/tarantool/fiber_db.rb +152 -0
- data/lib/tarantool/light_record.rb +68 -0
- data/lib/tarantool/query.rb +127 -0
- data/lib/tarantool/record/select.rb +59 -0
- data/lib/tarantool/record.rb +93 -282
- data/lib/tarantool/request.rb +351 -52
- data/lib/tarantool/response.rb +108 -45
- data/lib/tarantool/serializers/bson.rb +2 -2
- data/lib/tarantool/serializers.rb +32 -4
- data/lib/tarantool/space_array.rb +153 -0
- data/lib/tarantool/space_hash.rb +262 -0
- data/lib/tarantool/util.rb +182 -0
- data/lib/tarantool/version.rb +3 -0
- data/lib/tarantool.rb +79 -29
- data/test/box.pid +1 -0
- data/test/helper.rb +164 -0
- data/test/run_all.rb +3 -0
- data/test/shared_query.rb +73 -0
- data/test/shared_record.rb +474 -0
- data/test/shared_space_array.rb +284 -0
- data/test/shared_space_hash.rb +239 -0
- data/test/tarant/init.lua +22 -0
- data/test/tarantool.cfg +62 -0
- data/test/tarantool.log +6 -0
- data/{spec/tarantool.cfg → test/tarantool_repl.cfg} +11 -5
- data/test/test_light_record.rb +48 -0
- data/test/test_query_block.rb +6 -0
- data/test/test_query_fiber.rb +7 -0
- data/test/test_record.rb +88 -0
- data/{spec/tarantool/composite_primary_key_spec.rb → test/test_record_composite_pk.rb} +5 -7
- data/test/test_space_array_block.rb +6 -0
- data/test/test_space_array_callback.rb +255 -0
- data/test/test_space_array_callback_nodef.rb +190 -0
- data/test/test_space_array_fiber.rb +7 -0
- data/test/test_space_hash_block.rb +6 -0
- data/test/test_space_hash_fiber.rb +7 -0
- metadata +78 -55
- data/Gemfile.lock +0 -54
- data/examples/em_simple.rb +0 -16
- data/examples/record.rb +0 -68
- data/examples/simple.rb +0 -13
- data/lib/tarantool/requests/call.rb +0 -20
- data/lib/tarantool/requests/delete.rb +0 -18
- data/lib/tarantool/requests/insert.rb +0 -19
- data/lib/tarantool/requests/ping.rb +0 -16
- data/lib/tarantool/requests/select.rb +0 -22
- data/lib/tarantool/requests/update.rb +0 -35
- data/lib/tarantool/requests.rb +0 -19
- data/lib/tarantool/serializers/integer.rb +0 -14
- data/lib/tarantool/serializers/string.rb +0 -14
- data/lib/tarantool/space.rb +0 -39
- data/spec/helpers/let.rb +0 -11
- data/spec/helpers/truncate.rb +0 -12
- data/spec/spec_helper.rb +0 -21
- data/spec/tarantool/em_spec.rb +0 -22
- data/spec/tarantool/record_spec.rb +0 -316
- data/spec/tarantool/request_spec.rb +0 -103
- data/tarantool.gemspec +0 -69
data/Gemfile
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
|
3
|
+
group :development do
|
4
|
+
gem "debugger"
|
5
|
+
end
|
4
6
|
|
5
|
-
|
6
|
-
gem "
|
7
|
-
gem "
|
8
|
-
gem "
|
9
|
-
|
10
|
-
gem "
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
gemspec
|
7
|
+
group :test do
|
8
|
+
gem "rr"
|
9
|
+
gem "activesupport"
|
10
|
+
gem "activemodel"
|
11
|
+
gem "yajl-ruby"
|
12
|
+
gem "bson"
|
13
|
+
gem "bson_ext"
|
14
|
+
end
|
15
|
+
# Specify your gem's dependencies in em-tarantool.gemspec
|
16
|
+
gemspec
|
data/LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
/*
|
2
|
-
* Copyright (C) 2011 Mail.RU
|
3
|
-
|
1
|
+
/*
|
2
|
+
* Copyright (C) 2011-2012 Mail.RU
|
3
|
+
*
|
4
4
|
* Redistribution and use in source and binary forms, with or without
|
5
5
|
* modification, are permitted provided that the following conditions
|
6
6
|
* are met:
|
@@ -21,4 +21,4 @@
|
|
21
21
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
22
22
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
23
23
|
* SUCH DAMAGE.
|
24
|
-
*/
|
24
|
+
*/
|
data/README.md
CHANGED
@@ -22,7 +22,13 @@ DB = Tarantool.new host: 'locahost', port: 33013
|
|
22
22
|
space = DB.space 0
|
23
23
|
```
|
24
24
|
|
25
|
-
The driver internals can work in
|
25
|
+
The driver internals can work in three modes:
|
26
|
+
- blocking via TCPSocket
|
27
|
+
- callback style via EventMachine
|
28
|
+
- EM::Synchrony like via EventMachine and fibers, so that control flow is visually
|
29
|
+
blocked, but eventloop is not (see EM::Synchrony)
|
30
|
+
|
31
|
+
By default it uses block mode.
|
26
32
|
|
27
33
|
|
28
34
|
```ruby
|
@@ -49,7 +55,7 @@ EM.synchrony do
|
|
49
55
|
end
|
50
56
|
```
|
51
57
|
|
52
|
-
The driver itself provides ActiveModel API: Callbacks, Validations, Serialization, Dirty.
|
58
|
+
The driver itself provides ActiveModel API: Callbacks, Validations, Serialization, Dirty.
|
53
59
|
Type casting is automatic, based on the index type chosen to process the query.
|
54
60
|
For example:
|
55
61
|
|
@@ -59,7 +65,7 @@ require 'tarantool/serializers/bson'
|
|
59
65
|
class User < Tarantool::Record
|
60
66
|
field :login, :string
|
61
67
|
field :name, :string
|
62
|
-
field :email, :string
|
68
|
+
field :email, :string
|
63
69
|
field :apples_count, :integer, default: 0
|
64
70
|
field :info, :bson
|
65
71
|
index :name, :email
|
@@ -76,16 +82,16 @@ User.create login: 'prepor', email: 'ceo@prepor.ru', name: 'Andrew'
|
|
76
82
|
User.create login: 'ruden', name: 'Andrew', email: 'rudenkoco@gmail.com'
|
77
83
|
|
78
84
|
# find by primary key login
|
79
|
-
User.find 'prepor'
|
85
|
+
User.find 'prepor'
|
80
86
|
# first 2 users with name Andrew
|
81
|
-
User.where(name: 'Andrew').limit(2).all
|
87
|
+
User.where(name: 'Andrew').limit(2).all
|
82
88
|
# second user with name Andrew
|
83
|
-
User.where(name: 'Andrew').offset(1).limit(1).all
|
89
|
+
User.where(name: 'Andrew').offset(1).limit(1).all
|
84
90
|
# user with name Andrew and email ceo@prepor.ru
|
85
91
|
User.where(name: 'Andrew', email: 'ceo@prepor.ru').first
|
86
92
|
# raise exception, becouse we can't select query started from not first part of index
|
87
93
|
begin
|
88
|
-
User.where(email: 'ceo@prepor.ru')
|
94
|
+
User.where(email: 'ceo@prepor.ru')
|
89
95
|
rescue Tarantool::ArgumentError => e
|
90
96
|
end
|
91
97
|
# increment field apples_count by one. Its atomic operation via native Tarantool interface
|
@@ -105,7 +111,7 @@ user.destroy
|
|
105
111
|
```
|
106
112
|
|
107
113
|
When definining a record, field order is important: this is the order of fields
|
108
|
-
in the tuple stored by Tarantool. By default, the primary key is field 0.
|
114
|
+
in the tuple stored by Tarantool. By default, the primary key is field 0.
|
109
115
|
|
110
116
|
`index` method just mapping to your Tarantool schema, client doesn't modify schema for you.
|
111
117
|
|
@@ -117,4 +123,4 @@ in the tuple stored by Tarantool. By default, the primary key is field 0.
|
|
117
123
|
* admin-socket protocol
|
118
124
|
* safe to add fields to exist model
|
119
125
|
* Hash, Array and lambdas as default values
|
120
|
-
* timers to response, reconnect strategies
|
126
|
+
* timers to response, reconnect strategies
|
data/Rakefile
CHANGED
@@ -1,131 +1,7 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
require 'date'
|
4
|
-
|
5
|
-
#############################################################################
|
6
|
-
#
|
7
|
-
# Helper functions
|
8
|
-
#
|
9
|
-
#############################################################################
|
10
|
-
|
11
|
-
def name
|
12
|
-
@name ||= Dir['*.gemspec'].first.split('.').first
|
13
|
-
end
|
14
|
-
|
15
|
-
def version
|
16
|
-
line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
|
17
|
-
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
18
|
-
end
|
19
|
-
|
20
|
-
def date
|
21
|
-
Date.today.to_s
|
22
|
-
end
|
23
|
-
|
24
|
-
def rubyforge_project
|
25
|
-
name
|
26
|
-
end
|
27
|
-
|
28
|
-
def gemspec_file
|
29
|
-
"#{name}.gemspec"
|
30
|
-
end
|
31
|
-
|
32
|
-
def gem_file
|
33
|
-
"#{name}-#{version}.gem"
|
34
|
-
end
|
35
|
-
|
36
|
-
def replace_header(head, header_name)
|
37
|
-
head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
|
38
|
-
end
|
39
|
-
|
40
|
-
#############################################################################
|
41
|
-
#
|
42
|
-
# Standard tasks
|
43
|
-
#
|
44
|
-
#############################################################################
|
45
|
-
|
46
|
-
|
47
|
-
task :default => :spec
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
48
3
|
require 'rake/testtask'
|
49
|
-
Rake::TestTask.new
|
50
|
-
|
51
|
-
|
52
|
-
t.verbose = false
|
53
|
-
end
|
54
|
-
|
55
|
-
desc "Generate RCov test coverage and open in your browser"
|
56
|
-
task :coverage do
|
57
|
-
require 'rcov'
|
58
|
-
sh "rm -fr coverage"
|
59
|
-
sh "rcov test/test_*.rb"
|
60
|
-
sh "open coverage/index.html"
|
61
|
-
end
|
62
|
-
|
63
|
-
desc "Open an irb session preloaded with this library"
|
64
|
-
task :console do
|
65
|
-
sh "irb -rubygems -r ./lib/#{name}.rb"
|
66
|
-
end
|
67
|
-
|
68
|
-
|
69
|
-
#############################################################################
|
70
|
-
#
|
71
|
-
# Packaging tasks
|
72
|
-
#
|
73
|
-
#############################################################################
|
74
|
-
|
75
|
-
desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
|
76
|
-
task :release => :build do
|
77
|
-
unless `git branch` =~ /^\* master$/
|
78
|
-
puts "You must be on the master branch to release!"
|
79
|
-
exit!
|
80
|
-
end
|
81
|
-
sh "git commit --allow-empty -m 'Release #{version}'"
|
82
|
-
sh "git tag v#{version}"
|
83
|
-
sh "git push origin master"
|
84
|
-
sh "git push origin v#{version}"
|
85
|
-
sh "gem push pkg/#{name}-#{version}.gem"
|
86
|
-
end
|
87
|
-
|
88
|
-
desc "Build #{gem_file} into the pkg directory"
|
89
|
-
task :build => :gemspec do
|
90
|
-
sh "mkdir -p pkg"
|
91
|
-
sh "gem build #{gemspec_file}"
|
92
|
-
sh "mv #{gem_file} pkg"
|
93
|
-
end
|
94
|
-
|
95
|
-
desc "Generate #{gemspec_file}"
|
96
|
-
task :gemspec => :validate do
|
97
|
-
# read spec file and split out manifest section
|
98
|
-
spec = File.read(gemspec_file)
|
99
|
-
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
100
|
-
|
101
|
-
# replace name version and date
|
102
|
-
replace_header(head, :name)
|
103
|
-
replace_header(head, :version)
|
104
|
-
replace_header(head, :date)
|
105
|
-
#comment this out if your rubyforge_project has a different name
|
106
|
-
replace_header(head, :rubyforge_project)
|
107
|
-
|
108
|
-
# determine file list from git ls-files
|
109
|
-
files = `git ls-files`.
|
110
|
-
split("\n").
|
111
|
-
sort.
|
112
|
-
reject { |file| file =~ /^\./ }.
|
113
|
-
reject { |file| file =~ /^(rdoc|pkg)/ }.
|
114
|
-
map { |file| " #{file}" }.
|
115
|
-
join("\n")
|
116
|
-
|
117
|
-
# piece file back together and write
|
118
|
-
manifest = " s.files = %w[\n#{files}\n ]\n"
|
119
|
-
spec = [head, manifest, tail].join(" # = MANIFEST =\n")
|
120
|
-
File.open(gemspec_file, 'w') { |io| io.write(spec) }
|
121
|
-
puts "Updated #{gemspec_file}"
|
4
|
+
Rake::TestTask.new do |i|
|
5
|
+
i.options = '-v'
|
6
|
+
i.verbose = true
|
122
7
|
end
|
123
|
-
|
124
|
-
desc "Validate #{gemspec_file}"
|
125
|
-
task :validate do
|
126
|
-
libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
|
127
|
-
unless Dir['VERSION*'].empty?
|
128
|
-
puts "A `VERSION` file at root level violates Gem best practices."
|
129
|
-
exit!
|
130
|
-
end
|
131
|
-
end
|
@@ -0,0 +1,288 @@
|
|
1
|
+
require 'tarantool'
|
2
|
+
require 'tarantool/record/select'
|
3
|
+
require 'active_support/core_ext/class/attribute'
|
4
|
+
|
5
|
+
module Tarantool
|
6
|
+
class RecordError < StandardError; end
|
7
|
+
class UpdateNewRecord < RecordError; end
|
8
|
+
|
9
|
+
class BaseRecord
|
10
|
+
class_attribute :fields, instance_reader: false, instance_writer: false
|
11
|
+
self.fields = {}.freeze
|
12
|
+
|
13
|
+
class_attribute :default_values, instance_reader: false, instance_writer: false
|
14
|
+
self.default_values = {}.freeze
|
15
|
+
|
16
|
+
class_attribute :indexes, instance_reader: false, instance_writer: false
|
17
|
+
self.indexes = [].freeze
|
18
|
+
|
19
|
+
class_attribute :space_no, instance_reader: false, instance_writer: false
|
20
|
+
class_attribute :tarantool, instance_reader: false, instance_writer: false
|
21
|
+
|
22
|
+
class << self
|
23
|
+
alias set_space_no space_no=
|
24
|
+
alias set_tarantool tarantool=
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
def field(name, type, params = {})
|
29
|
+
type = Serializers.check_type(type)
|
30
|
+
|
31
|
+
raise ArgumentError, "_tail should be last declaration" if fields.include?(:_tail)
|
32
|
+
self.fields = fields.merge(name => type)
|
33
|
+
index name if indexes.empty?
|
34
|
+
|
35
|
+
if params[:default]
|
36
|
+
self.default_values = default_values.merge name => params[:default]
|
37
|
+
end
|
38
|
+
|
39
|
+
define_field_accessor(name, type)
|
40
|
+
end
|
41
|
+
|
42
|
+
def _tail(*types)
|
43
|
+
types = types.map{|type| Serializers.check_type(type)}
|
44
|
+
|
45
|
+
raise ArgumentError, "double _tail declaration" if fields.include?(:_tail)
|
46
|
+
self.fields = fields.merge(:_tail => types)
|
47
|
+
|
48
|
+
define_field_accessor(:_tail, types)
|
49
|
+
end
|
50
|
+
|
51
|
+
def index(*fields)
|
52
|
+
options = Hash === fields.last ? fields.pop : {}
|
53
|
+
if options[:primary]
|
54
|
+
self.indexes = indexes.dup.tap{|ind| ind[0] = fields}
|
55
|
+
else
|
56
|
+
self.indexes += [fields]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def primary_index
|
61
|
+
indexes[0]
|
62
|
+
end
|
63
|
+
|
64
|
+
def space
|
65
|
+
@space ||= begin
|
66
|
+
tarantool.space_hash(space_no, fields.dup, keys: indexes)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# space that will return records as results for calls
|
71
|
+
# it is useful, if you wish to use callback interface
|
72
|
+
def auto_space
|
73
|
+
@auto_space ||= begin
|
74
|
+
space.with_translator(method(:from_fetched))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def by_pk(pk)
|
79
|
+
if Hash === (res = space.by_pk(pk))
|
80
|
+
from_fetched(res)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def by_pks(pks)
|
85
|
+
space.all_by_pks(pks).map{|hash| from_fetched(hash)}
|
86
|
+
end
|
87
|
+
|
88
|
+
def find(*args)
|
89
|
+
if args.size == 1
|
90
|
+
by_pk(args[0])
|
91
|
+
else
|
92
|
+
by_pks(args)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def first(cond)
|
97
|
+
if Hash === cond
|
98
|
+
if Hash === (res = space.first(cond))
|
99
|
+
from_fetched(res)
|
100
|
+
end
|
101
|
+
else
|
102
|
+
by_pk(cond)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def all(cond, opts={})
|
107
|
+
res = space.all(cond, opts)
|
108
|
+
res.map!{|hash| from_fetched(hash)}
|
109
|
+
res
|
110
|
+
end
|
111
|
+
|
112
|
+
def select(cond=nil, opts={})
|
113
|
+
cond.nil? ? Select.new(self) : all(cond, opts)
|
114
|
+
end
|
115
|
+
|
116
|
+
def create(attributes = {})
|
117
|
+
r = new(attributes)
|
118
|
+
r.save
|
119
|
+
r
|
120
|
+
end
|
121
|
+
|
122
|
+
# Call stored procedure without returning tuples. By default, it prepends
|
123
|
+
# +space_no+ to arguments. To avoid prepending, set +space_no: nil+ in options.
|
124
|
+
#
|
125
|
+
# MyRecord.call('box.select_range', offset, limit)
|
126
|
+
# MyRecord.call('myfunction', arg1, arg2, space_no: nil)
|
127
|
+
#
|
128
|
+
def invoke(proc_name, *args)
|
129
|
+
opts = Hash === args.last ? args.pop : {}
|
130
|
+
space.invoke(proc_name, args, opts)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Call stored procedure. By default, it prepends +space_no+ to arguments.
|
134
|
+
# To avoid prepending, set +space_no: nil+ in options.
|
135
|
+
#
|
136
|
+
# MyRecord.call('box.select_range', offset, limit)
|
137
|
+
# MyRecord.call('myfunction', arg1, arg2, space_no: nil)
|
138
|
+
#
|
139
|
+
# You could recieve arbitarry arrays or hashes instead of instances of
|
140
|
+
# record, if you pass +:returns+ argument. See documentation for +SpaceHash+
|
141
|
+
# for this.
|
142
|
+
def call(proc_name, *args)
|
143
|
+
opts = Hash === args.last ? args.pop : {}
|
144
|
+
res = space.call(proc_name, args, opts)
|
145
|
+
if Array === res && !opts[:returns]
|
146
|
+
res.map{|hash| from_fetched(hash) }
|
147
|
+
else
|
148
|
+
res
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def from_fetched(hash)
|
153
|
+
hash && allocate.__fetched(hash)
|
154
|
+
end
|
155
|
+
|
156
|
+
def insert(hash, ret_tuple = false)
|
157
|
+
hash = default_values.merge(hash)
|
158
|
+
if ret_tuple
|
159
|
+
from_fetched space.insert(hash, return_tuple: true)
|
160
|
+
else
|
161
|
+
space.insert(hash)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def replace(hash, ret_tuple = false)
|
166
|
+
hash = default_values.merge(hash)
|
167
|
+
if ret_tuple
|
168
|
+
from_fetched space.replace(hash, return_tuple: true)
|
169
|
+
else
|
170
|
+
space.replace(hash)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def update(pk, ops, ret_tuple=false)
|
175
|
+
if ret_tuple
|
176
|
+
from_fetched space.update(pk, ops, return_tuple: true)
|
177
|
+
else
|
178
|
+
space.update(pk, ops)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def delete(pk, ret_tuple=false)
|
183
|
+
if ret_tuple
|
184
|
+
from_fetched space.delete(pk, return_tuple: true)
|
185
|
+
else
|
186
|
+
space.delete(pk)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
%w{where limit offset}.each do |meth|
|
191
|
+
class_eval <<-"EOF", __FILE__, __LINE__
|
192
|
+
def #{meth}(arg)
|
193
|
+
select.#{meth}(arg)
|
194
|
+
end
|
195
|
+
EOF
|
196
|
+
end
|
197
|
+
end
|
198
|
+
extend ClassMethods
|
199
|
+
|
200
|
+
module InstanceMethods
|
201
|
+
attr_accessor :__new_record
|
202
|
+
attr_reader :attributes
|
203
|
+
alias new_record __new_record
|
204
|
+
alias new_record? new_record
|
205
|
+
|
206
|
+
def new_record!
|
207
|
+
@__new_record = true
|
208
|
+
self
|
209
|
+
end
|
210
|
+
|
211
|
+
def old_record!
|
212
|
+
@__new_record = false
|
213
|
+
self
|
214
|
+
end
|
215
|
+
|
216
|
+
def set_attributes(attributes)
|
217
|
+
attributes.each do |k, v|
|
218
|
+
send("#{k}=", v)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def _tail
|
223
|
+
@attributes[:_tail]
|
224
|
+
end
|
225
|
+
|
226
|
+
def _tail=(v)
|
227
|
+
@attributes[:_tail] = v
|
228
|
+
end
|
229
|
+
|
230
|
+
def update_attributes(attributes)
|
231
|
+
set_attributes(attributes)
|
232
|
+
save
|
233
|
+
end
|
234
|
+
|
235
|
+
def id
|
236
|
+
(primary = self.class.primary_index).size == 1 ?
|
237
|
+
@attributes[primary[0]] :
|
238
|
+
@attributes.values_at(*primary)
|
239
|
+
end
|
240
|
+
|
241
|
+
def space
|
242
|
+
self.class.space
|
243
|
+
end
|
244
|
+
|
245
|
+
def auto_space
|
246
|
+
self.class.space
|
247
|
+
end
|
248
|
+
|
249
|
+
def reload
|
250
|
+
if hash = space.by_pk(id)
|
251
|
+
@__new_record = false
|
252
|
+
@attributes = hash
|
253
|
+
self
|
254
|
+
else
|
255
|
+
_raise_doesnt_exists("reload")
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def ==(other)
|
260
|
+
equal?(other) || (other.class == self.class && id == other.id)
|
261
|
+
end
|
262
|
+
|
263
|
+
# update record in db first, reload it then
|
264
|
+
#
|
265
|
+
# record.update({:state => 'sleep', :sleep_count => [:+, 1]})
|
266
|
+
# record.update([[:state, 'sleep'], [:sleep_count, :+, 1]])
|
267
|
+
def update(ops)
|
268
|
+
raise UpdateNewRecord, "Could not call update on new record" if @__new_record
|
269
|
+
unless new_attrs = space.update(id, ops, return_tuple: true)
|
270
|
+
_raise_doesnt_exists
|
271
|
+
end
|
272
|
+
@attributes = new_attrs
|
273
|
+
|
274
|
+
self
|
275
|
+
end
|
276
|
+
|
277
|
+
def increment(field, by = 1)
|
278
|
+
update([[field.to_sym, :+, by]])
|
279
|
+
end
|
280
|
+
|
281
|
+
def _raise_doesnt_exists(action = "update")
|
282
|
+
raise TupleDoesntExists.new(0x3102, "Record which you wish to #{action}, doesn't exists")
|
283
|
+
end
|
284
|
+
end
|
285
|
+
include InstanceMethods
|
286
|
+
|
287
|
+
end
|
288
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module Tarantool
|
2
|
+
class BlockDB < DB
|
3
|
+
def establish_connection
|
4
|
+
@connection = IProto.get_connection(@host, @port, :block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def close_connection
|
8
|
+
@connection.close
|
9
|
+
end
|
10
|
+
|
11
|
+
def _send_request(request_type, body, cb)
|
12
|
+
cb.call @connection.send_request(request_type, body)
|
13
|
+
end
|
14
|
+
|
15
|
+
module CommonSpaceBlockingMethods
|
16
|
+
def _block_cb
|
17
|
+
@_block_cb ||= method(:_raise_or_return)
|
18
|
+
end
|
19
|
+
|
20
|
+
def all_by_pks(pks, opts={})
|
21
|
+
all_by_pks_cb(pks, _block_cb, opts)
|
22
|
+
end
|
23
|
+
|
24
|
+
def by_pk(pk)
|
25
|
+
by_pk_cb(pk, _block_cb)
|
26
|
+
end
|
27
|
+
|
28
|
+
def insert(tuple, opts={})
|
29
|
+
insert_cb(tuple, _block_cb, opts)
|
30
|
+
end
|
31
|
+
|
32
|
+
def replace(tuple, opts={})
|
33
|
+
replace_cb(tuple, _block_cb, opts)
|
34
|
+
end
|
35
|
+
|
36
|
+
def update(pk, operations, opts={})
|
37
|
+
update_cb(pk, operations, _block_cb, opts)
|
38
|
+
end
|
39
|
+
|
40
|
+
def delete(pk, opts={})
|
41
|
+
delete_cb(pk, _block_cb, opts)
|
42
|
+
end
|
43
|
+
|
44
|
+
def invoke(func_name, values = [], opts = {})
|
45
|
+
invoke_cb(func_name, values, _block_cb, opts)
|
46
|
+
end
|
47
|
+
|
48
|
+
def call(func_name, values = [], opts = {})
|
49
|
+
call_cb(func_name, values, _block_cb, opts)
|
50
|
+
end
|
51
|
+
|
52
|
+
def ping
|
53
|
+
ping_cb(_block_cb)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class SpaceArray < ::Tarantool::SpaceArray
|
58
|
+
include CommonSpaceBlockingMethods
|
59
|
+
|
60
|
+
def all_by_key(index_no, key, opts={})
|
61
|
+
all_by_key_cb(index_no, key, _block_cb, opts)
|
62
|
+
end
|
63
|
+
|
64
|
+
def first_by_key(index_no, key)
|
65
|
+
first_by_key_cb(index_no, key, _block_cb)
|
66
|
+
end
|
67
|
+
|
68
|
+
def all_by_keys(index_no, keys, opts={})
|
69
|
+
all_by_keys_cb(index_no, keys, _block_cb, opts)
|
70
|
+
end
|
71
|
+
|
72
|
+
def select(index_no, keys, offset=0, limit=-1)
|
73
|
+
select_cb(index_no, keys, offset, limit, _block_cb)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class SpaceHash < ::Tarantool::SpaceHash
|
78
|
+
include CommonSpaceBlockingMethods
|
79
|
+
|
80
|
+
def all(keys, opts = {})
|
81
|
+
all_cb(keys, _block_cb, opts)
|
82
|
+
end
|
83
|
+
|
84
|
+
def first(key)
|
85
|
+
first_cb(key, _block_cb)
|
86
|
+
end
|
87
|
+
|
88
|
+
def select(keys, offset=0, limit=-1)
|
89
|
+
select_cb(keys, offset, limit, _block_cb)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class Query < ::Tarantool::Query
|
94
|
+
def select(space_no, index_no, keys, offset, limit, opts={})
|
95
|
+
select_cb(space_no, index_no, keys, offset, limit, _block_cb, opts)
|
96
|
+
end
|
97
|
+
|
98
|
+
def all(space_no, index_no, keys, opts={})
|
99
|
+
all_cb(space_no, index_no, keys, _block_cb, opts)
|
100
|
+
end
|
101
|
+
|
102
|
+
def first(space_no, index_no, key, opts={})
|
103
|
+
first_cb(space_no, index_no, key, _block_cb, opts)
|
104
|
+
end
|
105
|
+
|
106
|
+
def insert(space_no, tuple, opts={})
|
107
|
+
insert_cb(space_no, tuple, _block_cb, opts)
|
108
|
+
end
|
109
|
+
|
110
|
+
def replace(space_no, tuple, opts={})
|
111
|
+
replace_cb(space_no, tuple, _block_cb, opts)
|
112
|
+
end
|
113
|
+
|
114
|
+
def update(space_no, pk, operation, opts={})
|
115
|
+
update_cb(space_no, pk, operation, _block_cb, opts)
|
116
|
+
end
|
117
|
+
|
118
|
+
def delete(space_no, pk, opts={})
|
119
|
+
delete_cb(space_no, pk, _block_cb, opts)
|
120
|
+
end
|
121
|
+
|
122
|
+
def invoke(func_name, values, opts={})
|
123
|
+
invoke_cb(func_name, values, _block_cb, opts)
|
124
|
+
end
|
125
|
+
|
126
|
+
def call(func_name, values, opts={})
|
127
|
+
call_cb(func_name, values, _block_cb, opts)
|
128
|
+
end
|
129
|
+
|
130
|
+
def ping
|
131
|
+
ping_cb(_block_cb)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|