mythic-beasts 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ff19b7149b4146959d99412af78e9cc2ffe5b9c7a210faff7ee5fc83dbbcb957
4
+ data.tar.gz: e200bed772b3a84c4c39ed454ab4f2b957f066d3710d83d0b3fa9e5e77865d51
5
+ SHA512:
6
+ metadata.gz: bf9e744f9f587ca9ea65d78a5d86b4cb6b20876770ec559667c94b635fc2d04687c510b5b125f8560560e33df4595142d6a2dae9a7307608e668cf082676303d
7
+ data.tar.gz: f5d16e71f563a97e9c4b570d7b90e7c48a0f546bc1ab3428b570feff147fed6f69453855e46e2de4682137a311a56b9ffcfd6a968f2edb9eded8ec18f75058ec
data/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2025-01-27
9
+
10
+ ### Added
11
+
12
+ - Initial release by James Inman (@jfi)
13
+ - OAuth2 authentication support with automatic token refresh
14
+ - DNS API v2 client with full CRUD operations
15
+ - VPS provisioning and management API
16
+ - Comprehensive error handling with custom exception classes
17
+ - Retry logic for transient failures
18
+ - Convenience methods for common DNS record types (A, AAAA, CNAME, MX, TXT)
19
+ - Complete test suite with RSpec and WebMock
20
+ - StandardRB compliance for code style
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Otaina Limited
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # Mythic Beasts Ruby Client
2
+
3
+ A Ruby gem for interacting with [Mythic Beasts](https://www.mythic-beasts.com/) APIs including DNS management, VPS provisioning, and domain management.
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/mythic-beasts.svg)](https://badge.fury.io/rb/mythic-beasts)
6
+ [![CI](https://github.com/tastybamboo/mythic-beasts/workflows/CI/badge.svg)](https://github.com/tastybamboo/mythic-beasts/actions)
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'mythic-beasts'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ ```bash
19
+ bundle install
20
+ ```
21
+
22
+ ## Configuration
23
+
24
+ Get your API credentials from the [Mythic Beasts control panel](https://www.mythic-beasts.com/customer/api-keys).
25
+
26
+ ```ruby
27
+ MythicBeasts.configure do |config|
28
+ config.api_key = 'your_api_key'
29
+ config.api_secret = 'your_api_secret'
30
+ end
31
+ ```
32
+
33
+ Or create a client directly:
34
+
35
+ ```ruby
36
+ client = MythicBeasts::Client.new(
37
+ api_key: 'your_api_key',
38
+ api_secret: 'your_api_secret'
39
+ )
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ ### DNS Management
45
+
46
+ ```ruby
47
+ # Get all zones
48
+ zones = MythicBeasts.client.dns.zones
49
+
50
+ # Get records for a zone
51
+ records = MythicBeasts.client.dns.records('example.com')
52
+
53
+ # Create an A record
54
+ MythicBeasts.client.dns.create_a_record(
55
+ 'example.com',
56
+ 'www',
57
+ '1.2.3.4',
58
+ ttl: 300
59
+ )
60
+
61
+ # Create multiple records at once
62
+ MythicBeasts.client.dns.create_records('example.com', [
63
+ { host: 'www', ttl: 300, type: 'A', data: '1.2.3.4' },
64
+ { host: 'mail', ttl: 300, type: 'A', data: '1.2.3.5' },
65
+ { host: '@', ttl: 300, type: 'MX', data: '10 mail.example.com' }
66
+ ])
67
+
68
+ # Update records
69
+ MythicBeasts.client.dns.update_records(
70
+ 'example.com',
71
+ [{ host: 'www', ttl: 600, type: 'A', data: '1.2.3.6' }],
72
+ host: 'www',
73
+ type: 'A'
74
+ )
75
+
76
+ # Delete records
77
+ MythicBeasts.client.dns.delete_records(
78
+ 'example.com',
79
+ host: 'www',
80
+ type: 'A'
81
+ )
82
+
83
+ # Dynamic DNS update (updates to client IP)
84
+ MythicBeasts.client.dns.dynamic_update('home.example.com')
85
+ ```
86
+
87
+ ### VPS Management
88
+
89
+ ```ruby
90
+ # List all VPS servers
91
+ servers = MythicBeasts.client.vps.list
92
+
93
+ # Get server details
94
+ server = MythicBeasts.client.vps.get('my-server')
95
+
96
+ # Create a new VPS
97
+ MythicBeasts.client.vps.create(
98
+ name: 'my-new-server',
99
+ type: 'VPS-2',
100
+ ssh_key: 'ssh-rsa AAAAB3...'
101
+ )
102
+
103
+ # Control servers
104
+ MythicBeasts.client.vps.start('my-server')
105
+ MythicBeasts.client.vps.stop('my-server')
106
+ MythicBeasts.client.vps.restart('my-server')
107
+
108
+ # Get console access
109
+ console = MythicBeasts.client.vps.console('my-server')
110
+
111
+ # Delete a server
112
+ MythicBeasts.client.vps.delete('my-server')
113
+ ```
114
+
115
+ ## Error Handling
116
+
117
+ The gem provides specific error classes:
118
+
119
+ ```ruby
120
+ begin
121
+ MythicBeasts.client.dns.records('nonexistent.com')
122
+ rescue MythicBeasts::NotFoundError => e
123
+ puts "Zone not found: #{e.message}"
124
+ rescue MythicBeasts::AuthenticationError => e
125
+ puts "Authentication failed: #{e.message}"
126
+ rescue MythicBeasts::RateLimitError => e
127
+ puts "Rate limit exceeded: #{e.message}"
128
+ rescue MythicBeasts::Error => e
129
+ puts "General error: #{e.message}"
130
+ end
131
+ ```
132
+
133
+ Available error classes:
134
+ - `MythicBeasts::Error` - Base error class
135
+ - `MythicBeasts::AuthenticationError` - Invalid credentials
136
+ - `MythicBeasts::NotFoundError` - Resource not found
137
+ - `MythicBeasts::ValidationError` - Invalid parameters
138
+ - `MythicBeasts::RateLimitError` - Rate limit exceeded
139
+ - `MythicBeasts::ServerError` - Server-side error
140
+
141
+ ## Development
142
+
143
+ After checking out the repo:
144
+
145
+ ```bash
146
+ bundle install
147
+ bundle exec rspec
148
+ ```
149
+
150
+ ## Contributing
151
+
152
+ Bug reports and pull requests are welcome on GitHub at https://github.com/tastybamboo/mythic-beasts.
153
+
154
+ ## Author
155
+
156
+ **James Inman** ([@jfi](https://github.com/jfi))
157
+ - Email: james@otaina.co.uk
158
+ - GitHub: https://github.com/tastybamboo
159
+
160
+ ## License
161
+
162
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
163
+
164
+ Copyright (c) 2025 Otaina Limited
165
+
166
+ ## Resources
167
+
168
+ - [Mythic Beasts API Documentation](https://www.mythic-beasts.com/support/api)
169
+ - [DNS API v2 Docs](https://www.mythic-beasts.com/support/api/dnsv2)
170
+ - [VPS API Docs](https://www.mythic-beasts.com/support/api/vps)
@@ -0,0 +1,52 @@
1
+ module MythicBeasts
2
+ class Auth
3
+ AUTH_URL = "https://auth.mythic-beasts.com/login"
4
+
5
+ attr_reader :api_key, :api_secret
6
+
7
+ def initialize(api_key:, api_secret:)
8
+ @api_key = api_key
9
+ @api_secret = api_secret
10
+ @token = nil
11
+ @token_expires_at = nil
12
+ end
13
+
14
+ def token
15
+ return @token if @token && !token_expired?
16
+
17
+ fetch_token
18
+ @token
19
+ end
20
+
21
+ private
22
+
23
+ def token_expired?
24
+ return true unless @token_expires_at
25
+
26
+ Time.now >= @token_expires_at
27
+ end
28
+
29
+ def fetch_token
30
+ response = connection.post do |req|
31
+ req.body = {grant_type: "client_credentials"}
32
+ req.options.timeout = 10
33
+ end
34
+
35
+ data = JSON.parse(response.body)
36
+ @token = data["access_token"]
37
+ # Expire token 30 seconds before actual expiry to be safe
38
+ @token_expires_at = Time.now + (data["expires_in"].to_i - 30)
39
+ rescue Faraday::Error => e
40
+ raise AuthenticationError, "Failed to authenticate: #{e.message}"
41
+ end
42
+
43
+ def connection
44
+ @connection ||= Faraday.new(url: AUTH_URL) do |conn|
45
+ conn.request :authorization, :basic, api_key, api_secret
46
+ conn.request :url_encoded
47
+ conn.response :raise_error
48
+ conn.adapter Faraday.default_adapter
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,70 @@
1
+ module MythicBeasts
2
+ class Client
3
+ API_BASE_URL = "https://api.mythic-beasts.com"
4
+
5
+ attr_reader :auth, :dns, :vps
6
+
7
+ def initialize(api_key:, api_secret:)
8
+ @auth = Auth.new(api_key: api_key, api_secret: api_secret)
9
+ @dns = DNS.new(self)
10
+ @vps = VPS.new(self)
11
+ end
12
+
13
+ def get(path, params: {})
14
+ request(:get, path, params: params)
15
+ end
16
+
17
+ def post(path, body: {}, params: {})
18
+ request(:post, path, body: body, params: params)
19
+ end
20
+
21
+ def put(path, body: {}, params: {})
22
+ request(:put, path, body: body, params: params)
23
+ end
24
+
25
+ def delete(path, params: {})
26
+ request(:delete, path, params: params)
27
+ end
28
+
29
+ private
30
+
31
+ def request(method, path, body: {}, params: {})
32
+ response = connection.send(method) do |req|
33
+ req.url path
34
+ req.params = params if params.any?
35
+ req.body = body.to_json if body.any?
36
+ end
37
+
38
+ JSON.parse(response.body) if response.body && !response.body.empty?
39
+ rescue Faraday::UnauthorizedError, Faraday::ClientError => e
40
+ if e.response&.dig(:status) == 401
41
+ raise AuthenticationError, "Invalid credentials"
42
+ elsif e.response&.dig(:status) == 404
43
+ raise NotFoundError, e.message
44
+ elsif e.response&.dig(:status) == 400
45
+ raise ValidationError, e.message
46
+ elsif e.response&.dig(:status) == 429
47
+ raise RateLimitError, e.message
48
+ else
49
+ raise Error, e.message
50
+ end
51
+ rescue Faraday::ServerError => e
52
+ raise ServerError, e.message
53
+ end
54
+
55
+ def connection
56
+ @connection ||= Faraday.new(url: API_BASE_URL) do |conn|
57
+ conn.request :authorization, :bearer, -> { auth.token }
58
+ conn.request :json
59
+ conn.request :retry, {
60
+ max: 3,
61
+ interval: 0.5,
62
+ backoff_factor: 2,
63
+ exceptions: [Faraday::ServerError]
64
+ }
65
+ conn.response :raise_error
66
+ conn.adapter Faraday.default_adapter
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,76 @@
1
+ module MythicBeasts
2
+ class DNS
3
+ attr_reader :client
4
+
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ # List all DNS zones
10
+ def zones
11
+ client.get("/dns/v2/zones")
12
+ end
13
+
14
+ # Get all records for a zone
15
+ def records(zone)
16
+ client.get("/dns/v2/zones/#{zone}/records")
17
+ end
18
+
19
+ # Get specific record(s) by host and type
20
+ def get_record(zone, host, type)
21
+ client.get("/dns/v2/zones/#{zone}/records/#{host}/#{type}")
22
+ end
23
+
24
+ # Create new DNS record(s)
25
+ # records: Array of hashes with keys: host, ttl, type, data
26
+ # Example: [{ host: 'www', ttl: 300, type: 'A', data: '1.2.3.4' }]
27
+ def create_records(zone, records)
28
+ client.post("/dns/v2/zones/#{zone}/records", body: {records: records})
29
+ end
30
+
31
+ # Replace record(s)
32
+ def update_records(zone, records, host: nil, type: nil)
33
+ path = "/dns/v2/zones/#{zone}/records"
34
+ path += "/#{host}" if host
35
+ path += "/#{type}" if type
36
+
37
+ client.put(path, body: {records: records})
38
+ end
39
+
40
+ # Delete record(s)
41
+ def delete_records(zone, host: nil, type: nil, data: nil)
42
+ path = "/dns/v2/zones/#{zone}/records"
43
+ path += "/#{host}" if host
44
+ path += "/#{type}" if type
45
+
46
+ params = data ? {data: data} : {}
47
+ client.delete(path, params: params)
48
+ end
49
+
50
+ # Dynamic DNS update - updates A/AAAA record to client IP
51
+ def dynamic_update(host)
52
+ client.put("/dns/v2/dynamic/#{host}")
53
+ end
54
+
55
+ # Convenience methods for common record types
56
+ def create_a_record(zone, host, ip, ttl: 300)
57
+ create_records(zone, [{host: host, ttl: ttl, type: "A", data: ip}])
58
+ end
59
+
60
+ def create_aaaa_record(zone, host, ip, ttl: 300)
61
+ create_records(zone, [{host: host, ttl: ttl, type: "AAAA", data: ip}])
62
+ end
63
+
64
+ def create_cname_record(zone, host, target, ttl: 300)
65
+ create_records(zone, [{host: host, ttl: ttl, type: "CNAME", data: target}])
66
+ end
67
+
68
+ def create_mx_record(zone, host, priority, mail_server, ttl: 300)
69
+ create_records(zone, [{host: host, ttl: ttl, type: "MX", data: "#{priority} #{mail_server}"}])
70
+ end
71
+
72
+ def create_txt_record(zone, host, text, ttl: 300)
73
+ create_records(zone, [{host: host, ttl: ttl, type: "TXT", data: text}])
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,8 @@
1
+ module MythicBeasts
2
+ class Error < StandardError; end
3
+ class AuthenticationError < Error; end
4
+ class NotFoundError < Error; end
5
+ class ValidationError < Error; end
6
+ class RateLimitError < Error; end
7
+ class ServerError < Error; end
8
+ end
@@ -0,0 +1,3 @@
1
+ module MythicBeasts
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,61 @@
1
+ module MythicBeasts
2
+ class VPS
3
+ attr_reader :client
4
+
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ # List all VPS servers
10
+ def list
11
+ client.get("/api/vps")
12
+ end
13
+
14
+ # Get details of a specific VPS
15
+ def get(server_name)
16
+ client.get("/api/vps/#{server_name}")
17
+ end
18
+
19
+ # Create a new VPS
20
+ # Options:
21
+ # - name: Server name
22
+ # - type: Server type (e.g., 'VPS-1', 'VPS-2')
23
+ # - disk: Disk size in GB
24
+ # - ssh_key: SSH public key for access
25
+ def create(name:, type:, ssh_key: nil, **options)
26
+ body = {
27
+ name: name,
28
+ type: type,
29
+ **options
30
+ }
31
+ body[:ssh_key] = ssh_key if ssh_key
32
+
33
+ client.post("/api/vps", body: body)
34
+ end
35
+
36
+ # Start a VPS
37
+ def start(server_name)
38
+ client.post("/api/vps/#{server_name}/start")
39
+ end
40
+
41
+ # Stop a VPS
42
+ def stop(server_name)
43
+ client.post("/api/vps/#{server_name}/stop")
44
+ end
45
+
46
+ # Restart a VPS
47
+ def restart(server_name)
48
+ client.post("/api/vps/#{server_name}/restart")
49
+ end
50
+
51
+ # Delete a VPS
52
+ def delete(server_name)
53
+ client.delete("/api/vps/#{server_name}")
54
+ end
55
+
56
+ # Get VPS console access
57
+ def console(server_name)
58
+ client.get("/api/vps/#{server_name}/console")
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,24 @@
1
+ require "faraday"
2
+ require "faraday/retry"
3
+ require "json"
4
+
5
+ require_relative "mythic_beasts/version"
6
+ require_relative "mythic_beasts/client"
7
+ require_relative "mythic_beasts/auth"
8
+ require_relative "mythic_beasts/dns"
9
+ require_relative "mythic_beasts/vps"
10
+ require_relative "mythic_beasts/errors"
11
+
12
+ module MythicBeasts
13
+ class << self
14
+ attr_accessor :api_key, :api_secret
15
+
16
+ def configure
17
+ yield self
18
+ end
19
+
20
+ def client
21
+ @client ||= Client.new(api_key: api_key, api_secret: api_secret)
22
+ end
23
+ end
24
+ end
metadata ADDED
@@ -0,0 +1,166 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mythic-beasts
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - James Inman
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: faraday
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: faraday-retry
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rspec
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.12'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.12'
54
+ - !ruby/object:Gem::Dependency
55
+ name: webmock
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.18'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.18'
68
+ - !ruby/object:Gem::Dependency
69
+ name: vcr
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '6.1'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '6.1'
82
+ - !ruby/object:Gem::Dependency
83
+ name: standard
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.35'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.35'
96
+ - !ruby/object:Gem::Dependency
97
+ name: rake
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '13.0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '13.0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: pry
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0.14'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '0.14'
124
+ description: A Ruby gem for interacting with Mythic Beasts APIs including DNS, VPS
125
+ provisioning, and domain management
126
+ email:
127
+ - james@otaina.co.uk
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - CHANGELOG.md
133
+ - LICENSE
134
+ - README.md
135
+ - lib/mythic_beasts.rb
136
+ - lib/mythic_beasts/auth.rb
137
+ - lib/mythic_beasts/client.rb
138
+ - lib/mythic_beasts/dns.rb
139
+ - lib/mythic_beasts/errors.rb
140
+ - lib/mythic_beasts/version.rb
141
+ - lib/mythic_beasts/vps.rb
142
+ homepage: https://github.com/tastybamboo/mythic-beasts
143
+ licenses:
144
+ - MIT
145
+ metadata:
146
+ homepage_uri: https://github.com/tastybamboo/mythic-beasts
147
+ source_code_uri: https://github.com/tastybamboo/mythic-beasts
148
+ changelog_uri: https://github.com/tastybamboo/mythic-beasts/blob/main/CHANGELOG.md
149
+ rdoc_options: []
150
+ require_paths:
151
+ - lib
152
+ required_ruby_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: 3.0.0
157
+ required_rubygems_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ requirements: []
163
+ rubygems_version: 3.7.2
164
+ specification_version: 4
165
+ summary: Ruby client for Mythic Beasts API
166
+ test_files: []