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.
Files changed (68) hide show
  1. data/Gemfile +14 -12
  2. data/LICENSE +4 -4
  3. data/README.md +15 -9
  4. data/Rakefile +5 -129
  5. data/lib/tarantool/base_record.rb +288 -0
  6. data/lib/tarantool/block_db.rb +135 -0
  7. data/lib/tarantool/callback_db.rb +47 -0
  8. data/lib/tarantool/core-ext.rb +12 -0
  9. data/lib/tarantool/em_db.rb +37 -0
  10. data/lib/tarantool/exceptions.rb +52 -7
  11. data/lib/tarantool/fiber_db.rb +152 -0
  12. data/lib/tarantool/light_record.rb +68 -0
  13. data/lib/tarantool/query.rb +127 -0
  14. data/lib/tarantool/record/select.rb +59 -0
  15. data/lib/tarantool/record.rb +93 -282
  16. data/lib/tarantool/request.rb +351 -52
  17. data/lib/tarantool/response.rb +108 -45
  18. data/lib/tarantool/serializers/bson.rb +2 -2
  19. data/lib/tarantool/serializers.rb +32 -4
  20. data/lib/tarantool/space_array.rb +153 -0
  21. data/lib/tarantool/space_hash.rb +262 -0
  22. data/lib/tarantool/util.rb +182 -0
  23. data/lib/tarantool/version.rb +3 -0
  24. data/lib/tarantool.rb +79 -29
  25. data/test/box.pid +1 -0
  26. data/test/helper.rb +164 -0
  27. data/test/run_all.rb +3 -0
  28. data/test/shared_query.rb +73 -0
  29. data/test/shared_record.rb +474 -0
  30. data/test/shared_space_array.rb +284 -0
  31. data/test/shared_space_hash.rb +239 -0
  32. data/test/tarant/init.lua +22 -0
  33. data/test/tarantool.cfg +62 -0
  34. data/test/tarantool.log +6 -0
  35. data/{spec/tarantool.cfg → test/tarantool_repl.cfg} +11 -5
  36. data/test/test_light_record.rb +48 -0
  37. data/test/test_query_block.rb +6 -0
  38. data/test/test_query_fiber.rb +7 -0
  39. data/test/test_record.rb +88 -0
  40. data/{spec/tarantool/composite_primary_key_spec.rb → test/test_record_composite_pk.rb} +5 -7
  41. data/test/test_space_array_block.rb +6 -0
  42. data/test/test_space_array_callback.rb +255 -0
  43. data/test/test_space_array_callback_nodef.rb +190 -0
  44. data/test/test_space_array_fiber.rb +7 -0
  45. data/test/test_space_hash_block.rb +6 -0
  46. data/test/test_space_hash_fiber.rb +7 -0
  47. metadata +78 -55
  48. data/Gemfile.lock +0 -54
  49. data/examples/em_simple.rb +0 -16
  50. data/examples/record.rb +0 -68
  51. data/examples/simple.rb +0 -13
  52. data/lib/tarantool/requests/call.rb +0 -20
  53. data/lib/tarantool/requests/delete.rb +0 -18
  54. data/lib/tarantool/requests/insert.rb +0 -19
  55. data/lib/tarantool/requests/ping.rb +0 -16
  56. data/lib/tarantool/requests/select.rb +0 -22
  57. data/lib/tarantool/requests/update.rb +0 -35
  58. data/lib/tarantool/requests.rb +0 -19
  59. data/lib/tarantool/serializers/integer.rb +0 -14
  60. data/lib/tarantool/serializers/string.rb +0 -14
  61. data/lib/tarantool/space.rb +0 -39
  62. data/spec/helpers/let.rb +0 -11
  63. data/spec/helpers/truncate.rb +0 -12
  64. data/spec/spec_helper.rb +0 -21
  65. data/spec/tarantool/em_spec.rb +0 -22
  66. data/spec/tarantool/record_spec.rb +0 -316
  67. data/spec/tarantool/request_spec.rb +0 -103
  68. data/tarantool.gemspec +0 -69
@@ -0,0 +1,153 @@
1
+ require 'tarantool/util'
2
+ require 'tarantool/request'
3
+ require 'tarantool/response'
4
+ require 'tarantool/serializers'
5
+ require 'tarantool/core-ext'
6
+
7
+ module Tarantool
8
+ class SpaceArray
9
+ include Request
10
+ include Util::Array
11
+
12
+ def initialize(tarantool, space_no, fields, indexes)
13
+ @tarantool = tarantool
14
+ @space_no = space_no
15
+
16
+ fields = ([*fields].empty? ? TYPES_AUTO : fields).dup
17
+ tail_size = Integer === fields.last ? fields.pop : 1
18
+ fields.map!{|type| check_type(type)}
19
+ fields << tail_size
20
+ @fields = fields
21
+
22
+ indexes = [*indexes].map{|ind| frozen_array(ind) }.freeze
23
+ if !indexes.empty?
24
+ @index_fields = indexes
25
+ @indexes = _map_indexes(indexes)
26
+ else
27
+ @index_fields = nil
28
+ @indexes = [TYPES_FALLBACK]
29
+ end
30
+ end
31
+
32
+ def _map_indexes(indexes)
33
+ indexes.map do |index|
34
+ (index.map do |i|
35
+ unless (field = @fields[i]) && !field.is_a?(Integer)
36
+ raise ArgumentError, "Wrong index field number: #{index} #{i}"
37
+ end
38
+ field
39
+ end << :error).freeze
40
+ end.freeze
41
+ end
42
+
43
+ def _send_request(type, body, cb)
44
+ @tarantool._send_request(type, body, cb)
45
+ end
46
+
47
+ def all_by_pks_cb(keys, cb, opts={})
48
+ all_by_keys_cb(0, keys, cb, opts)
49
+ end
50
+
51
+ def by_pk_cb(pk, cb)
52
+ first_by_key_cb(0, pk, cb)
53
+ end
54
+
55
+ def all_by_key_cb(index_no, key, cb, opts={})
56
+ select_cb(index_no, [key], opts[:offset] || 0, opts[:limit] || -1, cb)
57
+ end
58
+
59
+ def first_by_key_cb(index_no, key, cb)
60
+ select_cb(index_no, [key], 0, :first, cb)
61
+ end
62
+
63
+ def all_by_keys_cb(index_no, keys, cb, opts = {})
64
+ select_cb(index_no, keys, opts[:offset] || 0, opts[:limit] || -1, cb)
65
+ end
66
+
67
+ def _fix_index_fields(index_fields, keys)
68
+ sorted = index_fields.sort
69
+ if index_no = @index_fields.index{|fields| fields.take(index_fields.size).sort == sorted}
70
+ real_fields = @index_fields[index_no]
71
+ permutation = index_fields.map{|i| real_fields.index(i)}
72
+ keys = [*keys].map{|v| [*v].values_at(*permutation)}
73
+ [index_no, keys]
74
+ end
75
+ end
76
+
77
+ def select_cb(index_no, keys, offset, limit, cb)
78
+ if Array === index_no
79
+ raise ArgumentError, "Has no defined indexes to search index #{index_no}" unless @index_fields
80
+ index_fields = index_no
81
+ index_no = @index_fields.index{|fields| fields.take(index_fields.size) == index_fields}
82
+ unless index_no || index_fields.size == 1
83
+ index_no, keys = _fix_index_fields(index_fields, keys)
84
+ unless index_no
85
+ raise(ArgumentError, "Not found index with field numbers #{index_no}, " +
86
+ "(defined indexes #{@index_fields})")
87
+ end
88
+ end
89
+ end
90
+ if @index_fields
91
+ unless index_types = @indexes[index_no]
92
+ raise ArgumentError, "No index ##{index_no}"
93
+ end
94
+ else
95
+ index_types = _detect_types([*[*keys][0]])
96
+ end
97
+
98
+ _select(@space_no, index_no, offset, limit, keys, cb, @fields, index_types)
99
+ end
100
+
101
+ def insert_cb(tuple, cb, opts = {})
102
+ _insert(@space_no, BOX_ADD, tuple, @fields, cb, opts[:return_tuple])
103
+ end
104
+
105
+ def replace_cb(tuple, cb, opts = {})
106
+ _insert(@space_no, BOX_REPLACE, tuple, @fields, cb, opts[:return_tuple])
107
+ end
108
+
109
+ def update_cb(pk, operations, cb, opts = {})
110
+ _update(@space_no, pk, operations, @fields,
111
+ @indexes[0] || _detect_types([*pk]),
112
+ cb, opts[:return_tuple])
113
+ end
114
+
115
+ def delete_cb(pk, cb, opts = {})
116
+ _delete(@space_no, pk, @fields,
117
+ @indexes[0] || _detect_types([*pk]),
118
+ cb, opts[:return_tuple])
119
+ end
120
+
121
+ def invoke_cb(func_name, values, cb, opts = {})
122
+ values, opts = _space_call_fix_values(values, @space_no, opts)
123
+ _call(func_name, values, cb, opts)
124
+ end
125
+
126
+ def call_cb(func_name, values, cb, opts = {})
127
+ values, opts = _space_call_fix_values(values, @space_no, opts)
128
+
129
+ opts[:return_tuple] = true if opts[:return_tuple].nil?
130
+ opts[:returns] ||= @fields if opts[:return_tuple]
131
+
132
+ _call(func_name, values, cb, opts)
133
+ end
134
+
135
+ include CommonSpaceBlockMethods
136
+ # callback with block api
137
+ def all_by_key_blk(index_no, key, opts={}, &block)
138
+ all_by_key_cb(index_no, key, block, opts)
139
+ end
140
+
141
+ def first_by_key_blk(index_no, key, &block)
142
+ first_by_key_cb(index_no, key, block)
143
+ end
144
+
145
+ def all_by_keys_blk(index_no, keys, opts={}, &block)
146
+ all_by_keys_cb(index_no, keys, block, opts)
147
+ end
148
+
149
+ def select_blk(index_no, keys, offset=0, limit=-1, &block)
150
+ select_cb(index_no, keys, offset, limit, block)
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,262 @@
1
+ require 'tarantool/util'
2
+ require 'tarantool/request'
3
+ require 'tarantool/response'
4
+ require 'tarantool/core-ext'
5
+
6
+ module Tarantool
7
+ class SpaceHash
8
+ include Request
9
+
10
+ def initialize(tarantool, space_no, fields_def, indexes)
11
+ @tarantool = tarantool
12
+ @space_no = space_no
13
+
14
+ field_to_pos = {}
15
+ field_to_type = {}
16
+ field_types = []
17
+ i = 0
18
+ last_type = nil
19
+ for k, t in fields_def
20
+ k = k.to_sym
21
+ unless k == :_tail
22
+ raise ArgumentError, ":_tail field should be defined last" if field_to_pos[:_tail]
23
+ t = check_type(t)
24
+ last_type = t
25
+ field_to_pos[k] = i
26
+ field_to_type[k] = t
27
+ field_types << t
28
+ else
29
+ t = [*t].map{|tt| check_type(tt)}
30
+ field_to_pos[:_tail] = i
31
+ field_to_type[:_tail] = t
32
+ field_types.concat t
33
+ field_types << t.size
34
+ end
35
+ i += 1
36
+ end
37
+ field_to_pos[:_tail] ||= i
38
+ field_to_type[:_tail] ||= [last_type]
39
+ @field_to_pos = field_to_pos
40
+ @field_to_type = field_to_type
41
+ @field_names = field_to_pos.keys
42
+ @field_types = field_types
43
+ @tail_pos = field_to_pos[:_tail]
44
+ @tail_size = field_to_type[:_tail].size
45
+
46
+ @index_fields = [*indexes].map{|ind| [*ind].map{|fld| fld.to_sym}.freeze}.freeze
47
+ @indexes = _map_indexes(@index_fields)
48
+ @translators = [TranslateToHash.new(@field_names - [:_tail], @tail_size)].freeze
49
+ end
50
+
51
+ def _add_translator(v)
52
+ @translators += [v]
53
+ end
54
+
55
+ def with_translator(cb = nil, &block)
56
+ copy = dup
57
+ copy._add_translator(cb || block)
58
+ copy
59
+ end
60
+
61
+ def _map_indexes(indexes)
62
+ indexes.map do |index|
63
+ (index.map do |name|
64
+ @field_to_type[name.to_sym] or raise "Wrong index field name: #{index} #{name}"
65
+ end << :error).freeze
66
+ end.freeze
67
+ end
68
+
69
+ def _send_request(type, body, cb)
70
+ @tarantool._send_request(type, body, cb)
71
+ end
72
+
73
+ def select_cb(keys, offset, limit, cb)
74
+ index_names = Hash === keys ? keys.keys : keys.first.keys
75
+ index_no = @index_fields.index{|fields|
76
+ fields.take(index_names.size) == index_names
77
+ }
78
+ unless index_no
79
+ index_names.sort!
80
+ index_no = @index_fields.index{|fields|
81
+ fields.take(index_names.size).sort == index_names
82
+ }
83
+ raise ArgumentError, "Could not find index for keys #{index_names}" unless index_no
84
+ index_names = @index_fields[index_no].take(index_names.size)
85
+ end
86
+ index_types = @indexes[index_no]
87
+
88
+ unless Hash === keys
89
+ keys = keys.map{|key| key.values_at(*index_names)}
90
+ else
91
+ keys = keys.values_at(*index_names)
92
+ if keys.all?{|v| Array === v}
93
+ if (max_size = keys.map{|v| v.size}.max) > 1
94
+ keys.map!{|v| v.size == max_size ? v :
95
+ v.size == 1 ? v*max_size :
96
+ raise(ArgumentError, "size of array keys ought to be 1 or equal to others")
97
+ }
98
+ end
99
+ keys = keys.transpose
100
+ else
101
+ keys = [keys]
102
+ end
103
+ end
104
+
105
+ _select(@space_no, index_no, offset, limit, keys, cb, @field_types, index_types, @translators)
106
+ end
107
+
108
+ def all_cb(keys, cb, opts = {})
109
+ select_cb(keys, opts[:offset] || 0, opts[:limit] || -1, cb)
110
+ end
111
+
112
+ def first_cb(key, cb)
113
+ select_cb([key], 0, :first, cb)
114
+ end
115
+
116
+ def all_by_pks_cb(keys, cb, opts={})
117
+ keys = [*keys].map{|key| _prepare_pk(key)}
118
+ _select(@space_no, 0,
119
+ opts[:offset] || 0, opts[:limit] || -1,
120
+ keys, cb, @field_types, @indexes[0], @translators)
121
+ end
122
+
123
+ def by_pk_cb(key_array, cb)
124
+ key_array = _prepare_pk(key_array)
125
+ _select(@space_no, 0, 0, :first, [key_array], cb, @field_types, @indexes[0], @translators)
126
+ end
127
+
128
+ def _prepare_tuple(tuple)
129
+ unless (exc = (tuple.keys - @field_names)).empty?
130
+ raise ArgumentError, "wrong keys #{exc} for tuple"
131
+ end
132
+ tuple_ar = tuple.values_at(*@field_names)
133
+ case tail = tuple_ar.pop
134
+ when Array
135
+ tail = tail.flatten(1) if @tail_size > 1
136
+ tuple_ar.concat tail
137
+ when nil
138
+ else
139
+ raise ArgumentError, "_tail ought to be an array, but it == #{tail.inspect}"
140
+ end
141
+ tuple_ar
142
+ end
143
+
144
+ def insert_cb(tuple, cb, opts = {})
145
+ _insert(@space_no, BOX_ADD, _prepare_tuple(tuple),
146
+ @field_types, cb, opts[:return_tuple], @translators)
147
+ end
148
+
149
+ def replace_cb(tuple, cb, opts = {})
150
+ _insert(@space_no, BOX_REPLACE, _prepare_tuple(tuple),
151
+ @field_types, cb, opts[:return_tuple], @translators)
152
+ end
153
+
154
+ def _prepare_pk(pk)
155
+ if Hash === pk
156
+ pk_fields = pk.keys
157
+ unless (pindex = @index_fields[0]) == pk_fields
158
+ if !(exc = (pk_fields - pindex)).empty?
159
+ raise ArgumentError, "Wrong keys #{exc} for primary index"
160
+ elsif !(exc = (pindex - pk_fields)).empty?
161
+ raise ArgumentError, "you should provide values for all keys of primary index (missed #{exc})"
162
+ end
163
+ end
164
+ pk.values_at *pindex
165
+ else
166
+ [*pk]
167
+ end
168
+ end
169
+
170
+ def update_cb(pk, operations, cb, opts = {})
171
+ pk = _prepare_pk(pk)
172
+ opers = []
173
+ operations.each{|oper|
174
+ if Array === oper[0]
175
+ oper = oper[0] + oper.drop(1)
176
+ elsif Array === oper[1] && oper.size == 2 && UPDATE_OPS[oper[1][0]]
177
+ oper = [oper[0]] + oper[1]
178
+ end
179
+ case oper[0]
180
+ when Integer
181
+ opers << oper[1..-1].unshift(oper[0] + @tail_pos)
182
+ when :_tail
183
+ if UPDATE_OPS[oper[1]] == 0
184
+ tail = oper[2]
185
+ unless Array === tail[0] && @tail_size > 1
186
+ tail.each_with_index{|val, i| opers << [i + @tail_pos, :set, val]}
187
+ else
188
+ tail.each_with_index{|vals, i|
189
+ vals.each_with_index{|val, j|
190
+ opers << [i*@tail_size + j + @tail_pos, :set, val]
191
+ }
192
+ }
193
+ end
194
+ else
195
+ raise ArgumentError, "_tail update should be array with operations" unless Array === oper[1] && Array === oper[1][0]
196
+ if @tail_size == 1 || !(Array === oper[1][0][0])
197
+ oper[1].each_with_index{|op, i| opers << [i + @tail_pos, op]}
198
+ else
199
+ oper[1].each_with_index{|ops, i|
200
+ ops.each_with_index{|op, j|
201
+ opers << [i*@tail_size + j + @tail_pos, op]
202
+ }
203
+ }
204
+ end
205
+ end
206
+ else
207
+ opers << oper[1..-1].unshift(
208
+ @field_to_pos[oper[0]] || raise(ArgumentError, "Not defined field name #{oper[0]}")
209
+ )
210
+ end
211
+ }
212
+ _update(@space_no, pk, opers, @field_types,
213
+ @indexes[0], cb, opts[:return_tuple], @translators)
214
+ end
215
+
216
+ def delete_cb(pk, cb, opts = {})
217
+ _delete(@space_no, _prepare_pk(pk), @field_types,
218
+ @indexes[0], cb, opts[:return_tuple], @translators)
219
+ end
220
+
221
+ def invoke_cb(func_name, values, cb, opts = {})
222
+ values, opts = _space_call_fix_values(values, @space_no, opts)
223
+ _call(func_name, values, cb, opts)
224
+ end
225
+
226
+ def call_cb(func_name, values, cb, opts = {})
227
+ values, opts = _space_call_fix_values(values, @space_no, opts)
228
+
229
+ opts[:return_tuple] = true if opts[:return_tuple].nil?
230
+ if opts[:return_tuple]
231
+ if opts[:returns]
232
+ if Hash === opts[:returns]
233
+ opts[:returns], *opts[:translators] =
234
+ _parse_hash_definition(opts[:returns])
235
+ end
236
+ else
237
+ types = @field_to_type.values
238
+ types << [*types.last].size
239
+ types.flatten!
240
+ opts[:returns] = types.flatten
241
+ opts[:translators] = @translators
242
+ end
243
+ end
244
+
245
+ _call(func_name, values, cb, opts)
246
+ end
247
+
248
+ include CommonSpaceBlockMethods
249
+ # callback with block api
250
+ def all_blk(keys, opts = {}, &block)
251
+ all_cb(keys, block, opts)
252
+ end
253
+
254
+ def first_blk(key, &block)
255
+ first_blk(key, block)
256
+ end
257
+
258
+ def select_blk(keys, offset=0, limit=1, &block)
259
+ select_cb(keys, offset, limit, block)
260
+ end
261
+ end
262
+ end
@@ -0,0 +1,182 @@
1
+ module Tarantool
2
+ module Util
3
+ module Packer
4
+ private
5
+ EMPTY = ''.freeze
6
+ def unpack_int16(data)
7
+ data.getbyte(0) + data.getbyte(1) * 256
8
+ end
9
+
10
+ def unpack_int16!(data)
11
+ int = data.getbyte(0) + data.getbyte(1) * 256
12
+ data[0, 2] = EMPTY
13
+ data
14
+ end
15
+
16
+ def unpack_int32(int)
17
+ (int.getbyte(0) + int.getbyte(1) * 256 +
18
+ int.getbyte(2) * 65536 + int.getbyte(3) * 16777216)
19
+ end
20
+
21
+ def unpack_int32!(data)
22
+ int = (data.getbyte(0) + data.getbyte(1) * 256 +
23
+ data.getbyte(2) * 65536 + data.getbyte(3) * 16777216)
24
+ data[0, 4] = EMPTY
25
+ int
26
+ end
27
+
28
+ def unpack_int64!(data)
29
+ int = (data.getbyte(0) + data.getbyte(1) * 256 +
30
+ data.getbyte(2) * 65536 + data.getbyte(3) * 16777216 +
31
+ data.getbyte(4) << 32 + data.getbyte(5) << 40 +
32
+ data.getbyte(6) << 48 + data.getbyte(7) << 56
33
+ )
34
+ data[0, 8] = EMPTY
35
+ int
36
+ end
37
+
38
+ def unpack_int64(data)
39
+ data.getbyte(0) + data.getbyte(1) * 256 +
40
+ data.getbyte(2) * 65536 + data.getbyte(3) * 16777216 +
41
+ data.getbyte(4) << 32 + data.getbyte(5) << 40 +
42
+ data.getbyte(6) << 48 + data.getbyte(7) << 56
43
+ end
44
+
45
+ def ber_size(int)
46
+ int < 128 ? 1 :
47
+ int < 16384 ? 2 :
48
+ int < 2097153 ? 3 :
49
+ int < 268435456 ? 4 : 5
50
+ end
51
+
52
+ def unpack_ber!(data)
53
+ res = 0
54
+ pos = 0
55
+ while true
56
+ if (byte = data.getbyte(pos)) <= 127
57
+ res += byte
58
+ break
59
+ else
60
+ res = (res + (byte - 128)) * 128
61
+ pos += 1
62
+ end
63
+ end
64
+ data[0, pos+1] = EMPTY
65
+ res
66
+ end
67
+ end
68
+
69
+ module TailGetter
70
+ private
71
+ def get_tail_item(array, index, tail)
72
+ tail == 1 ?
73
+ array.last :
74
+ array[array.size - tail + (index - array.size) % tail]
75
+ end
76
+ end
77
+
78
+ module Array
79
+ def frozen_array(obj)
80
+ (Array === obj ? obj.dup : [*obj]).freeze
81
+ end
82
+ end
83
+
84
+ class AutoType
85
+ include Packer
86
+ include Comparable
87
+
88
+ attr_reader :data
89
+ def initialize(data)
90
+ @data = data
91
+ end
92
+
93
+ def to_int
94
+ case @data.bytesize
95
+ when 8
96
+ unpack_int64(@data)
97
+ when 4
98
+ unpack_int32(@data)
99
+ when 2
100
+ unpack_int16(@data)
101
+ else
102
+ raise ValueError, "Bad field size #{field_size} for integer field ##{i}"
103
+ end
104
+ end
105
+ alias to_i to_int
106
+
107
+ def coerce(oth)
108
+ case oth
109
+ when Numeric
110
+ [oth, to_i]
111
+ when String
112
+ [oth, @data]
113
+ end
114
+ end
115
+
116
+ alias to_str data
117
+ alias to_s data
118
+ def inspect
119
+ "<#{self.class.name} data=#{@data.inspect}>"
120
+ end
121
+
122
+ def ==(oth)
123
+ case oth
124
+ when Numeric
125
+ to_i == oth
126
+ when String
127
+ @data == oth
128
+ when AutoType
129
+ @data == oth.data
130
+ end
131
+ end
132
+ alias eql? ==
133
+
134
+ def <=>(oth)
135
+ case oth
136
+ when Numeric
137
+ to_i <=> oth
138
+ when String
139
+ @data <=> oth
140
+ when AutoType
141
+ @data <=> oth.data
142
+ end
143
+ end
144
+
145
+ def +(oth)
146
+ case oth
147
+ when Numeric
148
+ to_i + oth
149
+ when String
150
+ @data + oth
151
+ when AutoType
152
+ @data + oth.data
153
+ end
154
+ end
155
+
156
+ def -(oth) to_i - oth end
157
+ def *(oth) to_i * oth end
158
+ def /(oth) to_i / oth end
159
+ def %(oth) to_i % oth end
160
+ def **(oth) to_i ** oth end
161
+
162
+ def empty?; @data.empty? end
163
+ def bytesize; @data.bytesize end
164
+ def size; @data.size end
165
+ def length; @data.length end
166
+ def hash; @data.hash end
167
+ %w{[] sub gsub slice =~ match scan split upcase downcase bytes each_byte
168
+ byteslice capitalize casecmp center chars each_char chomp chop chr
169
+ codepoints each_codepoint count crypt delete dump each_line lines
170
+ empty? encode end_with? getbyte hex include? index intern to_sym
171
+ ljust lstrip succ next oct ord partition reverse rindex
172
+ rjust rpartition rstrip squeeze start_with? strip swapcase to_c to_f
173
+ to_r intern to_sym tr unpack upto}.each do |meth|
174
+ class_eval <<-EOF, __FILE__, __LINE__
175
+ def #{meth}(*args, &block)
176
+ @data.#{meth}(*args, &block)
177
+ end
178
+ EOF
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,3 @@
1
+ module Tarantool
2
+ VERSION = "0.3.0.7"
3
+ end