mcp_on_ruby 0.2.0 → 1.0.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -16
  3. data/CODE_OF_CONDUCT.md +30 -58
  4. data/CONTRIBUTING.md +61 -67
  5. data/LICENSE.txt +2 -2
  6. data/README.md +160 -442
  7. data/bin/console +11 -0
  8. data/bin/setup +6 -0
  9. data/docs/advanced-usage.md +132 -0
  10. data/docs/api-reference.md +35 -0
  11. data/docs/testing.md +55 -0
  12. data/examples/claude/README.md +171 -0
  13. data/examples/claude/claude-bridge.js +122 -0
  14. data/lib/mcp_on_ruby/configuration.rb +74 -0
  15. data/lib/mcp_on_ruby/errors.rb +137 -0
  16. data/lib/mcp_on_ruby/generators/install_generator.rb +46 -0
  17. data/lib/mcp_on_ruby/generators/resource_generator.rb +63 -0
  18. data/lib/mcp_on_ruby/generators/templates/README +31 -0
  19. data/lib/mcp_on_ruby/generators/templates/application_resource.rb +20 -0
  20. data/lib/mcp_on_ruby/generators/templates/application_tool.rb +18 -0
  21. data/lib/mcp_on_ruby/generators/templates/initializer.rb +41 -0
  22. data/lib/mcp_on_ruby/generators/templates/resource.rb +50 -0
  23. data/lib/mcp_on_ruby/generators/templates/resource_spec.rb +67 -0
  24. data/lib/mcp_on_ruby/generators/templates/sample_resource.rb +57 -0
  25. data/lib/mcp_on_ruby/generators/templates/sample_tool.rb +59 -0
  26. data/lib/mcp_on_ruby/generators/templates/tool.rb +38 -0
  27. data/lib/mcp_on_ruby/generators/templates/tool_spec.rb +55 -0
  28. data/lib/mcp_on_ruby/generators/tool_generator.rb +51 -0
  29. data/lib/mcp_on_ruby/railtie.rb +108 -0
  30. data/lib/mcp_on_ruby/resource.rb +161 -0
  31. data/lib/mcp_on_ruby/server.rb +378 -0
  32. data/lib/mcp_on_ruby/tool.rb +134 -0
  33. data/lib/mcp_on_ruby/transport.rb +330 -0
  34. data/lib/mcp_on_ruby/version.rb +6 -0
  35. data/lib/mcp_on_ruby.rb +142 -0
  36. metadata +64 -146
  37. data/lib/ruby_mcp/client.rb +0 -15
  38. data/lib/ruby_mcp/configuration.rb +0 -69
  39. data/lib/ruby_mcp/errors.rb +0 -17
  40. data/lib/ruby_mcp/models/context.rb +0 -51
  41. data/lib/ruby_mcp/models/engine.rb +0 -31
  42. data/lib/ruby_mcp/models/message.rb +0 -60
  43. data/lib/ruby_mcp/providers/anthropic.rb +0 -269
  44. data/lib/ruby_mcp/providers/base.rb +0 -57
  45. data/lib/ruby_mcp/providers/openai.rb +0 -265
  46. data/lib/ruby_mcp/schemas.rb +0 -56
  47. data/lib/ruby_mcp/server/app.rb +0 -84
  48. data/lib/ruby_mcp/server/base_controller.rb +0 -49
  49. data/lib/ruby_mcp/server/content_controller.rb +0 -68
  50. data/lib/ruby_mcp/server/contexts_controller.rb +0 -63
  51. data/lib/ruby_mcp/server/controller.rb +0 -29
  52. data/lib/ruby_mcp/server/engines_controller.rb +0 -34
  53. data/lib/ruby_mcp/server/generate_controller.rb +0 -140
  54. data/lib/ruby_mcp/server/messages_controller.rb +0 -30
  55. data/lib/ruby_mcp/server/router.rb +0 -84
  56. data/lib/ruby_mcp/storage/base.rb +0 -43
  57. data/lib/ruby_mcp/storage/error.rb +0 -8
  58. data/lib/ruby_mcp/storage/memory.rb +0 -69
  59. data/lib/ruby_mcp/storage/redis.rb +0 -197
  60. data/lib/ruby_mcp/storage_factory.rb +0 -32
  61. data/lib/ruby_mcp/validator.rb +0 -45
  62. data/lib/ruby_mcp/version.rb +0 -6
  63. data/lib/ruby_mcp.rb +0 -68
metadata CHANGED
@@ -1,31 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mcp_on_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nagendra Dhanakeerthi
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-21 00:00:00.000000000 Z
11
+ date: 2025-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: faraday
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '2.7'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '2.7'
27
- - !ruby/object:Gem::Dependency
28
- name: faraday-net_http
14
+ name: json-schema
29
15
  requirement: !ruby/object:Gem::Requirement
30
16
  requirements:
31
17
  - - "~>"
@@ -39,47 +25,19 @@ dependencies:
39
25
  - !ruby/object:Gem::Version
40
26
  version: '3.0'
41
27
  - !ruby/object:Gem::Dependency
42
- name: jwt
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '2.7'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '2.7'
55
- - !ruby/object:Gem::Dependency
56
- name: concurrent-ruby
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '1.2'
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '1.2'
69
- - !ruby/object:Gem::Dependency
70
- name: dry-schema
28
+ name: rack
71
29
  requirement: !ruby/object:Gem::Requirement
72
30
  requirements:
73
31
  - - "~>"
74
32
  - !ruby/object:Gem::Version
75
- version: '1.13'
33
+ version: '2.2'
76
34
  type: :runtime
77
35
  prerelease: false
78
36
  version_requirements: !ruby/object:Gem::Requirement
79
37
  requirements:
80
38
  - - "~>"
81
39
  - !ruby/object:Gem::Version
82
- version: '1.13'
40
+ version: '2.2'
83
41
  - !ruby/object:Gem::Dependency
84
42
  name: webrick
85
43
  requirement: !ruby/object:Gem::Requirement
@@ -95,61 +53,61 @@ dependencies:
95
53
  - !ruby/object:Gem::Version
96
54
  version: '1.7'
97
55
  - !ruby/object:Gem::Dependency
98
- name: rack
56
+ name: rake
99
57
  requirement: !ruby/object:Gem::Requirement
100
58
  requirements:
101
59
  - - "~>"
102
60
  - !ruby/object:Gem::Version
103
- version: '2.2'
104
- type: :runtime
61
+ version: '13.0'
62
+ type: :development
105
63
  prerelease: false
106
64
  version_requirements: !ruby/object:Gem::Requirement
107
65
  requirements:
108
66
  - - "~>"
109
67
  - !ruby/object:Gem::Version
110
- version: '2.2'
68
+ version: '13.0'
111
69
  - !ruby/object:Gem::Dependency
112
- name: rack-cors
70
+ name: rspec
113
71
  requirement: !ruby/object:Gem::Requirement
114
72
  requirements:
115
73
  - - "~>"
116
74
  - !ruby/object:Gem::Version
117
- version: '1.1'
118
- type: :runtime
75
+ version: '3.12'
76
+ type: :development
119
77
  prerelease: false
120
78
  version_requirements: !ruby/object:Gem::Requirement
121
79
  requirements:
122
80
  - - "~>"
123
81
  - !ruby/object:Gem::Version
124
- version: '1.1'
82
+ version: '3.12'
125
83
  - !ruby/object:Gem::Dependency
126
- name: redis
84
+ name: rubocop
127
85
  requirement: !ruby/object:Gem::Requirement
128
86
  requirements:
129
87
  - - "~>"
130
88
  - !ruby/object:Gem::Version
131
- version: '5.0'
132
- type: :runtime
89
+ version: '1.50'
90
+ type: :development
133
91
  prerelease: false
134
92
  version_requirements: !ruby/object:Gem::Requirement
135
93
  requirements:
136
94
  - - "~>"
137
95
  - !ruby/object:Gem::Version
138
- version: '5.0'
96
+ version: '1.50'
139
97
  - !ruby/object:Gem::Dependency
140
- name: vcr
98
+ name: simplecov
141
99
  requirement: !ruby/object:Gem::Requirement
142
100
  requirements:
143
101
  - - "~>"
144
102
  - !ruby/object:Gem::Version
145
- version: '6.1'
103
+ version: '0.22'
146
104
  type: :development
147
105
  prerelease: false
148
106
  version_requirements: !ruby/object:Gem::Requirement
149
107
  requirements:
150
108
  - - "~>"
151
109
  - !ruby/object:Gem::Version
152
- version: '6.1'
110
+ version: '0.22'
153
111
  - !ruby/object:Gem::Dependency
154
112
  name: webmock
155
113
  requirement: !ruby/object:Gem::Requirement
@@ -164,20 +122,6 @@ dependencies:
164
122
  - - "~>"
165
123
  - !ruby/object:Gem::Version
166
124
  version: '3.18'
167
- - !ruby/object:Gem::Dependency
168
- name: codecov
169
- requirement: !ruby/object:Gem::Requirement
170
- requirements:
171
- - - "~>"
172
- - !ruby/object:Gem::Version
173
- version: 0.6.0
174
- type: :development
175
- prerelease: false
176
- version_requirements: !ruby/object:Gem::Requirement
177
- requirements:
178
- - - "~>"
179
- - !ruby/object:Gem::Version
180
- version: 0.6.0
181
125
  - !ruby/object:Gem::Dependency
182
126
  name: rack-test
183
127
  requirement: !ruby/object:Gem::Requirement
@@ -192,40 +136,14 @@ dependencies:
192
136
  - - "~>"
193
137
  - !ruby/object:Gem::Version
194
138
  version: '2.1'
195
- - !ruby/object:Gem::Dependency
196
- name: simplecov
197
- requirement: !ruby/object:Gem::Requirement
198
- requirements:
199
- - - "~>"
200
- - !ruby/object:Gem::Version
201
- version: 0.21.2
202
- type: :development
203
- prerelease: false
204
- version_requirements: !ruby/object:Gem::Requirement
205
- requirements:
206
- - - "~>"
207
- - !ruby/object:Gem::Version
208
- version: 0.21.2
209
- - !ruby/object:Gem::Dependency
210
- name: simplecov-cobertura
211
- requirement: !ruby/object:Gem::Requirement
212
- requirements:
213
- - - "~>"
214
- - !ruby/object:Gem::Version
215
- version: '2.1'
216
- type: :development
217
- prerelease: false
218
- version_requirements: !ruby/object:Gem::Requirement
219
- requirements:
220
- - - "~>"
221
- - !ruby/object:Gem::Version
222
- version: '2.1'
223
- description: |-
224
- A comprehensive Ruby gem for implementing Model Context Protocol servers
225
- to standardize interactions with AI language models
139
+ description: A comprehensive Ruby library for building Model Context Protocol (MCP)
140
+ servers in Rails applications, featuring tools, resources, authentication, and real-time
141
+ capabilities.
226
142
  email:
227
143
  - nagendra.dhanakeerthi@gmail.com
228
- executables: []
144
+ executables:
145
+ - console
146
+ - setup
229
147
  extensions: []
230
148
  extra_rdoc_files: []
231
149
  files:
@@ -234,42 +152,42 @@ files:
234
152
  - CONTRIBUTING.md
235
153
  - LICENSE.txt
236
154
  - README.md
237
- - lib/ruby_mcp.rb
238
- - lib/ruby_mcp/client.rb
239
- - lib/ruby_mcp/configuration.rb
240
- - lib/ruby_mcp/errors.rb
241
- - lib/ruby_mcp/models/context.rb
242
- - lib/ruby_mcp/models/engine.rb
243
- - lib/ruby_mcp/models/message.rb
244
- - lib/ruby_mcp/providers/anthropic.rb
245
- - lib/ruby_mcp/providers/base.rb
246
- - lib/ruby_mcp/providers/openai.rb
247
- - lib/ruby_mcp/schemas.rb
248
- - lib/ruby_mcp/server/app.rb
249
- - lib/ruby_mcp/server/base_controller.rb
250
- - lib/ruby_mcp/server/content_controller.rb
251
- - lib/ruby_mcp/server/contexts_controller.rb
252
- - lib/ruby_mcp/server/controller.rb
253
- - lib/ruby_mcp/server/engines_controller.rb
254
- - lib/ruby_mcp/server/generate_controller.rb
255
- - lib/ruby_mcp/server/messages_controller.rb
256
- - lib/ruby_mcp/server/router.rb
257
- - lib/ruby_mcp/storage/base.rb
258
- - lib/ruby_mcp/storage/error.rb
259
- - lib/ruby_mcp/storage/memory.rb
260
- - lib/ruby_mcp/storage/redis.rb
261
- - lib/ruby_mcp/storage_factory.rb
262
- - lib/ruby_mcp/validator.rb
263
- - lib/ruby_mcp/version.rb
264
- homepage: https://github.com/nagstler/mcp_on_ruby
155
+ - bin/console
156
+ - bin/setup
157
+ - docs/advanced-usage.md
158
+ - docs/api-reference.md
159
+ - docs/testing.md
160
+ - examples/claude/README.md
161
+ - examples/claude/claude-bridge.js
162
+ - lib/mcp_on_ruby.rb
163
+ - lib/mcp_on_ruby/configuration.rb
164
+ - lib/mcp_on_ruby/errors.rb
165
+ - lib/mcp_on_ruby/generators/install_generator.rb
166
+ - lib/mcp_on_ruby/generators/resource_generator.rb
167
+ - lib/mcp_on_ruby/generators/templates/README
168
+ - lib/mcp_on_ruby/generators/templates/application_resource.rb
169
+ - lib/mcp_on_ruby/generators/templates/application_tool.rb
170
+ - lib/mcp_on_ruby/generators/templates/initializer.rb
171
+ - lib/mcp_on_ruby/generators/templates/resource.rb
172
+ - lib/mcp_on_ruby/generators/templates/resource_spec.rb
173
+ - lib/mcp_on_ruby/generators/templates/sample_resource.rb
174
+ - lib/mcp_on_ruby/generators/templates/sample_tool.rb
175
+ - lib/mcp_on_ruby/generators/templates/tool.rb
176
+ - lib/mcp_on_ruby/generators/templates/tool_spec.rb
177
+ - lib/mcp_on_ruby/generators/tool_generator.rb
178
+ - lib/mcp_on_ruby/railtie.rb
179
+ - lib/mcp_on_ruby/resource.rb
180
+ - lib/mcp_on_ruby/server.rb
181
+ - lib/mcp_on_ruby/tool.rb
182
+ - lib/mcp_on_ruby/transport.rb
183
+ - lib/mcp_on_ruby/version.rb
184
+ homepage: https://github.com/rubyonai/mcp_on_ruby
265
185
  licenses:
266
186
  - MIT
267
187
  metadata:
268
- allowed_push_host: https://rubygems.org
269
- homepage_uri: https://github.com/nagstler/mcp_on_ruby
270
- source_code_uri: https://github.com/nagstler/mcp_on_ruby
271
- changelog_uri: https://github.com/nagstler/mcp_on_ruby/blob/main/CHANGELOG.md
272
- rubygems_mfa_required: 'true'
188
+ homepage_uri: https://github.com/rubyonai/mcp_on_ruby
189
+ source_code_uri: https://github.com/rubyonai/mcp_on_ruby
190
+ changelog_uri: https://github.com/rubyonai/mcp_on_ruby/blob/main/CHANGELOG.md
273
191
  post_install_message:
274
192
  rdoc_options: []
275
193
  require_paths:
@@ -278,15 +196,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
278
196
  requirements:
279
197
  - - ">="
280
198
  - !ruby/object:Gem::Version
281
- version: 2.6.0
199
+ version: 2.7.0
282
200
  required_rubygems_version: !ruby/object:Gem::Requirement
283
201
  requirements:
284
202
  - - ">="
285
203
  - !ruby/object:Gem::Version
286
204
  version: '0'
287
205
  requirements: []
288
- rubygems_version: 3.5.2
206
+ rubygems_version: 3.4.10
289
207
  signing_key:
290
208
  specification_version: 4
291
- summary: Ruby implementation of the Model Context Protocol (MCP)
209
+ summary: Production-ready MCP server for Rails applications
292
210
  test_files: []
@@ -1,15 +0,0 @@
1
- # lib/ruby_mcp/client.rb
2
- # frozen_string_literal: true
3
-
4
- module RubyMCP
5
- class Client
6
- attr_reader :storage
7
-
8
- def initialize(storage)
9
- @storage = storage
10
- end
11
-
12
- # You can add additional convenience methods here
13
- # that delegate to storage or provide higher-level functionality
14
- end
15
- end
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyMCP
4
- class Configuration
5
- attr_accessor :providers, :storage, :server_port, :server_host,
6
- :auth_required, :jwt_secret, :token_expiry, :max_contexts, :redis
7
-
8
- def initialize
9
- @providers = {}
10
- @storage = :memory
11
- @server_port = 3000
12
- @server_host = '0.0.0.0'
13
- @auth_required = false
14
- @jwt_secret = nil
15
- @token_expiry = 3600 # 1 hour
16
- @max_contexts = 1000
17
- @storage = :memory # Default to memory storage
18
- @redis = {} # Default empty Redis config
19
- end
20
-
21
- def storage_config
22
- if @storage == :redis
23
- {
24
- type: :redis,
25
- connection: redis_connection_config,
26
- namespace: @redis[:namespace] || 'ruby_mcp',
27
- ttl: @redis[:ttl] || 86_400
28
- }
29
- else
30
- { type: @storage }
31
- end
32
- end
33
-
34
- def storage_instance
35
- @storage_instance ||= case @storage
36
- when :memory
37
- RubyMCP::Storage::Memory.new
38
- when :redis
39
- # Future implementation
40
- raise RubyMCP::Errors::ConfigurationError, 'Redis storage not yet implemented'
41
- when :active_record
42
- # Future implementation
43
- raise RubyMCP::Errors::ConfigurationError, 'ActiveRecord storage not yet implemented'
44
- else
45
- unless @storage.is_a?(RubyMCP::Storage::Base)
46
- raise RubyMCP::Errors::ConfigurationError, "Unknown storage type: #{@storage}"
47
- end
48
-
49
- @storage # Allow custom storage instance
50
-
51
- end
52
- end
53
-
54
- private
55
-
56
- def redis_connection_config
57
- if @redis[:url]
58
- { url: @redis[:url] }
59
- else
60
- {
61
- host: @redis[:host] || 'localhost',
62
- port: @redis[:port] || 6379,
63
- db: @redis[:db] || 0,
64
- password: @redis[:password]
65
- }.compact
66
- end
67
- end
68
- end
69
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyMCP
4
- module Errors
5
- class Error < StandardError; end
6
-
7
- class ConfigurationError < Error; end
8
- class AuthenticationError < Error; end
9
- class ValidationError < Error; end
10
- class ProviderError < Error; end
11
- class ContextError < Error; end
12
- class EngineError < Error; end
13
- class MessageError < Error; end
14
- class ContentError < Error; end
15
- class ServerError < Error; end
16
- end
17
- end
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'securerandom'
4
-
5
- module RubyMCP
6
- module Models
7
- class Context
8
- attr_reader :id, :messages, :content_map, :created_at, :updated_at
9
- attr_accessor :metadata
10
-
11
- def initialize(id: nil, messages: [], metadata: {})
12
- @id = id || "ctx_#{SecureRandom.hex(10)}"
13
- @messages = messages || []
14
- @content_map = {}
15
- @metadata = metadata || {}
16
- @created_at = Time.now.utc
17
- @updated_at = @created_at
18
- end
19
-
20
- def add_message(message)
21
- @messages << message
22
- @updated_at = Time.now.utc
23
- message
24
- end
25
-
26
- def add_content(content_id, content_data)
27
- @content_map[content_id] = content_data
28
- @updated_at = Time.now.utc
29
- content_id
30
- end
31
-
32
- def get_content(content_id)
33
- @content_map[content_id]
34
- end
35
-
36
- def to_h
37
- {
38
- id: @id,
39
- messages: @messages.map(&:to_h),
40
- created_at: @created_at.iso8601,
41
- updated_at: @updated_at.iso8601,
42
- metadata: @metadata
43
- }
44
- end
45
-
46
- def estimated_token_count
47
- @messages.sum(&:estimated_token_count)
48
- end
49
- end
50
- end
51
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyMCP
4
- module Models
5
- class Engine
6
- attr_reader :id, :provider, :model, :capabilities, :config
7
-
8
- def initialize(id:, provider:, model:, capabilities: [], config: {})
9
- @id = id
10
- @provider = provider
11
- @model = model
12
- @capabilities = capabilities
13
- @config = config
14
- end
15
-
16
- def to_h
17
- {
18
- id: @id,
19
- provider: @provider,
20
- model: @model,
21
- capabilities: @capabilities,
22
- config: @config
23
- }
24
- end
25
-
26
- def supports?(capability)
27
- @capabilities.include?(capability.to_s)
28
- end
29
- end
30
- end
31
- end
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'securerandom'
4
-
5
- module RubyMCP
6
- module Models
7
- class Message
8
- VALID_ROLES = %w[user assistant system tool].freeze
9
-
10
- attr_reader :id, :role, :content, :created_at
11
- attr_accessor :metadata
12
-
13
- def initialize(role:, content:, id: nil, metadata: {})
14
- @id = id || "msg_#{SecureRandom.hex(10)}"
15
-
16
- unless VALID_ROLES.include?(role)
17
- raise RubyMCP::Errors::ValidationError, "Invalid role: #{role}. Must be one of: #{VALID_ROLES.join(', ')}"
18
- end
19
-
20
- @role = role
21
- @content = content
22
- @created_at = Time.now.utc
23
- @metadata = metadata || {}
24
- end
25
-
26
- def to_h
27
- {
28
- id: @id,
29
- role: @role,
30
- content: @content,
31
- created_at: @created_at.iso8601,
32
- metadata: @metadata
33
- }
34
- end
35
-
36
- def content_type
37
- return 'array' if @content.is_a?(Array)
38
-
39
- 'text'
40
- end
41
-
42
- def estimated_token_count
43
- # Very basic estimation, would need to be improved with a real tokenizer
44
- if @content.is_a?(String)
45
- @content.split(/\s+/).size
46
- elsif @content.is_a?(Array)
47
- @content.sum do |part|
48
- if part.is_a?(String) || part[:text]
49
- (part.is_a?(String) ? part : part[:text]).split(/\s+/).size
50
- else
51
- 10 # Arbitrary count for non-text content
52
- end
53
- end
54
- else
55
- 10 # Default for unknown content format
56
- end
57
- end
58
- end
59
- end
60
- end