square-arel 2.0.9.20110222133018
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/.autotest +26 -0
- data/History.txt +105 -0
- data/MIT-LICENSE.txt +20 -0
- data/Manifest.txt +124 -0
- data/README.markdown +94 -0
- data/Rakefile +20 -0
- data/lib/arel.rb +39 -0
- data/lib/arel/attributes.rb +20 -0
- data/lib/arel/attributes/attribute.rb +18 -0
- data/lib/arel/compatibility/wheres.rb +33 -0
- data/lib/arel/crud.rb +37 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/deprecated.rb +4 -0
- data/lib/arel/expression.rb +4 -0
- data/lib/arel/expressions.rb +23 -0
- data/lib/arel/insert_manager.rb +34 -0
- data/lib/arel/nodes.rb +53 -0
- data/lib/arel/nodes/and.rb +6 -0
- data/lib/arel/nodes/as.rb +6 -0
- data/lib/arel/nodes/assignment.rb +6 -0
- data/lib/arel/nodes/avg.rb +6 -0
- data/lib/arel/nodes/between.rb +6 -0
- data/lib/arel/nodes/binary.rb +12 -0
- data/lib/arel/nodes/count.rb +13 -0
- data/lib/arel/nodes/delete_statement.rb +19 -0
- data/lib/arel/nodes/does_not_match.rb +6 -0
- data/lib/arel/nodes/equality.rb +9 -0
- data/lib/arel/nodes/except.rb +7 -0
- data/lib/arel/nodes/exists.rb +7 -0
- data/lib/arel/nodes/function.rb +18 -0
- data/lib/arel/nodes/greater_than.rb +6 -0
- data/lib/arel/nodes/greater_than_or_equal.rb +6 -0
- data/lib/arel/nodes/group.rb +6 -0
- data/lib/arel/nodes/grouping.rb +6 -0
- data/lib/arel/nodes/having.rb +6 -0
- data/lib/arel/nodes/in.rb +6 -0
- data/lib/arel/nodes/inner_join.rb +6 -0
- data/lib/arel/nodes/insert_statement.rb +19 -0
- data/lib/arel/nodes/intersect.rb +7 -0
- data/lib/arel/nodes/join.rb +13 -0
- data/lib/arel/nodes/less_than.rb +6 -0
- data/lib/arel/nodes/less_than_or_equal.rb +6 -0
- data/lib/arel/nodes/limit.rb +7 -0
- data/lib/arel/nodes/lock.rb +6 -0
- data/lib/arel/nodes/matches.rb +6 -0
- data/lib/arel/nodes/max.rb +6 -0
- data/lib/arel/nodes/min.rb +6 -0
- data/lib/arel/nodes/node.rb +44 -0
- data/lib/arel/nodes/not.rb +6 -0
- data/lib/arel/nodes/not_equal.rb +6 -0
- data/lib/arel/nodes/not_in.rb +6 -0
- data/lib/arel/nodes/offset.rb +7 -0
- data/lib/arel/nodes/on.rb +6 -0
- data/lib/arel/nodes/or.rb +6 -0
- data/lib/arel/nodes/ordering.rb +20 -0
- data/lib/arel/nodes/outer_join.rb +6 -0
- data/lib/arel/nodes/select_core.rb +26 -0
- data/lib/arel/nodes/select_statement.rb +22 -0
- data/lib/arel/nodes/sql_literal.rb +8 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/sum.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +13 -0
- data/lib/arel/nodes/top.rb +6 -0
- data/lib/arel/nodes/unary.rb +11 -0
- data/lib/arel/nodes/union.rb +7 -0
- data/lib/arel/nodes/union_all.rb +7 -0
- data/lib/arel/nodes/unqualified_column.rb +16 -0
- data/lib/arel/nodes/update_statement.rb +21 -0
- data/lib/arel/nodes/values.rb +14 -0
- data/lib/arel/predications.rb +183 -0
- data/lib/arel/relation.rb +6 -0
- data/lib/arel/select_manager.rb +237 -0
- data/lib/arel/sql/engine.rb +10 -0
- data/lib/arel/sql_literal.rb +4 -0
- data/lib/arel/table.rb +134 -0
- data/lib/arel/tree_manager.rb +36 -0
- data/lib/arel/update_manager.rb +49 -0
- data/lib/arel/visitors.rb +38 -0
- data/lib/arel/visitors/depth_first.rb +154 -0
- data/lib/arel/visitors/dot.rb +230 -0
- data/lib/arel/visitors/join_sql.rb +40 -0
- data/lib/arel/visitors/mssql.rb +16 -0
- data/lib/arel/visitors/mysql.rb +34 -0
- data/lib/arel/visitors/oracle.rb +116 -0
- data/lib/arel/visitors/order_clauses.rb +11 -0
- data/lib/arel/visitors/postgresql.rb +58 -0
- data/lib/arel/visitors/sqlite.rb +11 -0
- data/lib/arel/visitors/to_sql.rb +331 -0
- data/lib/arel/visitors/visitor.rb +27 -0
- data/lib/arel/visitors/where_sql.rb +9 -0
- data/square-arel.gemspec +36 -0
- data/test/attributes/test_attribute.rb +664 -0
- data/test/helper.rb +13 -0
- data/test/nodes/test_as.rb +16 -0
- data/test/nodes/test_count.rb +18 -0
- data/test/nodes/test_delete_statement.rb +14 -0
- data/test/nodes/test_equality.rb +74 -0
- data/test/nodes/test_insert_statement.rb +18 -0
- data/test/nodes/test_node.rb +33 -0
- data/test/nodes/test_not.rb +20 -0
- data/test/nodes/test_or.rb +22 -0
- data/test/nodes/test_select_core.rb +22 -0
- data/test/nodes/test_select_statement.rb +13 -0
- data/test/nodes/test_sql_literal.rb +52 -0
- data/test/nodes/test_sum.rb +12 -0
- data/test/nodes/test_update_statement.rb +18 -0
- data/test/support/fake_record.rb +91 -0
- data/test/test_activerecord_compat.rb +18 -0
- data/test/test_attributes.rb +46 -0
- data/test/test_crud.rb +69 -0
- data/test/test_delete_manager.rb +42 -0
- data/test/test_insert_manager.rb +125 -0
- data/test/test_select_manager.rb +659 -0
- data/test/test_table.rb +193 -0
- data/test/test_update_manager.rb +86 -0
- data/test/visitors/test_depth_first.rb +212 -0
- data/test/visitors/test_dot.rb +29 -0
- data/test/visitors/test_join_sql.rb +35 -0
- data/test/visitors/test_mssql.rb +18 -0
- data/test/visitors/test_mysql.rb +45 -0
- data/test/visitors/test_oracle.rb +147 -0
- data/test/visitors/test_postgres.rb +36 -0
- data/test/visitors/test_sqlite.rb +18 -0
- data/test/visitors/test_to_sql.rb +255 -0
- metadata +261 -0
data/test/helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'arel'
|
5
|
+
|
6
|
+
require 'support/fake_record'
|
7
|
+
Arel::Table.engine = Arel::Sql::Engine.new(FakeRecord::Base.new)
|
8
|
+
|
9
|
+
class Object
|
10
|
+
def must_be_like other
|
11
|
+
self.gsub(/\s+/, ' ').strip.must_equal other.gsub(/\s+/, ' ').strip
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module Nodes
|
5
|
+
describe 'As' do
|
6
|
+
describe '#as' do
|
7
|
+
it 'makes an AS node' do
|
8
|
+
attr = Table.new(:users)[:id]
|
9
|
+
as = attr.as(Arel.sql('foo'))
|
10
|
+
assert_equal attr, as.left
|
11
|
+
assert_equal 'foo', as.right
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Arel::Nodes::Count do
|
4
|
+
describe 'backwards compatibility' do
|
5
|
+
it 'must be an expression' do
|
6
|
+
Arel::Nodes::Count.new('foo').must_be_kind_of Arel::Expression
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "as" do
|
11
|
+
it 'should alias the count' do
|
12
|
+
table = Arel::Table.new :users
|
13
|
+
table[:id].count.as('foo').to_sql.must_be_like %{
|
14
|
+
COUNT("users"."id") AS foo
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Arel::Nodes::DeleteStatement do
|
4
|
+
describe "#clone" do
|
5
|
+
it "clones wheres" do
|
6
|
+
statement = Arel::Nodes::DeleteStatement.new
|
7
|
+
statement.wheres = %w[a b c]
|
8
|
+
|
9
|
+
dolly = statement.clone
|
10
|
+
dolly.wheres.must_equal statement.wheres
|
11
|
+
dolly.wheres.wont_be_same_as statement.wheres
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module Nodes
|
5
|
+
describe 'equality' do
|
6
|
+
# FIXME: backwards compat
|
7
|
+
describe 'backwards compat' do
|
8
|
+
describe 'operator' do
|
9
|
+
it 'returns :==' do
|
10
|
+
attr = Table.new(:users)[:id]
|
11
|
+
left = attr.eq(10)
|
12
|
+
left.operator.must_equal :==
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'operand1' do
|
17
|
+
it "should equal left" do
|
18
|
+
attr = Table.new(:users)[:id]
|
19
|
+
left = attr.eq(10)
|
20
|
+
left.left.must_equal left.operand1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'operand2' do
|
25
|
+
it "should equal right" do
|
26
|
+
attr = Table.new(:users)[:id]
|
27
|
+
left = attr.eq(10)
|
28
|
+
left.right.must_equal left.operand2
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'to_sql' do
|
33
|
+
it 'takes an engine' do
|
34
|
+
engine = FakeRecord::Base.new
|
35
|
+
engine.connection.extend Module.new {
|
36
|
+
attr_accessor :quote_count
|
37
|
+
def quote(*args) @quote_count += 1; super; end
|
38
|
+
def quote_column_name(*args) @quote_count += 1; super; end
|
39
|
+
def quote_table_name(*args) @quote_count += 1; super; end
|
40
|
+
}
|
41
|
+
engine.connection.quote_count = 0
|
42
|
+
|
43
|
+
attr = Table.new(:users)[:id]
|
44
|
+
test = attr.eq(10)
|
45
|
+
test.to_sql engine
|
46
|
+
engine.connection.quote_count.must_equal 2
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'or' do
|
52
|
+
it 'makes an OR node' do
|
53
|
+
attr = Table.new(:users)[:id]
|
54
|
+
left = attr.eq(10)
|
55
|
+
right = attr.eq(11)
|
56
|
+
node = left.or right
|
57
|
+
node.expr.left.must_equal left
|
58
|
+
node.expr.right.must_equal right
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'and' do
|
63
|
+
it 'makes and AND node' do
|
64
|
+
attr = Table.new(:users)[:id]
|
65
|
+
left = attr.eq(10)
|
66
|
+
right = attr.eq(11)
|
67
|
+
node = left.and right
|
68
|
+
node.left.must_equal left
|
69
|
+
node.right.must_equal right
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Arel::Nodes::InsertStatement do
|
4
|
+
describe "#clone" do
|
5
|
+
it "clones columns and values" do
|
6
|
+
statement = Arel::Nodes::InsertStatement.new
|
7
|
+
statement.columns = %w[a b c]
|
8
|
+
statement.values = %w[x y z]
|
9
|
+
|
10
|
+
dolly = statement.clone
|
11
|
+
dolly.columns.must_equal statement.columns
|
12
|
+
dolly.values.must_equal statement.values
|
13
|
+
|
14
|
+
dolly.columns.wont_be_same_as statement.columns
|
15
|
+
dolly.values.wont_be_same_as statement.values
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
class TestNode < MiniTest::Unit::TestCase
|
5
|
+
def test_all_nodes_are_nodes
|
6
|
+
Nodes.constants.map { |k|
|
7
|
+
Nodes.const_get(k)
|
8
|
+
}.grep(Class).each do |klass|
|
9
|
+
next if Nodes::SqlLiteral == klass
|
10
|
+
assert klass.ancestors.include?(Nodes::Node), klass.name
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_each
|
15
|
+
list = []
|
16
|
+
node = Nodes::Node.new
|
17
|
+
node.each { |n| list << n }
|
18
|
+
assert_equal [node], list
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_generator
|
22
|
+
list = []
|
23
|
+
node = Nodes::Node.new
|
24
|
+
node.each.each { |n| list << n }
|
25
|
+
assert_equal [node], list
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_enumerable
|
29
|
+
node = Nodes::Node.new
|
30
|
+
assert_kind_of Enumerable, node
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module Nodes
|
5
|
+
describe 'not' do
|
6
|
+
describe '#not' do
|
7
|
+
it 'makes a NOT node' do
|
8
|
+
attr = Table.new(:users)[:id]
|
9
|
+
left = attr.eq(10)
|
10
|
+
right = attr.eq(11)
|
11
|
+
node = left.or right
|
12
|
+
node.expr.left.must_equal left
|
13
|
+
node.expr.right.must_equal right
|
14
|
+
|
15
|
+
node.or(right).not
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module Nodes
|
5
|
+
describe 'or' do
|
6
|
+
describe '#or' do
|
7
|
+
it 'makes an OR node' do
|
8
|
+
attr = Table.new(:users)[:id]
|
9
|
+
left = attr.eq(10)
|
10
|
+
right = attr.eq(11)
|
11
|
+
node = left.or right
|
12
|
+
node.expr.left.must_equal left
|
13
|
+
node.expr.right.must_equal right
|
14
|
+
|
15
|
+
oror = node.or(right)
|
16
|
+
oror.expr.left.must_equal node
|
17
|
+
oror.expr.right.must_equal right
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Arel::Nodes::SelectCore do
|
4
|
+
describe "#clone" do
|
5
|
+
it "clones froms, projections and wheres" do
|
6
|
+
core = Arel::Nodes::SelectCore.new
|
7
|
+
core.froms = %w[a b c]
|
8
|
+
core.projections = %w[d e f]
|
9
|
+
core.wheres = %w[g h i]
|
10
|
+
|
11
|
+
dolly = core.clone
|
12
|
+
|
13
|
+
dolly.froms.must_equal core.froms
|
14
|
+
dolly.projections.must_equal core.projections
|
15
|
+
dolly.wheres.must_equal core.wheres
|
16
|
+
|
17
|
+
dolly.froms.wont_be_same_as core.froms
|
18
|
+
dolly.projections.wont_be_same_as core.projections
|
19
|
+
dolly.wheres.wont_be_same_as core.wheres
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Arel::Nodes::SelectStatement do
|
4
|
+
describe "#clone" do
|
5
|
+
it "clones cores" do
|
6
|
+
statement = Arel::Nodes::SelectStatement.new %w[a b c]
|
7
|
+
|
8
|
+
dolly = statement.clone
|
9
|
+
dolly.cores.must_equal statement.cores
|
10
|
+
dolly.cores.wont_be_same_as statement.cores
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module Nodes
|
5
|
+
describe 'sql literal' do
|
6
|
+
describe 'sql' do
|
7
|
+
it 'makes a sql literal node' do
|
8
|
+
sql = Arel.sql 'foo'
|
9
|
+
sql.must_be_kind_of Arel::Nodes::SqlLiteral
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'count' do
|
14
|
+
it 'makes a count node' do
|
15
|
+
node = SqlLiteral.new('*').count
|
16
|
+
viz = Visitors::ToSql.new Table.engine
|
17
|
+
viz.accept(node).must_be_like %{ COUNT(*) }
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'makes a distinct node' do
|
21
|
+
node = SqlLiteral.new('*').count true
|
22
|
+
viz = Visitors::ToSql.new Table.engine
|
23
|
+
viz.accept(node).must_be_like %{ COUNT(DISTINCT *) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'equality' do
|
28
|
+
it 'makes an equality node' do
|
29
|
+
node = SqlLiteral.new('foo').eq(1)
|
30
|
+
viz = Visitors::ToSql.new Table.engine
|
31
|
+
viz.accept(node).must_be_like %{ foo = 1 }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'grouped "or" equality' do
|
36
|
+
it 'makes a grouping node with an or node' do
|
37
|
+
node = SqlLiteral.new('foo').eq_any([1,2])
|
38
|
+
viz = Visitors::ToSql.new Table.engine
|
39
|
+
viz.accept(node).must_be_like %{ (foo = 1 OR foo = 2) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'grouped "and" equality' do
|
44
|
+
it 'makes a grouping node with an or node' do
|
45
|
+
node = SqlLiteral.new('foo').eq_all([1,2])
|
46
|
+
viz = Visitors::ToSql.new Table.engine
|
47
|
+
viz.accept(node).must_be_like %{ (foo = 1 AND foo = 2) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Arel::Nodes::UpdateStatement do
|
4
|
+
describe "#clone" do
|
5
|
+
it "clones wheres and values" do
|
6
|
+
statement = Arel::Nodes::UpdateStatement.new
|
7
|
+
statement.wheres = %w[a b c]
|
8
|
+
statement.values = %w[x y z]
|
9
|
+
|
10
|
+
dolly = statement.clone
|
11
|
+
dolly.wheres.must_equal statement.wheres
|
12
|
+
dolly.wheres.wont_be_same_as statement.wheres
|
13
|
+
|
14
|
+
dolly.values.must_equal statement.values
|
15
|
+
dolly.values.wont_be_same_as statement.values
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module FakeRecord
|
2
|
+
class Column < Struct.new(:name, :type)
|
3
|
+
end
|
4
|
+
|
5
|
+
class Connection
|
6
|
+
attr_reader :tables
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@tables = %w{ users photos developers }
|
10
|
+
@columns = {
|
11
|
+
'users' => [
|
12
|
+
Column.new('id', :integer),
|
13
|
+
Column.new('name', :string),
|
14
|
+
Column.new('bool', :boolean),
|
15
|
+
Column.new('created_at', :date),
|
16
|
+
]
|
17
|
+
}
|
18
|
+
@primary_keys = {
|
19
|
+
'users' => 'id'
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def primary_key name
|
24
|
+
@primary_keys[name.to_s]
|
25
|
+
end
|
26
|
+
|
27
|
+
def table_exists? name
|
28
|
+
@tables.include? name.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
def columns name, message = nil
|
32
|
+
@columns[name.to_s]
|
33
|
+
end
|
34
|
+
|
35
|
+
def quote_table_name name
|
36
|
+
"\"#{name.to_s}\""
|
37
|
+
end
|
38
|
+
|
39
|
+
def quote_column_name name
|
40
|
+
"\"#{name.to_s}\""
|
41
|
+
end
|
42
|
+
|
43
|
+
def quote thing, column = nil
|
44
|
+
if column && column.type == :integer
|
45
|
+
return 'NULL' if thing.nil?
|
46
|
+
return thing.to_i
|
47
|
+
end
|
48
|
+
|
49
|
+
case thing
|
50
|
+
when true
|
51
|
+
"'t'"
|
52
|
+
when false
|
53
|
+
"'f'"
|
54
|
+
when nil
|
55
|
+
'NULL'
|
56
|
+
when Numeric
|
57
|
+
thing
|
58
|
+
else
|
59
|
+
"'#{thing}'"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class ConnectionPool
|
65
|
+
class Spec < Struct.new(:config)
|
66
|
+
end
|
67
|
+
|
68
|
+
attr_reader :spec, :connection
|
69
|
+
|
70
|
+
def initialize
|
71
|
+
@spec = Spec.new(:adapter => 'america')
|
72
|
+
@connection = Connection.new
|
73
|
+
end
|
74
|
+
|
75
|
+
def with_connection
|
76
|
+
yield connection
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class Base
|
81
|
+
attr_accessor :connection_pool
|
82
|
+
|
83
|
+
def initialize
|
84
|
+
@connection_pool = ConnectionPool.new
|
85
|
+
end
|
86
|
+
|
87
|
+
def connection
|
88
|
+
connection_pool.connection
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|