azeroth 0.0.4 → 0.0.5
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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +10 -2
- data/.gitignore +8 -2
- data/.rubocop.yml +16 -0
- data/.rubocop_todo.yml +5 -24
- data/Dockerfile +26 -2
- data/Gemfile +2 -0
- data/README.md +2 -1
- data/Rakefile +1 -0
- data/azeroth.gemspec +20 -13
- data/config/check_specs.yml +5 -0
- data/config/rubycritc.rb +12 -0
- data/config/yardstick.yml +7 -2
- data/lib/azeroth.rb +6 -3
- data/lib/azeroth/model.rb +20 -1
- data/lib/azeroth/options.rb +12 -0
- data/lib/azeroth/resource_builder.rb +64 -6
- data/lib/azeroth/resource_route_builder.rb +112 -9
- data/lib/azeroth/resourceable.rb +5 -1
- data/lib/azeroth/resourceable/builder.rb +104 -9
- data/lib/azeroth/routes_builder.rb +54 -20
- data/lib/azeroth/version.rb +3 -1
- data/spec/lib/azeroth/model_spec.rb +7 -5
- data/spec/lib/azeroth/resource_builder_spec.rb +5 -3
- data/spec/lib/azeroth/resource_route_builder_spec.rb +6 -2
- data/spec/lib/azeroth/resourceable/builder_spec.rb +6 -1
- data/spec/lib/azeroth/routes_builder_spec.rb +14 -8
- data/spec/spec_helper.rb +5 -1
- data/spec/support/app/controllers/controller.rb +2 -0
- data/spec/support/app/controllers/resource_builder_controller.rb +2 -0
- data/spec/support/app/controllers/resource_route_builder_controller.rb +2 -0
- data/spec/support/app/controllers/routes_builder_controller.rb +2 -0
- data/spec/support/app/models/document.rb +2 -0
- data/spec/support/app/serializers/serializer.rb +2 -0
- data/spec/support/matchers/add_method.rb +6 -2
- data/spec/support/schema.rb +2 -0
- data/spec/support/shared_examples/resource_routes.rb +5 -3
- metadata +151 -51
- data/scripts/check_readme.sh +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 932722b68ea70d25532bae9611143e1858edbe50b38459bf1134b6429ec960e0
|
4
|
+
data.tar.gz: 77b46098f931ba91e40e00b28b8d4fe823cc81bdaeebcbb8f9e44a0d2ab150c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4bad15f9ba9d71be3da8a48507cd3a96b9834892ed14001abd3122b780d465a787da7c34979222f7b13a1603c5510265c6b09ea9e92f7b176f9a6307741b737
|
7
|
+
data.tar.gz: 8c437bf58301cfc6a28893b7bdff6390beed0640c843be66d662505cfb582200e7f847333ac6d7b5b4719e4bb3fb5af26408844d62899e77846e14fceef229f0
|
data/.circleci/config.yml
CHANGED
@@ -2,7 +2,9 @@ version: 2
|
|
2
2
|
jobs:
|
3
3
|
build:
|
4
4
|
docker:
|
5
|
-
- image: darthjee/circleci_ruby_gems:0.
|
5
|
+
- image: darthjee/circleci_ruby_gems:0.2.3
|
6
|
+
environment:
|
7
|
+
PROJECT: azeroth
|
6
8
|
steps:
|
7
9
|
- checkout
|
8
10
|
- run:
|
@@ -25,4 +27,10 @@ jobs:
|
|
25
27
|
command: bundle exec rake verify_measurements
|
26
28
|
- run:
|
27
29
|
name: Check version documentation
|
28
|
-
command:
|
30
|
+
command: check_readme.sh
|
31
|
+
- run:
|
32
|
+
name: Rubycritcs check
|
33
|
+
command: rubycritic.sh
|
34
|
+
- run:
|
35
|
+
name: Check unit tests
|
36
|
+
command: check_specs
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1 +1,17 @@
|
|
1
|
+
require: rubocop-rspec
|
1
2
|
inherit_from: .rubocop_todo.yml
|
3
|
+
|
4
|
+
AllCops:
|
5
|
+
TargetRubyVersion: 2.5
|
6
|
+
|
7
|
+
Metrics/BlockLength:
|
8
|
+
Exclude:
|
9
|
+
- 'spec/**/*_spec.rb'
|
10
|
+
- 'spec/support/shared_*/**/*.rb'
|
11
|
+
- 'azeroth.gemspec'
|
12
|
+
|
13
|
+
RSpec/NestedGroups:
|
14
|
+
Max: 4
|
15
|
+
|
16
|
+
RSpec/AlignLeftLetBrace:
|
17
|
+
Enabled: true
|
data/.rubocop_todo.yml
CHANGED
@@ -1,40 +1,21 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on
|
3
|
+
# on 2019-03-26 17:30:42 +0000 using RuboCop version 0.58.1.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
8
8
|
|
9
|
-
# Offense count:
|
10
|
-
Metrics/AbcSize:
|
11
|
-
Max: 22
|
12
|
-
|
13
|
-
# Offense count: 2
|
14
|
-
# Configuration parameters: CountComments, ExcludedMethods.
|
15
|
-
Metrics/BlockLength:
|
16
|
-
Max: 58
|
17
|
-
|
18
|
-
# Offense count: 1
|
19
|
-
# Configuration parameters: CountComments.
|
20
|
-
Metrics/MethodLength:
|
21
|
-
Max: 11
|
22
|
-
|
23
|
-
# Offense count: 7
|
9
|
+
# Offense count: 8
|
24
10
|
Style/Documentation:
|
25
11
|
Exclude:
|
26
12
|
- 'spec/**/*'
|
27
13
|
- 'test/**/*'
|
28
14
|
- 'lib/azeroth.rb'
|
29
15
|
- 'lib/azeroth/model.rb'
|
30
|
-
- 'lib/azeroth/
|
31
|
-
- 'lib/azeroth/resourceable/builder.rb'
|
16
|
+
- 'lib/azeroth/options.rb'
|
32
17
|
- 'lib/azeroth/resource_builder.rb'
|
33
18
|
- 'lib/azeroth/resource_route_builder.rb'
|
19
|
+
- 'lib/azeroth/resourceable.rb'
|
20
|
+
- 'lib/azeroth/resourceable/builder.rb'
|
34
21
|
- 'lib/azeroth/routes_builder.rb'
|
35
|
-
|
36
|
-
# Offense count: 6
|
37
|
-
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
38
|
-
# URISchemes: http, https
|
39
|
-
Metrics/LineLength:
|
40
|
-
Max: 121
|
data/Dockerfile
CHANGED
@@ -1,6 +1,30 @@
|
|
1
|
-
FROM darthjee/ruby_gems:0.
|
1
|
+
FROM darthjee/ruby_gems:0.2.3 as base
|
2
|
+
FROM darthjee/scripts:0.1.2 as scripts
|
3
|
+
|
4
|
+
######################################
|
5
|
+
|
6
|
+
FROM base as builder
|
2
7
|
|
3
|
-
USER app
|
4
8
|
COPY --chown=app ./ /home/app/app/
|
9
|
+
COPY --chown=app:app --from=scripts /home/scripts/builder/bundle_builder.sh /usr/local/sbin/
|
10
|
+
|
11
|
+
ENV HOME_DIR /home/app
|
12
|
+
RUN bundle_builder.sh
|
5
13
|
|
14
|
+
#######################
|
15
|
+
#FINAL IMAGE
|
16
|
+
FROM base
|
17
|
+
RUN mkdir lib/azeroth -p
|
18
|
+
|
19
|
+
USER root
|
20
|
+
|
21
|
+
COPY --chown=app:app --from=builder /home/app/bundle/gems /usr/local/bundle/gems
|
22
|
+
COPY --chown=app:app --from=builder /home/app/bundle/cache /usr/local/bundle/cache
|
23
|
+
COPY --chown=app:app --from=builder /home/app/bundle/specifications /usr/local/bundle/specifications
|
24
|
+
COPY --chown=app:app --from=builder /home/app/bundle/bin /usr/local/bundle/bin
|
25
|
+
COPY --chown=app:app --from=builder /home/app/bundle/extensions /usr/local/bundle/extensions
|
26
|
+
|
27
|
+
COPY --chown=app ./*.gemspec ./Gemfile /home/app/app/
|
28
|
+
COPY --chown=app ./lib/azeroth/version.rb /home/app/app/lib/azeroth/
|
29
|
+
USER app
|
6
30
|
RUN bundle install
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -3,9 +3,10 @@ Azeroth
|
|
3
3
|
[](https://codeclimate.com/github/darthjee/azeroth)
|
4
4
|
[](https://codeclimate.com/github/darthjee/azeroth/coverage)
|
5
5
|
[](https://codeclimate.com/github/darthjee/azeroth)
|
6
|
+
[](https://badge.fury.io/rb/azeroth)
|
6
7
|
|
7
8
|

|
8
9
|
|
9
10
|
Yard Documentation
|
10
11
|
-------------------
|
11
|
-
https://www.rubydoc.info/gems/azeroth/0.0.
|
12
|
+
https://www.rubydoc.info/gems/azeroth/0.0.5
|
data/Rakefile
CHANGED
data/azeroth.gemspec
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
lib = File.expand_path('lib', __dir__)
|
2
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
5
|
require 'azeroth/version'
|
@@ -16,22 +18,27 @@ Gem::Specification.new do |gem|
|
|
16
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
19
|
gem.require_paths = ['lib']
|
18
20
|
|
19
|
-
gem.add_runtime_dependency '
|
20
|
-
|
21
|
-
gem.add_runtime_dependency 'activesupport', '~> 5.x'
|
21
|
+
gem.add_runtime_dependency 'activesupport', '~> 5.2.0'
|
22
22
|
gem.add_runtime_dependency 'darthjee-active_ext', '>= 1.3.2'
|
23
|
+
gem.add_runtime_dependency 'sinclair', '>= 1.5.2'
|
23
24
|
|
24
25
|
gem.add_development_dependency 'actionpack', '~> 5.x'
|
25
26
|
gem.add_development_dependency 'activerecord', '~> 5.x'
|
27
|
+
gem.add_development_dependency 'bundler', '~> 1.16.1'
|
28
|
+
gem.add_development_dependency 'pry', '0.12.2'
|
29
|
+
gem.add_development_dependency 'pry-nav', '0.3.0'
|
30
|
+
gem.add_development_dependency 'rake', '12.3.3'
|
31
|
+
gem.add_development_dependency 'reek', '5.4.0'
|
32
|
+
gem.add_development_dependency 'rspec', '3.8.0'
|
33
|
+
gem.add_development_dependency 'rspec-core', '3.8.0'
|
34
|
+
gem.add_development_dependency 'rspec-expectations', '3.8.3'
|
35
|
+
gem.add_development_dependency 'rspec-mocks', '3.8.0'
|
36
|
+
gem.add_development_dependency 'rspec-support', '3.8.0'
|
37
|
+
gem.add_development_dependency 'rubocop', '0.73.0'
|
38
|
+
gem.add_development_dependency 'rubocop-rspec', '1.33.0'
|
39
|
+
gem.add_development_dependency 'rubycritic', '4.1.0'
|
40
|
+
gem.add_development_dependency 'simplecov', '0.17.0'
|
26
41
|
gem.add_development_dependency 'sqlite3', '>= 1.3.13'
|
27
|
-
|
28
|
-
gem.add_development_dependency '
|
29
|
-
gem.add_development_dependency 'pry-nav', '~> 0.2.4'
|
30
|
-
gem.add_development_dependency 'rake', '>= 12.3.1'
|
31
|
-
gem.add_development_dependency 'rspec', '>= 3.8'
|
32
|
-
gem.add_development_dependency 'rubocop', '0.58.1'
|
33
|
-
gem.add_development_dependency 'rubocop-rspec', '1.30.0'
|
34
|
-
gem.add_development_dependency 'simplecov', '~> 0.16.x'
|
35
|
-
gem.add_development_dependency 'yard', '>= 0.9.18'
|
36
|
-
gem.add_development_dependency 'yardstick', '>= 0.9.9'
|
42
|
+
gem.add_development_dependency 'yard', '0.9.20'
|
43
|
+
gem.add_development_dependency 'yardstick', '0.9.9'
|
37
44
|
end
|
data/config/rubycritc.rb
ADDED
data/config/yardstick.yml
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
threshold:
|
1
|
+
threshold: 98.5
|
2
2
|
require_exact_threshold: false
|
3
3
|
rules:
|
4
4
|
ApiTag::Presence:
|
@@ -21,7 +21,12 @@ rules:
|
|
21
21
|
exclude: []
|
22
22
|
Summary::Presence:
|
23
23
|
enabled: true
|
24
|
-
exclude:
|
24
|
+
exclude:
|
25
|
+
- Azeroth::Model#initialize
|
26
|
+
- Azeroth::ResourceBuilder#initialize
|
27
|
+
- Azeroth::ResourceRouteBuilder#initialize
|
28
|
+
- Azeroth::Resourceable::Builder#initialize
|
29
|
+
- Azeroth::RoutesBuilder#initialize
|
25
30
|
Summary::Length:
|
26
31
|
enabled: true
|
27
32
|
exclude: []
|
data/lib/azeroth.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Azeroth
|
2
|
-
autoload :Resourceable, 'azeroth/resourceable'
|
3
4
|
autoload :Model, 'azeroth/model'
|
4
|
-
autoload :
|
5
|
-
autoload :ResourceRouteBuilder, 'azeroth/resource_route_builder'
|
5
|
+
autoload :Resourceable, 'azeroth/resourceable'
|
6
6
|
autoload :ResourceBuilder, 'azeroth/resource_builder'
|
7
|
+
autoload :ResourceRouteBuilder, 'azeroth/resource_route_builder'
|
8
|
+
autoload :RoutesBuilder, 'azeroth/routes_builder'
|
9
|
+
autoload :Options, 'azeroth/options'
|
7
10
|
end
|
data/lib/azeroth/model.rb
CHANGED
@@ -1,15 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Azeroth
|
4
|
+
# @api private
|
5
|
+
# @author Darthjee
|
6
|
+
#
|
7
|
+
# Model responsible for making the conection to the resource model class
|
2
8
|
class Model
|
3
9
|
attr_reader :name
|
10
|
+
# @method name
|
11
|
+
# @api private
|
12
|
+
#
|
13
|
+
# Returns the name of the resource represented by the model
|
14
|
+
#
|
15
|
+
# @return [String]
|
4
16
|
|
17
|
+
# @param name [String,Symbol] name of the resource
|
5
18
|
def initialize(name)
|
6
|
-
@name
|
19
|
+
@name = name.to_s
|
7
20
|
end
|
8
21
|
|
22
|
+
# Resource class (real model class)
|
23
|
+
#
|
24
|
+
# @return [Class]
|
9
25
|
def klass
|
10
26
|
@klass ||= name.camelize.constantize
|
11
27
|
end
|
12
28
|
|
29
|
+
# Return the pluralized version of resource name
|
30
|
+
#
|
31
|
+
# @return [String]
|
13
32
|
def plural
|
14
33
|
name.pluralize
|
15
34
|
end
|
@@ -1,17 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Azeroth
|
4
|
+
# @api private
|
5
|
+
# @author Darthjee
|
6
|
+
#
|
7
|
+
# Builder responsible for for building the resource methods
|
8
|
+
#
|
9
|
+
# The builder adds 2 methods, one for listing all
|
10
|
+
# entries of a resource, and one for fetching an specific
|
11
|
+
# entry
|
2
12
|
class ResourceBuilder
|
3
|
-
|
4
|
-
|
5
|
-
delegate :add_method, to: :builder
|
6
|
-
|
13
|
+
# @param model [Model] Resource model interface
|
14
|
+
# @param builder [Sinclair] method builder
|
7
15
|
def initialize(model, builder)
|
8
16
|
@model = model
|
9
17
|
@builder = builder
|
10
18
|
end
|
11
19
|
|
20
|
+
# Append methods to be built to the builder
|
21
|
+
#
|
22
|
+
# Methods are the listing of all entities and fetching
|
23
|
+
# of an specific entity
|
24
|
+
#
|
25
|
+
# @return [Array<Sinclair::MethodDefinition>]
|
12
26
|
def append
|
13
|
-
add_method(
|
14
|
-
add_method(
|
27
|
+
add_method(plural, "@#{plural} ||= #{model.klass}.all")
|
28
|
+
add_method(name, "@#{name} ||= #{plural}.find(#{name}_id)")
|
15
29
|
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :model, :builder
|
34
|
+
# @method model
|
35
|
+
# @api private
|
36
|
+
# @private
|
37
|
+
#
|
38
|
+
# Returns the model class of the resource
|
39
|
+
#
|
40
|
+
# @return [Model]
|
41
|
+
|
42
|
+
# @method builder
|
43
|
+
# @api private
|
44
|
+
# @private
|
45
|
+
#
|
46
|
+
# Returns a method builder
|
47
|
+
#
|
48
|
+
# @return [Sinclair]
|
49
|
+
#
|
50
|
+
# @see https://www.rubydoc.info/gems/sinclair Sinclair
|
51
|
+
|
52
|
+
delegate :add_method, to: :builder
|
53
|
+
# @api private
|
54
|
+
# @method add_method
|
55
|
+
#
|
56
|
+
# Add a method to be build on the controller
|
57
|
+
#
|
58
|
+
# @return [Array<Sinclair::MethodDefinition>]
|
59
|
+
|
60
|
+
delegate :name, :plural, to: :model
|
61
|
+
# @method name
|
62
|
+
# @api private
|
63
|
+
#
|
64
|
+
# Returns the name of the resource represented by the model
|
65
|
+
#
|
66
|
+
# @return [String]
|
67
|
+
|
68
|
+
# @method plural
|
69
|
+
# @api private
|
70
|
+
#
|
71
|
+
# Return the pluralized version of resource name
|
72
|
+
#
|
73
|
+
# @return [String]
|
16
74
|
end
|
17
75
|
end
|
@@ -1,21 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Azeroth
|
4
|
+
# @api private
|
5
|
+
# @author Darthjee
|
6
|
+
#
|
7
|
+
# Builder for resources for each route
|
8
|
+
#
|
9
|
+
# For each route method in the controller, there
|
10
|
+
# should be a resource, index should have listing
|
11
|
+
# of all entries, update should update and return
|
12
|
+
# updated resource, new should return an empty object,
|
13
|
+
# etc..
|
2
14
|
class ResourceRouteBuilder
|
3
|
-
|
4
|
-
|
5
|
-
delegate :add_method, to: :builder
|
6
|
-
|
15
|
+
# @param (see ResourceBuilder#initialize)
|
7
16
|
def initialize(model, builder)
|
8
17
|
@model = model
|
9
18
|
@builder = builder
|
10
19
|
end
|
11
20
|
|
21
|
+
# Append methods to be build to the builder
|
22
|
+
#
|
23
|
+
# The methods to be added are resources related
|
24
|
+
# to the routes:
|
25
|
+
# - new
|
26
|
+
# - show
|
27
|
+
# - create
|
28
|
+
# - update
|
29
|
+
# - edit
|
30
|
+
# - index
|
31
|
+
#
|
32
|
+
# @return [Array<Sinclair::MethodDefinition>]
|
12
33
|
def append
|
13
|
-
|
14
|
-
|
15
|
-
|
34
|
+
add_new_reource
|
35
|
+
add_create_resource
|
36
|
+
add_update_resource
|
37
|
+
|
16
38
|
add_method(:index_resource, model.plural)
|
17
|
-
add_method(:edit_resource,
|
18
|
-
add_method(:show_resource,
|
39
|
+
add_method(:edit_resource, name)
|
40
|
+
add_method(:show_resource, name)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
attr_reader :model, :builder
|
46
|
+
# @method model
|
47
|
+
# @api private
|
48
|
+
# @private
|
49
|
+
#
|
50
|
+
# Returns the model class of the resource
|
51
|
+
#
|
52
|
+
# @return [Model]
|
53
|
+
|
54
|
+
# @method builder
|
55
|
+
# @api private
|
56
|
+
# @private
|
57
|
+
#
|
58
|
+
# Returns a method builder
|
59
|
+
#
|
60
|
+
# @return [Sinclair]
|
61
|
+
#
|
62
|
+
# @see https://www.rubydoc.info/gems/sinclair Sinclair
|
63
|
+
|
64
|
+
delegate :add_method, to: :builder
|
65
|
+
# @api private
|
66
|
+
# @method add_method
|
67
|
+
#
|
68
|
+
# Add a method to be build on the controller
|
69
|
+
#
|
70
|
+
# @return [Array<Sinclair::MethodDefinition>]
|
71
|
+
|
72
|
+
delegate :name, :klass, to: :model
|
73
|
+
# @method name
|
74
|
+
# @api private
|
75
|
+
#
|
76
|
+
# Returns the name of the resource represented by the model
|
77
|
+
#
|
78
|
+
# @return [String]
|
79
|
+
|
80
|
+
# @method klass
|
81
|
+
# @api private
|
82
|
+
#
|
83
|
+
# Resource class (real model class)
|
84
|
+
#
|
85
|
+
# @return [Class]
|
86
|
+
|
87
|
+
# Adds the reource for new route
|
88
|
+
#
|
89
|
+
# It returns a new empty object
|
90
|
+
#
|
91
|
+
# @return [Array<Sinclair::MethodDefinition>]
|
92
|
+
def add_new_reource
|
93
|
+
add_method(:new_resource, "@new_resource ||= #{klass}.new")
|
94
|
+
end
|
95
|
+
|
96
|
+
# Adds the reource for create route
|
97
|
+
#
|
98
|
+
# It creates an entry in the database from the parameters
|
99
|
+
#
|
100
|
+
# @return [Array<Sinclair::MethodDefinition>]
|
101
|
+
def add_create_resource
|
102
|
+
add_method(
|
103
|
+
:create_resource,
|
104
|
+
"@create_resource ||= #{klass}.create(#{name}_params)"
|
105
|
+
)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Adds the reource for update route
|
109
|
+
#
|
110
|
+
# It updates an entry in the database from the parameters
|
111
|
+
#
|
112
|
+
# @return [Array<Sinclair::MethodDefinition>]
|
113
|
+
def add_update_resource
|
114
|
+
add_method(
|
115
|
+
:update_resource,
|
116
|
+
<<-CODE
|
117
|
+
@update_resource ||= #{name}.tap do |value|
|
118
|
+
value.update(#{name}_params)
|
119
|
+
end
|
120
|
+
CODE
|
121
|
+
)
|
19
122
|
end
|
20
123
|
end
|
21
124
|
end
|