pg_ltree 1.1.6 → 1.1.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f2b26d6c59f672404d75047f8111c4279ba48997
4
- data.tar.gz: 01e1adf826c8d150d244102a3db4c374b18cefb4
2
+ SHA256:
3
+ metadata.gz: ba0cf379067eb0d7c24ff36828795d78b6cbbdc5b6738c45427868164940516d
4
+ data.tar.gz: c63501e14473075c2916567fdee20d974c09fe77d93c0553b9d16c185af436a1
5
5
  SHA512:
6
- metadata.gz: 2594e997cde3d85f712b3a635c55a00384e3191e34b86c0463677a1df7e57523fab057d24f6d5f4009bcc9ef5f9b999f462bc57bf6501ec30c6ffb0a7ff4cbe3
7
- data.tar.gz: 2304afa590efe9ba7ae065f37b74479ddfe331661d95291361c773bce97c1d06b7023455d488b196a9cc4b1b276700067cc2882db0604423ff080b4af3129895
6
+ metadata.gz: 8c8545986852ec4d61a2b9623a2142d4f79e2a3613d75fb7efff6511e70699531831527a625b89693b4b9d21c8bc7e4937f432bb14315ea82c01cc4a6eb34079
7
+ data.tar.gz: 1be5c7b2a06dc1228ba07ab50284f5d6af6147a27bbec92fb6768b92d7f718399dacf6150532ea2c1939a5660cd62e7f650463d7f534301df5cde8b29d7e9fb6
data/Rakefile CHANGED
@@ -20,30 +20,3 @@ Rake::TestTask.new(:test) do |t|
20
20
  t.verbose = false
21
21
  end
22
22
 
23
- desc 'Default: run unit tests.'
24
- task default: 'test:all'
25
-
26
- namespace :test do
27
- AVAILABLE_CASES = %w(
28
- activerecord_40_pg_017 activerecord_40_pg_018
29
- activerecord_41_pg_017 activerecord_41_pg_018
30
- activerecord_42_pg_017 activerecord_42_pg_018
31
- activerecord_50_pg_018 activerecord_51_pg_020
32
- activerecord_51_pg_021
33
- ).freeze
34
-
35
- AVAILABLE_CASES.each do |version|
36
- desc "Test pg_ltree against #{version}"
37
- task version do
38
- sh "bundle install --gemfile=gemfiles/#{version}.gemfile --quiet"
39
- sh "BUNDLE_GEMFILE='gemfiles/#{version}.gemfile' bundle exec rake -t test"
40
- end
41
- end
42
-
43
- desc 'Run all tests for pg_ltree'
44
- task :all do
45
- AVAILABLE_CASES.each do |version|
46
- sh "rake test:#{version}"
47
- end
48
- end
49
- end
@@ -3,18 +3,16 @@ module PgLtree
3
3
  #
4
4
  # @see [ActiveRecord::Base]
5
5
  # @see http://www.postgresql.org/docs/current/static/ltree.html
6
- #
7
- # @author a.ponomarenko
8
6
  module Ltree
9
7
  # Initialzie ltree for active model
10
8
  #
11
9
  # @param column [String] ltree column name
12
- def ltree(column = :path, options: { cascade: true })
10
+ def ltree(column = :path, cascade: true)
13
11
  cattr_accessor :ltree_path_column
14
12
 
15
13
  self.ltree_path_column = column
16
14
 
17
- if options[:cascade]
15
+ if cascade
18
16
  after_update :cascade_update
19
17
  after_destroy :cascade_destroy
20
18
  end
@@ -44,12 +42,12 @@ module PgLtree
44
42
  #
45
43
  # @return [ActiveRecord::Relation] relations of node's leaves
46
44
  def leaves
47
- subquery =
48
- unscoped.select("COUNT(subquery.#{ltree_path_column}) = 1")
49
- .from("#{table_name} AS subquery")
50
- .where("subquery.#{ltree_path_column} <@ #{table_name}.#{ltree_path_column}")
51
- subquery = subquery.where(subquery: current_scope.where_values_hash) if current_scope
52
- where subquery.to_sql
45
+ subquery = unscoped.select("#{table_name}.#{ltree_path_column}")
46
+ .from("#{table_name} AS subquery")
47
+ .where("#{table_name}.#{ltree_path_column} <> subquery.#{ltree_path_column}")
48
+ .where("#{table_name}.#{ltree_path_column} @> subquery.#{ltree_path_column}")
49
+
50
+ where.not ltree_path_column => subquery
53
51
  end
54
52
 
55
53
  # Get all with nodes when path liked the lquery
@@ -92,11 +90,22 @@ module PgLtree
92
90
  public_send ltree_path_column
93
91
  end
94
92
 
95
- # Get lTree previous value
93
+ # Get ltree original value before the save just occurred
94
+ # https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Dirty.html#method-i-attribute_before_last_save
96
95
  #
97
96
  # @return [String] ltree previous value
98
- def ltree_path_was
99
- public_send :"#{ltree_path_column}_was"
97
+ def ltree_path_before_last_save
98
+ public_send :attribute_before_last_save, ltree_path_column
99
+ end
100
+
101
+ # Get lTree previous value
102
+ # originally +attribute_was+ used in before create/update, destroy won't call +save+ so this work
103
+ # https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Dirty.html#method-i-attribute_in_database
104
+ #
105
+ # @return [String] ltree value in database
106
+
107
+ def ltree_path_in_database
108
+ public_send :attribute_in_database, ltree_path_column
100
109
  end
101
110
 
102
111
  # Check what current node is root
@@ -226,15 +235,15 @@ module PgLtree
226
235
  #
227
236
  # @return [ActiveRecord::Relation]
228
237
  def cascade_update
229
- ltree_scope.where(["#{ltree_scope.table_name}.#{ltree_path_column} <@ ?", ltree_path_was]).where(["#{ltree_scope.table_name}.#{ltree_path_column} != ?", ltree_path])
230
- .update_all ["#{ltree_path_column} = ? || subpath(#{ltree_path_column}, nlevel(?))", ltree_path, ltree_path_was]
238
+ ltree_scope.where(["#{ltree_scope.table_name}.#{ltree_path_column} <@ ?", ltree_path_before_last_save]).where(["#{ltree_scope.table_name}.#{ltree_path_column} != ?", ltree_path])
239
+ .update_all ["#{ltree_path_column} = ? || subpath(#{ltree_path_column}, nlevel(?))", ltree_path, ltree_path_before_last_save]
231
240
  end
232
241
 
233
242
  # Delete all children for current path
234
243
  #
235
244
  # @return [ActiveRecord::Relation]
236
245
  def cascade_destroy
237
- ltree_scope.where("#{ltree_scope.table_name}.#{ltree_path_column} <@ ?", ltree_path_was).delete_all
246
+ ltree_scope.where("#{ltree_scope.table_name}.#{ltree_path_column} <@ ?", ltree_path_in_database).destroy_all
238
247
  end
239
248
  end
240
249
  end
@@ -1,8 +1,6 @@
1
1
  module PgLtree
2
2
  # Narrowing the Scope for ActiveRecord Model
3
3
  # @note When model have composite uniq key (for example: state + path), you should use this module for narrowing the scope
4
- #
5
- # @author a.ponomarenko
6
4
  module ScopedFor
7
5
  # Define base instance scope for model by columns
8
6
  #
@@ -21,7 +19,7 @@ module PgLtree
21
19
  #
22
20
  # @return current class
23
21
  def ltree_scope
24
- self.class.where *(ltree_scoped_for.map { |column| { column => public_send(column) } })
22
+ self.class.where(*(ltree_scoped_for.map { |column| { column => public_send(column) } }))
25
23
  end
26
24
  end
27
25
  end
@@ -1,7 +1,4 @@
1
1
  # Organize ActiveRecord model into a tree structure with PostgreSQL LTree
2
- #
3
- # @author a.ponomarenko
4
2
  module PgLtree
5
- # Gem Version
6
- VERSION = '1.1.6'.freeze
3
+ VERSION = '1.1.9'.freeze
7
4
  end
@@ -0,0 +1,28 @@
1
+ module PgLtree
2
+ module Versions
3
+ module RailsOlderThan51
4
+ def ltree(column = :path, options: { cascade: true })
5
+ super
6
+ include InstanceMethods
7
+ end
8
+
9
+ module InstanceMethods
10
+ # Get lTree previous value
11
+ #
12
+ # Related changes in > Rails 5.1.0
13
+ # https://github.com/rails/rails/pull/25337
14
+ #
15
+ # @return [String] Rails 5.1 replace attribute_was method with two methods, this is a wrapper for older rails
16
+ def ltree_path_before_last_save
17
+ public_send :attribute_was, ltree_path_column
18
+ end
19
+
20
+ #
21
+ # @return [String] Rails 5.1 replace attribute_was method with two methods, this is a wrapper for older rails
22
+ def ltree_path_in_database
23
+ public_send :attribute_was, ltree_path_column
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
data/lib/pg_ltree.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'active_record'
1
2
  require 'pg_ltree/ltree'
2
3
  require 'pg_ltree/scoped_for'
3
4
  require 'pg_ltree/version'
@@ -9,7 +10,8 @@ if defined?(ActiveRecord)
9
10
 
10
11
  # The behavior of _was changes in Rails 5.1
11
12
  # http://blog.toshima.ru/2017/04/06/saved-change-to-attribute.html
12
- if ActiveRecord::VERSION::MAJOR > 5 || (ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR >= 1)
13
- ActiveRecord::Base.extend(PgLtree::Versions::Rails51)
13
+ # This is for backward compability
14
+ if ActiveRecord::VERSION::MAJOR < 5 || (ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR < 1)
15
+ ActiveRecord::Base.extend(PgLtree::Versions::RailsOlderThan51)
14
16
  end
15
17
  end
data/test/database.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  adapter: postgresql
2
2
  host: localhost
3
3
  username: postgres
4
- password: postgres
4
+ password:
5
5
  database: pg_ltree_test
@@ -1,5 +1,5 @@
1
1
  adapter: postgresql
2
2
  host: localhost
3
3
  username: postgres
4
- password:
4
+ password: postgres
5
5
  database: pg_ltree_test
@@ -17,6 +17,9 @@ class PgLtree::LtreeTest < BaseTest
17
17
  Top.Collections.Pictures.Astronomy.Stars
18
18
  Top.Collections.Pictures.Astronomy.Galaxies
19
19
  Top.Collections.Pictures.Astronomy.Astronauts
20
+ Top.Collections.Videos
21
+ Top.Collections.Videos.Vacation
22
+ Top.Collections.Videos.NewYear
20
23
  ).each do |path|
21
24
  TreeNode.create! path: path
22
25
  end
@@ -50,6 +53,20 @@ class PgLtree::LtreeTest < BaseTest
50
53
  Top.Collections.Pictures.Astronomy.Stars
51
54
  Top.Collections.Pictures.Astronomy.Galaxies
52
55
  Top.Collections.Pictures.Astronomy.Astronauts
56
+ Top.Collections.Videos.Vacation
57
+ Top.Collections.Videos.NewYear
58
+ )
59
+ end
60
+
61
+ test '#leaves with a relation' do
62
+ assert_equal TreeNode.where("path <> 'Top.Collections.Pictures.Astronomy.Stars'").leaves.pluck(:path), %w(
63
+ Top.Science.Astronomy.Astrophysics
64
+ Top.Science.Astronomy.Cosmology
65
+ Top.Hobbies.Amateurs_Astronomy
66
+ Top.Collections.Pictures.Astronomy.Galaxies
67
+ Top.Collections.Pictures.Astronomy.Astronauts
68
+ Top.Collections.Videos.Vacation
69
+ Top.Collections.Videos.NewYear
53
70
  )
54
71
  end
55
72
 
@@ -181,8 +198,52 @@ class PgLtree::LtreeTest < BaseTest
181
198
  end
182
199
 
183
200
  test '.cascade_destroy' do
184
- TreeNode.find_by(path: 'Top.Collections').destroy
201
+ assert_equal TreeNode.where("path <@ 'Top.Collections'").pluck(:path), %w(
202
+ Top.Collections
203
+ Top.Collections.Pictures
204
+ Top.Collections.Pictures.Astronomy
205
+ Top.Collections.Pictures.Astronomy.Stars
206
+ Top.Collections.Pictures.Astronomy.Galaxies
207
+ Top.Collections.Pictures.Astronomy.Astronauts
208
+ Top.Collections.Videos
209
+ Top.Collections.Videos.Vacation
210
+ Top.Collections.Videos.NewYear
211
+ )
212
+
213
+ TreeNode.find_by(path: 'Top.Collections.Pictures').destroy
185
214
 
186
- assert_equal TreeNode.where("path ~ 'Top.Collections'").pluck(:path), %w()
215
+ assert_equal TreeNode.where("path <@ 'Top.Collections'").pluck(:path), %w(
216
+ Top.Collections
217
+ Top.Collections.Videos
218
+ Top.Collections.Videos.Vacation
219
+ Top.Collections.Videos.NewYear
220
+ )
221
+ end
222
+
223
+ test '.destroy' do
224
+ assert_equal TreeWithoutCascadeNode.where("path <@ 'Top.Collections'").pluck(:path), %w(
225
+ Top.Collections
226
+ Top.Collections.Pictures
227
+ Top.Collections.Pictures.Astronomy
228
+ Top.Collections.Pictures.Astronomy.Stars
229
+ Top.Collections.Pictures.Astronomy.Galaxies
230
+ Top.Collections.Pictures.Astronomy.Astronauts
231
+ Top.Collections.Videos
232
+ Top.Collections.Videos.Vacation
233
+ Top.Collections.Videos.NewYear
234
+ )
235
+
236
+ TreeWithoutCascadeNode.find_by(path: 'Top.Collections.Pictures').destroy
237
+
238
+ assert_equal TreeWithoutCascadeNode.where("path <@ 'Top.Collections'").pluck(:path), %w(
239
+ Top.Collections
240
+ Top.Collections.Pictures.Astronomy
241
+ Top.Collections.Pictures.Astronomy.Stars
242
+ Top.Collections.Pictures.Astronomy.Galaxies
243
+ Top.Collections.Pictures.Astronomy.Astronauts
244
+ Top.Collections.Videos
245
+ Top.Collections.Videos.Vacation
246
+ Top.Collections.Videos.NewYear
247
+ )
187
248
  end
188
249
  end
@@ -17,6 +17,9 @@ class PgLtree::ScopedForTest < BaseTest
17
17
  Top.Collections.Pictures.Astronomy.Stars
18
18
  Top.Collections.Pictures.Astronomy.Galaxies
19
19
  Top.Collections.Pictures.Astronomy.Astronauts
20
+ Top.Collections.Videos
21
+ Top.Collections.Videos.Vacation
22
+ Top.Collections.Videos.NewYear
20
23
  ).each do |path|
21
24
  %i( active deactive ).each do |status|
22
25
  NotUniqTreeNode.create! new_path: path, status: status
@@ -47,6 +50,8 @@ class PgLtree::ScopedForTest < BaseTest
47
50
  Top.Collections.Pictures.Astronomy.Stars
48
51
  Top.Collections.Pictures.Astronomy.Galaxies
49
52
  Top.Collections.Pictures.Astronomy.Astronauts
53
+ Top.Collections.Videos.Vacation
54
+ Top.Collections.Videos.NewYear
50
55
  )
51
56
  end
52
57
 
@@ -161,32 +166,25 @@ class PgLtree::ScopedForTest < BaseTest
161
166
  end
162
167
 
163
168
  test '.cascade_destroy' do
164
- assert_equal NotUniqTreeNode.where("new_path <@ 'Top.Collections'").pluck(:new_path), %w(
169
+ assert_equal NotUniqTreeNode.where("new_path <@ 'Top.Collections'").where(status: :active).pluck(:new_path), %w(
165
170
  Top.Collections
166
- Top.Collections
167
- Top.Collections.Pictures
168
171
  Top.Collections.Pictures
169
172
  Top.Collections.Pictures.Astronomy
170
- Top.Collections.Pictures.Astronomy
171
173
  Top.Collections.Pictures.Astronomy.Stars
172
- Top.Collections.Pictures.Astronomy.Stars
173
- Top.Collections.Pictures.Astronomy.Galaxies
174
174
  Top.Collections.Pictures.Astronomy.Galaxies
175
175
  Top.Collections.Pictures.Astronomy.Astronauts
176
- Top.Collections.Pictures.Astronomy.Astronauts
176
+ Top.Collections.Videos
177
+ Top.Collections.Videos.Vacation
178
+ Top.Collections.Videos.NewYear
177
179
  )
178
180
 
179
- NotUniqTreeNode.find_by(new_path: 'Top.Collections', status: :active).destroy
181
+ NotUniqTreeNode.find_by(new_path: 'Top.Collections.Pictures', status: :active).destroy
180
182
 
181
- assert_equal NotUniqTreeNode.where("new_path <@ 'Top.Collections'").where(status: :active).pluck(:new_path), %w()
182
-
183
- assert_equal NotUniqTreeNode.where("new_path <@ 'Top.Collections'").where(status: :deactive).pluck(:new_path), %w(
183
+ assert_equal NotUniqTreeNode.where("new_path <@ 'Top.Collections'").where(status: :active).pluck(:new_path), %w(
184
184
  Top.Collections
185
- Top.Collections.Pictures
186
- Top.Collections.Pictures.Astronomy
187
- Top.Collections.Pictures.Astronomy.Stars
188
- Top.Collections.Pictures.Astronomy.Galaxies
189
- Top.Collections.Pictures.Astronomy.Astronauts
185
+ Top.Collections.Videos
186
+ Top.Collections.Videos.Vacation
187
+ Top.Collections.Videos.NewYear
190
188
  )
191
189
  end
192
190
  end
data/test/test_helper.rb CHANGED
@@ -7,6 +7,8 @@ rescue Bundler::BundlerError => e
7
7
  exit e.status_code
8
8
  end
9
9
 
10
+ $VERBOSE=nil
11
+
10
12
  require 'pg'
11
13
  require 'pg_ltree'
12
14
  require 'minitest/autorun'
@@ -31,6 +33,13 @@ class BaseTest < ActiveSupport::TestCase
31
33
  exit 0
32
34
  end
33
35
 
36
+ begin
37
+ PG.connect(host: db_connection["host"], user: db_connection["username"], password: db_connection["password"])
38
+ .exec("CREATE DATABASE #{db_connection['database']}")
39
+ rescue
40
+ # Ignore errors on DB:CEATE
41
+ end
42
+
34
43
  ActiveRecord::Base.establish_connection db_connection
35
44
  ActiveRecord::Schema.verbose = false
36
45
 
@@ -69,3 +78,8 @@ end
69
78
  class TreeNode < ActiveRecord::Base
70
79
  ltree
71
80
  end
81
+
82
+ class TreeWithoutCascadeNode < ActiveRecord::Base
83
+ self.table_name = 'tree_nodes'
84
+ ltree :path, cascade: false
85
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_ltree
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.6
4
+ version: 1.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrei Panamarenka
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-19 00:00:00.000000000 Z
11
+ date: 2022-08-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 4.0.0
19
+ version: '5.2'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '5.2'
22
+ version: '8.0'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 4.0.0
29
+ version: '5.2'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '5.2'
32
+ version: '8.0'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: pg
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -39,7 +39,7 @@ dependencies:
39
39
  version: 0.17.0
40
40
  - - "<"
41
41
  - !ruby/object:Gem::Version
42
- version: '0.22'
42
+ version: '2'
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
@@ -49,7 +49,7 @@ dependencies:
49
49
  version: 0.17.0
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
- version: '0.22'
52
+ version: '2'
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: bundler
55
55
  requirement: !ruby/object:Gem::Requirement
@@ -120,6 +120,20 @@ dependencies:
120
120
  - - ">="
121
121
  - !ruby/object:Gem::Version
122
122
  version: '0'
123
+ - !ruby/object:Gem::Dependency
124
+ name: appraisal
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
123
137
  description: Organise ActiveRecord model into a tree structure with PostgreSQL LTree
124
138
  email:
125
139
  - andrei.panamarenka@gmail.com
@@ -133,7 +147,7 @@ files:
133
147
  - lib/pg_ltree/ltree.rb
134
148
  - lib/pg_ltree/scoped_for.rb
135
149
  - lib/pg_ltree/version.rb
136
- - lib/pg_ltree/versions/rails_5_1.rb
150
+ - lib/pg_ltree/versions/rails_older_than_51.rb
137
151
  - test/database.yml
138
152
  - test/database.yml.sample
139
153
  - test/pg_ltree/ltree_test.rb
@@ -144,7 +158,7 @@ homepage: https://github.com/sjke/pg_ltree
144
158
  licenses:
145
159
  - MIT
146
160
  metadata: {}
147
- post_install_message:
161
+ post_install_message:
148
162
  rdoc_options: []
149
163
  require_paths:
150
164
  - lib
@@ -159,9 +173,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
159
173
  - !ruby/object:Gem::Version
160
174
  version: '0'
161
175
  requirements: []
162
- rubyforge_project:
163
- rubygems_version: 2.6.12
164
- signing_key:
176
+ rubygems_version: 3.1.6
177
+ signing_key:
165
178
  specification_version: 4
166
179
  summary: Organise ActiveRecord model into a tree structure with PostgreSQL LTree
167
180
  test_files:
@@ -1,32 +0,0 @@
1
- module PgLtree
2
- module Versions
3
- module Rails51
4
- def ltree(column = :path, options: { cascade: true })
5
- super
6
-
7
- before_destroy :delete_ltree_column_value if options[:cascade]
8
- include InstanceMethods
9
- end
10
-
11
- module InstanceMethods
12
- # Get lTree previous value
13
- #
14
- # Related changes in > Rails 5.1.0
15
- # https://github.com/rails/rails/pull/25337
16
- #
17
- # @return [String] ltree previous value
18
- def ltree_path_was
19
- public_send :"#{ltree_path_column}_before_last_save"
20
- end
21
-
22
- #
23
- # In order for for cascade_destroy to work with the current callbacks, let's first delete the column :/.
24
- # @author HoyaBoya [https://github.com/HoyaBoya]
25
- #
26
- def delete_ltree_column_value
27
- update!(ltree_path_column => nil)
28
- end
29
- end
30
- end
31
- end
32
- end