ruby_core_extensions 0.0.1 → 0.4.0

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 (48) hide show
  1. checksums.yaml +5 -5
  2. data/.github/dependabot.yml +6 -0
  3. data/.github/workflows/release.yml +59 -0
  4. data/.github/workflows/ruby.yml +24 -0
  5. data/.gitignore +2 -0
  6. data/.rubocop.yml +21 -0
  7. data/.ruby-version +1 -0
  8. data/CHANGELOG.md +32 -0
  9. data/README.md +35 -22
  10. data/Rakefile +1 -1
  11. data/gemfiles/rails60.gemfile +6 -0
  12. data/gemfiles/rails61.gemfile +6 -0
  13. data/gemfiles/rails70.gemfile +6 -0
  14. data/lib/ruby_core_extensions/array.rb +1 -10
  15. data/lib/ruby_core_extensions/class.rb +0 -3
  16. data/lib/ruby_core_extensions/compact/array.rb +1 -2
  17. data/lib/ruby_core_extensions/compact/hash.rb +3 -4
  18. data/lib/ruby_core_extensions/enumerable.rb +0 -3
  19. data/lib/ruby_core_extensions/file.rb +0 -1
  20. data/lib/ruby_core_extensions/hash.rb +3 -15
  21. data/lib/ruby_core_extensions/kernel.rb +0 -2
  22. data/lib/ruby_core_extensions/numeric.rb +0 -2
  23. data/lib/ruby_core_extensions/object.rb +5 -7
  24. data/lib/ruby_core_extensions/recursive/array.rb +42 -44
  25. data/lib/ruby_core_extensions/recursive/hash.rb +14 -17
  26. data/lib/ruby_core_extensions/recursive/object.rb +8 -4
  27. data/lib/ruby_core_extensions/recursive.rb +0 -5
  28. data/lib/ruby_core_extensions/string.rb +8 -9
  29. data/lib/ruby_core_extensions/version.rb +1 -1
  30. data/lib/ruby_core_extensions.rb +1 -4
  31. data/ruby_core_extensions.gemspec +6 -6
  32. data/spec/array_spec.rb +33 -12
  33. data/spec/compact_spec.rb +13 -13
  34. data/spec/enumerable_spec.rb +13 -9
  35. data/spec/filename_spec.rb +0 -1
  36. data/spec/hash_spec.rb +41 -30
  37. data/spec/object_spec.rb +4 -5
  38. data/spec/string_spec.rb +3 -1
  39. data/spec/support/coverage.rb +2 -28
  40. metadata +19 -33
  41. data/.travis.yml +0 -14
  42. data/gemfiles/rails3.gemfile +0 -11
  43. data/gemfiles/rails4.gemfile +0 -11
  44. data/lib/ruby_core_extensions/recursive/big_decimal.rb +0 -5
  45. data/lib/ruby_core_extensions/recursive/date.rb +0 -8
  46. data/lib/ruby_core_extensions/recursive/date_time.rb +0 -8
  47. data/lib/ruby_core_extensions/recursive/fixnum.rb +0 -7
  48. data/lib/ruby_core_extensions/recursive/time.rb +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 73d409278a49f15845c54d39663e24a0942bd004
4
- data.tar.gz: 61a669479de59c7b8e9105d674cc44d28824151a
2
+ SHA256:
3
+ metadata.gz: 0700fecac203e841ffa712ed7aaa1fc9378e0bb5e524edcf406c70ea16bbec99
4
+ data.tar.gz: 19c5c4e143100fd43696da08ea51d1331812ccde5c6f6c83438f53035b243b6e
5
5
  SHA512:
6
- metadata.gz: addb261d51cdd07ced187a5efefa83d474d712875133108585a255be62168add46445b532a7816292466110c8766720394f91012c2b38db2e584a70830b700de
7
- data.tar.gz: 3e37676250b767f9d97808b353e25e187c386de97f99ec457ff8a6823a7c25fbb8c74195e3488e0ed367de7db71aabb5b18ed9e600d10b558b5d839a908cbe47
6
+ metadata.gz: 45f360e004e32891c8f6d9b8a8bc1d2972f613d38595a005cadcb93855ecfb9ee2e86ac3ce25d79427c8854d3d21674c5ce286c953633a564cf5b3107bdb6e76
7
+ data.tar.gz: 25b268a41f2cd077cd8a6c02366c7d2ef27eb7ece3126ec35023134e056ab8e6d165226aa4cbcaeed26a679e4233422e6b12ff8a669fee802e666f561017f598
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "bundler"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "daily"
@@ -0,0 +1,59 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ build:
10
+ name: Build
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout
14
+ uses: actions/checkout@v2
15
+ - uses: ruby/setup-ruby@v1
16
+ with:
17
+ bundler-cache: true
18
+ - run: bundle exec rake
19
+
20
+ release:
21
+ needs: build
22
+ name: Release
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - name: Checkout
26
+ uses: actions/checkout@v2
27
+
28
+ - name: Generate Changelog
29
+ run: |
30
+ # Get version from github ref (remove 'refs/tags/' and prefix 'v')
31
+ version="${GITHUB_REF#refs/tags/v}"
32
+ npx changelog-parser CHANGELOG.md | jq -cr ".versions | .[] | select(.version == \"$version\") | .body" > ${{ github.workflow }}-CHANGELOG.txt
33
+
34
+ - name: Release
35
+ uses: softprops/action-gh-release@v1
36
+ with:
37
+ body_path: ${{ github.workflow }}-CHANGELOG.txt
38
+ env:
39
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40
+
41
+ publish:
42
+ needs: [build, release]
43
+ name: Publish
44
+ runs-on: ubuntu-latest
45
+
46
+ steps:
47
+ - uses: actions/checkout@v2
48
+ - uses: ruby/setup-ruby@v1
49
+
50
+ - name: Publish to RubyGems
51
+ run: |
52
+ mkdir -p $HOME/.gem
53
+ touch $HOME/.gem/credentials
54
+ chmod 0600 $HOME/.gem/credentials
55
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
56
+ gem build *.gemspec
57
+ gem push *.gem
58
+ env:
59
+ GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
@@ -0,0 +1,24 @@
1
+ name: Build and Test
2
+ on: [push, pull_request]
3
+ jobs:
4
+ test:
5
+ strategy:
6
+ fail-fast: false
7
+ matrix:
8
+ gemfile: [rails60, rails61, rails70]
9
+ ruby: ["2.7", "3.0", "3.1"]
10
+ runs-on: ubuntu-latest
11
+ env:
12
+ BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile
13
+ steps:
14
+ - uses: actions/checkout@v2
15
+ - uses: ruby/setup-ruby@v1
16
+ with:
17
+ ruby-version: ${{ matrix.ruby }}
18
+ bundler-cache: true
19
+ - run: bundle exec rake
20
+ - name: Coveralls
21
+ uses: coverallsapp/github-action@master
22
+ with:
23
+ github-token: ${{ secrets.GITHUB_TOKEN }}
24
+ path-to-lcov: coverage/lcov.info
data/.gitignore CHANGED
@@ -2,3 +2,5 @@ pkg
2
2
  coverage
3
3
  Gemfile.lock
4
4
  tmp
5
+ vendor
6
+ .bundle
data/.rubocop.yml ADDED
@@ -0,0 +1,21 @@
1
+ inherit_gem:
2
+ rubocop-rails:
3
+ - config/rails.yml
4
+
5
+ AllCops:
6
+ TargetRubyVersion: 2.4
7
+
8
+ Style/FrozenStringLiteralComment:
9
+ Enabled: false
10
+
11
+ Style/StringLiterals:
12
+ Enabled: false
13
+
14
+ Layout/IndentationWidth:
15
+ IgnoredPatterns: ['^\s*private$']
16
+
17
+ Style/BracesAroundHashParameters:
18
+ Enabled: false
19
+
20
+ Metrics/LineLength:
21
+ Max: 100
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.1.0
data/CHANGELOG.md ADDED
@@ -0,0 +1,32 @@
1
+ # Change Log
2
+ All notable changes to this project will be documented in this file.
3
+ This project adheres to [Semantic Versioning](http://semver.org/).
4
+ This changelog adheres to [Keep a CHANGELOG](http://keepachangelog.com/).
5
+
6
+ ## 0.4.0
7
+
8
+ - [PLAT-183] Ruby 3.1, Rails 7.0, and publish coverage with github action
9
+
10
+ ## 0.3.0
11
+
12
+ - [TT-8627] Update to build with github actions / ruby 3.0 / rails 6.1
13
+
14
+ ## 0.2.0
15
+
16
+ - [TT-5814] Update to Rails5+ and Ruby2.4+
17
+
18
+ ## 0.1.0
19
+
20
+ ### Added
21
+ - [TT-4020] Implemented Rubocop
22
+
23
+ ### Removed
24
+ - Remove no longer supported Array methods #to_param/#show_name
25
+
26
+ ### Changed
27
+ - stringify_values_recursively now just works on to_s for all objects
28
+
29
+ ## 0.0.1
30
+
31
+ ### Added
32
+ - [TT-1392] Changelog file
data/README.md CHANGED
@@ -1,37 +1,50 @@
1
- Ruby Core Extensions
2
- ====================
1
+ # Ruby Core Extensions
3
2
 
4
- [![Build Status](https://travis-ci.org/sealink/ruby_core_extensions.png?branch=master)](https://travis-ci.org/sealink/ruby_core_extensions)
3
+ [![Gem Version](https://badge.fury.io/rb/ruby_core_extensions.svg)](http://badge.fury.io/rb/ruby_core_extensions)
4
+ [![Build Status](https://github.com/sealink/ruby_core_extensions/workflows/Build%20and%20Test/badge.svg?branch=master)](https://github.com/sealink/ruby_core_extensions/actions)
5
5
  [![Coverage Status](https://coveralls.io/repos/sealink/ruby_core_extensions/badge.png)](https://coveralls.io/r/sealink/ruby_core_extensions)
6
- [![Dependency Status](https://gemnasium.com/sealink/ruby_core_extensions.png?travis)](https://gemnasium.com/sealink/ruby_core_extensions)
7
6
  [![Code Climate](https://codeclimate.com/github/sealink/ruby_core_extensions.png)](https://codeclimate.com/github/sealink/ruby_core_extensions)
8
7
 
9
- # DESCRIPTION
8
+ # Description
10
9
 
11
10
  Extends the core ruby classes with helpful functions
12
11
 
13
- # INSTALLATION
12
+ # Installation
14
13
 
15
14
  Add to your Gemfile:
15
+
16
+ ```ruby
16
17
  gem 'ruby_core_extensions'
18
+ ```
17
19
 
18
20
  # Extensions
19
21
 
20
22
  The following classes are extend in this gem:
21
23
 
22
- * Array
23
- * BigDecimal
24
- * Class
25
- * Date
26
- * DateTime
27
- * Enumerable
28
- * File
29
- * Fixnum
30
- * Hash
31
- * Kernel
32
- * Numeric
33
- * Object
34
- * Range
35
- * String
36
- * Time
37
-
24
+ - Array
25
+ - BigDecimal
26
+ - Class
27
+ - Date
28
+ - DateTime
29
+ - Enumerable
30
+ - File
31
+ - Fixnum
32
+ - Hash
33
+ - Kernel
34
+ - Numeric
35
+ - Object
36
+ - Range
37
+ - String
38
+ - Time
39
+
40
+ ## Release
41
+
42
+ To publish a new version of this gem the following steps must be taken.
43
+
44
+ * Update the version in the following files
45
+ ```
46
+ CHANGELOG.md
47
+ lib/ruby_core_extensions/version.rb
48
+ ````
49
+ * Create a tag using the format v0.1.0
50
+ * Follow build progress in GitHub actions
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
3
  desc 'Default: run specs.'
4
- task :default => :spec
4
+ task default: :spec
5
5
 
6
6
  require 'rspec/core/rake_task'
7
7
 
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+ gemspec :path => '../'
3
+
4
+ group :development, :test do
5
+ gem 'activerecord', '~> 6.0'
6
+ end
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+ gemspec :path => '../'
3
+
4
+ group :development, :test do
5
+ gem 'activerecord', '~> 6.1'
6
+ end
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+ gemspec :path => '../'
3
+
4
+ group :development, :test do
5
+ gem 'activerecord', '~> 7.0'
6
+ end
@@ -1,13 +1,4 @@
1
1
  class Array
2
-
3
- def to_param
4
- self.collect { |element| element.respond_to?(:to_param) ? element.to_param : element }
5
- end
6
-
7
- def show_name
8
- first.titleize
9
- end
10
-
11
2
  # Key should be unique, or latest element with that key will override previous ones.
12
3
  def hash_by(key = nil, method = nil, &block)
13
4
  self.inject({}) do |h, element|
@@ -31,7 +22,7 @@ class Array
31
22
  end
32
23
 
33
24
  def intersects?(other)
34
- self.any?{|i| other.include?(i)}
25
+ self.any? { |i| other.include?(i) }
35
26
  end
36
27
 
37
28
  # Same effect as Array.wrap(object).first
@@ -1,8 +1,5 @@
1
1
  class Class
2
-
3
2
  def downcase_symbol
4
3
  self.to_s.downcase.to_sym
5
4
  end
6
-
7
5
  end
8
-
@@ -1,6 +1,6 @@
1
1
  class Array
2
2
  def compact_blank!
3
- delete_if{|v| v.blank?}
3
+ delete_if { |v| v.blank? }
4
4
  end
5
5
 
6
6
  def recursive_compact_blank!
@@ -17,4 +17,3 @@ class Array
17
17
  end
18
18
  end
19
19
  end
20
-
@@ -6,7 +6,7 @@ class Hash
6
6
 
7
7
  # Remove nil values - !desctructively!
8
8
  def compact!
9
- delete_if{|k,v| v.nil?}
9
+ delete_if { |k, v| v.nil? }
10
10
  end
11
11
 
12
12
  def compact_blank
@@ -14,11 +14,11 @@ class Hash
14
14
  end
15
15
 
16
16
  def compact_blank!
17
- delete_if{|k,v| v.blank?}
17
+ delete_if { |k, v| v.blank? }
18
18
  end
19
19
 
20
20
  def recursive_compact_blank!
21
- delete_if do |k,v|
21
+ delete_if do |k, v|
22
22
  if v.is_a?(Hash)
23
23
  v.recursive_compact_blank!
24
24
  v.recursive_blank?
@@ -31,4 +31,3 @@ class Hash
31
31
  end
32
32
  end
33
33
  end
34
-
@@ -1,5 +1,4 @@
1
1
  module Enumerable
2
-
3
2
  def map_methods(*methods)
4
3
  map do |object|
5
4
  methods.inject({}) do |h, method|
@@ -34,6 +33,4 @@ module Enumerable
34
33
  obj
35
34
  end
36
35
  end
37
-
38
36
  end
39
-
@@ -9,4 +9,3 @@ class File
9
9
  gsub(/\-+/, '-') # limit - character to once
10
10
  end
11
11
  end
12
-
@@ -1,34 +1,22 @@
1
1
  class Hash
2
-
3
- unless self.method_defined?(:extract!)
4
- #Imported from Rails 3
5
- def extract!(*keys)
6
- result = {}
7
- keys.each { |key| result[key] = delete(key) }
8
- result
9
- end
10
- end
11
-
12
-
13
2
  def map_key_value(key_method, value_method = nil)
14
3
  value_method ||= key_method
15
- each.with_object({}) do |(k,v), new_hash|
4
+ each.with_object({}) do |(k, v), new_hash|
16
5
  new_hash[k.send(key_method)] = v.send(value_method)
17
6
  end
18
7
  end
19
8
 
20
9
 
21
10
  def map_key(method)
22
- each.with_object({}) do |(k,v), new_hash|
11
+ each.with_object({}) do |(k, v), new_hash|
23
12
  new_hash[k.send(method)] = v
24
13
  end
25
14
  end
26
15
 
27
16
 
28
17
  def map_value(method)
29
- each.with_object({}) do |(k,v), new_hash|
18
+ each.with_object({}) do |(k, v), new_hash|
30
19
  new_hash[k] = v.send(method)
31
20
  end
32
21
  end
33
-
34
22
  end
@@ -1,5 +1,4 @@
1
1
  module Kernel
2
-
3
2
  def require_dirs(dirs)
4
3
  Array.wrap(dirs).each do |load_path|
5
4
  Dir.glob("#{load_path}/**/*.rb").each do |file|
@@ -7,5 +6,4 @@ module Kernel
7
6
  end
8
7
  end
9
8
  end
10
-
11
9
  end
@@ -1,7 +1,5 @@
1
1
  class Numeric
2
-
3
2
  def to_bool
4
3
  !zero?
5
4
  end
6
-
7
5
  end
@@ -3,16 +3,16 @@ class Object
3
3
  def sounds_like?(other)
4
4
  self.phonetic_code == other.phonetic_code
5
5
  end
6
-
6
+
7
7
  # Convert this object into a string, then convert that to phonetic code
8
8
  def phonetic_code
9
9
  self.to_s.phonetic_code
10
10
  end
11
-
11
+
12
12
  def to_long_s
13
13
  to_s
14
14
  end
15
-
15
+
16
16
  def virtual_belongs_to(*associations)
17
17
  options = associations.extract_options!
18
18
 
@@ -62,11 +62,9 @@ class Object
62
62
  end
63
63
  EVAL
64
64
  end
65
-
66
-
65
+
66
+
67
67
  def to_bool
68
68
  self.to_s.to_bool
69
69
  end
70
-
71
70
  end
72
-
@@ -1,45 +1,43 @@
1
- class Array
2
-
3
- def convert
4
- self
5
- end
1
+ class Array
2
+ def convert
3
+ self
4
+ end
6
5
 
7
- def convert_keys_recursively(&converter)
8
- map { |v| v.convert_keys_recursively(&converter) }
9
- end
10
-
11
- def convert_values_recursively(&converter)
12
- map { |v| v.convert_values_recursively(&converter) }
13
- end
14
-
15
- def symbolize_keys_recursively
16
- map(&:symbolize_keys_recursively)
17
- end
18
-
19
- def stringify_values_recursively
20
- map(&:stringify_values_recursively)
21
- end
22
-
23
- def make_indifferent_access_recursively
24
- map(&:make_indifferent_access_recursively)
25
- end
26
-
27
- def recursive_blank?
28
- each do |v|
29
- if v.respond_to?(:recursive_blank?)
30
- return false unless v.recursive_blank?
31
- else
32
- return false unless v.blank?
33
- end
34
- end
35
- true
36
- end
37
-
38
- def recursively(&block)
39
- each do |element|
40
- block.call(element)
41
- element.recursively(&block) if element.respond_to?(:recursively)
42
- end
43
- end
44
-
45
- end
6
+ def convert_keys_recursively(&converter)
7
+ map { |v| v.convert_keys_recursively(&converter) }
8
+ end
9
+
10
+ def convert_values_recursively(&converter)
11
+ map { |v| v.convert_values_recursively(&converter) }
12
+ end
13
+
14
+ def symbolize_keys_recursively
15
+ map(&:symbolize_keys_recursively)
16
+ end
17
+
18
+ def stringify_values_recursively
19
+ map(&:stringify_values_recursively)
20
+ end
21
+
22
+ def make_indifferent_access_recursively
23
+ map(&:make_indifferent_access_recursively)
24
+ end
25
+
26
+ def recursive_blank?
27
+ each do |v|
28
+ if v.respond_to?(:recursive_blank?)
29
+ return false unless v.recursive_blank?
30
+ else
31
+ return false unless v.blank?
32
+ end
33
+ end
34
+ true
35
+ end
36
+
37
+ def recursively(&block)
38
+ each do |element|
39
+ block.call(element)
40
+ element.recursively(&block) if element.respond_to?(:recursively)
41
+ end
42
+ end
43
+ end
@@ -1,5 +1,4 @@
1
1
  class Hash
2
-
3
2
  def recursive_blank?
4
3
  each do |k, v|
5
4
  if v.respond_to?(:recursive_blank?)
@@ -10,18 +9,18 @@ class Hash
10
9
  end
11
10
  true
12
11
  end
13
-
12
+
14
13
  def convert
15
14
  self
16
15
  end
17
-
16
+
18
17
  def convert_keys(&converter)
19
18
  inject({}) do |hash, (key, value)|
20
19
  hash[converter.call(key)] = value
21
20
  hash
22
21
  end
23
22
  end
24
-
23
+
25
24
  def convert_values(*keys, &converter)
26
25
  inject(clone) do |hash, (key, value)|
27
26
  hash[key] = value.convert(&converter) if keys.blank? || keys.include?(key)
@@ -33,54 +32,52 @@ class Hash
33
32
  Hash[map do |key, value|
34
33
  k = converter.call(key)
35
34
  v = value.convert_keys_recursively(&converter)
36
- [k,v]
35
+ [k, v]
37
36
  end]
38
37
  end
39
-
38
+
40
39
  def convert_values_recursively(&converter)
41
40
  inject({}) do |hash, (key, value)|
42
41
  hash[key] = value.convert_values_recursively(&converter)
43
42
  hash
44
43
  end
45
44
  end
46
-
45
+
47
46
  def symbolize_keys_recursively
48
47
  Hash[map do |key, value|
49
48
  k = key.is_a?(String) ? key.to_sym : key
50
49
  v = value.symbolize_keys_recursively
51
- [k,v]
50
+ [k, v]
52
51
  end]
53
52
  end
54
-
53
+
55
54
  def stringify_values_recursively
56
55
  inject({}) do |options, (key, value)|
57
56
  options[key] = value.stringify_values_recursively
58
57
  options
59
58
  end
60
59
  end
61
-
60
+
62
61
  def make_indifferent_access_recursively
63
62
  HashWithIndifferentAccess.new(inject({}) do |options, (key, value)|
64
63
  options[key] = value.make_indifferent_access_recursively
65
64
  options
66
65
  end)
67
66
  end
68
-
67
+
69
68
  def deep_dup
70
69
  duplicate = self.dup
71
- duplicate.each_pair do |k,v|
70
+ duplicate.each_pair do |k, v|
72
71
  tv = duplicate[k]
73
72
  duplicate[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_dup : v
74
73
  end
75
74
  duplicate
76
75
  end
77
-
76
+
78
77
  def recursively(&block)
79
- each do |key,value|
80
- block.call(key,value)
78
+ each do |key, value|
79
+ block.call(key, value)
81
80
  value.recursively(&block) if value.respond_to?(:recursively)
82
81
  end
83
82
  end
84
-
85
83
  end
86
-
@@ -2,16 +2,20 @@ class Object
2
2
  def convert(&converter)
3
3
  converter.call(self)
4
4
  end
5
-
5
+
6
6
  def return_self
7
7
  self
8
8
  end
9
-
9
+
10
+ def return_to_s
11
+ to_s
12
+ end
13
+
10
14
  alias_method :convert_values_recursively, :convert
11
15
  alias_method :convert_recursively, :convert
12
-
16
+
13
17
  alias_method :convert_keys_recursively, :return_self
14
18
  alias_method :symbolize_keys_recursively, :return_self
15
- alias_method :stringify_values_recursively, :return_self
19
+ alias_method :stringify_values_recursively, :return_to_s
16
20
  alias_method :make_indifferent_access_recursively, :return_self
17
21
  end