camille 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +220 -11
- data/lib/camille/basic_type.rb +9 -4
- data/lib/camille/controller_extension.rb +3 -2
- data/lib/camille/generators/install_generator.rb +4 -4
- data/lib/camille/generators/templates/.keep +0 -0
- data/lib/camille/generators/templates/schema_template.erb +1 -1
- data/lib/camille/generators/templates/type_template.erb +4 -4
- data/lib/camille/syntax.rb +75 -0
- data/lib/camille/type_error_printer.rb +2 -2
- data/lib/camille/types/number.rb +1 -1
- data/lib/camille/types/number_literal.rb +25 -0
- data/lib/camille/types/string_literal.rb +25 -0
- data/lib/camille/version.rb +1 -1
- data/lib/camille.rb +3 -1
- metadata +6 -5
- data/lib/camille/core_ext.rb +0 -33
- data/lib/camille/generators/templates/schema_example.rb +0 -35
- data/lib/camille/generators/templates/type_example.rb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d5aa31349909cebc62309be91631d632419b925d9a6ded594485dfa204e9170
|
4
|
+
data.tar.gz: d1b1cb24a630a0c109fc1042636372cf193dd135f1ef17784f3907c1a169502e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d65f32be6f7d8beee2d22b9d5cf5ed4fc84fa91c925605c6aa0be09d51475211ddbee5e23f321892d0f46fa59878af2d9753e3daac7c1e62f2ccf285e8147b6
|
7
|
+
data.tar.gz: f2195c3c54f6c571abeb2fa3b541160f734c1ecee6adfaec4bc4025ad0be820ae4235028a3c91427aec3c7cbdcc4ba19ba5f7a3e7cb530a3e943913b57955a78
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,31 @@
|
|
1
1
|
# Camille
|
2
2
|
|
3
|
-
|
3
|
+
## Why?
|
4
4
|
|
5
|
-
|
5
|
+
Traditionally, the JSON response from a Rails API server isn't typed. So even if we have TypeScript at the front-end, we still have little guarantee that our back-end would return the correct type and structure of data. In order to eliminate type mismatch between both ends, Camille provides a syntax for you to define type schema for your Rails API, and uses these schemas to generate the TypeScript functions for calling the API.
|
6
|
+
|
7
|
+
For example, an endpoint defined in Ruby, where `data` is a controller action,
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
get :data do
|
11
|
+
params(
|
12
|
+
id: Number
|
13
|
+
)
|
14
|
+
response(
|
15
|
+
name: String
|
16
|
+
)
|
17
|
+
end
|
18
|
+
```
|
19
|
+
|
20
|
+
will become a function in TypeScript:
|
21
|
+
|
22
|
+
```typescript
|
23
|
+
data(params: {id: number}): Promise<{name: string}>
|
24
|
+
```
|
25
|
+
|
26
|
+
Therefore, if the front-end requests the API by calling `data`, we have guarantee that `id` is presented in `params`, and Camille will require the response to contain a string `name`, so the front-end can receive the correct type of data.
|
27
|
+
|
28
|
+
By using these request functions, we also don't need to know about HTTP verbs and paths. It's impossible to have unrecognized routes, since Camille will make sure that each function handled by the correct Rails action.
|
6
29
|
|
7
30
|
## Installation
|
8
31
|
|
@@ -14,22 +37,208 @@ gem 'camille'
|
|
14
37
|
|
15
38
|
And then execute:
|
16
39
|
|
17
|
-
|
40
|
+
```bash
|
41
|
+
bundle install
|
42
|
+
bundle exec rails g camille:install
|
43
|
+
```
|
18
44
|
|
19
|
-
|
45
|
+
## Usage
|
20
46
|
|
21
|
-
|
47
|
+
### Schemas
|
22
48
|
|
23
|
-
|
49
|
+
A schema defines the type of `params` and `response` for a controller action. The following commands will generate schema definition files in `config/camille/schemas`.
|
24
50
|
|
25
|
-
|
51
|
+
```bash
|
52
|
+
# to generate a schema for ProductsController
|
53
|
+
bundle exec rails g camille:schema products
|
54
|
+
# to generate a schema for Api::ProductController
|
55
|
+
bundle exec rails g camille:schema api/products
|
56
|
+
```
|
26
57
|
|
27
|
-
|
58
|
+
An example of schema definition:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
using Camille::Syntax
|
62
|
+
|
63
|
+
class Camille::Schemas::Api::Products < Camille::Schema
|
64
|
+
include Camille::Types
|
65
|
+
|
66
|
+
get :data do
|
67
|
+
params(
|
68
|
+
id: Number
|
69
|
+
)
|
70
|
+
response(
|
71
|
+
name: String
|
72
|
+
)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
The `Api::Products` schema defines one endpoint `data` and its params and response type. This endpoint corresponds to the `data` action on `Api::ProductsController`. Inside the action, you can assume that `params[:id]` is a number, and you will need to `render json: {name: 'some string'}` in order to pass the typecheck.
|
78
|
+
|
79
|
+
When generating TypeScript request functions, the `data` endpoint will become a function having the following signature:
|
80
|
+
|
81
|
+
```typescript
|
82
|
+
data(params: {id: number}): Promise<{name: string}>
|
83
|
+
```
|
84
|
+
|
85
|
+
Therefore, the front-end user is required to provide an `id` when they call this function. And they can expect to get a `name` from the response of this request. There are no more type mismatch between both ends.
|
86
|
+
|
87
|
+
Camille will automatically add a Rails route for each endpoint. You don't need to do anything other than having the schema file in place.
|
88
|
+
|
89
|
+
When defining an endpoint, you can also use `post` instead of `get` for non-idempotent requests. However, no other HTTP verbs are supported, because verbs in RESTful like `patch` and `delete` indicate what we do on resources, but in RPC-style design each request is merely a function call that does not concern RESTful resources.
|
90
|
+
|
91
|
+
### Custom types
|
92
|
+
|
93
|
+
In addition to primitive types, you can define custom types in Camille. The following commands will generate type definition files in `config/camille/types`.
|
94
|
+
|
95
|
+
```bash
|
96
|
+
# to generate a type named Product
|
97
|
+
rails g camille:type product
|
98
|
+
# to generate a type named Nested::Product
|
99
|
+
rails g camille:type nested/product
|
100
|
+
```
|
101
|
+
|
102
|
+
An example of custom type definition:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
using Camille::Syntax
|
106
|
+
|
107
|
+
class Camille::Types::Product < Camille::Type
|
108
|
+
include Camille::Types
|
109
|
+
|
110
|
+
alias_of(
|
111
|
+
id: Number,
|
112
|
+
name: String
|
113
|
+
)
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
117
|
+
Each custom type is considered a type alias in TypeScript. And `alias_of` defines what this type is aliasing. In this case, the `Product` type is an alias of an object type having fields `id` as `Number` and `name` as `String`. When generating TypeScript, it will be converted to the following:
|
118
|
+
|
119
|
+
```typescript
|
120
|
+
type Product = {id: number, name: string}
|
121
|
+
```
|
28
122
|
|
29
|
-
|
123
|
+
### Available syntax for types
|
124
|
+
|
125
|
+
Camille supports most of the type syntax in TypeScript. Below is a list of types that you can use in type and schema definition.
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
params(
|
129
|
+
# primitive types in TypeScript
|
130
|
+
number: Number,
|
131
|
+
string: String,
|
132
|
+
boolean: Boolean,
|
133
|
+
null: Null,
|
134
|
+
undefined: Undefined,
|
135
|
+
any: Any,
|
136
|
+
# an array type is a type name followed by '[]'
|
137
|
+
array: Number[],
|
138
|
+
# an object type looks like hash
|
139
|
+
object: {
|
140
|
+
field: Number
|
141
|
+
},
|
142
|
+
# an array of objects also works
|
143
|
+
object_array: {
|
144
|
+
field: Number
|
145
|
+
}[]
|
146
|
+
# a union type is two types connected by '|'
|
147
|
+
union: Number | String,
|
148
|
+
# a tuple type is several types put inside '[]'
|
149
|
+
tuple: [Number, String, Boolean],
|
150
|
+
# a field followed by '?' is optional, the same as in TypeScript
|
151
|
+
optional?: Number,
|
152
|
+
# literal types
|
153
|
+
number_literal: 1,
|
154
|
+
string_literal: 'hello',
|
155
|
+
# a custom type we defined above
|
156
|
+
product: Product
|
157
|
+
)
|
158
|
+
```
|
159
|
+
|
160
|
+
String literal types and probably enums are planned for the future.
|
161
|
+
|
162
|
+
### TypeScript generation
|
163
|
+
|
164
|
+
After you have your types and schemas in place, you can visit `/camille/endpoints.ts` in development environment to have the TypeScript request functions generated.
|
165
|
+
|
166
|
+
An example from our previously defined type and schema will be:
|
167
|
+
|
168
|
+
```typescript
|
169
|
+
import request from './request'
|
170
|
+
|
171
|
+
export type Product = {id: number, name: string}
|
172
|
+
|
173
|
+
export default {
|
174
|
+
api: {
|
175
|
+
data(params: {id: number}): Promise<{name: string}> {
|
176
|
+
return request('get', '/api/products/data', params)
|
177
|
+
}
|
178
|
+
}
|
179
|
+
}
|
180
|
+
```
|
181
|
+
|
182
|
+
The first line of `import` is configurable as `config.ts_header` in `config/camille/configuration.rb`. You would need to implement a `request` function that performs the HTTP request.
|
183
|
+
|
184
|
+
### Conversion between camelCase and snake_case
|
185
|
+
|
186
|
+
In TypeScript world, people usually use camelCase to name functions and variables, while in Ruby the convention is to use snake_case. Camille will automatically convert between these two when processing request.
|
187
|
+
|
188
|
+
For example,
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
get :special_data do
|
192
|
+
params(
|
193
|
+
long_id: Number
|
194
|
+
)
|
195
|
+
response(
|
196
|
+
long_name: String
|
197
|
+
)
|
198
|
+
end
|
199
|
+
```
|
200
|
+
|
201
|
+
will have TS signature:
|
202
|
+
|
203
|
+
```typescript
|
204
|
+
specialData(params: {longId: number}): Promise<{longName: string}>
|
205
|
+
```
|
206
|
+
|
207
|
+
In the Rails action you still use `params[:long_id]` to access the parameter and return `long_name` in response.
|
208
|
+
|
209
|
+
### Typechecking
|
210
|
+
|
211
|
+
If a controller action has a corresponding schema, Camille will raise an error if the returned JSON doesn't match the response type specified in the schema.
|
212
|
+
|
213
|
+
For example for
|
214
|
+
```ruby
|
215
|
+
response(
|
216
|
+
object: {
|
217
|
+
array: Number[]
|
218
|
+
}
|
219
|
+
)
|
220
|
+
```
|
221
|
+
|
222
|
+
if we return such a JSON in our action
|
223
|
+
```ruby
|
224
|
+
render json: {
|
225
|
+
object: {
|
226
|
+
array: [1, 2, '3']
|
227
|
+
}
|
228
|
+
}
|
229
|
+
```
|
230
|
+
|
231
|
+
Camille will print the following error:
|
232
|
+
```
|
233
|
+
object:
|
234
|
+
array:
|
235
|
+
[2]: Expected number, got "3".
|
236
|
+
```
|
237
|
+
|
238
|
+
## Development
|
30
239
|
|
31
|
-
|
240
|
+
Run tests with `bundle exec rake`.
|
32
241
|
|
33
242
|
## Contributing
|
34
243
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
244
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/onyxblade/camille.
|
data/lib/camille/basic_type.rb
CHANGED
@@ -24,13 +24,18 @@ module Camille
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def self.instance value
|
27
|
-
|
27
|
+
case
|
28
|
+
when value.is_a?(::Hash)
|
28
29
|
Camille::Types::Object.new(value)
|
29
|
-
|
30
|
+
when value.is_a?(::Array)
|
30
31
|
Camille::Types::Tuple.new(value)
|
31
|
-
|
32
|
+
when value.is_a?(Integer) || value.is_a?(Float)
|
33
|
+
Camille::Types::NumberLiteral.new(value)
|
34
|
+
when value.is_a?(::String)
|
35
|
+
Camille::Types::StringLiteral.new(value)
|
36
|
+
when value.is_a?(Camille::BasicType)
|
32
37
|
value
|
33
|
-
|
38
|
+
when value.is_a?(Class) && value < Camille::BasicType && value.directly_instantiable?
|
34
39
|
value.new
|
35
40
|
else
|
36
41
|
raise InvalidTypeError.new("#{value} cannot be converted to a type instance.")
|
@@ -17,8 +17,9 @@ module Camille
|
|
17
17
|
if value = render_options[:json]
|
18
18
|
error = endpoint.response_type.check(value)
|
19
19
|
if error
|
20
|
-
|
21
|
-
|
20
|
+
string_io = StringIO.new
|
21
|
+
Camille::TypeErrorPrinter.new(error).print(string_io)
|
22
|
+
raise TypeError.new("\nType check failed for response.\n#{string_io.string}")
|
22
23
|
else
|
23
24
|
if value.is_a? Hash
|
24
25
|
value.deep_transform_keys!{|k| k.to_s.camelize(:lower)}
|
@@ -10,12 +10,12 @@ module Camille
|
|
10
10
|
copy_file "configuration.rb", "config/camille/configuration.rb"
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
14
|
-
copy_file "
|
13
|
+
def create_types_folder
|
14
|
+
copy_file ".keep", "config/camille/types/.keep"
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
copy_file "
|
17
|
+
def create_schemas_folder
|
18
|
+
copy_file ".keep", "config/camille/schemas/.keep"
|
19
19
|
end
|
20
20
|
|
21
21
|
end
|
File without changes
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Camille
|
2
|
+
module Syntax
|
3
|
+
module NULL_VALUE; end
|
4
|
+
|
5
|
+
refine ::Hash do
|
6
|
+
def [] key = NULL_VALUE
|
7
|
+
if key == NULL_VALUE
|
8
|
+
Camille::Types::Object.new(self)[]
|
9
|
+
else
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def | other
|
15
|
+
Camille::Types::Union.new(Camille::Types::Object.new(self), other)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
refine ::Array do
|
20
|
+
def [] key = NULL_VALUE
|
21
|
+
if key == NULL_VALUE
|
22
|
+
Camille::Types::Tuple.new(self)[]
|
23
|
+
else
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def | other
|
29
|
+
Camille::Types::Union.new(Camille::Types::Tuple.new(self), other)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
refine Integer do
|
34
|
+
def [] key = NULL_VALUE
|
35
|
+
if key == NULL_VALUE
|
36
|
+
Camille::Types::NumberLiteral.new(self)[]
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def | other
|
43
|
+
Camille::Types::Union.new(Camille::Types::NumberLiteral.new(self), other)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
refine Float do
|
48
|
+
def [] key = NULL_VALUE
|
49
|
+
if key == NULL_VALUE
|
50
|
+
Camille::Types::NumberLiteral.new(self)[]
|
51
|
+
else
|
52
|
+
super
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def | other
|
57
|
+
Camille::Types::Union.new(Camille::Types::NumberLiteral.new(self), other)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
refine ::String do
|
62
|
+
def [] key = NULL_VALUE
|
63
|
+
if key == NULL_VALUE
|
64
|
+
Camille::Types::StringLiteral.new(self)[]
|
65
|
+
else
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def | other
|
71
|
+
Camille::Types::Union.new(Camille::Types::StringLiteral.new(self), other)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -18,9 +18,9 @@ module Camille
|
|
18
18
|
def print_composite_error io, error, indentation
|
19
19
|
error.components.each do |key, error|
|
20
20
|
if error.basic?
|
21
|
-
io.puts
|
21
|
+
io.puts "\u00A0" * indentation + "#{key}: #{error.message}"
|
22
22
|
else
|
23
|
-
io.puts
|
23
|
+
io.puts "\u00A0" * indentation + "#{key}:"
|
24
24
|
print_composite_error io, error, indentation + 2
|
25
25
|
end
|
26
26
|
end
|
data/lib/camille/types/number.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Camille
|
2
|
+
module Types
|
3
|
+
class NumberLiteral < Camille::BasicType
|
4
|
+
class ArgumentError < ::ArgumentError; end
|
5
|
+
|
6
|
+
def initialize value
|
7
|
+
if value.is_a?(Integer) || value.is_a?(Float)
|
8
|
+
@value = value
|
9
|
+
else
|
10
|
+
raise ArgumentError.new("Expecting an integer or a float, got #{value.inspect}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def check value
|
15
|
+
unless value == @value
|
16
|
+
Camille::TypeError.new("Expected number literal #{@value.inspect}, got #{value.inspect}.")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def literal
|
21
|
+
@value.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Camille
|
2
|
+
module Types
|
3
|
+
class StringLiteral < Camille::BasicType
|
4
|
+
class ArgumentError < ::ArgumentError; end
|
5
|
+
|
6
|
+
def initialize value
|
7
|
+
if value.is_a?(::String)
|
8
|
+
@value = value
|
9
|
+
else
|
10
|
+
raise ArgumentError.new("Expecting a string, got #{value.inspect}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def check value
|
15
|
+
unless value == @value
|
16
|
+
Camille::TypeError.new("Expected string literal #{@value.inspect}, got #{value.inspect}.")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def literal
|
21
|
+
@value.inspect
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/camille/version.rb
CHANGED
data/lib/camille.rb
CHANGED
@@ -15,10 +15,12 @@ require_relative "camille/types/undefined"
|
|
15
15
|
require_relative "camille/types/union"
|
16
16
|
require_relative "camille/types/tuple"
|
17
17
|
require_relative "camille/types/any"
|
18
|
+
require_relative "camille/types/number_literal"
|
19
|
+
require_relative "camille/types/string_literal"
|
18
20
|
require_relative "camille/type"
|
19
21
|
require_relative "camille/type_error"
|
20
22
|
require_relative "camille/type_error_printer"
|
21
|
-
require_relative "camille/
|
23
|
+
require_relative "camille/syntax"
|
22
24
|
require_relative "camille/endpoint"
|
23
25
|
require_relative "camille/schema"
|
24
26
|
require_relative "camille/schemas"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: camille
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- 辻彩
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-03-
|
11
|
+
date: 2023-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -49,14 +49,12 @@ files:
|
|
49
49
|
- lib/camille/code_generator.rb
|
50
50
|
- lib/camille/configuration.rb
|
51
51
|
- lib/camille/controller_extension.rb
|
52
|
-
- lib/camille/core_ext.rb
|
53
52
|
- lib/camille/endpoint.rb
|
54
53
|
- lib/camille/generators/install_generator.rb
|
55
54
|
- lib/camille/generators/schema_generator.rb
|
55
|
+
- lib/camille/generators/templates/.keep
|
56
56
|
- lib/camille/generators/templates/configuration.rb
|
57
|
-
- lib/camille/generators/templates/schema_example.rb
|
58
57
|
- lib/camille/generators/templates/schema_template.erb
|
59
|
-
- lib/camille/generators/templates/type_example.rb
|
60
58
|
- lib/camille/generators/templates/type_template.erb
|
61
59
|
- lib/camille/generators/type_generator.rb
|
62
60
|
- lib/camille/line.rb
|
@@ -65,6 +63,7 @@ files:
|
|
65
63
|
- lib/camille/railtie.rb
|
66
64
|
- lib/camille/schema.rb
|
67
65
|
- lib/camille/schemas.rb
|
66
|
+
- lib/camille/syntax.rb
|
68
67
|
- lib/camille/type.rb
|
69
68
|
- lib/camille/type_error.rb
|
70
69
|
- lib/camille/type_error_printer.rb
|
@@ -74,8 +73,10 @@ files:
|
|
74
73
|
- lib/camille/types/boolean.rb
|
75
74
|
- lib/camille/types/null.rb
|
76
75
|
- lib/camille/types/number.rb
|
76
|
+
- lib/camille/types/number_literal.rb
|
77
77
|
- lib/camille/types/object.rb
|
78
78
|
- lib/camille/types/string.rb
|
79
|
+
- lib/camille/types/string_literal.rb
|
79
80
|
- lib/camille/types/tuple.rb
|
80
81
|
- lib/camille/types/undefined.rb
|
81
82
|
- lib/camille/types/union.rb
|
data/lib/camille/core_ext.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
module Camille
|
2
|
-
module CoreExt
|
3
|
-
module NULL_VALUE; end
|
4
|
-
|
5
|
-
refine ::Hash do
|
6
|
-
def [] key = NULL_VALUE
|
7
|
-
if key == NULL_VALUE
|
8
|
-
Camille::Types::Object.new(self)[]
|
9
|
-
else
|
10
|
-
super
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def | other
|
15
|
-
Camille::Types::Union.new(Camille::Types::Object.new(self), other)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
refine ::Array do
|
20
|
-
def [] key = NULL_VALUE
|
21
|
-
if key == NULL_VALUE
|
22
|
-
Camille::Types::Tuple.new(self)[]
|
23
|
-
else
|
24
|
-
super
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def | other
|
29
|
-
Camille::Types::Union.new(Camille::Types::Tuple.new(self), other)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
using Camille::CoreExt
|
2
|
-
|
3
|
-
class Camille::Schemas::Examples < Camille::Schema
|
4
|
-
include Camille::Types
|
5
|
-
|
6
|
-
get :find do
|
7
|
-
params(
|
8
|
-
id: Number
|
9
|
-
)
|
10
|
-
|
11
|
-
response(
|
12
|
-
example?: Example
|
13
|
-
)
|
14
|
-
end
|
15
|
-
|
16
|
-
get :list do
|
17
|
-
response(
|
18
|
-
examples: Example[]
|
19
|
-
)
|
20
|
-
end
|
21
|
-
|
22
|
-
post :update do
|
23
|
-
params(
|
24
|
-
id: Number,
|
25
|
-
example: Example
|
26
|
-
)
|
27
|
-
|
28
|
-
response(
|
29
|
-
success: Boolean,
|
30
|
-
errors: {
|
31
|
-
message: String
|
32
|
-
}[]
|
33
|
-
)
|
34
|
-
end
|
35
|
-
end
|