camille 1.7.0 → 1.8.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/CHANGELOG.md +11 -0
- data/Gemfile.lock +1 -1
- data/README.md +24 -0
- data/camille.gemspec +36 -0
- data/lib/camille/basic_type.rb +14 -0
- data/lib/camille/controller.rb +3 -2
- data/lib/camille/rendered.rb +17 -1
- data/lib/camille/type.rb +4 -0
- data/lib/camille/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b669731de34897fd2acb06cac773161fe3118a5203d4c9fd3ae41f5a6fca39e8
|
|
4
|
+
data.tar.gz: bdeca5f3af494575e42fece40de7682f441f43040ac11fe9e88d63516c0fdbc9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d65f75dc0f47bb06962313132d639ed6acd876ef5524cdbc8fb7e9ec55643f3de5b7e7984c372de017e1e3ddfbd161deb7794ee0876f019ef13de401301a4e62
|
|
7
|
+
data.tar.gz: 92cab47708e350863586256efab349a85b4f16572f402b336802f4c035f0f2511cbc406d6215c9b9312cc4958d498d3c7bdd41556f6566118f0271e6ff59d326
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.8.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
* Added `render!` to every type (`Camille::Types::Product.render!(value)`), which checks a value and returns a `Camille::Rendered`, raising `Camille::BasicType::RenderError` on a type mismatch. A `Rendered` can be cached and placed into a later response, where the type accepts it by fingerprint without re-checking and its JSON is spliced verbatim. See "Caching rendered fragments" in the README.
|
|
8
|
+
* `Camille::Rendered` now marshals as `[fingerprint, json]`, so entries stored with the default `Rails.cache` coder stay valid across changes to the class.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
* Renamed `Camille::Controller::InvalidRenderArgumentError` to `Camille::Controller::RenderArgumentError`, matching the naming of the other controller errors. The old name remains as an alias.
|
|
13
|
+
|
|
3
14
|
## 1.7.0
|
|
4
15
|
|
|
5
16
|
### Fixed
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -259,6 +259,30 @@ object:
|
|
|
259
259
|
array[2]: Expected number, got "3".
|
|
260
260
|
```
|
|
261
261
|
|
|
262
|
+
### Caching rendered fragments
|
|
263
|
+
|
|
264
|
+
Type checking and key conversion run on every `render`. For data that is expensive to build and shared across requests, you can check it once and cache the result as a `Camille::Rendered`:
|
|
265
|
+
|
|
266
|
+
```ruby
|
|
267
|
+
rendered = Camille::Types::Product.render!(serialize(product))
|
|
268
|
+
# => Camille::Rendered with the type's fingerprint and the final JSON string
|
|
269
|
+
Rails.cache.write("product/#{product.id}/#{Camille::Types::Product.new.fingerprint}", rendered)
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
`render!` raises `Camille::BasicType::RenderError` if the value doesn't match the type. A `Rendered` can be placed anywhere a value of that type is expected:
|
|
273
|
+
|
|
274
|
+
```ruby
|
|
275
|
+
render json: {
|
|
276
|
+
products: Rails.cache.read_multi(*keys).values # Product[]
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
The type accepts it by comparing fingerprints instead of re-checking, and `to_json` splices the stored string verbatim, so no parsing or re-serialization happens on the cached fragments.
|
|
281
|
+
|
|
282
|
+
A `Rendered` is immutable and opaque. If a fragment needs to be modified before rendering, cache the plain hash instead and let Camille check it as usual; the two forms are a per-fragment choice. Keep volatile or per-user fields outside cached fragments, and include the type's `fingerprint` in the cache key so entries are invalidated when the type changes.
|
|
283
|
+
|
|
284
|
+
`Rendered` defines `marshal_dump`/`marshal_load` as `[fingerprint, json]`, so the default `Rails.cache` coder stores just the two strings. If you use the `:message_pack` cache serializer, register the class with `ActiveSupport::MessagePack::CacheSerializer` using the same pair.
|
|
285
|
+
|
|
262
286
|
### Reloading
|
|
263
287
|
|
|
264
288
|
Everything in `config/camille/types` and `config/camille/schemas` will automatically reload after changes in development environment, just like other files in Rails.
|
data/camille.gemspec
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/camille/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "camille"
|
|
7
|
+
spec.version = Camille::VERSION
|
|
8
|
+
spec.authors = ["merely"]
|
|
9
|
+
spec.email = ["git@merely.ca"]
|
|
10
|
+
spec.license = "MIT"
|
|
11
|
+
|
|
12
|
+
spec.summary = "Typed API schema for Rails with TypeScript codegen"
|
|
13
|
+
spec.description = ""
|
|
14
|
+
spec.homepage = "https://github.com/onyxblade/camille"
|
|
15
|
+
spec.required_ruby_version = ">= 2.6.0"
|
|
16
|
+
|
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/onyxblade/camille"
|
|
19
|
+
|
|
20
|
+
# Specify which files should be added to the gem when it is released.
|
|
21
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
22
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
23
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
24
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
spec.bindir = "exe"
|
|
28
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
29
|
+
spec.require_paths = ["lib"]
|
|
30
|
+
|
|
31
|
+
# Uncomment to register a new dependency of your gem
|
|
32
|
+
spec.add_dependency "rails", ">= 6.1", "< 8.1"
|
|
33
|
+
|
|
34
|
+
# For more information and examples about making a new gem, checkout our
|
|
35
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
|
36
|
+
end
|
data/lib/camille/basic_type.rb
CHANGED
|
@@ -4,6 +4,7 @@ module Camille
|
|
|
4
4
|
# This class specifies the methods available for all types includeing built-in and custom ones.
|
|
5
5
|
class BasicType
|
|
6
6
|
class InvalidTypeError < ::ArgumentError; end
|
|
7
|
+
class RenderError < ::StandardError; end
|
|
7
8
|
|
|
8
9
|
module CheckRendered
|
|
9
10
|
def check value
|
|
@@ -25,6 +26,19 @@ module Camille
|
|
|
25
26
|
@fingerprint = Digest::MD5.hexdigest self.class.name
|
|
26
27
|
end
|
|
27
28
|
|
|
29
|
+
# Checks `value` and returns a `Camille::Rendered`, raising `RenderError` with
|
|
30
|
+
# the printed type error if the check fails.
|
|
31
|
+
def render! value
|
|
32
|
+
result = check(value)
|
|
33
|
+
if result.type_error?
|
|
34
|
+
string_io = StringIO.new
|
|
35
|
+
Camille::TypeErrorPrinter.new(result).print(string_io)
|
|
36
|
+
raise RenderError.new("\nType check failed for render!.\n#{string_io.string}")
|
|
37
|
+
else
|
|
38
|
+
result.render
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
28
42
|
def | other
|
|
29
43
|
Camille::Types::Union.new(self, other)
|
|
30
44
|
end
|
data/lib/camille/controller.rb
CHANGED
|
@@ -3,7 +3,8 @@ module Camille
|
|
|
3
3
|
class TypeError < ::StandardError; end
|
|
4
4
|
class ParamsTypeError < TypeError; end
|
|
5
5
|
class ResponseTypeError < TypeError; end
|
|
6
|
-
class
|
|
6
|
+
class RenderArgumentError < ::ArgumentError; end
|
|
7
|
+
InvalidRenderArgumentError = RenderArgumentError
|
|
7
8
|
class MissingRenderError < ::StandardError; end
|
|
8
9
|
|
|
9
10
|
def camille_schema
|
|
@@ -31,7 +32,7 @@ module Camille
|
|
|
31
32
|
super(json: rendered)
|
|
32
33
|
end
|
|
33
34
|
else
|
|
34
|
-
raise
|
|
35
|
+
raise RenderArgumentError.new("Expected key :json for `render` call.")
|
|
35
36
|
end
|
|
36
37
|
else
|
|
37
38
|
super
|
data/lib/camille/rendered.rb
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
module Camille
|
|
2
|
+
# A JSON string that has already passed a type check for the type identified by
|
|
3
|
+
# `fingerprint`. Any type will accept a `Rendered` with a matching fingerprint
|
|
4
|
+
# without re-checking it, and `to_json` splices the string verbatim, so a
|
|
5
|
+
# `Rendered` can be stored (e.g. in `Rails.cache`) and placed into a later
|
|
6
|
+
# response as an opaque, immutable fragment.
|
|
2
7
|
class Rendered
|
|
3
8
|
attr_reader :fingerprint, :json
|
|
4
9
|
|
|
@@ -10,5 +15,16 @@ module Camille
|
|
|
10
15
|
def to_json options = nil
|
|
11
16
|
@json
|
|
12
17
|
end
|
|
18
|
+
|
|
19
|
+
# Marshal (and therefore the default `Rails.cache` coder) stores only the two
|
|
20
|
+
# strings, so cached entries stay valid across changes to this class's
|
|
21
|
+
# internals.
|
|
22
|
+
def marshal_dump
|
|
23
|
+
[@fingerprint, @json]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def marshal_load array
|
|
27
|
+
@fingerprint, @json = array
|
|
28
|
+
end
|
|
13
29
|
end
|
|
14
|
-
end
|
|
30
|
+
end
|
data/lib/camille/type.rb
CHANGED
data/lib/camille/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: camille
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- merely
|
|
@@ -48,6 +48,7 @@ files:
|
|
|
48
48
|
- benchmarks/returning_multiple_values.rb
|
|
49
49
|
- bin/console
|
|
50
50
|
- bin/setup
|
|
51
|
+
- camille.gemspec
|
|
51
52
|
- gemfiles/rails-7.2
|
|
52
53
|
- gemfiles/rails-8.0
|
|
53
54
|
- lib/camille.rb
|