verquest 0.1.0 → 0.2.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 +4 -4
- data/.yardopts +8 -0
- data/CHANGELOG.md +2 -0
- data/README.md +416 -13
- data/lib/verquest/base/helper_class_methods.rb +37 -0
- data/lib/verquest/base/private_class_methods.rb +247 -0
- data/lib/verquest/base/public_class_methods.rb +108 -0
- data/lib/verquest/base.rb +38 -0
- data/lib/verquest/configuration.rb +73 -0
- data/lib/verquest/gem_version.rb +5 -0
- data/lib/verquest/properties/array.rb +63 -0
- data/lib/verquest/properties/base.rb +104 -0
- data/lib/verquest/properties/collection.rb +147 -0
- data/lib/verquest/properties/field.rb +65 -0
- data/lib/verquest/properties/object.rb +83 -0
- data/lib/verquest/properties/reference.rb +92 -0
- data/lib/verquest/properties.rb +47 -0
- data/lib/verquest/result.rb +75 -0
- data/lib/verquest/transformer.rb +179 -0
- data/lib/verquest/version.rb +208 -1
- data/lib/verquest/version_resolver.rb +42 -0
- data/lib/verquest/versions.rb +65 -0
- data/lib/verquest.rb +96 -3
- metadata +40 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 602d7b11dee9986d5005901641dc12ef407b61ee172f6c60972e16bff2bcae6b
|
4
|
+
data.tar.gz: 021af67a6befd09f2375683c2641696694690fceee4954eec9c71adce48bb78f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d39fc339be03bac745e9e0e353136d25940de8cca6bfe8a28224bfa64fd05db984bc957473b404fcc68b40c1d1ec62c010238b374ec204eaad7fc1d434d5ca85
|
7
|
+
data.tar.gz: 24292a5eb4c594b1f58e724987c9acc2427347152f037027c234d0f0fbff0945afaaf2aa5369e322b022ab8e61ad92302a73397dccbbeb44f24a67eb8f1e28ca
|
data/.yardopts
ADDED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,38 +1,441 @@
|
|
1
1
|
# Verquest
|
2
2
|
|
3
|
-
|
3
|
+
[](https://badge.fury.io/rb/verquest)
|
4
|
+
[](LICENSE.txt)
|
4
5
|
|
5
|
-
|
6
|
+
Verquest is a Ruby gem that offers an elegant solution for versioning API requests. It simplifies the process of defining and evolving your API schema over time, with robust support for:
|
7
|
+
|
8
|
+
- Defining versioned request structures
|
9
|
+
- Gracefully handling API versioning
|
10
|
+
- Mapping between external and internal parameter structures
|
11
|
+
- Validating parameters against [JSON Schema](https://json-schema.org/learn)
|
12
|
+
- Generating components for OpenAPI documentation
|
13
|
+
- Mapping error keys back to the external API structure (planned feature)
|
14
|
+
|
15
|
+
> The gem is still in development. Until version 1.0, the API may change. There are some features like `oneOf`, `anyOf`, `allOf` that are not implemented yet.
|
6
16
|
|
7
17
|
## Installation
|
8
18
|
|
9
|
-
|
19
|
+
Add this line to your application's Gemfile:
|
10
20
|
|
11
|
-
|
21
|
+
```ruby
|
22
|
+
gem "verquest", "~> 0.2"
|
23
|
+
```
|
24
|
+
|
25
|
+
And then execute:
|
12
26
|
|
13
27
|
```bash
|
14
|
-
bundle
|
28
|
+
bundle install
|
15
29
|
```
|
16
30
|
|
17
|
-
|
31
|
+
## Quick Start
|
18
32
|
|
19
|
-
|
20
|
-
|
33
|
+
### Define a versioned API requests
|
34
|
+
|
35
|
+
Address Create Request
|
36
|
+
```ruby
|
37
|
+
class AddressCreateRequest < Verquest::Base
|
38
|
+
description "Address Create Request"
|
39
|
+
schema_options additional_properties: false
|
40
|
+
|
41
|
+
version "2025-06" do # or v1 or anything you need (use a custom version_resolver if needed)
|
42
|
+
with_options type: :string, required: true do
|
43
|
+
field :street, description: "Street address"
|
44
|
+
field :city, description: "City of residence"
|
45
|
+
field :postal_code, description: "Postal code"
|
46
|
+
field :country, description: "Country of residence"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
````
|
51
|
+
|
52
|
+
User Create Request that uses the `AddressCreateRequest`
|
53
|
+
```ruby
|
54
|
+
class UserCreateRequest < Verquest::Base
|
55
|
+
description "User Create Request"
|
56
|
+
schema_options additional_properties: false
|
57
|
+
|
58
|
+
version "2025-06" do # or v1 or anything you need (use a custom version_resolver if needed)
|
59
|
+
with_options type: :string, required: true do
|
60
|
+
field :first_name, description: "The first name of the user", max_length: 50
|
61
|
+
field :last_name, description: "The last name of the user", max_length: 50
|
62
|
+
field :email, format: "email", description: "The email address of the user"
|
63
|
+
end
|
64
|
+
|
65
|
+
field :birth_date, type: :string, format: "date", description: "The birth date of the user"
|
66
|
+
|
67
|
+
reference :address, from: AddressCreateRequest, required: true
|
68
|
+
|
69
|
+
collection :permissions, description: "Permissions associated with the user" do
|
70
|
+
field :name, type: :string, required: true, description: "Name of the permission"
|
71
|
+
|
72
|
+
with_options type: :boolean do
|
73
|
+
field :read, description: "Permission to read"
|
74
|
+
field :write, description: "Permission to write"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
field :role, type: :string, description: "Role of the user", enum: %w[member manager], default: "member"
|
79
|
+
|
80
|
+
object :profile_details do
|
81
|
+
field :bio, type: :string, description: "Short biography of the user"
|
82
|
+
|
83
|
+
array :hobbies, type: :string, description: "Tags associated with the user"
|
84
|
+
|
85
|
+
object :social_links, description: "Some social networks" do
|
86
|
+
with_options type: :string, format: "uri" do
|
87
|
+
field :github, description: "GitHub profile URL"
|
88
|
+
field :mastodon, description: "Mastodon profile URL"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
### Example usage in Rails Controller
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
|
100
|
+
class UsersController < ApplicationController
|
101
|
+
rescue_from Verquest::InvalidParamsError, with: :handle_invalid_params
|
102
|
+
|
103
|
+
def create
|
104
|
+
result = Users::Create.call(params: user_params) # service object to handle the creation logic
|
105
|
+
|
106
|
+
if result.success?
|
107
|
+
# render success response
|
108
|
+
else
|
109
|
+
# render error response
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def user_params
|
116
|
+
UserCreateRequest.process(params, version: params[:api_version])
|
117
|
+
end
|
118
|
+
end
|
119
|
+
```
|
120
|
+
|
121
|
+
### JSON schema for OpenAPI
|
122
|
+
You can generate JSON Schema for your versioned requests, which can be used for API documentation:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
UserCreateRequest.to_schema(version: "2025-06")
|
126
|
+
```
|
127
|
+
|
128
|
+
Output:
|
129
|
+
```ruby
|
130
|
+
{
|
131
|
+
type: :object,
|
132
|
+
description: "User Create Request",
|
133
|
+
required: [:first_name, :last_name, :email, :address],
|
134
|
+
properties: {
|
135
|
+
first_name: {type: :string, description: "The first name of the user", maxLength: 50},
|
136
|
+
last_name: {type: :string, description: "The last name of the user", maxLength: 50},
|
137
|
+
email: {type: :string, format: "email", description: "The email address of the user"},
|
138
|
+
birth_date: {type: :string, format: "date", description: "The birth date of the user"},
|
139
|
+
address: {"$ref": "#/components/schemas/AddressCreateRequest"},
|
140
|
+
permissions: {
|
141
|
+
type: :array,
|
142
|
+
items: {
|
143
|
+
type: :object,
|
144
|
+
required: [:name],
|
145
|
+
properties: {
|
146
|
+
name: {type: :string, description: "Name of the permission"},
|
147
|
+
read: {type: :boolean, description: "Permission to read"},
|
148
|
+
write: {type: :boolean, description: "Permission to write"}
|
149
|
+
}
|
150
|
+
},
|
151
|
+
description: "Permissions associated with the user"
|
152
|
+
},
|
153
|
+
role: {type: :string, description: "Role of the user", enum: ["member", "manager"], default: "member"},
|
154
|
+
profile_details: {
|
155
|
+
type: :object,
|
156
|
+
required: [],
|
157
|
+
properties: {
|
158
|
+
bio: {type: :string, description: "Short biography of the user"},
|
159
|
+
hobbies: {type: :array, items: {type: :string}, description: "Tags associated with the user"},
|
160
|
+
social_links: {
|
161
|
+
type: :object,
|
162
|
+
required: [],
|
163
|
+
properties: {
|
164
|
+
github: {type: :string, format: "uri", description: "GitHub profile URL"},
|
165
|
+
mastodon: {type: :string, format: "uri", description: "Mastodon profile URL"}
|
166
|
+
},
|
167
|
+
description: "Some social networks"
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
},
|
172
|
+
additionalProperties: false
|
173
|
+
}
|
174
|
+
```
|
175
|
+
|
176
|
+
### JSON schema for validation
|
177
|
+
|
178
|
+
You can check the validation JSON schema for a specific version of your request:
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
UserCreateRequest.to_validation_schema(version: "2025-06")
|
182
|
+
```
|
183
|
+
|
184
|
+
Output:
|
185
|
+
```ruby
|
186
|
+
{
|
187
|
+
type: :object,
|
188
|
+
description: "User Create Request",
|
189
|
+
required: [:first_name, :last_name, :email, :address],
|
190
|
+
properties: {
|
191
|
+
first_name: {type: :string, description: "The first name of the user", maxLength: 50},
|
192
|
+
last_name: {type: :string, description: "The last name of the user", maxLength: 50},
|
193
|
+
email: {type: :string, format: "email", description: "The email address of the user"},
|
194
|
+
birth_date: {type: :string, format: "date", description: "The birth date of the user"},
|
195
|
+
address: { # from the AddressCreateRequest
|
196
|
+
type: :object,
|
197
|
+
description: "Address Create Request",
|
198
|
+
required: [:street, :city, :postal_code, :country],
|
199
|
+
properties: {
|
200
|
+
street: {type: :string, description: "Street address"},
|
201
|
+
city: {type: :string, description: "City of residence"},
|
202
|
+
postal_code: {type: :string, description: "Postal code"},
|
203
|
+
country: {type: :string, description: "Country of residence"}
|
204
|
+
},
|
205
|
+
additionalProperties: false
|
206
|
+
},
|
207
|
+
permissions: {
|
208
|
+
type: :array,
|
209
|
+
items: {
|
210
|
+
type: :object,
|
211
|
+
required: [:name],
|
212
|
+
properties: {
|
213
|
+
name: {type: :string, description: "Name of the permission"},
|
214
|
+
read: {type: :boolean, description: "Permission to read"},
|
215
|
+
write: {type: :boolean, description: "Permission to write"}
|
216
|
+
}
|
217
|
+
},
|
218
|
+
description: "Permissions associated with the user"
|
219
|
+
},
|
220
|
+
role: {type: :string, description: "Role of the user", enum: ["member", "manager"], default: "member"},
|
221
|
+
profile_details: {
|
222
|
+
type: :object,
|
223
|
+
required: [],
|
224
|
+
properties: {
|
225
|
+
bio: {type: :string, description: "Short biography of the user"},
|
226
|
+
hobbies: {type: :array, items: {type: :string}, description: "Tags associated with the user"},
|
227
|
+
social_links: {
|
228
|
+
type: :object,
|
229
|
+
required: [],
|
230
|
+
properties: {
|
231
|
+
github: {type: :string, format: "uri", description: "GitHub profile URL"},
|
232
|
+
mastodon: {type: :string, format: "uri", description: "Mastodon profile URL"}
|
233
|
+
},
|
234
|
+
description: "Some social networks"
|
235
|
+
}
|
236
|
+
}
|
237
|
+
}
|
238
|
+
},
|
239
|
+
additionalProperties: false
|
240
|
+
}
|
241
|
+
```
|
242
|
+
|
243
|
+
You can also validate it to ensure it meets the JSON Schema standards:
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
UserCreateRequest.validate_schema(version: "2025-06") # => true/false
|
247
|
+
```
|
248
|
+
|
249
|
+
## Core Features
|
250
|
+
|
251
|
+
### Schema Definition and Validation
|
252
|
+
|
253
|
+
See the example above for how to define a request schema. Verquest provides a DSL to define your API requests with various component types and helper methods based on JSON Schema, which is also used in [OpenAPI specification](https://swagger.io/specification/#schema-object-examples) for components.
|
254
|
+
|
255
|
+
The JSON schema can be used for both validation of incoming parameters and for generating OpenAPI documentation components.
|
256
|
+
|
257
|
+
#### Component types
|
258
|
+
|
259
|
+
- `field`: Represents a scalar value (string, integer, boolean, etc.).
|
260
|
+
- `object`: Represents a JSON object with properties.
|
261
|
+
- `array`: Represents a JSON array with scalar items.
|
262
|
+
- `collection`: Represents a array of objects defined manually or by a reference to another request.
|
263
|
+
- `reference`: Represents a reference to another request, allowing you to reuse existing request structures.
|
264
|
+
|
265
|
+
#### Helper methods
|
266
|
+
|
267
|
+
- `description`: Adds a description to the request or per version.
|
268
|
+
- `schema_options`: Allows you to set additional options for the JSON Schema, such as `additional_properties` for request or per version. All fields (except `reference`) can be defined with options like `required`, `format`, `min_lenght`, `max_length`, etc. all in snake case.
|
269
|
+
- `with_options`: Allows you to define multiple fields with the same options, reducing repetition.
|
270
|
+
|
271
|
+
### Versioning
|
272
|
+
|
273
|
+
Verquest allows you to define multiple versions of your API requests, making it easy to evolve your API over time:
|
274
|
+
|
275
|
+
```ruby
|
276
|
+
class UserCreateRequest < Verquest::Base
|
277
|
+
version "2025-04" do
|
278
|
+
field :name, type: :string, required: true
|
279
|
+
field :email, type: :string, format: "email", required: true
|
280
|
+
|
281
|
+
field :street, type: :string
|
282
|
+
field :city, type: :string
|
283
|
+
end
|
284
|
+
|
285
|
+
# Implicit inheritance from the previous version
|
286
|
+
version "2025-06", exclude_properties: %i[street city] do
|
287
|
+
field :phone, type: :string
|
288
|
+
|
289
|
+
# Replace street and city with a structured address object with zip
|
290
|
+
object :address do
|
291
|
+
field :street, type: :string
|
292
|
+
field :city, type: :string
|
293
|
+
field :zip, type: :string
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
# Disabled inheritance, `inherit` can also be set to a specific version
|
298
|
+
version "2025-08", inherit: false do
|
299
|
+
field :name, type: :string, required: true
|
300
|
+
field :email, type: :string, format: "email", required: true
|
301
|
+
field :phone, type: :string
|
302
|
+
|
303
|
+
# Replace address with a more structured version
|
304
|
+
object :address do
|
305
|
+
field :street_line1, type: :string, required: true
|
306
|
+
field :street_line2, type: :string
|
307
|
+
field :city, type: :string, required: true
|
308
|
+
field :state, type: :string
|
309
|
+
field :postal_code, type: :string, required: true
|
310
|
+
field :country, type: :string, required: true
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
```
|
315
|
+
|
316
|
+
Internal `Verquest::VersionResolver` is then used to resolve the right version for the one specified in the call. It implements a "downgrading" strategy - when an exact version match isn't found, it returns the closest earlier version.
|
317
|
+
|
318
|
+
Example:
|
319
|
+
```ruby
|
320
|
+
UserCreateRequest.process(params, version: "2025-05") # => use the defined version "2025-04"
|
321
|
+
UserCreateRequest.process(params, version: "2025-06") # => use the defined version "2025-06"
|
322
|
+
UserCreateRequest.process(params, version: "2025-07") # => use the closest earlier version "2025-06"
|
323
|
+
UserCreateRequest.process(params, version: "2025-08") # => use the defined version "2025-08"
|
324
|
+
UserCreateRequest.process(params, version: "2025-10") # => use the closest earlier version "2025-08"
|
325
|
+
```
|
326
|
+
|
327
|
+
This is used across all referenced requests, so if you have a `UserRequest` that references an `AddressCreateRequest`, it will also resolve the correct version of the `AddressCreateRequest` based on the initial requested version (as the `AddressCreateRequest` can have different versions defined).
|
328
|
+
|
329
|
+
The goal here is to avoid redefining the same request structure in multiple versions when there are no changes, and to facilitate the easy evolution of API requests over time. When a new API version is created and there are no changes to the requests, you don't need to update anything.
|
330
|
+
|
331
|
+
### Mapping request structure
|
332
|
+
|
333
|
+
Verquest's mapping system allows transforming external API request structures into your internal application data structures.
|
334
|
+
|
335
|
+
Here’s a short example: we store the address in the same table as the user internally, but the API request structure is different.
|
336
|
+
|
337
|
+
```ruby
|
338
|
+
class UserCreateRequest < Verquest::Base
|
339
|
+
version "2025-06", exclude_properties: %i[street city] do
|
340
|
+
field :full_name, type: :string, map: "name"
|
341
|
+
field :email, type: :string, format: "email", required: true
|
342
|
+
field :phone, type: :string
|
343
|
+
|
344
|
+
object :address do
|
345
|
+
field :street, type: :string, map: "/address_street"
|
346
|
+
field :city, type: :string, map: "/address_city"
|
347
|
+
field :postal_code, type: :string, map: "/address_zip"
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
```
|
352
|
+
|
353
|
+
When called with `UserCreateRequest.process(params)`, the `address` object will be mapped to the internal structure with keys `address_street`, `address_city`, and `address_zip`.
|
354
|
+
|
355
|
+
Example request params
|
356
|
+
```ruby
|
357
|
+
{
|
358
|
+
"full_name": "John Doe",
|
359
|
+
"email": "john@doe.com",
|
360
|
+
"phone": "1234567890",
|
361
|
+
"address": {
|
362
|
+
"street": "123 Main St",
|
363
|
+
"city": "Springfield",
|
364
|
+
"postal_code": "12345"
|
365
|
+
}
|
366
|
+
}
|
367
|
+
```
|
368
|
+
|
369
|
+
Will be transformed to:
|
370
|
+
```ruby
|
371
|
+
{
|
372
|
+
"name": "John Doe",
|
373
|
+
"email": "john@doe.com",
|
374
|
+
"phone": "1234567890",
|
375
|
+
"address_street": "123 Main St",
|
376
|
+
"address_city": "Springfield",
|
377
|
+
"address_zip": "12345"
|
378
|
+
}
|
379
|
+
````
|
380
|
+
|
381
|
+
What you can use:
|
382
|
+
- `/` to reference the root of the request structure
|
383
|
+
- `nested.structure` use dot notation to reference nested structures
|
384
|
+
- if the `map` is not set, the field name will be used as the key in the internal structure
|
385
|
+
|
386
|
+
There are some limitations and the implementation can be improved, but it should works for most common use cases.
|
387
|
+
|
388
|
+
See the mapping test (in `test/verquest/base_test.rb`) for more examples of mapping.
|
389
|
+
|
390
|
+
### Component Generation for OpenAPI
|
391
|
+
|
392
|
+
Generate JSON Schema, component name and reference for OpenAPI documentation:
|
393
|
+
|
394
|
+
```ruby
|
395
|
+
UserCreateRequest.component_name # => "UserCreateRequest"
|
396
|
+
UserCreateRequest.to_ref # => "#/components/schemas/UserCreateRequest"
|
397
|
+
component_schema = UserCreateRequest.to_schema(version: "2025-06")
|
398
|
+
```
|
399
|
+
|
400
|
+
## Configuration
|
401
|
+
|
402
|
+
Configure Verquest globally:
|
403
|
+
|
404
|
+
```ruby
|
405
|
+
Verquest.configure do |config|
|
406
|
+
# Enable validation by default
|
407
|
+
config.validate_params = true # default
|
408
|
+
|
409
|
+
# Set the default version to use
|
410
|
+
config.current_version = -> { Current.api_version }
|
411
|
+
|
412
|
+
# Set the JSON Schema version
|
413
|
+
config.json_schema_version = :draft6 # default
|
414
|
+
|
415
|
+
# Set the error handling strategy for processing params
|
416
|
+
config.validation_error_handling = :raise # default, can be set also to :result
|
417
|
+
|
418
|
+
# Remove extra root keys from provided params
|
419
|
+
config.remove_extra_root_keys = true # default
|
420
|
+
|
421
|
+
# Set custom version resolver
|
422
|
+
config.version_resolver = CustomeVersionResolver # default is `Verquest::VersionResolver`
|
423
|
+
end
|
21
424
|
```
|
22
425
|
|
23
|
-
##
|
426
|
+
## Documentation
|
24
427
|
|
25
|
-
|
428
|
+
For detailed documentation, please visit the [YARD documentation](https://www.rubydoc.info/gems/verquest).
|
26
429
|
|
27
430
|
## Development
|
28
431
|
|
29
432
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
433
|
|
31
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `
|
434
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `gem_version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
435
|
|
33
436
|
## Contributing
|
34
437
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
438
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/CiTroNaK/verquest. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/CiTroNaK/verquest/blob/main/CODE_OF_CONDUCT.md).
|
36
439
|
|
37
440
|
## License
|
38
441
|
|
@@ -40,4 +443,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
40
443
|
|
41
444
|
## Code of Conduct
|
42
445
|
|
43
|
-
Everyone interacting in the Verquest project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
446
|
+
Everyone interacting in the Verquest project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/CiTroNaK/verquest/blob/main/CODE_OF_CONDUCT.md).
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Verquest
|
4
|
+
# Helper methods for Verquest::Base class methods
|
5
|
+
#
|
6
|
+
# This module contains utility methods for working with string and hash
|
7
|
+
# transformations. It provides methods for converting between different
|
8
|
+
# naming conventions (snake_case to camelCase) which is particularly useful
|
9
|
+
# when working with JSON Schema properties.
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
module Base::HelperClassMethods
|
13
|
+
# Converts hash keys from snake_case to camelCase format
|
14
|
+
#
|
15
|
+
# Transforms all keys in the given hash from snake_case (e.g., "max_length")
|
16
|
+
# to camelCase (e.g., "maxLength") format, which is commonly used in JSON Schema.
|
17
|
+
# The transformation happens in place, modifying the original hash.
|
18
|
+
#
|
19
|
+
# @param hash [Hash] The hash containing snake_case keys
|
20
|
+
# @return [Hash] The same hash with keys transformed to camelCase
|
21
|
+
def camelize(hash)
|
22
|
+
hash.transform_keys! { |key| snake_to_camel(key.to_s).to_sym }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Converts a snake_case string to camelCase
|
26
|
+
#
|
27
|
+
# Takes a string in snake_case format (e.g., "max_length") and converts it
|
28
|
+
# to camelCase format (e.g., "maxLength") by capitalizing each word after
|
29
|
+
# the first one and removing underscores.
|
30
|
+
#
|
31
|
+
# @param str [String] The snake_case string to convert
|
32
|
+
# @return [String] The converted camelCase string
|
33
|
+
def snake_to_camel(str)
|
34
|
+
str.split("_").inject { |memo, word| memo + word.capitalize }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|