voiceml 0.8.1 → 0.9.2

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.
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../models/messaging_v1'
4
+
5
+ module VoiceML
6
+ # `client.messaging_v1` — Twilio Messaging v1 (messaging.twilio.com/v1).
7
+ # The whole group is routed at the messaging host (`messaging.voicetel.com`) by the
8
+ # client, which is what disambiguates a Messaging Service (`MG...`) from a
9
+ # Conversation Service (`IS...`) — they share the `/v1/Services` path shape.
10
+ class MessagingV1Resource
11
+ attr_reader :services
12
+
13
+ def initialize(transport)
14
+ @services = MessagingV1ServicesResource.new(transport)
15
+ end
16
+ end
17
+
18
+ # Operations on `/v1/Services` at the messaging host.
19
+ #
20
+ # `create` / `list` / `fetch` / `delete` reuse the shared path; `update`
21
+ # (`POST /v1/Services/{sid}`) is unique to Messaging Service.
22
+ class MessagingV1ServicesResource
23
+ SERVICE_FIELDS = {
24
+ 'FriendlyName' => :friendly_name,
25
+ 'InboundRequestUrl' => :inbound_request_url,
26
+ 'InboundMethod' => :inbound_method,
27
+ 'FallbackUrl' => :fallback_url,
28
+ 'FallbackMethod' => :fallback_method,
29
+ 'StatusCallback' => :status_callback,
30
+ 'StickySender' => :sticky_sender,
31
+ 'MmsConverter' => :mms_converter,
32
+ 'SmartEncoding' => :smart_encoding,
33
+ 'ScanMessageContent' => :scan_message_content,
34
+ 'FallbackToLongCode' => :fallback_to_long_code,
35
+ 'AreaCodeGeomatch' => :area_code_geomatch,
36
+ 'SynchronousValidation' => :synchronous_validation,
37
+ 'ValidityPeriod' => :validity_period,
38
+ 'Usecase' => :usecase,
39
+ 'UseInboundWebhookOnNumber' => :use_inbound_webhook_on_number
40
+ }.freeze
41
+
42
+ def initialize(transport)
43
+ @transport = transport
44
+ end
45
+
46
+ def create(friendly_name:, **kwargs)
47
+ kwargs[:friendly_name] = friendly_name
48
+ MessagingService.from_hash(
49
+ @transport.request(:post, '/v1/Services', form: build_form(kwargs))
50
+ )
51
+ end
52
+
53
+ def list(page_size: nil)
54
+ params = {}
55
+ params['PageSize'] = page_size unless page_size.nil?
56
+ MessagingServiceList.new(@transport.request(:get, '/v1/Services', params: params))
57
+ end
58
+
59
+ def fetch(sid)
60
+ MessagingService.from_hash(@transport.request(:get, "/v1/Services/#{sid}"))
61
+ end
62
+
63
+ def update(sid, **kwargs)
64
+ MessagingService.from_hash(
65
+ @transport.request(:post, "/v1/Services/#{sid}", form: build_form(kwargs))
66
+ )
67
+ end
68
+
69
+ def delete(sid)
70
+ @transport.request(:delete, "/v1/Services/#{sid}")
71
+ nil
72
+ end
73
+
74
+ private
75
+
76
+ def build_form(kwargs)
77
+ out = {}
78
+ SERVICE_FIELDS.each do |wire, k|
79
+ value = kwargs[k]
80
+ next if value.nil?
81
+
82
+ out[wire] = value
83
+ end
84
+ out
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+
5
+ require_relative '../models/pricing'
6
+
7
+ module VoiceML
8
+ # `client.pricing` — Twilio Pricing v1/v2 (pricing.twilio.com) surface.
9
+ #
10
+ # Read-only. Served on the default host (VoiceML has no pricing subdomain). Layout:
11
+ #
12
+ # client.pricing.v1.voice.countries.list / fetch
13
+ # client.pricing.v1.voice.numbers.fetch
14
+ # client.pricing.v1.messaging.countries.list / fetch
15
+ # client.pricing.v1.phone_numbers.countries.list / fetch
16
+ # client.pricing.v2.voice.countries.list / fetch
17
+ # client.pricing.v2.voice.numbers.fetch
18
+ # client.pricing.v2.trunking.countries.list / fetch
19
+ # client.pricing.v2.trunking.numbers.fetch
20
+ #
21
+ # Every `countries.list` returns the shared PricingCountriesList envelope; `fetch`
22
+ # returns the product-specific country/number body. Number path segments are
23
+ # URL-encoded (E.164 `+` -> `%2B`).
24
+ class PricingResource
25
+ attr_reader :v1, :v2
26
+
27
+ def initialize(transport)
28
+ @v1 = PricingV1Resource.new(transport)
29
+ @v2 = PricingV2Resource.new(transport)
30
+ end
31
+ end
32
+
33
+ # A pricing product group exposing `.countries` and optionally `.numbers`.
34
+ class PricingProduct
35
+ attr_reader :countries, :numbers
36
+
37
+ def initialize(countries, numbers = nil)
38
+ @countries = countries
39
+ @numbers = numbers
40
+ end
41
+ end
42
+
43
+ # `.../Countries` list + per-country fetch. `model` is the fetch body class.
44
+ class PricingCountriesResource
45
+ def initialize(transport, base_path, model)
46
+ @transport = transport
47
+ @base = base_path
48
+ @model = model
49
+ end
50
+
51
+ def list(page_size: nil)
52
+ params = {}
53
+ params['PageSize'] = page_size unless page_size.nil?
54
+ PricingCountriesList.new(@transport.request(:get, @base, params: params))
55
+ end
56
+
57
+ def fetch(iso_country)
58
+ @model.from_hash(@transport.request(:get, "#{@base}/#{iso_country}"))
59
+ end
60
+ end
61
+
62
+ # `/v1/Voice/Numbers/{Number}` — single-number fetch.
63
+ class PricingV1VoiceNumbersResource
64
+ def initialize(transport)
65
+ @transport = transport
66
+ end
67
+
68
+ def fetch(number)
69
+ PricingVoiceNumber.from_hash(
70
+ @transport.request(:get, "/v1/Voice/Numbers/#{URI.encode_www_form_component(number)}")
71
+ )
72
+ end
73
+ end
74
+
75
+ # `/v2/Voice/Numbers/{Destination}` — origin-aware single-number fetch.
76
+ class PricingV2VoiceNumbersResource
77
+ def initialize(transport)
78
+ @transport = transport
79
+ end
80
+
81
+ def fetch(destination_number, origination_number: nil)
82
+ params = {}
83
+ params['OriginationNumber'] = origination_number unless origination_number.nil?
84
+ PricingVoiceNumberV2.from_hash(
85
+ @transport.request(:get, "/v2/Voice/Numbers/#{URI.encode_www_form_component(destination_number)}",
86
+ params: params)
87
+ )
88
+ end
89
+ end
90
+
91
+ # `/v2/Trunking/Numbers/{Destination}` — origin-aware single-number fetch.
92
+ class PricingV2TrunkingNumbersResource
93
+ def initialize(transport)
94
+ @transport = transport
95
+ end
96
+
97
+ def fetch(destination_number, origination_number: nil)
98
+ params = {}
99
+ params['OriginationNumber'] = origination_number unless origination_number.nil?
100
+ PricingTrunkingNumber.from_hash(
101
+ @transport.request(:get, "/v2/Trunking/Numbers/#{URI.encode_www_form_component(destination_number)}",
102
+ params: params)
103
+ )
104
+ end
105
+ end
106
+
107
+ # `client.pricing.v1.*` — Voice, Messaging, PhoneNumbers.
108
+ class PricingV1Resource
109
+ attr_reader :voice, :messaging, :phone_numbers
110
+
111
+ def initialize(transport)
112
+ @voice = PricingProduct.new(
113
+ PricingCountriesResource.new(transport, '/v1/Voice/Countries', PricingVoiceCountry),
114
+ PricingV1VoiceNumbersResource.new(transport)
115
+ )
116
+ @messaging = PricingProduct.new(
117
+ PricingCountriesResource.new(transport, '/v1/Messaging/Countries', PricingMessagingCountry)
118
+ )
119
+ @phone_numbers = PricingProduct.new(
120
+ PricingCountriesResource.new(transport, '/v1/PhoneNumbers/Countries', PricingPhoneNumberCountry)
121
+ )
122
+ end
123
+ end
124
+
125
+ # `client.pricing.v2.*` — Voice, Trunking.
126
+ class PricingV2Resource
127
+ attr_reader :voice, :trunking
128
+
129
+ def initialize(transport)
130
+ @voice = PricingProduct.new(
131
+ PricingCountriesResource.new(transport, '/v2/Voice/Countries', PricingVoiceCountryV2),
132
+ PricingV2VoiceNumbersResource.new(transport)
133
+ )
134
+ @trunking = PricingProduct.new(
135
+ PricingCountriesResource.new(transport, '/v2/Trunking/Countries', PricingTrunkingCountry),
136
+ PricingV2TrunkingNumbersResource.new(transport)
137
+ )
138
+ end
139
+ end
140
+ end
@@ -6,9 +6,10 @@ module VoiceML
6
6
  # `client.routes_v2` — Twilio routes/v2 Inbound Processing Region API.
7
7
  # Sits outside the /2010-04-01/Accounts/... namespace.
8
8
  class RoutesV2Resource
9
- attr_reader :sip_domains
9
+ attr_reader :sip_domains, :phone_numbers
10
10
  def initialize(transport)
11
- @sip_domains = RoutesV2SipDomainsResource.new(transport)
11
+ @sip_domains = RoutesV2SipDomainsResource.new(transport)
12
+ @phone_numbers = RoutesV2PhoneNumbersResource.new(transport)
12
13
  end
13
14
  end
14
15
 
@@ -30,4 +31,23 @@ module VoiceML
30
31
  RoutesV2SipDomain.from_hash(@transport.request(:post, "/v2/SipDomains/#{domain_name}", form: form))
31
32
  end
32
33
  end
34
+
35
+ # Operations on /v2/PhoneNumbers/{PhoneNumber}. Keyed by E.164 phone number or
36
+ # its PN sid; account resolved from HTTP Basic auth.
37
+ class RoutesV2PhoneNumbersResource
38
+ def initialize(transport)
39
+ @transport = transport
40
+ end
41
+
42
+ def fetch(phone_number)
43
+ RoutesV2PhoneNumber.from_hash(@transport.request(:get, "/v2/PhoneNumbers/#{phone_number}"))
44
+ end
45
+
46
+ def update(phone_number, voice_region: nil, friendly_name: nil)
47
+ form = {}
48
+ form['VoiceRegion'] = voice_region unless voice_region.nil?
49
+ form['FriendlyName'] = friendly_name unless friendly_name.nil?
50
+ RoutesV2PhoneNumber.from_hash(@transport.request(:post, "/v2/PhoneNumbers/#{phone_number}", form: form))
51
+ end
52
+ end
33
53
  end
@@ -0,0 +1,250 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../models/voice_v1'
4
+
5
+ module VoiceML
6
+ # `client.voice_v1` — Twilio Voice v1 (voice.twilio.com/v1) surface.
7
+ # Sits outside the /2010-04-01/Accounts/... namespace; account resolved from Basic auth.
8
+ class VoiceV1Resource
9
+ attr_reader :ip_records, :source_ip_mappings, :byoc_trunks,
10
+ :connection_policies, :dialing_permissions
11
+
12
+ def initialize(transport)
13
+ @ip_records = VoiceV1IpRecordsResource.new(transport)
14
+ @source_ip_mappings = VoiceV1SourceIpMappingsResource.new(transport)
15
+ @byoc_trunks = VoiceV1ByocTrunksResource.new(transport)
16
+ @connection_policies = VoiceV1ConnectionPoliciesResource.new(transport)
17
+ @dialing_permissions = VoiceV1DialingPermissionsResource.new(transport)
18
+ end
19
+ end
20
+
21
+ # /v1/IpRecords + /v1/IpRecords/{Sid}
22
+ class VoiceV1IpRecordsResource
23
+ def initialize(transport)
24
+ @transport = transport
25
+ end
26
+
27
+ def list(page_size: nil)
28
+ params = {}
29
+ params['PageSize'] = page_size unless page_size.nil?
30
+ VoiceV1IpRecordList.new(@transport.request(:get, '/v1/IpRecords', params: params))
31
+ end
32
+
33
+ def create(ip_address:, friendly_name: nil, cidr_prefix_length: nil)
34
+ form = { 'IpAddress' => ip_address }
35
+ form['FriendlyName'] = friendly_name unless friendly_name.nil?
36
+ form['CidrPrefixLength'] = cidr_prefix_length unless cidr_prefix_length.nil?
37
+ VoiceV1IpRecord.from_hash(@transport.request(:post, '/v1/IpRecords', form: form))
38
+ end
39
+
40
+ def fetch(sid)
41
+ VoiceV1IpRecord.from_hash(@transport.request(:get, "/v1/IpRecords/#{sid}"))
42
+ end
43
+
44
+ def update(sid, friendly_name: nil)
45
+ form = {}
46
+ form['FriendlyName'] = friendly_name unless friendly_name.nil?
47
+ VoiceV1IpRecord.from_hash(@transport.request(:post, "/v1/IpRecords/#{sid}", form: form))
48
+ end
49
+
50
+ def delete(sid)
51
+ @transport.request(:delete, "/v1/IpRecords/#{sid}")
52
+ nil
53
+ end
54
+ end
55
+
56
+ # /v1/SourceIpMappings + /v1/SourceIpMappings/{Sid}
57
+ class VoiceV1SourceIpMappingsResource
58
+ def initialize(transport)
59
+ @transport = transport
60
+ end
61
+
62
+ def list(page_size: nil)
63
+ params = {}
64
+ params['PageSize'] = page_size unless page_size.nil?
65
+ VoiceV1SourceIpMappingList.new(@transport.request(:get, '/v1/SourceIpMappings', params: params))
66
+ end
67
+
68
+ def create(ip_record_sid:, sip_domain_sid:)
69
+ form = { 'IpRecordSid' => ip_record_sid, 'SipDomainSid' => sip_domain_sid }
70
+ VoiceV1SourceIpMapping.from_hash(@transport.request(:post, '/v1/SourceIpMappings', form: form))
71
+ end
72
+
73
+ def fetch(sid)
74
+ VoiceV1SourceIpMapping.from_hash(@transport.request(:get, "/v1/SourceIpMappings/#{sid}"))
75
+ end
76
+
77
+ def update(sid, sip_domain_sid:)
78
+ form = { 'SipDomainSid' => sip_domain_sid }
79
+ VoiceV1SourceIpMapping.from_hash(@transport.request(:post, "/v1/SourceIpMappings/#{sid}", form: form))
80
+ end
81
+
82
+ def delete(sid)
83
+ @transport.request(:delete, "/v1/SourceIpMappings/#{sid}")
84
+ nil
85
+ end
86
+ end
87
+
88
+ # /v1/ByocTrunks + /v1/ByocTrunks/{Sid}
89
+ class VoiceV1ByocTrunksResource
90
+ TRUNK_FIELDS = {
91
+ 'FriendlyName' => :friendly_name,
92
+ 'VoiceUrl' => :voice_url,
93
+ 'VoiceMethod' => :voice_method,
94
+ 'VoiceFallbackUrl' => :voice_fallback_url,
95
+ 'VoiceFallbackMethod' => :voice_fallback_method,
96
+ 'StatusCallbackUrl' => :status_callback_url,
97
+ 'StatusCallbackMethod' => :status_callback_method,
98
+ 'CnamLookupEnabled' => :cnam_lookup_enabled,
99
+ 'ConnectionPolicySid' => :connection_policy_sid,
100
+ 'FromDomainSid' => :from_domain_sid
101
+ }.freeze
102
+
103
+ def initialize(transport)
104
+ @transport = transport
105
+ end
106
+
107
+ def list(page_size: nil)
108
+ params = {}
109
+ params['PageSize'] = page_size unless page_size.nil?
110
+ VoiceV1ByocTrunkList.new(@transport.request(:get, '/v1/ByocTrunks', params: params))
111
+ end
112
+
113
+ def create(**kwargs)
114
+ VoiceV1ByocTrunk.from_hash(@transport.request(:post, '/v1/ByocTrunks', form: build_form(kwargs)))
115
+ end
116
+
117
+ def fetch(sid)
118
+ VoiceV1ByocTrunk.from_hash(@transport.request(:get, "/v1/ByocTrunks/#{sid}"))
119
+ end
120
+
121
+ def update(sid, **kwargs)
122
+ VoiceV1ByocTrunk.from_hash(@transport.request(:post, "/v1/ByocTrunks/#{sid}", form: build_form(kwargs)))
123
+ end
124
+
125
+ def delete(sid)
126
+ @transport.request(:delete, "/v1/ByocTrunks/#{sid}")
127
+ nil
128
+ end
129
+
130
+ private
131
+
132
+ def build_form(kwargs)
133
+ out = {}
134
+ TRUNK_FIELDS.each do |wire, k|
135
+ value = kwargs[k]
136
+ next if value.nil?
137
+
138
+ out[wire] = value
139
+ end
140
+ out
141
+ end
142
+ end
143
+
144
+ # /v1/ConnectionPolicies + /v1/ConnectionPolicies/{Sid} + nested /Targets
145
+ class VoiceV1ConnectionPoliciesResource
146
+ TARGET_FIELDS = {
147
+ 'Target' => :target,
148
+ 'FriendlyName' => :friendly_name,
149
+ 'Priority' => :priority,
150
+ 'Weight' => :weight,
151
+ 'Enabled' => :enabled
152
+ }.freeze
153
+
154
+ def initialize(transport)
155
+ @transport = transport
156
+ end
157
+
158
+ def list(page_size: nil)
159
+ params = {}
160
+ params['PageSize'] = page_size unless page_size.nil?
161
+ VoiceV1ConnectionPolicyList.new(@transport.request(:get, '/v1/ConnectionPolicies', params: params))
162
+ end
163
+
164
+ def create(friendly_name: nil)
165
+ form = {}
166
+ form['FriendlyName'] = friendly_name unless friendly_name.nil?
167
+ VoiceV1ConnectionPolicy.from_hash(@transport.request(:post, '/v1/ConnectionPolicies', form: form))
168
+ end
169
+
170
+ def fetch(sid)
171
+ VoiceV1ConnectionPolicy.from_hash(@transport.request(:get, "/v1/ConnectionPolicies/#{sid}"))
172
+ end
173
+
174
+ def update(sid, friendly_name: nil)
175
+ form = {}
176
+ form['FriendlyName'] = friendly_name unless friendly_name.nil?
177
+ VoiceV1ConnectionPolicy.from_hash(@transport.request(:post, "/v1/ConnectionPolicies/#{sid}", form: form))
178
+ end
179
+
180
+ def delete(sid)
181
+ @transport.request(:delete, "/v1/ConnectionPolicies/#{sid}")
182
+ nil
183
+ end
184
+
185
+ # --- /v1/ConnectionPolicies/{ConnectionPolicySid}/Targets ---
186
+ def list_targets(connection_policy_sid, page_size: nil)
187
+ params = {}
188
+ params['PageSize'] = page_size unless page_size.nil?
189
+ VoiceV1ConnectionPolicyTargetList.new(
190
+ @transport.request(:get, "/v1/ConnectionPolicies/#{connection_policy_sid}/Targets", params: params)
191
+ )
192
+ end
193
+
194
+ def create_target(connection_policy_sid, target:, friendly_name: nil, priority: nil, weight: nil, enabled: nil)
195
+ kwargs = { target: target, friendly_name: friendly_name, priority: priority, weight: weight, enabled: enabled }
196
+ VoiceV1ConnectionPolicyTarget.from_hash(
197
+ @transport.request(:post, "/v1/ConnectionPolicies/#{connection_policy_sid}/Targets",
198
+ form: build_target_form(kwargs))
199
+ )
200
+ end
201
+
202
+ def fetch_target(connection_policy_sid, sid)
203
+ VoiceV1ConnectionPolicyTarget.from_hash(
204
+ @transport.request(:get, "/v1/ConnectionPolicies/#{connection_policy_sid}/Targets/#{sid}")
205
+ )
206
+ end
207
+
208
+ def update_target(connection_policy_sid, sid, **kwargs)
209
+ VoiceV1ConnectionPolicyTarget.from_hash(
210
+ @transport.request(:post, "/v1/ConnectionPolicies/#{connection_policy_sid}/Targets/#{sid}",
211
+ form: build_target_form(kwargs))
212
+ )
213
+ end
214
+
215
+ def delete_target(connection_policy_sid, sid)
216
+ @transport.request(:delete, "/v1/ConnectionPolicies/#{connection_policy_sid}/Targets/#{sid}")
217
+ nil
218
+ end
219
+
220
+ private
221
+
222
+ def build_target_form(kwargs)
223
+ out = {}
224
+ TARGET_FIELDS.each do |wire, k|
225
+ value = kwargs[k]
226
+ next if value.nil?
227
+
228
+ out[wire] = value
229
+ end
230
+ out
231
+ end
232
+ end
233
+
234
+ # /v1/Settings — DialingPermissions inheritance toggle (singleton).
235
+ class VoiceV1DialingPermissionsResource
236
+ def initialize(transport)
237
+ @transport = transport
238
+ end
239
+
240
+ def fetch_settings
241
+ VoiceV1DialingPermissionsSettings.from_hash(@transport.request(:get, '/v1/Settings'))
242
+ end
243
+
244
+ def update_settings(dialing_permissions_inheritance: nil)
245
+ form = {}
246
+ form['DialingPermissionsInheritance'] = dialing_permissions_inheritance unless dialing_permissions_inheritance.nil?
247
+ VoiceV1DialingPermissionsSettings.from_hash(@transport.request(:post, '/v1/Settings', form: form))
248
+ end
249
+ end
250
+ end
@@ -66,10 +66,14 @@ module VoiceML
66
66
  # Perform a request. Pass `form:` for a form-urlencoded POST body, `json:` for a JSON body,
67
67
  # or neither for a plain GET/DELETE. `params:` are query-string params.
68
68
  #
69
+ # `base_url:` overrides the client's default host for this one call (used to route product
70
+ # resources at their subdomain — Conversations, Messaging Service). When set, the request
71
+ # goes to an absolute URL so the correct host/SNI is used per request; see `ScopedTransport`.
72
+ #
69
73
  # @return [Hash, Array, nil] the parsed JSON body (or `nil` for empty 2xx).
70
74
  # @raise [VoiceML::ApiError] for non-2xx responses (subclasses by status family).
71
- def request(method, path, params: nil, form: nil, json: nil)
72
- uri = build_uri(path, params)
75
+ def request(method, path, params: nil, form: nil, json: nil, base_url: nil)
76
+ uri = build_uri(request_path(path, base_url), params)
73
77
 
74
78
  attempt = 0
75
79
  loop do
@@ -95,8 +99,8 @@ module VoiceML
95
99
  # redirect that `GET /Recordings/{sid}.wav` issues when audio has been archived.
96
100
  #
97
101
  # @return [Array(Integer, String, Hash)] status code, response body bytes, header hash.
98
- def fetch_bytes(path)
99
- uri = build_uri(path, nil)
102
+ def fetch_bytes(path, base_url: nil)
103
+ uri = build_uri(request_path(path, base_url), nil)
100
104
  visited = []
101
105
  loop do
102
106
  raise ApiError.new('too many redirects', status_code: 0) if visited.length > 5
@@ -126,6 +130,14 @@ module VoiceML
126
130
  Errno::EHOSTUNREACH, SocketError, EOFError]
127
131
  end
128
132
 
133
+ # Prefix `path` with a per-request `base_url` override, if any. Leaves absolute paths and
134
+ # the default-host case untouched so `build_uri` handles them as before.
135
+ def request_path(path, base_url)
136
+ return path if base_url.nil? || base_url.empty?
137
+
138
+ "#{base_url}#{path}"
139
+ end
140
+
129
141
  def build_uri(path, params)
130
142
  uri = if path.start_with?('http://', 'https://')
131
143
  URI.parse(path)
@@ -293,4 +305,32 @@ module VoiceML
293
305
  [8.0, 0.5 * (2**attempt)].min
294
306
  end
295
307
  end
308
+
309
+ # A transport view pinned to a specific product base URL.
310
+ #
311
+ # Wraps a real `Transport` and forwards every call, injecting a `base_url` override so an
312
+ # entire resource group lands on its product subdomain (`conversations.voicetel.com` /
313
+ # `messaging.voicetel.com`) without each resource needing to know its own host.
314
+ #
315
+ # @api private
316
+ class ScopedTransport
317
+ attr_reader :base_url
318
+
319
+ def initialize(inner, base_url)
320
+ @inner = inner
321
+ @base_url = Transport.strip_trailing_slashes(base_url)
322
+ end
323
+
324
+ def account_sid
325
+ @inner.account_sid
326
+ end
327
+
328
+ def request(method, path, params: nil, form: nil, json: nil)
329
+ @inner.request(method, path, params: params, form: form, json: json, base_url: @base_url)
330
+ end
331
+
332
+ def fetch_bytes(path)
333
+ @inner.fetch_bytes(path, base_url: @base_url)
334
+ end
335
+ end
296
336
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module VoiceML
4
- VERSION = '0.8.1'
4
+ VERSION = '0.9.2'
5
5
  end
data/lib/voiceml.rb CHANGED
@@ -25,3 +25,14 @@ require_relative 'voiceml/version'
25
25
  require_relative 'voiceml/errors'
26
26
  require_relative 'voiceml/transport'
27
27
  require_relative 'voiceml/client'
28
+
29
+ # Model-only resources — surfaces that voiceml runs as Twilio-compat stubs
30
+ # (no first-class client.* getter yet) but still need decodable Ruby classes
31
+ # for response-shape conformance and migration tooling. Resource files
32
+ # require their own models; these are the standalone ones.
33
+ require_relative 'voiceml/models/accounts'
34
+ require_relative 'voiceml/models/outgoing_caller_ids'
35
+ require_relative 'voiceml/models/media'
36
+ require_relative 'voiceml/models/notifications'
37
+ require_relative 'voiceml/models/user_defined_messages'
38
+ require_relative 'voiceml/models/assistants_v1'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: voiceml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - VoiceTel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-17 00:00:00.000000000 Z
11
+ date: 2026-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -64,14 +64,23 @@ files:
64
64
  - lib/voiceml.rb
65
65
  - lib/voiceml/client.rb
66
66
  - lib/voiceml/errors.rb
67
+ - lib/voiceml/hosts.rb
68
+ - lib/voiceml/models/accounts.rb
67
69
  - lib/voiceml/models/applications.rb
70
+ - lib/voiceml/models/assistants_v1.rb
68
71
  - lib/voiceml/models/calls.rb
69
72
  - lib/voiceml/models/common.rb
70
73
  - lib/voiceml/models/conferences.rb
74
+ - lib/voiceml/models/conversations_v1.rb
71
75
  - lib/voiceml/models/diagnostics.rb
72
76
  - lib/voiceml/models/incoming_phone_numbers.rb
77
+ - lib/voiceml/models/media.rb
73
78
  - lib/voiceml/models/messages.rb
79
+ - lib/voiceml/models/messaging_v1.rb
80
+ - lib/voiceml/models/notifications.rb
81
+ - lib/voiceml/models/outgoing_caller_ids.rb
74
82
  - lib/voiceml/models/payments.rb
83
+ - lib/voiceml/models/pricing.rb
75
84
  - lib/voiceml/models/queues.rb
76
85
  - lib/voiceml/models/recordings.rb
77
86
  - lib/voiceml/models/routes_v2.rb
@@ -79,18 +88,25 @@ files:
79
88
  - lib/voiceml/models/siprec.rb
80
89
  - lib/voiceml/models/streams.rb
81
90
  - lib/voiceml/models/transcriptions.rb
91
+ - lib/voiceml/models/user_defined_messages.rb
92
+ - lib/voiceml/models/voice_v1.rb
82
93
  - lib/voiceml/resources/applications.rb
94
+ - lib/voiceml/resources/assistants_v1.rb
83
95
  - lib/voiceml/resources/base.rb
84
96
  - lib/voiceml/resources/calls.rb
85
97
  - lib/voiceml/resources/conferences.rb
98
+ - lib/voiceml/resources/conversations_v1.rb
86
99
  - lib/voiceml/resources/diagnostics.rb
87
100
  - lib/voiceml/resources/incoming_phone_numbers.rb
88
101
  - lib/voiceml/resources/messages.rb
102
+ - lib/voiceml/resources/messaging_v1.rb
89
103
  - lib/voiceml/resources/notifications.rb
104
+ - lib/voiceml/resources/pricing.rb
90
105
  - lib/voiceml/resources/queues.rb
91
106
  - lib/voiceml/resources/recordings.rb
92
107
  - lib/voiceml/resources/routes_v2.rb
93
108
  - lib/voiceml/resources/sip.rb
109
+ - lib/voiceml/resources/voice_v1.rb
94
110
  - lib/voiceml/transport.rb
95
111
  - lib/voiceml/version.rb
96
112
  homepage: https://voiceml.voicetel.com