camille 0.0.1 → 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/Gemfile +3 -0
- data/Gemfile.lock +147 -0
- data/Rakefile +16 -1
- data/lib/camille/basic_type.rb +40 -0
- data/lib/camille/code_generator.rb +22 -0
- data/lib/camille/configuration.rb +22 -0
- data/lib/camille/controller_extension.rb +46 -0
- data/lib/camille/core_ext.rb +33 -0
- data/lib/camille/endpoint.rb +50 -0
- data/lib/camille/line.rb +43 -0
- data/lib/camille/loader.rb +92 -0
- data/lib/camille/main_controller.rb +13 -0
- data/lib/camille/railtie.rb +33 -0
- data/lib/camille/schema.rb +60 -0
- data/lib/camille/schemas.rb +47 -0
- data/lib/camille/type.rb +45 -0
- data/lib/camille/type_error.rb +21 -0
- data/lib/camille/type_error_printer.rb +29 -0
- data/lib/camille/types/any.rb +12 -0
- data/lib/camille/types/array.rb +26 -0
- data/lib/camille/types/boolean.rb +15 -0
- data/lib/camille/types/null.rb +15 -0
- data/lib/camille/types/number.rb +16 -0
- data/lib/camille/types/object.rb +46 -0
- data/lib/camille/types/string.rb +15 -0
- data/lib/camille/types/tuple.rb +29 -0
- data/lib/camille/types/undefined.rb +15 -0
- data/lib/camille/types/union.rb +29 -0
- data/lib/camille/types.rb +10 -0
- data/lib/camille/version.rb +1 -1
- data/lib/camille.rb +36 -1
- metadata +52 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f5911a8f4d1519a2a373ebee118bce4f110d64f5eaeafece5c8c8e46580b5c20
|
|
4
|
+
data.tar.gz: b59a4600bd145350d1217219b0074ffb6e80d8cdc97318995b6ba2b0bffe7450
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3088068a948ca92e1a6bc90690c5f27d5e2c788c3f76e2ba40b5ebf62b7c3fdffac809411941a3f05a77ace636ce16bf7742c12c1ae5700c4b45d7bfb9fdddfb
|
|
7
|
+
data.tar.gz: c6f0a0b72216805bf591bc4289172e69382f702ccc14d161a3b63267dc83816a47045e9d61884029349543aac5ecf5aeb43bbc82f0c95ef23ff7df569037b8b6
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -2,11 +2,140 @@ PATH
|
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
4
|
camille (0.1.0)
|
|
5
|
+
rails (>= 6.1, < 8)
|
|
5
6
|
|
|
6
7
|
GEM
|
|
7
8
|
remote: https://rubygems.org/
|
|
8
9
|
specs:
|
|
10
|
+
actioncable (7.0.4.2)
|
|
11
|
+
actionpack (= 7.0.4.2)
|
|
12
|
+
activesupport (= 7.0.4.2)
|
|
13
|
+
nio4r (~> 2.0)
|
|
14
|
+
websocket-driver (>= 0.6.1)
|
|
15
|
+
actionmailbox (7.0.4.2)
|
|
16
|
+
actionpack (= 7.0.4.2)
|
|
17
|
+
activejob (= 7.0.4.2)
|
|
18
|
+
activerecord (= 7.0.4.2)
|
|
19
|
+
activestorage (= 7.0.4.2)
|
|
20
|
+
activesupport (= 7.0.4.2)
|
|
21
|
+
mail (>= 2.7.1)
|
|
22
|
+
net-imap
|
|
23
|
+
net-pop
|
|
24
|
+
net-smtp
|
|
25
|
+
actionmailer (7.0.4.2)
|
|
26
|
+
actionpack (= 7.0.4.2)
|
|
27
|
+
actionview (= 7.0.4.2)
|
|
28
|
+
activejob (= 7.0.4.2)
|
|
29
|
+
activesupport (= 7.0.4.2)
|
|
30
|
+
mail (~> 2.5, >= 2.5.4)
|
|
31
|
+
net-imap
|
|
32
|
+
net-pop
|
|
33
|
+
net-smtp
|
|
34
|
+
rails-dom-testing (~> 2.0)
|
|
35
|
+
actionpack (7.0.4.2)
|
|
36
|
+
actionview (= 7.0.4.2)
|
|
37
|
+
activesupport (= 7.0.4.2)
|
|
38
|
+
rack (~> 2.0, >= 2.2.0)
|
|
39
|
+
rack-test (>= 0.6.3)
|
|
40
|
+
rails-dom-testing (~> 2.0)
|
|
41
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
|
42
|
+
actiontext (7.0.4.2)
|
|
43
|
+
actionpack (= 7.0.4.2)
|
|
44
|
+
activerecord (= 7.0.4.2)
|
|
45
|
+
activestorage (= 7.0.4.2)
|
|
46
|
+
activesupport (= 7.0.4.2)
|
|
47
|
+
globalid (>= 0.6.0)
|
|
48
|
+
nokogiri (>= 1.8.5)
|
|
49
|
+
actionview (7.0.4.2)
|
|
50
|
+
activesupport (= 7.0.4.2)
|
|
51
|
+
builder (~> 3.1)
|
|
52
|
+
erubi (~> 1.4)
|
|
53
|
+
rails-dom-testing (~> 2.0)
|
|
54
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
|
55
|
+
activejob (7.0.4.2)
|
|
56
|
+
activesupport (= 7.0.4.2)
|
|
57
|
+
globalid (>= 0.3.6)
|
|
58
|
+
activemodel (7.0.4.2)
|
|
59
|
+
activesupport (= 7.0.4.2)
|
|
60
|
+
activerecord (7.0.4.2)
|
|
61
|
+
activemodel (= 7.0.4.2)
|
|
62
|
+
activesupport (= 7.0.4.2)
|
|
63
|
+
activestorage (7.0.4.2)
|
|
64
|
+
actionpack (= 7.0.4.2)
|
|
65
|
+
activejob (= 7.0.4.2)
|
|
66
|
+
activerecord (= 7.0.4.2)
|
|
67
|
+
activesupport (= 7.0.4.2)
|
|
68
|
+
marcel (~> 1.0)
|
|
69
|
+
mini_mime (>= 1.1.0)
|
|
70
|
+
activesupport (7.0.4.2)
|
|
71
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
72
|
+
i18n (>= 1.6, < 2)
|
|
73
|
+
minitest (>= 5.1)
|
|
74
|
+
tzinfo (~> 2.0)
|
|
75
|
+
builder (3.2.4)
|
|
76
|
+
concurrent-ruby (1.2.2)
|
|
77
|
+
crass (1.0.6)
|
|
78
|
+
date (3.3.3)
|
|
9
79
|
diff-lcs (1.5.0)
|
|
80
|
+
erubi (1.12.0)
|
|
81
|
+
globalid (1.1.0)
|
|
82
|
+
activesupport (>= 5.0)
|
|
83
|
+
i18n (1.12.0)
|
|
84
|
+
concurrent-ruby (~> 1.0)
|
|
85
|
+
loofah (2.19.1)
|
|
86
|
+
crass (~> 1.0.2)
|
|
87
|
+
nokogiri (>= 1.5.9)
|
|
88
|
+
mail (2.8.1)
|
|
89
|
+
mini_mime (>= 0.1.1)
|
|
90
|
+
net-imap
|
|
91
|
+
net-pop
|
|
92
|
+
net-smtp
|
|
93
|
+
marcel (1.0.2)
|
|
94
|
+
method_source (1.0.0)
|
|
95
|
+
mini_mime (1.1.2)
|
|
96
|
+
minitest (5.17.0)
|
|
97
|
+
net-imap (0.3.4)
|
|
98
|
+
date
|
|
99
|
+
net-protocol
|
|
100
|
+
net-pop (0.1.2)
|
|
101
|
+
net-protocol
|
|
102
|
+
net-protocol (0.2.1)
|
|
103
|
+
timeout
|
|
104
|
+
net-smtp (0.3.3)
|
|
105
|
+
net-protocol
|
|
106
|
+
nio4r (2.5.8)
|
|
107
|
+
nokogiri (1.14.2-x86_64-linux)
|
|
108
|
+
racc (~> 1.4)
|
|
109
|
+
racc (1.6.2)
|
|
110
|
+
rack (2.2.6.3)
|
|
111
|
+
rack-test (2.0.2)
|
|
112
|
+
rack (>= 1.3)
|
|
113
|
+
rails (7.0.4.2)
|
|
114
|
+
actioncable (= 7.0.4.2)
|
|
115
|
+
actionmailbox (= 7.0.4.2)
|
|
116
|
+
actionmailer (= 7.0.4.2)
|
|
117
|
+
actionpack (= 7.0.4.2)
|
|
118
|
+
actiontext (= 7.0.4.2)
|
|
119
|
+
actionview (= 7.0.4.2)
|
|
120
|
+
activejob (= 7.0.4.2)
|
|
121
|
+
activemodel (= 7.0.4.2)
|
|
122
|
+
activerecord (= 7.0.4.2)
|
|
123
|
+
activestorage (= 7.0.4.2)
|
|
124
|
+
activesupport (= 7.0.4.2)
|
|
125
|
+
bundler (>= 1.15.0)
|
|
126
|
+
railties (= 7.0.4.2)
|
|
127
|
+
rails-dom-testing (2.0.3)
|
|
128
|
+
activesupport (>= 4.2.0)
|
|
129
|
+
nokogiri (>= 1.6)
|
|
130
|
+
rails-html-sanitizer (1.5.0)
|
|
131
|
+
loofah (~> 2.19, >= 2.19.1)
|
|
132
|
+
railties (7.0.4.2)
|
|
133
|
+
actionpack (= 7.0.4.2)
|
|
134
|
+
activesupport (= 7.0.4.2)
|
|
135
|
+
method_source
|
|
136
|
+
rake (>= 12.2)
|
|
137
|
+
thor (~> 1.0)
|
|
138
|
+
zeitwerk (~> 2.5)
|
|
10
139
|
rake (13.0.6)
|
|
11
140
|
rspec (3.12.0)
|
|
12
141
|
rspec-core (~> 3.12.0)
|
|
@@ -20,15 +149,33 @@ GEM
|
|
|
20
149
|
rspec-mocks (3.12.3)
|
|
21
150
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
22
151
|
rspec-support (~> 3.12.0)
|
|
152
|
+
rspec-rails (6.0.1)
|
|
153
|
+
actionpack (>= 6.1)
|
|
154
|
+
activesupport (>= 6.1)
|
|
155
|
+
railties (>= 6.1)
|
|
156
|
+
rspec-core (~> 3.11)
|
|
157
|
+
rspec-expectations (~> 3.11)
|
|
158
|
+
rspec-mocks (~> 3.11)
|
|
159
|
+
rspec-support (~> 3.11)
|
|
23
160
|
rspec-support (3.12.0)
|
|
161
|
+
thor (1.2.1)
|
|
162
|
+
timeout (0.3.2)
|
|
163
|
+
tzinfo (2.0.6)
|
|
164
|
+
concurrent-ruby (~> 1.0)
|
|
165
|
+
websocket-driver (0.7.5)
|
|
166
|
+
websocket-extensions (>= 0.1.0)
|
|
167
|
+
websocket-extensions (0.1.5)
|
|
168
|
+
zeitwerk (2.6.7)
|
|
24
169
|
|
|
25
170
|
PLATFORMS
|
|
26
171
|
x86_64-linux
|
|
27
172
|
|
|
28
173
|
DEPENDENCIES
|
|
29
174
|
camille!
|
|
175
|
+
rails
|
|
30
176
|
rake (~> 13.0)
|
|
31
177
|
rspec (~> 3.0)
|
|
178
|
+
rspec-rails
|
|
32
179
|
|
|
33
180
|
BUNDLED WITH
|
|
34
181
|
2.2.33
|
data/Rakefile
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
3
3
|
require "bundler/gem_tasks"
|
|
4
4
|
require "rspec/core/rake_task"
|
|
5
5
|
|
|
6
|
-
RSpec::Core::RakeTask.new(:
|
|
6
|
+
spec_gem_task = RSpec::Core::RakeTask.new(:spec_gem)
|
|
7
|
+
spec_gem_task.exclude_pattern = 'spec/camille/rails/*_spec.rb'
|
|
8
|
+
|
|
9
|
+
spec_rails_task = RSpec::Core::RakeTask.new(:spec_rails)
|
|
10
|
+
spec_rails_task.pattern = 'spec/camille/rails/*_spec.rb'
|
|
7
11
|
|
|
8
12
|
task default: :spec
|
|
13
|
+
|
|
14
|
+
task :spec do
|
|
15
|
+
Rake::Task[:spec_gem].invoke
|
|
16
|
+
|
|
17
|
+
ENV['RAILS_ENV'] = 'test'
|
|
18
|
+
Rake::Task[:spec_rails].invoke
|
|
19
|
+
|
|
20
|
+
ENV['RAILS_ENV'] = 'development'
|
|
21
|
+
Rake::Task[:spec_rails].reenable
|
|
22
|
+
Rake::Task[:spec_rails].invoke
|
|
23
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Camille
|
|
2
|
+
# This class specifies the methods available for all types includeing built-in and custom ones.
|
|
3
|
+
class BasicType
|
|
4
|
+
class InvalidTypeError < ::ArgumentError; end
|
|
5
|
+
|
|
6
|
+
def | other
|
|
7
|
+
Camille::Types::Union.new(self, other)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def []
|
|
11
|
+
Camille::Types::Array.new(self)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.| other
|
|
15
|
+
Camille::Type.instance(self) | other
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.[]
|
|
19
|
+
Camille::Type.instance(self)[]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.directly_instantiable?
|
|
23
|
+
instance_method(:initialize).arity == 0
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.instance value
|
|
27
|
+
if value.is_a? ::Hash
|
|
28
|
+
Camille::Types::Object.new(value)
|
|
29
|
+
elsif value.is_a? ::Array
|
|
30
|
+
Camille::Types::Tuple.new(value)
|
|
31
|
+
elsif value.is_a? Camille::BasicType
|
|
32
|
+
value
|
|
33
|
+
elsif value.is_a?(Class) && value < Camille::BasicType && value.directly_instantiable?
|
|
34
|
+
value.new
|
|
35
|
+
else
|
|
36
|
+
raise InvalidTypeError.new("#{value} cannot be converted to a type instance.")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'stringio'
|
|
2
|
+
|
|
3
|
+
module Camille
|
|
4
|
+
module CodeGenerator
|
|
5
|
+
def self.generate_ts
|
|
6
|
+
io = StringIO.new
|
|
7
|
+
io.puts "// This file is automatically generated."
|
|
8
|
+
io.puts Camille::Configuration.ts_header
|
|
9
|
+
io.puts
|
|
10
|
+
Camille::Types.literal_lines.each do |line|
|
|
11
|
+
io.puts "export #{line}"
|
|
12
|
+
end
|
|
13
|
+
io.puts
|
|
14
|
+
io.print "export default "
|
|
15
|
+
Camille::Schemas.literal_lines.each do |line|
|
|
16
|
+
io.puts line
|
|
17
|
+
end
|
|
18
|
+
io.string
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
module Camille
|
|
3
|
+
class Configuration
|
|
4
|
+
class << self
|
|
5
|
+
def ts_header= string
|
|
6
|
+
@ts_header = string
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def ts_header
|
|
10
|
+
@ts_header
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def ts_location= string
|
|
14
|
+
@ts_location = string
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def ts_location
|
|
18
|
+
@ts_location
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Camille
|
|
2
|
+
module ControllerExtension
|
|
3
|
+
class TypeError < ::StandardError; end
|
|
4
|
+
class ArgumentError < ::ArgumentError; end
|
|
5
|
+
|
|
6
|
+
def camille_schema
|
|
7
|
+
@camille_schema ||= Camille::Loader.controller_name_to_schema_map[self.class.name]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def camille_endpoint
|
|
11
|
+
camille_schema && camille_schema.endpoints[action_name.to_sym]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def render *args
|
|
15
|
+
if endpoint = camille_endpoint
|
|
16
|
+
render_options = args.last
|
|
17
|
+
if value = render_options[:json]
|
|
18
|
+
error = endpoint.response_type.check(value)
|
|
19
|
+
if error
|
|
20
|
+
Camille::TypeErrorPrinter.new(error).print
|
|
21
|
+
raise TypeError.new("Type check failed for response.")
|
|
22
|
+
else
|
|
23
|
+
if value.is_a? Hash
|
|
24
|
+
value.deep_transform_keys!{|k| k.to_s.camelize(:lower)}
|
|
25
|
+
end
|
|
26
|
+
super(json: value)
|
|
27
|
+
end
|
|
28
|
+
else
|
|
29
|
+
raise ArgumentError.new("Expected key :json for `render` call.")
|
|
30
|
+
end
|
|
31
|
+
else
|
|
32
|
+
super
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def process_action(*)
|
|
37
|
+
if endpoint = camille_endpoint
|
|
38
|
+
params.deep_transform_keys!{|key| key.to_s.underscore}
|
|
39
|
+
super
|
|
40
|
+
else
|
|
41
|
+
super
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
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
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Camille
|
|
2
|
+
class Endpoint
|
|
3
|
+
class ArgumentError < ::ArgumentError; end
|
|
4
|
+
class UnknownResponseError < ::ArgumentError; end
|
|
5
|
+
|
|
6
|
+
attr_reader :params_type, :response_type, :verb, :name, :schema
|
|
7
|
+
|
|
8
|
+
def initialize schema, verb, name
|
|
9
|
+
@verb = verb
|
|
10
|
+
@name = name
|
|
11
|
+
@schema = schema
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def signature
|
|
15
|
+
unless @response_type
|
|
16
|
+
raise UnknownResponseError.new("Endpoint lacking a `response` definition.")
|
|
17
|
+
end
|
|
18
|
+
if @params_type
|
|
19
|
+
"#{ActiveSupport::Inflector.camelize @name, false}(params: #{@params_type.literal}): Promise<#{@response_type.literal}>"
|
|
20
|
+
else
|
|
21
|
+
"#{ActiveSupport::Inflector.camelize @name, false}(): Promise<#{@response_type.literal}>"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def path
|
|
26
|
+
"#{@schema.path}/#{name}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def function
|
|
30
|
+
if @params_type
|
|
31
|
+
"#{signature}{ return request('#{@verb}', '#{path}', params) }"
|
|
32
|
+
else
|
|
33
|
+
"#{signature}{ return request('#{@verb}', '#{path}', {}) }"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
def params type
|
|
39
|
+
if type.is_a?(::Hash)
|
|
40
|
+
@params_type = Camille::Type.instance type
|
|
41
|
+
else
|
|
42
|
+
raise ArgumentError.new("`params` requires a hash as input, got #{type.inspect}.")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def response type
|
|
47
|
+
@response_type = Camille::Type.instance type
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
data/lib/camille/line.rb
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Camille
|
|
2
|
+
# lines with indentation
|
|
3
|
+
class Line
|
|
4
|
+
attr_reader :string, :indent
|
|
5
|
+
|
|
6
|
+
def initialize string, indent = 0
|
|
7
|
+
@string = string
|
|
8
|
+
@indent = indent
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def do_indent
|
|
12
|
+
@indent += 2
|
|
13
|
+
self
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def == other
|
|
17
|
+
@string == other.string && @indent == other.indent
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def prepend str
|
|
21
|
+
@string = str + @string
|
|
22
|
+
self
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def append str
|
|
26
|
+
@string = @string + str
|
|
27
|
+
self
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_s
|
|
31
|
+
' ' * @indent + @string
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.join lines, delimiter
|
|
35
|
+
size = lines.size
|
|
36
|
+
lines.each_with_index do |line, index|
|
|
37
|
+
if index < size - 1
|
|
38
|
+
line.append(delimiter)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
require 'monitor'
|
|
3
|
+
|
|
4
|
+
module Camille
|
|
5
|
+
module Loader
|
|
6
|
+
extend MonitorMixin
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def setup_zeitwerk_loader app
|
|
10
|
+
synchronize do
|
|
11
|
+
loader = Zeitwerk::Loader.new
|
|
12
|
+
loader.enable_reloading if !app.config.cache_classes
|
|
13
|
+
loader.push_dir "#{app.root}/config/camille/types", namespace: Camille::Types
|
|
14
|
+
loader.push_dir "#{app.root}/config/camille/schemas", namespace: Camille::Schemas
|
|
15
|
+
|
|
16
|
+
loader.setup
|
|
17
|
+
@zeitwerk_loader = loader
|
|
18
|
+
@configuration_location = "#{app.root}/config/camille/configuration.rb"
|
|
19
|
+
|
|
20
|
+
eager_load
|
|
21
|
+
construct_controller_name_to_schema_map
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def eager_load
|
|
26
|
+
@eager_loading = true
|
|
27
|
+
load @configuration_location
|
|
28
|
+
@zeitwerk_loader.eager_load
|
|
29
|
+
@eager_loading = false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def eager_loading?
|
|
33
|
+
@eager_loading
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def reload_types_and_schemas
|
|
37
|
+
synchronize do
|
|
38
|
+
Camille::Loader.loaded_types.clear
|
|
39
|
+
Camille::Loader.loaded_schemas.clear
|
|
40
|
+
@zeitwerk_loader.reload
|
|
41
|
+
eager_load
|
|
42
|
+
construct_controller_name_to_schema_map
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def register_routes router_context
|
|
47
|
+
Camille::Loader.loaded_schemas.each do |schema|
|
|
48
|
+
schema.endpoints.each do |name, endpoint|
|
|
49
|
+
router_context.public_send(endpoint.verb, endpoint.path, controller: schema.path.gsub(/^\//, ''), action: endpoint.name, as: false)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def loaded_types
|
|
55
|
+
synchronize do
|
|
56
|
+
@loaded_types ||= []
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def loaded_schemas
|
|
61
|
+
synchronize do
|
|
62
|
+
@loaded_schemas ||= []
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def controller_name_to_schema_map
|
|
67
|
+
synchronize do
|
|
68
|
+
@controller_name_to_schema_map ||= {}
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def construct_controller_name_to_schema_map
|
|
73
|
+
synchronize do
|
|
74
|
+
controller_name_to_schema_map.clear
|
|
75
|
+
loaded_schemas.each do |schema|
|
|
76
|
+
controller_class_name = "#{schema.klass_name}Controller"
|
|
77
|
+
controller_name_to_schema_map[controller_class_name] = schema
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def reload
|
|
83
|
+
synchronize do
|
|
84
|
+
reload_types_and_schemas
|
|
85
|
+
Rails.application.reload_routes!
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require "rails"
|
|
2
|
+
|
|
3
|
+
module Camille
|
|
4
|
+
class Railtie < ::Rails::Railtie
|
|
5
|
+
|
|
6
|
+
initializer "camille.configure_rails" do |app|
|
|
7
|
+
ActionController::API.include(Camille::ControllerExtension)
|
|
8
|
+
ActionController::Base.include(Camille::ControllerExtension)
|
|
9
|
+
|
|
10
|
+
Camille::Loader.setup_zeitwerk_loader(app)
|
|
11
|
+
|
|
12
|
+
app.routes.prepend do
|
|
13
|
+
get '/camille/endpoints.ts' => 'camille/main#endpoints_ts'
|
|
14
|
+
Camille::Loader.register_routes(self)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
dir = "#{Rails.root}/config/camille"
|
|
18
|
+
|
|
19
|
+
update_checker = ActiveSupport::FileUpdateChecker.new([], {dir => ['rb']}) do
|
|
20
|
+
Camille::Loader.reload
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
app.reloaders << update_checker
|
|
24
|
+
|
|
25
|
+
app.reloader.to_run do
|
|
26
|
+
require_unload_lock!
|
|
27
|
+
update_checker.execute_if_updated
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module Camille
|
|
2
|
+
class Schema
|
|
3
|
+
class AlreadyDefinedError < ::ArgumentError; end
|
|
4
|
+
|
|
5
|
+
include Camille::Types
|
|
6
|
+
|
|
7
|
+
def self.endpoints
|
|
8
|
+
@endpoints ||= {}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.const_missing name
|
|
12
|
+
if Camille::Loader.eager_loading?
|
|
13
|
+
Camille::Types.const_get(name)
|
|
14
|
+
else
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.path
|
|
20
|
+
"/#{ActiveSupport::Inflector.underscore klass_name}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.klass_name
|
|
24
|
+
self.name.gsub(/^Camille::Schemas::/, '')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.literal_lines
|
|
28
|
+
[
|
|
29
|
+
Camille::Line.new('{'),
|
|
30
|
+
*endpoints.map do |k, e|
|
|
31
|
+
Camille::Line.new("#{e.function},")
|
|
32
|
+
end.map(&:do_indent),
|
|
33
|
+
Camille::Line.new('}')
|
|
34
|
+
]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.inherited klass
|
|
38
|
+
Camille::Loader.loaded_schemas << klass
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
def self.define_endpoint verb, name, &block
|
|
43
|
+
if endpoints[name]
|
|
44
|
+
raise AlreadyDefinedError.new("Endpoint `#{name}` has already been defined.")
|
|
45
|
+
end
|
|
46
|
+
endpoint = Camille::Endpoint.new self, verb, name
|
|
47
|
+
endpoint.instance_exec &block
|
|
48
|
+
|
|
49
|
+
endpoints[name] = endpoint
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.get name, &block
|
|
53
|
+
define_endpoint :get, name, &block
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.post name, &block
|
|
57
|
+
define_endpoint :post, name, &block
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Camille
|
|
2
|
+
module Schemas
|
|
3
|
+
def self.literal_lines
|
|
4
|
+
[
|
|
5
|
+
Camille::Line.new('{'),
|
|
6
|
+
tree_literal_lines(namespace_tree).map(&:do_indent),
|
|
7
|
+
Camille::Line.new('}')
|
|
8
|
+
]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
def self.namespace_tree
|
|
13
|
+
tree = {}
|
|
14
|
+
|
|
15
|
+
Camille::Loader.loaded_schemas.sort_by(&:klass_name).each do |s|
|
|
16
|
+
path = s.klass_name.split('::')
|
|
17
|
+
*namespaces, schema_name = path
|
|
18
|
+
|
|
19
|
+
level = namespaces.inject(tree) do |level, namespace|
|
|
20
|
+
level[namespace] ||= {}
|
|
21
|
+
level[namespace]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
level[schema_name] = s
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
tree
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.tree_literal_lines tree
|
|
31
|
+
tree.map do |key, value|
|
|
32
|
+
if value.is_a? ::Hash
|
|
33
|
+
[
|
|
34
|
+
Camille::Line.new("#{ActiveSupport::Inflector.camelize(key, false)}: {"),
|
|
35
|
+
tree_literal_lines(value).map(&:do_indent),
|
|
36
|
+
Camille::Line.new('},')
|
|
37
|
+
]
|
|
38
|
+
else
|
|
39
|
+
value.literal_lines.tap do |lines|
|
|
40
|
+
lines.first.prepend("#{ActiveSupport::Inflector.camelize(key, false)}: ")
|
|
41
|
+
lines.last.append(',')
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end.flatten
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
data/lib/camille/type.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Camille
|
|
2
|
+
# This class represents all custom types defined by the user.
|
|
3
|
+
class Type < BasicType
|
|
4
|
+
class NotImplementedError < ::NotImplementedError; end
|
|
5
|
+
include Camille::Types
|
|
6
|
+
|
|
7
|
+
attr_reader :underlying
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
raise NotImplementedError.new("Missing `alias_of` definition for type.")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.alias_of type
|
|
14
|
+
underlying = Camille::Type.instance(type)
|
|
15
|
+
|
|
16
|
+
define_method(:initialize) do
|
|
17
|
+
@underlying = underlying
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def check value
|
|
22
|
+
@underlying.check value
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.klass_name
|
|
26
|
+
name.gsub(/^Camille::Types::/, '')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def literal
|
|
30
|
+
self.class.klass_name.gsub(/::/, '_')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.const_missing name
|
|
34
|
+
if Camille::Loader.eager_loading?
|
|
35
|
+
Camille::Types.const_get(name)
|
|
36
|
+
else
|
|
37
|
+
super
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.inherited klass
|
|
42
|
+
Camille::Loader.loaded_types << klass
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Camille
|
|
2
|
+
class TypeError
|
|
3
|
+
class ArgumentError < ::ArgumentError; end
|
|
4
|
+
|
|
5
|
+
attr_reader :message, :components
|
|
6
|
+
|
|
7
|
+
def initialize message = nil, **components
|
|
8
|
+
if message.is_a? String
|
|
9
|
+
@message = message
|
|
10
|
+
elsif !components.empty?
|
|
11
|
+
@components = components
|
|
12
|
+
else
|
|
13
|
+
raise ArgumentError.new("Expecting one string or one hash.")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def basic?
|
|
18
|
+
!!@message
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Camille
|
|
2
|
+
class TypeErrorPrinter
|
|
3
|
+
INDENTATION = 2
|
|
4
|
+
|
|
5
|
+
def initialize error
|
|
6
|
+
@error = error
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def print io = STDOUT
|
|
10
|
+
if @error.basic?
|
|
11
|
+
io.puts @error.message
|
|
12
|
+
else
|
|
13
|
+
print_composite_error io, @error, 0
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
def print_composite_error io, error, indentation
|
|
19
|
+
error.components.each do |key, error|
|
|
20
|
+
if error.basic?
|
|
21
|
+
io.puts ' ' * indentation + "#{key}: #{error.message}"
|
|
22
|
+
else
|
|
23
|
+
io.puts ' ' * indentation + "#{key}:"
|
|
24
|
+
print_composite_error io, error, indentation + 2
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Camille
|
|
2
|
+
module Types
|
|
3
|
+
class Array < Camille::BasicType
|
|
4
|
+
attr_reader :content
|
|
5
|
+
|
|
6
|
+
def initialize content
|
|
7
|
+
@content = Camille::Type.instance content
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def check value
|
|
11
|
+
if value.is_a? ::Array
|
|
12
|
+
errors = value.map.with_index{|e, i| ["array[#{i}]", @content.check(e)]}.select{|x| x[1]}
|
|
13
|
+
unless errors.empty?
|
|
14
|
+
Camille::TypeError.new(**errors.to_h)
|
|
15
|
+
end
|
|
16
|
+
else
|
|
17
|
+
Camille::TypeError.new("Expected array, got #{value.inspect}.")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def literal
|
|
22
|
+
"#{@content.literal}[]"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Camille
|
|
2
|
+
module Types
|
|
3
|
+
class Boolean < Camille::BasicType
|
|
4
|
+
def check value
|
|
5
|
+
unless value == false || value == true
|
|
6
|
+
Camille::TypeError.new("Expected boolean, got #{value.inspect}.")
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def literal
|
|
11
|
+
"boolean"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Camille
|
|
2
|
+
module Types
|
|
3
|
+
class Number < Camille::BasicType
|
|
4
|
+
|
|
5
|
+
def check value
|
|
6
|
+
unless value.is_a?(Integer) || value.is_a?(Float)
|
|
7
|
+
Camille::TypeError.new("Expected number, got #{value.inspect}.")
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def literal
|
|
12
|
+
"number"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Camille
|
|
2
|
+
module Types
|
|
3
|
+
class Object < Camille::BasicType
|
|
4
|
+
attr_reader :fields
|
|
5
|
+
|
|
6
|
+
def initialize fields
|
|
7
|
+
@fields = normalize_fields fields
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def check value
|
|
11
|
+
if value.is_a? Hash
|
|
12
|
+
errors = @fields.map do |key, type|
|
|
13
|
+
[key.to_s, type.check(value[key])]
|
|
14
|
+
end.select{|x| x[1]}
|
|
15
|
+
|
|
16
|
+
unless errors.empty?
|
|
17
|
+
Camille::TypeError.new(**errors.to_h)
|
|
18
|
+
end
|
|
19
|
+
else
|
|
20
|
+
Camille::TypeError.new("Expected hash, got #{value.inspect}.")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def literal
|
|
25
|
+
"{#{@fields.map{|k,v| "#{ActiveSupport::Inflector.camelize k.to_s, false}: #{v.literal}"}.join(', ')}}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
def normalize_fields fields
|
|
30
|
+
fields.map do |key, value|
|
|
31
|
+
type = Camille::Type.instance(value)
|
|
32
|
+
if key.end_with?('?')
|
|
33
|
+
new_key = remove_question_mark(key)
|
|
34
|
+
[new_key, type | Camille::Types::Undefined.new]
|
|
35
|
+
else
|
|
36
|
+
[key, type]
|
|
37
|
+
end
|
|
38
|
+
end.to_h
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def remove_question_mark sym
|
|
42
|
+
sym.to_s.gsub(/\?$/, '').to_sym
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Camille
|
|
2
|
+
module Types
|
|
3
|
+
class Tuple < Camille::BasicType
|
|
4
|
+
attr_reader :elements
|
|
5
|
+
|
|
6
|
+
def initialize elements
|
|
7
|
+
@elements = elements.map{|e| Camille::Type.instance e}
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def check value
|
|
11
|
+
if value.is_a? ::Array
|
|
12
|
+
errors = @elements.map.with_index do |type, index|
|
|
13
|
+
["tuple[#{index}]", type.check(value[index])]
|
|
14
|
+
end.select{|x| x[1]}
|
|
15
|
+
|
|
16
|
+
unless errors.empty?
|
|
17
|
+
Camille::TypeError.new(**errors.to_h)
|
|
18
|
+
end
|
|
19
|
+
else
|
|
20
|
+
Camille::TypeError.new("Expected array, got #{value.inspect}.")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def literal
|
|
25
|
+
"[#{elements.map(&:literal).join(', ')}]"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Camille
|
|
2
|
+
module Types
|
|
3
|
+
class Union < Camille::BasicType
|
|
4
|
+
attr_reader :left, :right
|
|
5
|
+
|
|
6
|
+
def initialize left, right
|
|
7
|
+
@left = Camille::Type.instance left
|
|
8
|
+
@right = Camille::Type.instance right
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def check value
|
|
12
|
+
left_result = @left.check value
|
|
13
|
+
if left_result
|
|
14
|
+
right_result = @right.check value
|
|
15
|
+
if right_result
|
|
16
|
+
Camille::TypeError.new(
|
|
17
|
+
'union.left' => left_result,
|
|
18
|
+
'union.right' => right_result
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def literal
|
|
25
|
+
"#{@left.literal} | #{@right.literal}"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/camille/version.rb
CHANGED
data/lib/camille.rb
CHANGED
|
@@ -1,8 +1,43 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "active_support"
|
|
4
|
+
|
|
3
5
|
require_relative "camille/version"
|
|
6
|
+
require_relative "camille/basic_type"
|
|
7
|
+
require_relative "camille/types"
|
|
8
|
+
require_relative "camille/types/number"
|
|
9
|
+
require_relative "camille/types/string"
|
|
10
|
+
require_relative "camille/types/boolean"
|
|
11
|
+
require_relative "camille/types/array"
|
|
12
|
+
require_relative "camille/types/object"
|
|
13
|
+
require_relative "camille/types/null"
|
|
14
|
+
require_relative "camille/types/undefined"
|
|
15
|
+
require_relative "camille/types/union"
|
|
16
|
+
require_relative "camille/types/tuple"
|
|
17
|
+
require_relative "camille/types/any"
|
|
18
|
+
require_relative "camille/type"
|
|
19
|
+
require_relative "camille/type_error"
|
|
20
|
+
require_relative "camille/type_error_printer"
|
|
21
|
+
require_relative "camille/core_ext"
|
|
22
|
+
require_relative "camille/endpoint"
|
|
23
|
+
require_relative "camille/schema"
|
|
24
|
+
require_relative "camille/schemas"
|
|
25
|
+
require_relative "camille/line"
|
|
26
|
+
require_relative "camille/railtie"
|
|
27
|
+
require_relative "camille/controller_extension"
|
|
28
|
+
require_relative "camille/loader"
|
|
29
|
+
require_relative "camille/configuration"
|
|
30
|
+
require_relative "camille/code_generator"
|
|
31
|
+
require_relative "camille/main_controller"
|
|
4
32
|
|
|
5
33
|
module Camille
|
|
6
34
|
class Error < StandardError; end
|
|
7
|
-
|
|
35
|
+
|
|
36
|
+
def self.configure &block
|
|
37
|
+
Camille::Configuration.instance_eval &block
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.generate_ts
|
|
41
|
+
Camille::CodeGenerator.generate_ts
|
|
42
|
+
end
|
|
8
43
|
end
|
metadata
CHANGED
|
@@ -1,15 +1,35 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: camille
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- 辻彩
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-
|
|
12
|
-
dependencies:
|
|
11
|
+
date: 2023-03-14 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rails
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '6.1'
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '8'
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '6.1'
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '8'
|
|
13
33
|
description: ''
|
|
14
34
|
email:
|
|
15
35
|
- cichol@live.cn
|
|
@@ -25,9 +45,36 @@ files:
|
|
|
25
45
|
- bin/console
|
|
26
46
|
- bin/setup
|
|
27
47
|
- lib/camille.rb
|
|
48
|
+
- lib/camille/basic_type.rb
|
|
49
|
+
- lib/camille/code_generator.rb
|
|
50
|
+
- lib/camille/configuration.rb
|
|
51
|
+
- lib/camille/controller_extension.rb
|
|
52
|
+
- lib/camille/core_ext.rb
|
|
53
|
+
- lib/camille/endpoint.rb
|
|
54
|
+
- lib/camille/line.rb
|
|
55
|
+
- lib/camille/loader.rb
|
|
56
|
+
- lib/camille/main_controller.rb
|
|
57
|
+
- lib/camille/railtie.rb
|
|
58
|
+
- lib/camille/schema.rb
|
|
59
|
+
- lib/camille/schemas.rb
|
|
60
|
+
- lib/camille/type.rb
|
|
61
|
+
- lib/camille/type_error.rb
|
|
62
|
+
- lib/camille/type_error_printer.rb
|
|
63
|
+
- lib/camille/types.rb
|
|
64
|
+
- lib/camille/types/any.rb
|
|
65
|
+
- lib/camille/types/array.rb
|
|
66
|
+
- lib/camille/types/boolean.rb
|
|
67
|
+
- lib/camille/types/null.rb
|
|
68
|
+
- lib/camille/types/number.rb
|
|
69
|
+
- lib/camille/types/object.rb
|
|
70
|
+
- lib/camille/types/string.rb
|
|
71
|
+
- lib/camille/types/tuple.rb
|
|
72
|
+
- lib/camille/types/undefined.rb
|
|
73
|
+
- lib/camille/types/union.rb
|
|
28
74
|
- lib/camille/version.rb
|
|
29
75
|
homepage: https://github.com/onyxblade/camille
|
|
30
|
-
licenses:
|
|
76
|
+
licenses:
|
|
77
|
+
- MIT
|
|
31
78
|
metadata:
|
|
32
79
|
homepage_uri: https://github.com/onyxblade/camille
|
|
33
80
|
source_code_uri: https://github.com/onyxblade/camille
|
|
@@ -49,5 +96,5 @@ requirements: []
|
|
|
49
96
|
rubygems_version: 3.2.33
|
|
50
97
|
signing_key:
|
|
51
98
|
specification_version: 4
|
|
52
|
-
summary:
|
|
99
|
+
summary: Typed API schema for Rails with TypeScript codegen
|
|
53
100
|
test_files: []
|