r2-oas 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +99 -0
  3. data/GEMSPEC.md +20 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.ja.md +271 -0
  6. data/README.md +271 -0
  7. data/lib/r2-oas.rb +35 -0
  8. data/lib/r2-oas/app_configuration.rb +121 -0
  9. data/lib/r2-oas/app_configuration/deprecation.rb +28 -0
  10. data/lib/r2-oas/app_configuration/server.rb +35 -0
  11. data/lib/r2-oas/app_configuration/swagger.rb +35 -0
  12. data/lib/r2-oas/app_configuration/swagger/editor.rb +47 -0
  13. data/lib/r2-oas/app_configuration/swagger/ui.rb +45 -0
  14. data/lib/r2-oas/app_configuration/tool.rb +31 -0
  15. data/lib/r2-oas/app_configuration/tool/paths/stats.rb +43 -0
  16. data/lib/r2-oas/base.rb +48 -0
  17. data/lib/r2-oas/configuration.rb +121 -0
  18. data/lib/r2-oas/configuration/paths_config.rb +44 -0
  19. data/lib/r2-oas/deploy/client.rb +70 -0
  20. data/lib/r2-oas/deploy/swagger-ui/index.html.erb +60 -0
  21. data/lib/r2-oas/dynamic/schema/v3/object/from_routes/hookable_base_object.rb +100 -0
  22. data/lib/r2-oas/errors.rb +13 -0
  23. data/lib/r2-oas/hooks/global_hook.rb +20 -0
  24. data/lib/r2-oas/hooks/hook.rb +79 -0
  25. data/lib/r2-oas/hooks/repository.rb +15 -0
  26. data/lib/r2-oas/lib/core_ext/hash/deep_merge.rb +44 -0
  27. data/lib/r2-oas/lib/core_ext/object/blank.rb +135 -0
  28. data/lib/r2-oas/lib/three-way-merge/twm.rb +83 -0
  29. data/lib/r2-oas/logger/stdout_logger.rb +129 -0
  30. data/lib/r2-oas/pluggable_configuration.rb +36 -0
  31. data/lib/r2-oas/plugin/base.rb +44 -0
  32. data/lib/r2-oas/plugin/executor.rb +148 -0
  33. data/lib/r2-oas/plugin/hookable.rb +42 -0
  34. data/lib/r2-oas/plugin/public.rb +3 -0
  35. data/lib/r2-oas/plugin/transform/transform.rb +10 -0
  36. data/lib/r2-oas/plugin/transform/v3/transform.rb +20 -0
  37. data/lib/r2-oas/plugin/transform/v3/visitable.rb +37 -0
  38. data/lib/r2-oas/public.rb +5 -0
  39. data/lib/r2-oas/routing/adjustor.rb +44 -0
  40. data/lib/r2-oas/routing/base.rb +12 -0
  41. data/lib/r2-oas/routing/components/all.rb +5 -0
  42. data/lib/r2-oas/routing/components/base_component.rb +10 -0
  43. data/lib/r2-oas/routing/components/path_component.rb +67 -0
  44. data/lib/r2-oas/routing/components/request_component.rb +75 -0
  45. data/lib/r2-oas/routing/components/verb_component.rb +21 -0
  46. data/lib/r2-oas/routing/parser.rb +93 -0
  47. data/lib/r2-oas/schema/analyzer.rb +23 -0
  48. data/lib/r2-oas/schema/base.rb +11 -0
  49. data/lib/r2-oas/schema/builder.rb +23 -0
  50. data/lib/r2-oas/schema/cleaner.rb +23 -0
  51. data/lib/r2-oas/schema/editor.rb +146 -0
  52. data/lib/r2-oas/schema/generator.rb +23 -0
  53. data/lib/r2-oas/schema/manager/file/path_item_file_manager.rb +24 -0
  54. data/lib/r2-oas/schema/manager/file_manager.rb +26 -0
  55. data/lib/r2-oas/schema/monitor.rb +52 -0
  56. data/lib/r2-oas/schema/squeezer.rb +23 -0
  57. data/lib/r2-oas/schema/ui.rb +73 -0
  58. data/lib/r2-oas/schema/v3/analyzer.rb +58 -0
  59. data/lib/r2-oas/schema/v3/analyzer/base_analyzer.rb +71 -0
  60. data/lib/r2-oas/schema/v3/analyzer/components/object_analyzer.rb +38 -0
  61. data/lib/r2-oas/schema/v3/analyzer/components_analyzer.rb +30 -0
  62. data/lib/r2-oas/schema/v3/analyzer/path_analyzer.rb +116 -0
  63. data/lib/r2-oas/schema/v3/analyzer/tag_analyzer.rb +38 -0
  64. data/lib/r2-oas/schema/v3/base.rb +28 -0
  65. data/lib/r2-oas/schema/v3/builder.rb +28 -0
  66. data/lib/r2-oas/schema/v3/builder/base_builder.rb +72 -0
  67. data/lib/r2-oas/schema/v3/builder/doc_builder.rb +51 -0
  68. data/lib/r2-oas/schema/v3/cleaner.rb +19 -0
  69. data/lib/r2-oas/schema/v3/cleaner/base_cleaner.rb +30 -0
  70. data/lib/r2-oas/schema/v3/cleaner/components_cleaner.rb +42 -0
  71. data/lib/r2-oas/schema/v3/generator.rb +24 -0
  72. data/lib/r2-oas/schema/v3/generator/base_generator.rb +122 -0
  73. data/lib/r2-oas/schema/v3/generator/components/object_generator.rb +64 -0
  74. data/lib/r2-oas/schema/v3/generator/components/request_body_generator.rb +45 -0
  75. data/lib/r2-oas/schema/v3/generator/components_generator.rb +38 -0
  76. data/lib/r2-oas/schema/v3/generator/doc_generator.rb +115 -0
  77. data/lib/r2-oas/schema/v3/generator/path_generator.rb +78 -0
  78. data/lib/r2-oas/schema/v3/generator/schema_generator.rb +58 -0
  79. data/lib/r2-oas/schema/v3/manager/diff/base_array_diff_manager.rb +60 -0
  80. data/lib/r2-oas/schema/v3/manager/diff/base_diff_manager.rb +29 -0
  81. data/lib/r2-oas/schema/v3/manager/diff/base_hash_diff_manager.rb +95 -0
  82. data/lib/r2-oas/schema/v3/manager/diff/components_diff_manager.rb +19 -0
  83. data/lib/r2-oas/schema/v3/manager/diff/tag_diff_manager.rb +17 -0
  84. data/lib/r2-oas/schema/v3/manager/file/base_file_manager.rb +66 -0
  85. data/lib/r2-oas/schema/v3/manager/file/components_file_manager.rb +22 -0
  86. data/lib/r2-oas/schema/v3/manager/file/include_ref_base_file_manager.rb +89 -0
  87. data/lib/r2-oas/schema/v3/manager/file/path_item_file_manager.rb +22 -0
  88. data/lib/r2-oas/schema/v3/manager/file_manager.rb +12 -0
  89. data/lib/r2-oas/schema/v3/manager/pathname_manager.rb +78 -0
  90. data/lib/r2-oas/schema/v3/object/from_files/base_object.rb +62 -0
  91. data/lib/r2-oas/schema/v3/object/from_files/components/request_body_object.rb +103 -0
  92. data/lib/r2-oas/schema/v3/object/from_files/components/schema_object.rb +102 -0
  93. data/lib/r2-oas/schema/v3/object/from_files/components_object.rb +46 -0
  94. data/lib/r2-oas/schema/v3/object/from_files/external_document_object.rb +23 -0
  95. data/lib/r2-oas/schema/v3/object/from_files/info_object.rb +23 -0
  96. data/lib/r2-oas/schema/v3/object/from_files/openapi_object.rb +61 -0
  97. data/lib/r2-oas/schema/v3/object/from_files/path_item_object.rb +110 -0
  98. data/lib/r2-oas/schema/v3/object/from_files/paths_object.rb +33 -0
  99. data/lib/r2-oas/schema/v3/object/from_files/utils/all.rb +4 -0
  100. data/lib/r2-oas/schema/v3/object/from_files/utils/deep_methods.rb +27 -0
  101. data/lib/r2-oas/schema/v3/object/from_files/utils/refs.rb +151 -0
  102. data/lib/r2-oas/schema/v3/object/from_routes/base_object.rb +75 -0
  103. data/lib/r2-oas/schema/v3/object/from_routes/components/request_body_object.rb +96 -0
  104. data/lib/r2-oas/schema/v3/object/from_routes/components/schema_object.rb +59 -0
  105. data/lib/r2-oas/schema/v3/object/from_routes/components_object.rb +81 -0
  106. data/lib/r2-oas/schema/v3/object/from_routes/external_document_object.rb +28 -0
  107. data/lib/r2-oas/schema/v3/object/from_routes/info_object.rb +43 -0
  108. data/lib/r2-oas/schema/v3/object/from_routes/openapi_object.rb +59 -0
  109. data/lib/r2-oas/schema/v3/object/from_routes/path_item_object.rb +175 -0
  110. data/lib/r2-oas/schema/v3/object/from_routes/paths_object.rb +77 -0
  111. data/lib/r2-oas/schema/v3/object/from_routes/public.rb +9 -0
  112. data/lib/r2-oas/schema/v3/object/from_routes/server_object.rb +21 -0
  113. data/lib/r2-oas/schema/v3/object/from_routes/tag_object.rb +37 -0
  114. data/lib/r2-oas/schema/v3/object/store.rb +54 -0
  115. data/lib/r2-oas/schema/v3/squeezer.rb +29 -0
  116. data/lib/r2-oas/schema/v3/squeezer/base_squeezer.rb +37 -0
  117. data/lib/r2-oas/schema/v3/squeezer/path_squeezer.rb +28 -0
  118. data/lib/r2-oas/schema/v3/squeezer/tag_squeezer.rb +19 -0
  119. data/lib/r2-oas/shared/all.rb +4 -0
  120. data/lib/r2-oas/shared/callable.rb +17 -0
  121. data/lib/r2-oas/shared/sortable.rb +23 -0
  122. data/lib/r2-oas/store.rb +122 -0
  123. data/lib/r2-oas/support/deprecation.rb +24 -0
  124. data/lib/r2-oas/support/deprecation/behavior.rb +21 -0
  125. data/lib/r2-oas/support/deprecation/instance_delegator.rb +42 -0
  126. data/lib/r2-oas/support/deprecation/reporting.rb +91 -0
  127. data/lib/r2-oas/task.rb +11 -0
  128. data/lib/r2-oas/task_logging.rb +41 -0
  129. data/lib/r2-oas/tasks/common.rake +25 -0
  130. data/lib/r2-oas/tasks/main.rake +132 -0
  131. data/lib/r2-oas/tasks/tool.rake +86 -0
  132. data/lib/r2-oas/tool/paths/ls.rb +15 -0
  133. data/lib/r2-oas/tool/paths/stats.rb +84 -0
  134. data/lib/r2-oas/version.rb +5 -0
  135. data/r2-oas.gemspec +48 -0
  136. metadata +415 -0
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_object'
4
+ require_relative 'path_item_object'
5
+
6
+ module R2OAS
7
+ module Schema
8
+ module V3
9
+ module FromFiles
10
+ class PathsObject < BaseObject
11
+ def initialize(doc, opts = {})
12
+ super(opts)
13
+ @doc = doc
14
+ end
15
+
16
+ def to_doc
17
+ create_doc
18
+ @doc
19
+ end
20
+
21
+ private
22
+
23
+ def create_doc
24
+ @doc.each do |path, doc_when_path|
25
+ ref = { path: path }
26
+ @doc[path] = PathItemObject.new(doc_when_path, ref, opts).to_doc
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'deep_methods'
4
+ require_relative 'refs'
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module R2OAS
4
+ module Schema
5
+ module V3
6
+ module FromFiles
7
+ module DeepMethods
8
+ def deep_replace!(data, target, &blk)
9
+ return unless data.is_a?(Hash)
10
+
11
+ data.each do |key, value|
12
+ if key.eql? target
13
+ # MEMO:
14
+ # When using the same schema, it has already been replaced by an object
15
+ if value.is_a?(String)
16
+ data[key] = block_given? ? yield(value) : value
17
+ end
18
+ else
19
+ deep_replace!(value, target, &blk)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'r2-oas/errors'
4
+
5
+ module R2OAS
6
+ module Schema
7
+ module V3
8
+ module FromFiles
9
+ class BaseRef
10
+ def pretty_print(q)
11
+ pp_hash(q, to_h)
12
+ end
13
+
14
+ attr_reader :parent
15
+
16
+ def []=(key, value)
17
+ if writable_keys.include?(key)
18
+ send(:"#{key}=", value)
19
+ value
20
+ else
21
+ display_key = key.is_a?(Symbol) ? ":#{key}" : key
22
+ raise ::R2OAS::RefInvalidAssignment, "invalid method `[#{display_key}]=' called for #{self}"
23
+ end
24
+ end
25
+
26
+ def [](key)
27
+ send(key)
28
+ end
29
+
30
+ def to_h
31
+ valid_keys.each_with_object({}) do |key, result|
32
+ result[key] = send(key)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ # MEMO:
39
+ # https://apidock.com/ruby/v1_9_3_392/PP/PPMethods/pp_hash
40
+ def pp_hash(q, obj)
41
+ q.group(1, '{', '}') do
42
+ q.seplist(obj, nil, :each_pair) do |k, v|
43
+ q.group do
44
+ q.pp k
45
+ q.text '=>'
46
+ q.group(1) do
47
+ q.breakable ''
48
+ q.pp v
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ def build(ref_or_data, merge_data = {})
56
+ data = ref_or_data.to_h.merge(merge_data)
57
+ data.keys.each do |key|
58
+ instance_variable_set(:"@#{key}", data[key])
59
+ end
60
+
61
+ @parent = ref_or_data if ref_or_data.respond_to?(:parent)
62
+ end
63
+
64
+ attr_writer :parent
65
+
66
+ def valid_keys
67
+ raise NoImplementError, 'Please implement in inherited class.'
68
+ end
69
+
70
+ def writable_keys
71
+ raise NoImplementError, 'Please implement in inherited class.'
72
+ end
73
+ end
74
+
75
+ class PathRef < BaseRef
76
+ VALID_KEYS = %i[type path].freeze
77
+
78
+ attr_reader(*VALID_KEYS)
79
+
80
+ def initialize(data)
81
+ build(data, { type: :path_item })
82
+ end
83
+
84
+ private
85
+
86
+ attr_writer(*VALID_KEYS)
87
+
88
+ def valid_keys
89
+ VALID_KEYS
90
+ end
91
+
92
+ def writable_keys
93
+ []
94
+ end
95
+ end
96
+
97
+ module Components
98
+ class SchemaRef < BaseRef
99
+ READONLY_KEYS = %i[type path parent_schema_name depth tag_name verb http_status from].freeze
100
+ WRITABLE_KEYS = [:schema_name].freeze
101
+ VALID_KEYS = READONLY_KEYS + WRITABLE_KEYS
102
+
103
+ attr_reader(*VALID_KEYS)
104
+ attr_writer(*WRITABLE_KEYS)
105
+
106
+ def initialize(ref_or_data)
107
+ build(ref_or_data, { type: :schema })
108
+ end
109
+
110
+ private
111
+
112
+ attr_writer(*READONLY_KEYS)
113
+
114
+ def valid_keys
115
+ VALID_KEYS
116
+ end
117
+
118
+ def writable_keys
119
+ WRITABLE_KEYS
120
+ end
121
+ end
122
+
123
+ class RequestBodyRef < BaseRef
124
+ READONLY_KEYS = %i[type path parent_schema_name depth tag_name verb from].freeze
125
+ WRITABLE_KEYS = [:schema_name].freeze
126
+ VALID_KEYS = READONLY_KEYS + WRITABLE_KEYS
127
+
128
+ attr_accessor(*VALID_KEYS)
129
+ attr_writer(*WRITABLE_KEYS)
130
+
131
+ def initialize(ref_or_data)
132
+ build(ref_or_data, { type: :request_body })
133
+ end
134
+
135
+ private
136
+
137
+ attr_writer(*READONLY_KEYS)
138
+
139
+ def valid_keys
140
+ VALID_KEYS
141
+ end
142
+
143
+ def writable_keys
144
+ WRITABLE_KEYS
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'r2-oas/plugin/executor'
5
+
6
+ module R2OAS
7
+ module Schema
8
+ module V3
9
+ class BaseObject
10
+ extend Forwardable
11
+
12
+ def_delegators :@plugin_executor, :execute_transform_plugins
13
+
14
+ def initialize(opts = {})
15
+ AppConfiguration::VALID_OPTIONS_KEYS.each do |key|
16
+ send("#{key}=", app_configuration_options[key])
17
+ end
18
+
19
+ PluggableConfiguration::VALID_OPTIONS_KEYS.each do |key|
20
+ instance_variable_set(:"@#{key}", pluggable_configuration_options[key])
21
+ end
22
+
23
+ @opts = opts
24
+ @plugin_executor = ::R2OAS::Plugin::Executor.new(@plugins, opts)
25
+ end
26
+
27
+ def info_object_class
28
+ @use_object_classes[:info_object]
29
+ end
30
+
31
+ def paths_object_class
32
+ @use_object_classes[:paths_object]
33
+ end
34
+
35
+ def path_item_object_class
36
+ @use_object_classes[:path_item_object]
37
+ end
38
+
39
+ def external_document_object_class
40
+ @use_object_classes[:external_document_object]
41
+ end
42
+
43
+ def components_object_class
44
+ @use_object_classes[:components_object]
45
+ end
46
+
47
+ def components_schema_object_class
48
+ @use_object_classes[:components_schema_object]
49
+ end
50
+
51
+ def components_request_body_object_class
52
+ @use_object_classes[:components_request_body_object]
53
+ end
54
+
55
+ private
56
+
57
+ def app_configuration_options
58
+ R2OAS.app_configuration_options
59
+ end
60
+
61
+ def pluggable_configuration_options
62
+ R2OAS.pluggable_configuration_options
63
+ end
64
+
65
+ # Can not define attr_accessor for PluggableConfiguration::VALID_OPTIONS_KEYS.
66
+ # Because, PuggableConfiguration module is not loaded when this class is loaded.
67
+ attr_accessor *AppConfiguration::VALID_OPTIONS_KEYS
68
+
69
+ def to_doc
70
+ raise 'Implement Inherit Class'
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'r2-oas/dynamic/schema/v3/object/from_routes/hookable_base_object'
4
+ require 'r2-oas/schema/v3/manager/file/components_file_manager'
5
+
6
+ module R2OAS
7
+ module Schema
8
+ module V3
9
+ module Components
10
+ class RequestBodyObject < R2OAS::Dynamic::Schema::V3::HookableBaseObject
11
+ def initialize(route_data, path, opts = {})
12
+ super(opts)
13
+ @path_comp = Routing::PathComponent.new(path)
14
+ @path = @path_comp.symbol_to_brace
15
+ @route_data = route_data
16
+ @verb = route_data[:verb]
17
+ @tag_name = route_data[:tag_name]
18
+ @schema_name = route_data[:schema_name]
19
+ # MEMO:
20
+ # Allow primitive types that cannot be passed by reference to be passed by reference
21
+ # This is Compromise
22
+ @ref = { schema_name: @schema_name, tag_name: @tag_name, verb: @verb }
23
+ end
24
+
25
+ def to_doc
26
+ execute_before_create(@schema_name)
27
+ create_doc do
28
+ child_file_manager = ComponentsFileManager.new("#/components/schemas/#{_components_schema_name}", :ref)
29
+ schema_object = components_schema_object_class.new(@route_data, @path, @opts)
30
+
31
+ unless child_file_manager.skip_save?
32
+ result = {
33
+ 'components' => {
34
+ 'schemas' => {
35
+ _components_schema_name => schema_object.to_doc
36
+ }
37
+ }
38
+ }
39
+ doc.deep_merge!(
40
+ 'has_one' => {
41
+ 'type' => 'schema',
42
+ 'original_path' => child_file_manager.original_path,
43
+ 'data' => result
44
+ }
45
+ )
46
+ end
47
+ end
48
+ execute_after_create(@schema_name)
49
+ doc
50
+ end
51
+
52
+ # MEMO:
53
+ # please override in inherited class.
54
+ def components_schema_name(_doc, _path_component, _tag_name, _verb, schema_name)
55
+ schema_name
56
+ end
57
+
58
+ # MEMO:
59
+ # please override in inherited class.
60
+ def components_request_body_name(_doc, _path_component, _tag_name, _verb, schema_name)
61
+ schema_name
62
+ end
63
+
64
+ def generate?
65
+ file_manager = ComponentsFileManager.new("#/components/schemas/#{_components_schema_name}", :ref)
66
+ (@verb.in? http_methods_when_generate_request_body) && !file_manager.skip_save?
67
+ end
68
+
69
+ private
70
+
71
+ def create_doc
72
+ file_manager = ComponentsFileManager.new("#/components/schemas/#{_components_schema_name}", :ref)
73
+ doc.deep_merge!(
74
+ 'content' => {
75
+ 'application/json' => {
76
+ 'schema' => {
77
+ '$ref' => file_manager.original_path
78
+ }
79
+ }
80
+ }
81
+ )
82
+ yield if block_given?
83
+ end
84
+
85
+ def _components_schema_name
86
+ components_schema_name(doc, @path_comp, @tag_name, @verb, @schema_name)
87
+ end
88
+
89
+ def _components_request_body_name
90
+ components_request_body_name(doc, @path_comp, @tag_name, @verb, @schema_name)
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'r2-oas/dynamic/schema/v3/object/from_routes/hookable_base_object'
4
+
5
+ module R2OAS
6
+ module Schema
7
+ module V3
8
+ module Components
9
+ class SchemaObject < R2OAS::Dynamic::Schema::V3::HookableBaseObject
10
+ def initialize(route_data, path, opts = {})
11
+ super(opts)
12
+ @path_comp = Routing::PathComponent.new(path)
13
+ @path = @path_comp.symbol_to_brace
14
+ @route_data = route_data
15
+ @verb = route_data[:verb]
16
+ @tag_name = route_data[:tag_name]
17
+ @schema_name = route_data[:schema_name]
18
+ # MEMO:
19
+ # Allow primitive types that cannot be passed by reference to be passed by reference
20
+ # This is Compromise
21
+ @ref = { schema_name: @schema_name, tag_name: @tag_name, verb: @verb }
22
+ end
23
+
24
+ def to_doc
25
+ execute_before_create(@schema_name)
26
+ create_doc
27
+ execute_after_create(@schema_name)
28
+ doc
29
+ end
30
+
31
+ # MEMO:
32
+ # please override in inherited class.
33
+ def components_schema_name(_doc, _path_component, _tag_name, _verb, _http_status, schema_name)
34
+ schema_name
35
+ end
36
+
37
+ private
38
+
39
+ def create_doc
40
+ result = {
41
+ 'type' => 'object',
42
+ 'properties' => {
43
+ 'id' => {
44
+ 'type' => 'integer',
45
+ 'format' => 'int64'
46
+ }
47
+ }
48
+ }
49
+ doc.merge!(result)
50
+ end
51
+
52
+ def _components_schema_name(http_status)
53
+ components_schema_name(doc, @path_comp, @tag_name, @verb, http_status, @schema_name)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end