tarantool 0.2.5 → 0.3.0.7

Sign up to get free protection for your applications and to get access to all the features.
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