rroonga 7.0.2 → 7.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +2 -2
- data/doc/text/news.md +46 -7
- data/ext/groonga/rb-grn-array.c +1 -272
- data/ext/groonga/rb-grn-column-cache.c +240 -0
- data/ext/groonga/rb-grn-column.c +1 -1
- data/ext/groonga/rb-grn-context.c +28 -4
- data/ext/groonga/rb-grn-expression.c +23 -1
- data/ext/groonga/rb-grn-object.c +44 -1
- data/ext/groonga/rb-grn-procedure.c +16 -1
- data/ext/groonga/rb-grn-query-logger.c +55 -6
- data/ext/groonga/rb-grn-table.c +170 -1
- data/ext/groonga/rb-grn-utils.c +21 -2
- data/ext/groonga/rb-grn.h +18 -3
- data/ext/groonga/rb-groonga.c +2 -1
- data/lib/groonga.rb +8 -5
- data/lib/groonga/column.rb +0 -5
- data/lib/groonga/database.rb +0 -10
- data/lib/groonga/index-column.rb +0 -10
- data/lib/groonga/query-logger.rb +1 -1
- data/rroonga-build.rb +6 -6
- data/rroonga.gemspec +1 -1
- data/test/groonga-test-utils.rb +5 -8
- data/test/test-array.rb +1 -131
- data/test/test-column-cache.rb +46 -0
- data/test/test-command-select.rb +36 -1
- data/test/test-context.rb +1 -2
- data/test/test-database.rb +16 -2
- data/test/test-logger.rb +13 -1
- data/test/test-procedure.rb +7 -1
- data/test/test-query-logger.rb +12 -1
- data/test/test-table-arrow.rb +193 -0
- data/test/test-table-offset-and-limit.rb +3 -1
- metadata +65 -64
- data/lib/groonga/statistic-measurer.rb +0 -37
- data/lib/groonga/table.rb +0 -25
- data/test/test-statistic-measurer.rb +0 -55
data/ext/groonga/rb-grn-utils.c
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
/* -*-
|
1
|
+
/* -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
2
2
|
/* vim: set sts=4 sw=4 ts=8 noet: */
|
3
3
|
/*
|
4
|
-
Copyright (C) 2009-
|
4
|
+
Copyright (C) 2009-2017 Kouhei Sutou <kou@clear-code.com>
|
5
5
|
|
6
6
|
This library is free software; you can redistribute it and/or
|
7
7
|
modify it under the terms of the GNU Lesser General Public
|
@@ -126,6 +126,25 @@ rb_grn_convert_to_array (VALUE object)
|
|
126
126
|
return rb_convert_type(object, RUBY_T_ARRAY, "Array", "to_ary");
|
127
127
|
}
|
128
128
|
|
129
|
+
VALUE
|
130
|
+
rb_grn_convert_to_path (VALUE object)
|
131
|
+
{
|
132
|
+
VALUE path;
|
133
|
+
|
134
|
+
path = rb_grn_check_convert_to_string(object);
|
135
|
+
if (NIL_P(path)) {
|
136
|
+
ID to_path;
|
137
|
+
CONST_ID(to_path, "to_path");
|
138
|
+
path = rb_check_funcall(object, to_path, 0, 0);
|
139
|
+
if (path == Qundef) {
|
140
|
+
path = object;
|
141
|
+
}
|
142
|
+
path = rb_grn_convert_to_string(path);
|
143
|
+
}
|
144
|
+
|
145
|
+
return path;
|
146
|
+
}
|
147
|
+
|
129
148
|
VALUE
|
130
149
|
rb_grn_check_convert_to_string (VALUE object)
|
131
150
|
{
|
data/ext/groonga/rb-grn.h
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/* -*- coding: utf-8; mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
2
2
|
/*
|
3
|
-
Copyright (C) 2009-
|
3
|
+
Copyright (C) 2009-2018 Kouhei Sutou <kou@clear-code.com>
|
4
4
|
Copyright (C) 2015-2017 Masafumi Yokoyama <yokoyama@clear-code.com>
|
5
5
|
|
6
6
|
This library is free software; you can redistribute it and/or
|
@@ -98,8 +98,8 @@ RB_GRN_BEGIN_DECLS
|
|
98
98
|
#endif
|
99
99
|
|
100
100
|
#define RB_GRN_MAJOR_VERSION 7
|
101
|
-
#define RB_GRN_MINOR_VERSION
|
102
|
-
#define RB_GRN_MICRO_VERSION
|
101
|
+
#define RB_GRN_MINOR_VERSION 1
|
102
|
+
#define RB_GRN_MICRO_VERSION 1
|
103
103
|
|
104
104
|
#define RB_GRN_OBJECT(object) ((RbGrnObject *)(object))
|
105
105
|
#define RB_GRN_NAMED_OBJECT(object) ((RbGrnNamedObject *)(object))
|
@@ -248,6 +248,18 @@ struct _RbGrnPlugin
|
|
248
248
|
grn_id id;
|
249
249
|
};
|
250
250
|
|
251
|
+
typedef struct _RbGrnColumnCache RbGrnColumnCache;
|
252
|
+
struct _RbGrnColumnCache
|
253
|
+
{
|
254
|
+
VALUE self;
|
255
|
+
grn_ctx *context;
|
256
|
+
VALUE rb_column;
|
257
|
+
grn_column_cache *column_cache;
|
258
|
+
grn_obj buffer;
|
259
|
+
grn_obj *range;
|
260
|
+
grn_obj *table;
|
261
|
+
};
|
262
|
+
|
251
263
|
RB_GRN_VAR grn_bool rb_grn_exited;
|
252
264
|
|
253
265
|
RB_GRN_VAR VALUE rb_eGrnError;
|
@@ -306,6 +318,7 @@ RB_GRN_VAR VALUE rb_cGrnIndex;
|
|
306
318
|
RB_GRN_VAR VALUE rb_mGrnRequestCanceler;
|
307
319
|
RB_GRN_VAR VALUE rb_mGrnRequestTimer;
|
308
320
|
RB_GRN_VAR VALUE rb_cGrnRequestTimerID;
|
321
|
+
RB_GRN_VAR VALUE rb_cGrnColumnCache;
|
309
322
|
|
310
323
|
void rb_grn_init_utils (VALUE mGrn);
|
311
324
|
void rb_grn_init_exception (VALUE mGrn);
|
@@ -369,6 +382,7 @@ void rb_grn_init_request_timer_id (VALUE mGrn);
|
|
369
382
|
void rb_grn_init_id (VALUE mGrn);
|
370
383
|
void rb_grn_init_name (VALUE mGrn);
|
371
384
|
void rb_grn_init_default_cache (VALUE mGrn);
|
385
|
+
void rb_grn_init_column_cache (VALUE mGrn);
|
372
386
|
|
373
387
|
VALUE rb_grn_rc_to_exception (grn_rc rc);
|
374
388
|
void rb_grn_rc_check (grn_rc rc,
|
@@ -404,6 +418,7 @@ grn_bool rb_grn_equal_string (const char *string1,
|
|
404
418
|
const char *string2);
|
405
419
|
VALUE rb_grn_convert_to_string (VALUE object);
|
406
420
|
VALUE rb_grn_convert_to_array (VALUE object);
|
421
|
+
VALUE rb_grn_convert_to_path (VALUE object);
|
407
422
|
VALUE rb_grn_check_convert_to_string (VALUE object);
|
408
423
|
VALUE rb_grn_check_convert_to_array (VALUE object);
|
409
424
|
VALUE rb_grn_check_convert_to_hash (VALUE object);
|
data/ext/groonga/rb-groonga.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/* -*- coding: utf-8; mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
2
2
|
/*
|
3
|
-
Copyright (C) 2009-
|
3
|
+
Copyright (C) 2009-2018 Kouhei Sutou <kou@clear-code.com>
|
4
4
|
Copyright (C) 2016 Masafumi Yokoyama <yokoyama@clear-code.com>
|
5
5
|
|
6
6
|
This library is free software; you can redistribute it and/or
|
@@ -257,4 +257,5 @@ Init_groonga (void)
|
|
257
257
|
rb_grn_init_id(mGrn);
|
258
258
|
rb_grn_init_name(mGrn);
|
259
259
|
rb_grn_init_default_cache(mGrn);
|
260
|
+
rb_grn_init_column_cache(mGrn);
|
260
261
|
}
|
data/lib/groonga.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
# Copyright (C) 2009-2013 Kouhei Sutou <kou@clear-code.com>
|
1
|
+
# Copyright (C) 2009-2018 Kouhei Sutou <kou@clear-code.com>
|
4
2
|
#
|
5
3
|
# This library is free software; you can redistribute it and/or
|
6
4
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -32,6 +30,13 @@ if local_groonga_bin_dir.exist?
|
|
32
30
|
end
|
33
31
|
|
34
32
|
prepend_path.call("PATH", File::PATH_SEPARATOR)
|
33
|
+
|
34
|
+
begin
|
35
|
+
require "ruby_installer/runtime"
|
36
|
+
rescue LoadError
|
37
|
+
else
|
38
|
+
RubyInstaller::Runtime.add_dll_directory(local_groonga_bin_dir.to_s)
|
39
|
+
end
|
35
40
|
end
|
36
41
|
|
37
42
|
require "groonga/geo-point"
|
@@ -91,9 +96,7 @@ module Groonga
|
|
91
96
|
end
|
92
97
|
|
93
98
|
require "groonga/context"
|
94
|
-
require "groonga/statistic-measurer"
|
95
99
|
require "groonga/database"
|
96
|
-
require "groonga/table"
|
97
100
|
require "groonga/column"
|
98
101
|
require "groonga/patricia-trie"
|
99
102
|
require "groonga/index-column"
|
data/lib/groonga/column.rb
CHANGED
@@ -17,11 +17,6 @@
|
|
17
17
|
|
18
18
|
module Groonga
|
19
19
|
class Column
|
20
|
-
def disk_usage
|
21
|
-
measurer = StatisticMeasurer.new
|
22
|
-
measurer.measure_disk_usage(path)
|
23
|
-
end
|
24
|
-
|
25
20
|
# @param [Groonga::Operator] operator (Groonga::Operator::MATCH)
|
26
21
|
# @return [Array<Groonga::IndexColumn>] Indexes on `column` which can
|
27
22
|
# execute `operator`.
|
data/lib/groonga/database.rb
CHANGED
@@ -44,16 +44,6 @@ module Groonga
|
|
44
44
|
paths
|
45
45
|
end
|
46
46
|
|
47
|
-
def disk_usage
|
48
|
-
return 0 if path.nil?
|
49
|
-
|
50
|
-
usage = 0
|
51
|
-
measurer = StatisticMeasurer.new
|
52
|
-
usage += measurer.measure_disk_usage(path)
|
53
|
-
usage += measurer.measure_disk_usage("%s.%07X" % [path, 0])
|
54
|
-
usage
|
55
|
-
end
|
56
|
-
|
57
47
|
def dump_index(output_directory)
|
58
48
|
each do |object|
|
59
49
|
next unless object.is_a?(Groonga::IndexColumn)
|
data/lib/groonga/index-column.rb
CHANGED
@@ -24,16 +24,6 @@ module Groonga
|
|
24
24
|
dumper = IndexColumnDumper.new(self, output_directory)
|
25
25
|
dumper.dump
|
26
26
|
end
|
27
|
-
|
28
|
-
def disk_usage
|
29
|
-
return 0 if path.nil?
|
30
|
-
|
31
|
-
usage = super
|
32
|
-
chunk_path = "#{path}.c"
|
33
|
-
measurer = StatisticMeasurer.new
|
34
|
-
usage += measurer.measure_disk_usage(chunk_path)
|
35
|
-
usage
|
36
|
-
end
|
37
27
|
end
|
38
28
|
|
39
29
|
class IndexColumnDumper
|
data/lib/groonga/query-logger.rb
CHANGED
data/rroonga-build.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2009-
|
1
|
+
# Copyright (C) 2009-2018 Kouhei Sutou <kou@clear-code.com>
|
2
2
|
# Copyright (C) 2015-2017 Masafumi Yokoyama <yokoyama@clear-code.com>
|
3
3
|
#
|
4
4
|
# This library is free software; you can redistribute it and/or
|
@@ -17,16 +17,16 @@
|
|
17
17
|
module RroongaBuild
|
18
18
|
module RequiredGroongaVersion
|
19
19
|
MAJOR = 7
|
20
|
-
MINOR =
|
21
|
-
MICRO =
|
20
|
+
MINOR = 1
|
21
|
+
MICRO = 1
|
22
22
|
VERSION = [MAJOR, MINOR, MICRO]
|
23
|
-
RELEASED_DATE = Time.utc(
|
23
|
+
RELEASED_DATE = Time.utc(2018, 1, 29)
|
24
24
|
end
|
25
25
|
|
26
26
|
module LatestGroongaVersion
|
27
27
|
MAJOR = 7
|
28
|
-
MINOR =
|
29
|
-
MICRO =
|
28
|
+
MINOR = 1
|
29
|
+
MICRO = 1
|
30
30
|
VERSION = [MAJOR, MINOR, MICRO]
|
31
31
|
end
|
32
32
|
|
data/rroonga.gemspec
CHANGED
@@ -89,7 +89,7 @@ Gem::Specification.new do |s|
|
|
89
89
|
s.add_development_dependency("test-unit", [">= 3.0.0"])
|
90
90
|
s.add_development_dependency("rake")
|
91
91
|
s.add_development_dependency("rake-compiler", [">= 0.9.5"])
|
92
|
-
s.add_development_dependency("rake-compiler-dock", [">= 0.6.
|
92
|
+
s.add_development_dependency("rake-compiler-dock", [">= 0.6.2"])
|
93
93
|
s.add_development_dependency("bundler")
|
94
94
|
s.add_development_dependency("yard")
|
95
95
|
s.add_development_dependency("packnga", [">= 1.0.0"])
|
data/test/groonga-test-utils.rb
CHANGED
@@ -68,12 +68,10 @@ module GroongaTestUtils
|
|
68
68
|
@dump_log = false
|
69
69
|
|
70
70
|
@log_path = @tmp_dir + "groonga.log"
|
71
|
-
|
72
|
-
Groonga::Logger.register(logger)
|
71
|
+
Groonga::Logger.path = @log_path.to_s
|
73
72
|
|
74
73
|
@query_log_path = @tmp_dir + "groonga-query.log"
|
75
|
-
|
76
|
-
Groonga::QueryLogger.register(query_logger, :all => true)
|
74
|
+
Groonga::QueryLogger.path = @query_log_path.to_s
|
77
75
|
end
|
78
76
|
|
79
77
|
def setup_tables_directory
|
@@ -130,11 +128,10 @@ module GroongaTestUtils
|
|
130
128
|
|
131
129
|
def teardown_log_path
|
132
130
|
return unless @dump_log
|
133
|
-
log_path
|
134
|
-
|
135
|
-
header = "--- log: #{log_path} ---"
|
131
|
+
if @log_path.exist?(log_path)
|
132
|
+
header = "--- log: #{@log_path} ---"
|
136
133
|
puts(header)
|
137
|
-
puts(
|
134
|
+
puts(@log_path.read)
|
138
135
|
puts("-" * header.length)
|
139
136
|
end
|
140
137
|
if @query_log_path.exist?
|
data/test/test-array.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2009-
|
1
|
+
# Copyright (C) 2009-2018 Kouhei Sutou <kou@clear-code.com>
|
2
2
|
#
|
3
3
|
# This library is free software; you can redistribute it and/or
|
4
4
|
# modify it under the terms of the GNU Lesser General Public
|
@@ -157,134 +157,4 @@ class ArrayTest < Test::Unit::TestCase
|
|
157
157
|
user_ids = users.each.collect(&:id)
|
158
158
|
assert_equal([1, 2, 3], user_ids)
|
159
159
|
end
|
160
|
-
|
161
|
-
class TestPushPull < self
|
162
|
-
def setup
|
163
|
-
@queue = Groonga::Array.create(:name => "Queue")
|
164
|
-
@queue.define_column("content", "ShortText")
|
165
|
-
end
|
166
|
-
|
167
|
-
def teardown
|
168
|
-
@queue = nil
|
169
|
-
end
|
170
|
-
|
171
|
-
def test_block?
|
172
|
-
Tempfile.open("output") do |output|
|
173
|
-
Tempfile.open("blocked_pull.rb") do |pull_rb|
|
174
|
-
pull_rb.puts(pull_rb_source(":block? => true"))
|
175
|
-
pull_rb.close
|
176
|
-
|
177
|
-
pid = spawn({}, RbConfig.ruby, pull_rb.path, :out => output.path)
|
178
|
-
sleep(0.5)
|
179
|
-
@queue.push do |record|
|
180
|
-
record.content = "The first record"
|
181
|
-
end
|
182
|
-
Process.waitpid(pid)
|
183
|
-
assert_equal(<<-EXPECTED_OUTPUT, output.read)
|
184
|
-
start
|
185
|
-
1, The first record
|
186
|
-
done
|
187
|
-
EXPECTED_OUTPUT
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
def test_not_block?
|
193
|
-
Tempfile.open("output") do |output|
|
194
|
-
Tempfile.open("not_blocked_pull.rb") do |pull_rb|
|
195
|
-
pull_rb.puts(pull_rb_source(":block? => false"))
|
196
|
-
pull_rb.close
|
197
|
-
|
198
|
-
pid = spawn({}, RbConfig.ruby, pull_rb.path, :out => output.path)
|
199
|
-
Process.waitpid(pid)
|
200
|
-
assert_equal(<<-EXPECTED_OUTPUT, output.read)
|
201
|
-
start
|
202
|
-
done
|
203
|
-
EXPECTED_OUTPUT
|
204
|
-
|
205
|
-
@queue.push do |record|
|
206
|
-
record.content = "The first record"
|
207
|
-
end
|
208
|
-
pid = spawn({}, RbConfig.ruby, pull_rb.path, :out => output.path)
|
209
|
-
Process.waitpid(pid)
|
210
|
-
output.rewind
|
211
|
-
assert_equal(<<-EXPECTED_OUTPUT, output.read)
|
212
|
-
start
|
213
|
-
1, The first record
|
214
|
-
done
|
215
|
-
EXPECTED_OUTPUT
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
def test_unblock
|
221
|
-
IO.pipe do |read_io, write_io|
|
222
|
-
Tempfile.open("not_blocked_pull.rb") do |pull_rb|
|
223
|
-
pull_rb.puts(pull_rb_source(":block? => true"))
|
224
|
-
pull_rb.close
|
225
|
-
|
226
|
-
pid = spawn({}, RbConfig.ruby, pull_rb.path, :out => write_io)
|
227
|
-
assert_equal("start", read_io.gets.chomp)
|
228
|
-
Process.kill(:TERM, pid)
|
229
|
-
@queue.unblock
|
230
|
-
assert_equal("SIGTERM", read_io.gets.chomp)
|
231
|
-
wait_finished(pid)
|
232
|
-
write_io.close
|
233
|
-
assert_equal("done", read_io.read.chomp)
|
234
|
-
end
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
private
|
239
|
-
def pull_rb_source(options)
|
240
|
-
base_dir = File.expand_path(File.join(File.dirname(__FILE__), ".."))
|
241
|
-
<<-CODE
|
242
|
-
$stdout.sync = true
|
243
|
-
|
244
|
-
base_dir = #{base_dir.dump}
|
245
|
-
|
246
|
-
groonga_command_dir = File.join(base_dir, "..", "groonga-command")
|
247
|
-
if File.exist?(groonga_command_dir)
|
248
|
-
$LOAD_PATH.unshift(File.join(groonga_command_dir, "lib"))
|
249
|
-
end
|
250
|
-
|
251
|
-
groonga_client_dir = File.join(base_dir, "..", "groonga-client")
|
252
|
-
if File.exist?(groonga_client_dir)
|
253
|
-
$LOAD_PATH.unshift(File.join(groonga_client_dir, "lib"))
|
254
|
-
end
|
255
|
-
|
256
|
-
ext_dir = File.join(base_dir, "ext", "groonga")
|
257
|
-
lib_dir = File.join(base_dir, "lib")
|
258
|
-
|
259
|
-
$LOAD_PATH.unshift(ext_dir)
|
260
|
-
$LOAD_PATH.unshift(lib_dir)
|
261
|
-
|
262
|
-
require "groonga"
|
263
|
-
|
264
|
-
trap(:TERM) do
|
265
|
-
puts("SIGTERM")
|
266
|
-
end
|
267
|
-
|
268
|
-
puts("start")
|
269
|
-
Groonga::Context.default.open_database(#{@database_path.to_s.dump}) do
|
270
|
-
queue = Groonga["Queue"]
|
271
|
-
queue.pull(#{options}) do |record|
|
272
|
-
puts([record.id, record.content].join(", "))
|
273
|
-
end
|
274
|
-
end
|
275
|
-
puts("done")
|
276
|
-
CODE
|
277
|
-
end
|
278
|
-
|
279
|
-
def wait_finished(pid)
|
280
|
-
n_retries = 3
|
281
|
-
n_retries.times do |i|
|
282
|
-
@queue.unblock
|
283
|
-
finished_pid = Process.waitpid(pid, Process::WNOHANG)
|
284
|
-
return if finished_pid
|
285
|
-
sleep((2 ** i) * 0.1)
|
286
|
-
end
|
287
|
-
Process.waitpid(pid)
|
288
|
-
end
|
289
|
-
end
|
290
160
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# Copyright (C) 2018 Kouhei Sutou <kou@clear-code.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License version 2.1 as published by the Free Software Foundation.
|
6
|
+
#
|
7
|
+
# This library is distributed in the hope that it will be useful,
|
8
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
10
|
+
# Lesser General Public License for more details.
|
11
|
+
#
|
12
|
+
# You should have received a copy of the GNU Lesser General Public
|
13
|
+
# License along with this library; if not, write to the Free Software
|
14
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
15
|
+
|
16
|
+
class ColumnCacheTest < Test::Unit::TestCase
|
17
|
+
include GroongaTestUtils
|
18
|
+
|
19
|
+
def setup
|
20
|
+
setup_database
|
21
|
+
|
22
|
+
setup_users_table
|
23
|
+
end
|
24
|
+
|
25
|
+
def setup_users_table
|
26
|
+
Groonga::Schema.define do |schema|
|
27
|
+
schema.create_table("Users", :type => :hash) do |table|
|
28
|
+
table.integer32("age")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
@users = context["Users"]
|
33
|
+
@users.add("alice", :age => 9)
|
34
|
+
@users.add("bob", :age => 19)
|
35
|
+
@users.add("chris", :age => 29)
|
36
|
+
|
37
|
+
@age = @users.column("age")
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_array_reference
|
41
|
+
Groonga::ColumnCache.open(@age) do |column_cache|
|
42
|
+
assert_equal(@users.collect(&:age),
|
43
|
+
@users.collect {|user| column_cache[user]})
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|