rabl 0.18.0 → 0.19.1
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 +29 -0
- data/README.md +25 -16
- data/SECURITY.md +14 -0
- data/lib/rabl/builder.rb +5 -6
- data/lib/rabl/cache_engine.rb +17 -18
- data/lib/rabl/configuration.rb +44 -0
- data/lib/rabl/digestor.rb +2 -22
- data/lib/rabl/engine.rb +16 -16
- data/lib/rabl/railtie.rb +10 -14
- data/lib/rabl/renderer.rb +10 -5
- data/lib/rabl/sources.rb +45 -16
- data/lib/rabl/template.rb +2 -45
- data/lib/rabl/version.rb +1 -1
- data/lib/rabl.rb +31 -11
- metadata +10 -8
- data/lib/rabl/digestor/rails3.rb +0 -14
- data/lib/rabl/digestor/rails41.rb +0 -14
- data/lib/rabl/digestor/rails5.rb +0 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f1bfaeaa40af7667acd5ed068e1bd0b54dd5f201ec360bb78dd51fcb42198343
|
|
4
|
+
data.tar.gz: 5389a879fe511adcf009b51edbf47c77042374cac7582cd287a699ca7560caa8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1071859094149f84a0d9127a9acd99101537278694390bab3d1a2eb1d5b59ac50466e222e7117a627df114fc5c2662eec9b7ea555bbb7b975737d9f979049c3e
|
|
7
|
+
data.tar.gz: 248bdcc28e051445cf08d4d8c6f5f5911ecb83cc453844514cf5460d240b522b12257e41a4ef82b3b2f47787079142ec56a0626e227306931ebd42764e1b7c09
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 0.19.1 (Nov 5, 2026)
|
|
4
|
+
|
|
5
|
+
### Other changes
|
|
6
|
+
|
|
7
|
+
* With `camelize_keys` on, a key is camelized once and remembered rather than for each item of a collection: a collection hands the same names out for every item, and camelizing one takes about 0.5 µs and six objects for a one-word name and about twenty for one with three words. Measured on a 50-item collection whose template has three `attributes`, one `node` and one `child`, six key names per item, YJIT off: with `camelize_keys = :lower` the render went from about 582 µs and 4050 allocations to about 457 µs and 2000 on Ruby 3.3.12, about 1.3x, and from about 592 µs and 4054 allocations to about 464 µs and 2004 on Ruby 4.0.7, where the same render without `camelize_keys` takes about 444 µs and 2000 on 3.3.12 and 453 µs and 2004 on 4.0.7 and is unchanged, so a template with more keys per item gains more. The names are kept in two maps, one per case style, so each style keeps its own; a map is emptied when it holds 10,000 names and a name longer than 128 bytes is camelized each time rather than kept, so a template that names keys at runtime (`node(field.key)`, `attribute :as => "field_#{i}"`, a name taken from user input) cannot grow it without bound. A name is kept as a frozen copy, because a Hash keeps an unfrozen String subclass such as `ActiveSupport::SafeBuffer` as it is and a caller that changed such a name in place would file another name under the camelized value; and a name keeps the acronyms it was first camelized with, so an inflection added after a name was first seen doesn't reach that name. The output is otherwise unchanged (@tagliala; change written by DeepSeek Flash)
|
|
8
|
+
|
|
9
|
+
* A template a collection uses for each item through `extends` or `partial` is now read and resolved once per render rather than once per item. With the default `cache_sources` off, every item ran the whole lookup itself: outside Rails, with a view path passed and one in `Rabl.configuration.view_paths`, which `Rabl::Renderer`'s lookup adds to it, one lookup costs about 117 µs and 62 allocations on Ruby 4.0.7 and about 110 µs on 3.3.12, YJIT off, against 1.8 µs and 4 with `cache_sources` on. The sources a render reads are now collected for the length of that render and dropped when it returns, so a template changed between renders is still read again, one changed while the render runs is not read again until the next render, and a render that raises leaves nothing behind. A render started inside the collection (a `Rabl::Renderer` in a `node`, a nested render under Rails) uses the collection it finds, whose keys are complete, so the render around it keeps its own sources. Outside Rails the collection keys a lookup by the values it depends on (the format, the locale, the default locale and the object its fallbacks come from, the app's views under Sinatra) and not by the numbers `LOOKUP_IDS` and `MANUAL_LOOKUP_IDS` keep for the life of the process, so the collection's keys add nothing to those tables nor to `I18n.fallbacks`. With `cache_sources` on or off, a template with more than one candidate file for a name still asks I18n for the locale's fallbacks when it ranks them, which keeps that locale in `I18n.fallbacks`, as it did before; with `cache_sources` on the tables and `I18n.fallbacks` also gain an entry per locale looked up in, as before. Under Rails the numbers come from the lookup details and view paths Rails itself keeps, so they are used as they are and a view path prepended for one request adds one, as it does to Rails; a Padrino lookup, which depends on the request's controller and content type, is not collected per render. Measured outside Rails on a 50-item collection whose template is three `attributes`, one `node` and one `child` plus `extends "posts/show"` (the same output as one template with all of them), the render went from about 5.8 ms and 7540 allocations to about 0.95 ms and 4456 on Ruby 3.3.12, about 6.2x, and from about 7.0 ms and 7244 allocations to about 1.0 ms and 4454 on Ruby 4.0.7, about 7.0x; a heavier template, with two more `node`s, a `child`, a `glue` and a `partial` per item, went from about 12.1 ms and 16649 allocations to about 2.2 ms and 10480 on 3.3.12, about 5.4x, and from about 13.8 ms and 16103 to about 2.3 ms and 10522 on 4.0.7, about 5.9x, so the gain follows how much of the per-item cost the lookup was. Under Rails 8.1.4 on Ruby 4.0.7, rendering the same collection through `ApplicationController.render` with `cache_sources` off went from about 2.7 ms and 6376 allocations to about 1.9 ms and 5203, about 1.4x, where the same render with `cache_sources` on takes about 1.8 ms either way: Rails repeats a lookup in about 17 µs per item, the difference between that render with `cache_sources` on and off on the same code, against about 117 µs for the manual lookup, so it has less to save. The one-template version of the output is unchanged within the noise, one allocation more (the render's own collection), and a single object using `extends` allocates about ten more objects than before (the render's own collection, and a frozen copy of each lookup's template name and view paths). The output is unchanged (@tagliala; change written by DeepSeek Flash)
|
|
10
|
+
|
|
11
|
+
* `Rabl::Builder#cache_results` and `Rabl::Engine#cache_results` now ask their cheapest question first: whether `cache_all_output` is on, and whether the template set a cache key, before asking whether caching is configured. Under Rails that second question is `ActionController::Base.perform_caching`, a delegated call that allocates an object and takes about 250 ns on Ruby 4.0.7, Rails 8.1.4, YJIT off; with the defaults (`cache_all_output` off, no `cache` in the template) its answer could not change the guard, and it was computed once per object built and once per engine rendered. On Ruby 4.0.7, where the call allocates one object, a 50-item collection whose template has one `child` (100 builders, 51 engines) saves those 151 allocations and about 40 µs per render, about 1.08x; on Ruby 3.3 it allocates two, so the same collection saves 302. A single object saves 4 allocations and about 1 µs. The output is unchanged; with `cache_all_output` on, the builder runs the same checks in the cheap-first order, and an engine whose template sets no cache key still skips the call. Outside Rails both checks are plain configuration reads, so the difference is inside the noise and the allocation count is unchanged (@tagliala)
|
|
12
|
+
|
|
13
|
+
## 0.19.0 (Nov 1, 2026)
|
|
14
|
+
|
|
15
|
+
### Requirements
|
|
16
|
+
|
|
17
|
+
* Require ActiveSupport 6.1 or newer, and so Rails 6.1 or newer under Rails. The code rabl kept for Rails 2 to 5 (their template handlers, digestors and template lookups) is removed; Rails 6.1 and newer, Sinatra and Padrino render as before. Apps on an older Rails or ActiveSupport can stay on 0.18.x (@nesquena)
|
|
18
|
+
|
|
19
|
+
### Fixes
|
|
20
|
+
|
|
21
|
+
* Load rabl's Rails integration on Rails 9 and later: rabl recognised Rails 3 to 8 by the first digit of `Rails.version`, so on a newer Rails the railtie and the `.rabl` template handler never loaded and actions rendered empty responses. rabl no longer checks the Rails version, so its integration loads on every Rails from 6.1 on, with no upper bound; Rails 6.1 to 8 load the same parts as before (@nesquena)
|
|
22
|
+
* Fix rabl defining a top-level `LRU` class for its own cache, which raised `TypeError: superclass mismatch for class LRU` in apps with an `LRU` class of their own; it is now `Rabl::CacheEngine::LRU` (use that name if your code used rabl's `LRU`). Once that cache (used outside Rails) held its 100,000 entries, each write also copied every key to drop the oldest one, about 0.5 ms per write; it now drops the same entries in a few µs (@nesquena)
|
|
23
|
+
* Fix `Rabl::Renderer` and `Rabl.render` raising `NoMethodError` (`undefined method 'underscore' for nil`) for an object, or a collection, whose class has no name, such as a `Struct.new` or `Class.new` object. The renderer sets an instance variable named after the object's class (`@post` for a `Post`); for an unnamed class it now sets none, and the template gets the object as for any other (@nesquena)
|
|
24
|
+
|
|
25
|
+
### Other changes
|
|
26
|
+
|
|
27
|
+
* `Rabl::Renderer#process_source` and the JSONP callback check in `Rabl::Engine` answer their yes/no question about a string with `String#include?` and `Regexp#match?` instead of `=~`, which built a `MatchData` neither check read: creating a renderer for a template string is about 1.2x faster and allocates 3 objects fewer, a full render about 2% faster and 3 objects fewer, and a render with `enable_json_callbacks` 3 objects fewer. The output is unchanged, and a source that is not valid in its encoding still raises the same `ArgumentError: invalid byte sequence in UTF-8`, from `Engine#eval_source` when it has a newline and from the template lookup when it doesn't; a source in a non-ASCII-compatible encoding, such as UTF-16LE, still raises `Encoding::CompatibilityError` from the check, now with Ruby's `incompatible character encodings` message rather than `incompatible encoding regexp match` (@tagliala; change written by DeepSeek Flash, reviewed by Claude Opus 5.5 and Claude Fable 5.1)
|
|
28
|
+
* CI now generates fresh Rails 7.2, Rails 8.1 and Sinatra apps that use rabl from the checkout and runs their tests (`test/apps/build_and_test.sh`), in place of the checked-in fixture apps for Rails 2 to 6 and Padrino under `fixtures/`, which CI never ran. No change to the gem (@nesquena)
|
|
29
|
+
* README: a `child` block is passed the child's data (e.g. the posts) and a `glue` block the glued object, not the parent, with examples that run; the partial example passes the post (`:object => post`, not `:post`); the JSON engine is Oj when it is loaded and Ruby's JSON otherwise (yajl isn't used); and under Rails, Rails sets the content type from the template's format. No behaviour change (@nesquena; reported by @nirvdrum in #393 and @adie in #580)
|
|
30
|
+
* The gem's rubygems.org page links the changelog, source code and issue tracker, and pushes to rubygems.org now require MFA (@nesquena)
|
|
31
|
+
|
|
3
32
|
## 0.18.0 (Oct 31, 2026)
|
|
4
33
|
|
|
5
34
|
### Requirements
|
data/README.md
CHANGED
|
@@ -26,6 +26,9 @@ For a breakdown of common misconceptions about RABL, please check out our guide
|
|
|
26
26
|
|
|
27
27
|
## Breaking Changes ##
|
|
28
28
|
|
|
29
|
+
* v0.19.0 requires ActiveSupport 6.1 or newer (and so Rails 6.1 or newer under Rails). Apps on an older Rails or
|
|
30
|
+
ActiveSupport can stay on 0.18.x.
|
|
31
|
+
|
|
29
32
|
* v0.18.0 requires Ruby 2.7 or newer (older Rubies can stay on 0.17.0). Every cache key changes once: expect a cold
|
|
30
33
|
cache after upgrading. It also fixes several long-standing output bugs in ways that can change existing output. Check the
|
|
31
34
|
[CHANGELOG](https://github.com/nesquena/rabl/blob/master/CHANGELOG.md) before upgrading.
|
|
@@ -55,12 +58,12 @@ or add to your Gemfile:
|
|
|
55
58
|
```ruby
|
|
56
59
|
# Gemfile
|
|
57
60
|
gem 'rabl'
|
|
58
|
-
#
|
|
61
|
+
# Optionally add `oj`: RABL encodes JSON with Oj when it is loaded, and with Ruby's JSON library otherwise
|
|
59
62
|
# If using `oj`, Rabl renders with Oj's :compat mode and sets it as Oj's default (with Oj 3.17.7+, only if you haven't configured Oj yet)
|
|
60
63
|
gem 'oj'
|
|
61
64
|
```
|
|
62
65
|
|
|
63
|
-
and run `bundle install` to install the dependency. RABL needs Ruby 2.7 or newer.
|
|
66
|
+
and run `bundle install` to install the dependency. RABL needs Ruby 2.7 or newer and ActiveSupport 6.1 or newer (on Rails, Rails 6.1 or newer).
|
|
64
67
|
|
|
65
68
|
To render XML, also add `gem 'builder'` (Rails apps already include it); RABL loads ActiveSupport's XML conversions itself.
|
|
66
69
|
|
|
@@ -135,7 +138,7 @@ Rabl.configure do |config|
|
|
|
135
138
|
# config.cache_engine = Rabl::CacheEngine.new # Defaults to Rails cache
|
|
136
139
|
# config.perform_caching = false
|
|
137
140
|
# config.escape_all_output = false
|
|
138
|
-
# config.json_engine = nil # Class with #dump class method (defaults JSON)
|
|
141
|
+
# config.json_engine = nil # Class with #dump class method (defaults to Oj when it is loaded, else ::JSON)
|
|
139
142
|
# config.msgpack_engine = nil # Defaults to ::MessagePack
|
|
140
143
|
# config.bson_engine = nil # Defaults to ::BSON
|
|
141
144
|
# config.plist_engine = nil # Defaults to ::Plist::Emit
|
|
@@ -369,15 +372,18 @@ child :posts => :foobar do
|
|
|
369
372
|
end
|
|
370
373
|
```
|
|
371
374
|
|
|
372
|
-
|
|
375
|
+
The block is passed the child's data (here the user's posts, not the user), and the parent is still
|
|
376
|
+
available as `@user`:
|
|
373
377
|
|
|
374
378
|
```ruby
|
|
375
379
|
object @user
|
|
376
|
-
child :posts do |
|
|
377
|
-
attribute :title unless user.suspended?
|
|
380
|
+
child :posts do |posts|
|
|
381
|
+
attribute :title unless @user.suspended?
|
|
378
382
|
end
|
|
379
383
|
```
|
|
380
384
|
|
|
385
|
+
To decide for each post, use a condition, which is passed the post: `attribute :title, :if => lambda { |post| post.published? }`.
|
|
386
|
+
|
|
381
387
|
### Gluing Attributes ###
|
|
382
388
|
|
|
383
389
|
You can also append child attributes back to the root node:
|
|
@@ -391,11 +397,11 @@ end
|
|
|
391
397
|
|
|
392
398
|
Use glue to add additional attributes to the parent object.
|
|
393
399
|
|
|
394
|
-
|
|
400
|
+
The block is passed the glued object (here `@post`, not the user):
|
|
395
401
|
|
|
396
402
|
```ruby
|
|
397
403
|
object @user
|
|
398
|
-
glue(@post) {|
|
|
404
|
+
glue(@post) { |post| attribute :title if post.published? }
|
|
399
405
|
```
|
|
400
406
|
|
|
401
407
|
### Custom Nodes ###
|
|
@@ -481,7 +487,7 @@ collection @posts
|
|
|
481
487
|
|
|
482
488
|
extends('posts/show', :locals => { :hide_comments => true })
|
|
483
489
|
# or using partial instead of extends
|
|
484
|
-
# node(false) { |post| partial('posts/show', :object =>
|
|
490
|
+
# node(false) { |post| partial('posts/show', :object => post, :locals => { :hide_comments => true })}
|
|
485
491
|
```
|
|
486
492
|
|
|
487
493
|
and then access locals in the sub-template:
|
|
@@ -622,7 +628,7 @@ entry per viewer, apart from the nil case above. Include `root_object` whenever
|
|
|
622
628
|
Under Rails, keys include the Rails template digest when the template is rendered through a controller or view. `Rabl.render` and
|
|
623
629
|
`Rabl::Renderer` used outside a request have no view context, so their keys identify the template differently and don't match the keys of
|
|
624
630
|
a request. To render outside a request with the same keys, for example to warm the cache in a background job, render through the
|
|
625
|
-
controller
|
|
631
|
+
controller:
|
|
626
632
|
|
|
627
633
|
```ruby
|
|
628
634
|
PostsController.render(:show, assigns: { post: post }, formats: [:json])
|
|
@@ -673,13 +679,14 @@ node(:title) { locals[:custom_title] }
|
|
|
673
679
|
|
|
674
680
|
### Content Type Headers ###
|
|
675
681
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
682
|
+
Under Rails, Rails sets the content type from the template's format: `show.json.rabl` is served as
|
|
683
|
+
`application/json` and `show.xml.rabl` as `application/xml`. A template without a format in its name
|
|
684
|
+
(`show.rabl`) is served as `application/json` whatever it renders, so name templates by format when you
|
|
685
|
+
serve more than JSON.
|
|
680
686
|
|
|
681
|
-
|
|
682
|
-
|
|
687
|
+
Outside Rails, RABL doesn't set the content type, as it is intended to work for any Rack-based framework and
|
|
688
|
+
to be as agnostic to format as possible (see [this issue](https://github.com/nesquena/rabl/issues/185#issuecomment-4501232)).
|
|
689
|
+
Set it in your app, e.g. with Sinatra's `content_type :json` in the route or a `before` filter.
|
|
683
690
|
|
|
684
691
|
## Resources ##
|
|
685
692
|
|
|
@@ -741,6 +748,8 @@ Check out the [Issues](https://github.com/nesquena/rabl/issues) tab for a full l
|
|
|
741
748
|
|
|
742
749
|
* Rigorous benchmarking and performance optimizations
|
|
743
750
|
|
|
751
|
+
To report a security vulnerability, don't open an issue; see [SECURITY.md](https://github.com/nesquena/rabl/blob/master/SECURITY.md).
|
|
752
|
+
|
|
744
753
|
## Authors and Contributors ##
|
|
745
754
|
|
|
746
755
|
Active Maintainers:
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Security fixes go into the latest release line of rabl. Please check that the problem occurs on the latest release.
|
|
6
|
+
|
|
7
|
+
## Reporting a vulnerability
|
|
8
|
+
|
|
9
|
+
Please report vulnerabilities privately, through GitHub's private vulnerability reporting: on the repository's
|
|
10
|
+
[Security tab](https://github.com/nesquena/rabl/security), choose "Report a vulnerability". Don't open a public issue or
|
|
11
|
+
pull request for a vulnerability.
|
|
12
|
+
|
|
13
|
+
Include the rabl version, the steps to reproduce the problem and its impact. The discussion continues in the report
|
|
14
|
+
until a fix is released.
|
data/lib/rabl/builder.rb
CHANGED
|
@@ -256,7 +256,8 @@ module Rabl
|
|
|
256
256
|
# Caches the results of the block based on object cache_key
|
|
257
257
|
# cache_results { compile_hash(options) }
|
|
258
258
|
def cache_results(&block)
|
|
259
|
-
|
|
259
|
+
# cheap questions first: template_cache_configured? allocates under Rails
|
|
260
|
+
if Rabl.configuration.cache_all_output && template_cache_configured? && @_object.respond_to?(:cache_key)
|
|
260
261
|
cache_key = [@_object, @options[:root_name], @options[:format]]
|
|
261
262
|
engine = rabl_template_engine
|
|
262
263
|
if engine
|
|
@@ -273,11 +274,9 @@ module Rabl
|
|
|
273
274
|
end
|
|
274
275
|
|
|
275
276
|
def create_key(name)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
name.to_sym
|
|
280
|
-
end
|
|
277
|
+
config = Rabl.configuration
|
|
278
|
+
style = config.camelize_keys
|
|
279
|
+
style ? config.camelized_key(name, style == :upper) : name.to_sym
|
|
281
280
|
end
|
|
282
281
|
end
|
|
283
282
|
end
|
data/lib/rabl/cache_engine.rb
CHANGED
|
@@ -7,29 +7,28 @@
|
|
|
7
7
|
# config.cache_engine = AdvancedCacheEngine.new
|
|
8
8
|
#
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
module Rabl
|
|
11
|
+
class CacheEngine
|
|
12
|
+
# A Hash holding at most max_size entries, which drops the entries added first
|
|
13
|
+
class LRU < Hash
|
|
14
|
+
attr_accessor :max_size
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
def initialize
|
|
17
|
+
super
|
|
18
|
+
self.max_size = 100_000
|
|
19
|
+
end
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
def []= k,v
|
|
22
|
+
r = super
|
|
23
|
+
limit_size
|
|
24
|
+
r
|
|
25
|
+
end
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
def limit_size
|
|
28
|
+
shift while size > max_size
|
|
29
|
+
end
|
|
27
30
|
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
31
|
|
|
31
|
-
module Rabl
|
|
32
|
-
class CacheEngine
|
|
33
32
|
def initialize
|
|
34
33
|
unless defined?(Rails)
|
|
35
34
|
@cache = LRU.new
|
data/lib/rabl/configuration.rb
CHANGED
|
@@ -122,6 +122,50 @@ module Rabl
|
|
|
122
122
|
@exclude_nil_values = false
|
|
123
123
|
@exclude_empty_values_in_collections = false
|
|
124
124
|
@camelize_keys = false
|
|
125
|
+
@camelized_keys = { false => {}, true => {} }
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Names camelized in each style, by whether the first letter is upper case: a collection
|
|
129
|
+
# builds the same keys for each of its items, and camelizing one takes about 0.5 µs.
|
|
130
|
+
# ceiling: one entry per distinct key name, so a template that names keys at runtime
|
|
131
|
+
# (node(field.key)) keeps them for the life of the process until a new name arrives and the
|
|
132
|
+
# map is already full, when it is emptied; a name over CAMELIZED_KEY_LENGTH_LIMIT bytes isn't
|
|
133
|
+
# kept at all; and a name keeps the acronyms it was first camelized with, so an inflection
|
|
134
|
+
# added later doesn't reach a name that was already seen.
|
|
135
|
+
CAMELIZED_KEYS_LIMIT = 10_000
|
|
136
|
+
# A name longer than this, in bytes, is camelized each time instead of being kept, so a map
|
|
137
|
+
# holds at most CAMELIZED_KEYS_LIMIT names of up to this many bytes each
|
|
138
|
+
CAMELIZED_KEY_LENGTH_LIMIT = 128
|
|
139
|
+
|
|
140
|
+
# Files a new name in a style's map: the maps are shared by every thread
|
|
141
|
+
CAMELIZED_KEYS_LOCK = Mutex.new
|
|
142
|
+
|
|
143
|
+
# Returns name camelized in the given style, camelizing each kept name once
|
|
144
|
+
def camelized_key(name, upper = false)
|
|
145
|
+
keys = @camelized_keys[upper]
|
|
146
|
+
cached = keys[name]
|
|
147
|
+
return cached if cached
|
|
148
|
+
|
|
149
|
+
string = name.to_s
|
|
150
|
+
style = upper ? :upper : :lower
|
|
151
|
+
return string.camelize(style).to_sym if string.bytesize > CAMELIZED_KEY_LENGTH_LIMIT
|
|
152
|
+
|
|
153
|
+
# a Hash freezes a plain String key and copies it, but keeps an unfrozen String subclass,
|
|
154
|
+
# such as ActiveSupport::SafeBuffer, as it is: a caller that changes the name in place would
|
|
155
|
+
# file another name under the camelized value, so the key is copied and frozen here
|
|
156
|
+
# the map is shared by every thread, so a new name is filed under the lock, as in LOOKUP_IDS
|
|
157
|
+
key = Rabl.frozen_copy(name)
|
|
158
|
+
CAMELIZED_KEYS_LOCK.synchronize do
|
|
159
|
+
keys.fetch(key) do
|
|
160
|
+
keys.clear if keys.size >= camelized_keys_limit
|
|
161
|
+
keys[key] = string.camelize(style).to_sym
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# How many names a style's map keeps before a new name empties it
|
|
167
|
+
def camelized_keys_limit
|
|
168
|
+
CAMELIZED_KEYS_LIMIT
|
|
125
169
|
end
|
|
126
170
|
|
|
127
171
|
# @return The JSON engine used to encode Rabl templates into JSON
|
data/lib/rabl/digestor.rb
CHANGED
|
@@ -3,28 +3,8 @@
|
|
|
3
3
|
require 'action_view'
|
|
4
4
|
|
|
5
5
|
module Rabl
|
|
6
|
+
# Rails' digestor, under rabl's name: rabl.rb defines it only with the rest of the Rails integration, so
|
|
7
|
+
# Engine#digestor_available? checks for it before adding a template digest to a cache key
|
|
6
8
|
class Digestor < ActionView::Digestor
|
|
7
|
-
# Override the original digest function to ignore partial which
|
|
8
|
-
# rabl doesn't use the Rails conventional _ symbol.
|
|
9
|
-
if Gem::Version.new(Rails.version) >= Gem::Version.new('5.0.0.beta1')
|
|
10
|
-
require 'rabl/digestor/rails5'
|
|
11
|
-
elsif Gem::Version.new(Rails.version) >= Gem::Version.new('4.1')
|
|
12
|
-
require 'rabl/digestor/rails41'
|
|
13
|
-
else
|
|
14
|
-
require 'rabl/digestor/rails3'
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
private
|
|
18
|
-
def dependency_digest
|
|
19
|
-
template_digests = (dependencies - [template.virtual_path]).collect do |template_name|
|
|
20
|
-
if Gem::Version.new(Rails.version) >= Gem::Version.new('4.1')
|
|
21
|
-
Digestor.digest(:name => template_name, :finder => finder)
|
|
22
|
-
else
|
|
23
|
-
Digestor.digest(template_name, format, finder)
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
(template_digests + injected_dependencies).join("-")
|
|
28
|
-
end
|
|
29
9
|
end
|
|
30
10
|
end
|
data/lib/rabl/engine.rb
CHANGED
|
@@ -60,9 +60,7 @@ module Rabl
|
|
|
60
60
|
# count when no rule matches.
|
|
61
61
|
def singular_rule_rank(word)
|
|
62
62
|
inflections = ActiveSupport::Inflector.inflections
|
|
63
|
-
|
|
64
|
-
# ActiveSupport < 5 keeps them in a plain Array, checked by the last word
|
|
65
|
-
return -1 if uncountables.respond_to?(:uncountable?) ? uncountables.uncountable?(word) : uncountables.include?(word.downcase[/\b\w+\Z/])
|
|
63
|
+
return -1 if inflections.uncountables.uncountable?(word)
|
|
66
64
|
|
|
67
65
|
rules = inflections.singulars
|
|
68
66
|
rules.index { |(rule, _)| rule.is_a?(Regexp) ? rule.match?(word) : word.include?(rule) } || rules.size
|
|
@@ -149,16 +147,7 @@ module Rabl
|
|
|
149
147
|
if digestor_available? && respond_to?(:lookup_context) && lookup_context
|
|
150
148
|
template = @_options[:template] || @virtual_path
|
|
151
149
|
|
|
152
|
-
digest
|
|
153
|
-
if Rails.version.to_s =~ /^[678]/
|
|
154
|
-
Digestor.digest(name: template, finder: lookup_context, format: :rabl)
|
|
155
|
-
elsif Gem::Version.new(Rails.version) >= Gem::Version.new('4.1')
|
|
156
|
-
Digestor.digest(:name => template, :finder => lookup_context)
|
|
157
|
-
else
|
|
158
|
-
Digestor.digest(template, :rabl, lookup_context)
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
cache_key << digest
|
|
150
|
+
cache_key << Digestor.digest(name: template, finder: lookup_context, format: :rabl)
|
|
162
151
|
# The digest covers the template source, but not the block that defines this engine
|
|
163
152
|
if @_block_identity
|
|
164
153
|
block, parent = @_block_identity
|
|
@@ -187,6 +176,14 @@ module Rabl
|
|
|
187
176
|
options[:root_name] = determine_object_root(data, root_name, options[:root])
|
|
188
177
|
options[:template_engine] = self
|
|
189
178
|
|
|
179
|
+
# A template the collection uses for each item (extends, partial) is read once per render,
|
|
180
|
+
# not once per item. The render that starts a collection ends it, so a render started inside
|
|
181
|
+
# this one (a Rabl::Renderer in a node, a nested render under Rails) uses the collection it
|
|
182
|
+
# finds, whose keys are complete, and the render around it keeps its own sources. A nested
|
|
183
|
+
# engine renders inside a collection anyway, so it skips the look for one.
|
|
184
|
+
owns_source_cache = !@_options[:nested] && Rabl.render_source_cache.nil?
|
|
185
|
+
Rabl.begin_render_source_cache if owns_source_cache
|
|
186
|
+
|
|
190
187
|
@_rendering = true
|
|
191
188
|
result =
|
|
192
189
|
if is_object?(data) || !data # object @user
|
|
@@ -201,6 +198,7 @@ module Rabl
|
|
|
201
198
|
result
|
|
202
199
|
ensure
|
|
203
200
|
@_rendering = false
|
|
201
|
+
Rabl.end_render_source_cache if owns_source_cache
|
|
204
202
|
end
|
|
205
203
|
|
|
206
204
|
def to_dumpable(options = {})
|
|
@@ -594,7 +592,7 @@ module Rabl
|
|
|
594
592
|
|
|
595
593
|
callback = request_params[:callback]
|
|
596
594
|
return unless callback.is_a?(String) && callback.length <= RABL_JSON_CALLBACK_MAX_LENGTH
|
|
597
|
-
return unless callback.ascii_only? && callback
|
|
595
|
+
return unless callback.ascii_only? && callback.match?(RABL_JSON_CALLBACK_NAME)
|
|
598
596
|
|
|
599
597
|
callback
|
|
600
598
|
end
|
|
@@ -638,7 +636,8 @@ module Rabl
|
|
|
638
636
|
# Caches the results of the block based on object cache_key
|
|
639
637
|
# cache_results { compile_hash(options) }
|
|
640
638
|
def cache_results(&block)
|
|
641
|
-
|
|
639
|
+
# cheap question first: template_cache_configured? allocates under Rails
|
|
640
|
+
return yield unless defined?(@_cache_key) && template_cache_configured?
|
|
642
641
|
|
|
643
642
|
if cache_read_on_render?
|
|
644
643
|
fetch_result_from_cache(cache_key, @_cache_options, &block)
|
|
@@ -647,8 +646,9 @@ module Rabl
|
|
|
647
646
|
end
|
|
648
647
|
end
|
|
649
648
|
|
|
649
|
+
# rabl.rb loads Rabl::Digestor with the rest of the Rails integration
|
|
650
650
|
def digestor_available?
|
|
651
|
-
defined?(
|
|
651
|
+
defined?(::Rabl::Digestor)
|
|
652
652
|
end
|
|
653
653
|
|
|
654
654
|
def valid_format?(format)
|
data/lib/rabl/railtie.rb
CHANGED
|
@@ -5,18 +5,16 @@ module Rabl
|
|
|
5
5
|
initializer "rabl.initialize" do |app|
|
|
6
6
|
# Force Rails to load view templates even in API mode
|
|
7
7
|
# Stolen shamelessly from jbuilder: https://github.com/rails/jbuilder/blob/master/lib/jbuilder/railtie.rb
|
|
8
|
-
|
|
9
|
-
module
|
|
10
|
-
|
|
11
|
-
include ActionView::Rendering
|
|
12
|
-
end
|
|
8
|
+
module ::ActionController
|
|
9
|
+
module ApiRendering
|
|
10
|
+
include ActionView::Rendering
|
|
13
11
|
end
|
|
12
|
+
end
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
end
|
|
14
|
+
ActiveSupport.on_load :action_controller do
|
|
15
|
+
if self == ActionController::API
|
|
16
|
+
include ActionController::Helpers
|
|
17
|
+
include ActionController::ImplicitRender
|
|
20
18
|
end
|
|
21
19
|
end
|
|
22
20
|
|
|
@@ -24,10 +22,8 @@ module Rabl
|
|
|
24
22
|
Rabl.register!
|
|
25
23
|
|
|
26
24
|
# Inject dependency tracker for :rabl
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
ActionView::DependencyTracker.register_tracker :rabl, Rabl::Tracker
|
|
30
|
-
end
|
|
25
|
+
require 'action_view/dependency_tracker'
|
|
26
|
+
ActionView::DependencyTracker.register_tracker :rabl, Rabl::Tracker
|
|
31
27
|
end
|
|
32
28
|
end
|
|
33
29
|
end # Railtie
|
data/lib/rabl/renderer.rb
CHANGED
|
@@ -63,24 +63,26 @@ module Rabl
|
|
|
63
63
|
|
|
64
64
|
# Returns the source given a relative template path
|
|
65
65
|
def process_source(source)
|
|
66
|
-
return source if source.is_a?(String) && source
|
|
66
|
+
return source if source.is_a?(String) && source.include?("\n")
|
|
67
67
|
|
|
68
68
|
source, location = engine.fetch_source(source, { :view_path => options[:view_path] })
|
|
69
69
|
options[:source_location] ||= location
|
|
70
70
|
source
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
# Internal: Sets an instance variable named after the class of `object
|
|
73
|
+
# Internal: Sets an instance variable named after the class of `object`,
|
|
74
|
+
# unless the class has no name (Struct.new or Class.new)
|
|
74
75
|
#
|
|
75
76
|
# Example:
|
|
76
77
|
# object.class.name # => User
|
|
77
78
|
# set_object_instance_variable # => @user == object
|
|
78
79
|
#
|
|
79
80
|
def set_object_instance_variable
|
|
80
|
-
|
|
81
|
+
name = object_model_name
|
|
82
|
+
instance_variable_set(:"@#{name}", object) if name
|
|
81
83
|
end
|
|
82
84
|
|
|
83
|
-
# Internal: Returns the model name for an object
|
|
85
|
+
# Internal: Returns the model name for an object, or nil when its class has no name
|
|
84
86
|
#
|
|
85
87
|
# Example:
|
|
86
88
|
# object.class.name # => User
|
|
@@ -92,7 +94,10 @@ module Rabl
|
|
|
92
94
|
is_collection = item.is_a?(Array)
|
|
93
95
|
item = item.first if is_collection
|
|
94
96
|
|
|
95
|
-
name = item.class.name
|
|
97
|
+
name = item.class.name
|
|
98
|
+
return unless name
|
|
99
|
+
|
|
100
|
+
name = name.underscore
|
|
96
101
|
|
|
97
102
|
name = name.pluralize if is_collection
|
|
98
103
|
|
data/lib/rabl/sources.rb
CHANGED
|
@@ -28,7 +28,10 @@ module Rabl
|
|
|
28
28
|
# fetch_source("show", :view_path => "...") => "...contents..."
|
|
29
29
|
def fetch_source(file, options = {})
|
|
30
30
|
resolver = source_resolver
|
|
31
|
+
# the process-wide cache keeps a number for each lookup for the life of the process; a
|
|
32
|
+
# collection that lives for one render only needs the lookup itself
|
|
31
33
|
lookup = source_lookup(resolver) if Rabl.configuration.cache_sources
|
|
34
|
+
lookup = render_source_lookup(resolver) if lookup.nil? && Rabl.render_source_cache
|
|
32
35
|
# a lookup that can't be told apart from others (see source_lookup) isn't cached
|
|
33
36
|
return resolve_source(file, options, resolver) if lookup == false
|
|
34
37
|
|
|
@@ -85,9 +88,11 @@ module Rabl
|
|
|
85
88
|
# details such as versioncake's versions) and the view paths; elsewhere the format
|
|
86
89
|
# and locales the manual lookup prefers, and under Sinatra the app's views. Returns
|
|
87
90
|
# false when a view path has no stable name, so the lookup isn't cached.
|
|
88
|
-
# ceiling:
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
+
# ceiling: with cache_sources on, the cache, LOOKUP_IDS and MANUAL_LOOKUP_IDS keep an entry
|
|
92
|
+
# per distinct set of lookup details and view path list, and per locale looked up in, as
|
|
93
|
+
# Rails' own registry does, so outside Rails they are bounded by I18n.available_locales only
|
|
94
|
+
# while I18n.enforce_available_locales is on (the default) and by the locales requests ask
|
|
95
|
+
# for; under Rails a view path prepended for one request adds one, as it does to Rails.
|
|
91
96
|
def source_lookup(resolver)
|
|
92
97
|
case resolver
|
|
93
98
|
when :rails
|
|
@@ -102,6 +107,38 @@ module Rabl
|
|
|
102
107
|
end
|
|
103
108
|
end
|
|
104
109
|
|
|
110
|
+
# Returns the lookup of the per-render collection: the same as source_lookup, without the
|
|
111
|
+
# numbers LOOKUP_IDS, MANUAL_LOOKUP_IDS and I18n.fallbacks keep for the life of the process.
|
|
112
|
+
# Outside Rails those gain an entry per locale looked up in, which is bounded only while
|
|
113
|
+
# I18n.enforce_available_locales is on, and a render that ends drops its collection anyway.
|
|
114
|
+
# Under Rails the numbers come from the lookup details Rails itself keeps, so they are used as
|
|
115
|
+
# they are. Padrino's lookup depends on the request too (its controller and content type), so
|
|
116
|
+
# it is not collected per render, as with cache_sources off.
|
|
117
|
+
def render_source_lookup(resolver)
|
|
118
|
+
case resolver
|
|
119
|
+
when :rails
|
|
120
|
+
source_lookup(resolver)
|
|
121
|
+
when :sinatra
|
|
122
|
+
[context_scope.settings.views, manual_lookup]
|
|
123
|
+
when :padrino
|
|
124
|
+
false
|
|
125
|
+
else
|
|
126
|
+
manual_lookup
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Returns what the manual lookup depends on, as values rather than as the number standing for
|
|
131
|
+
# it, and where the locale's fallbacks come from rather than the chain itself: asking I18n for
|
|
132
|
+
# a locale's fallbacks keeps that locale
|
|
133
|
+
def manual_lookup
|
|
134
|
+
format = manual_source_format
|
|
135
|
+
return [:manual, format] unless defined?(::I18n) && ::I18n.respond_to?(:config)
|
|
136
|
+
|
|
137
|
+
config = ::I18n.config
|
|
138
|
+
fallbacks = ::I18n.fallbacks if ::I18n.respond_to?(:fallbacks)
|
|
139
|
+
[:manual, format, config.locale, config.default_locale, fallbacks && fallbacks.__id__]
|
|
140
|
+
end
|
|
141
|
+
|
|
105
142
|
# Returns the number standing for the format and locales the manual lookup prefers.
|
|
106
143
|
# Nested Hashes of what the locales come from, as this runs on every source cache hit.
|
|
107
144
|
def manual_lookup_id
|
|
@@ -189,7 +226,7 @@ module Rabl
|
|
|
189
226
|
end
|
|
190
227
|
end
|
|
191
228
|
|
|
192
|
-
# Returns the format Rails
|
|
229
|
+
# Returns the format Rails looks templates up in:
|
|
193
230
|
# the request's format, or for HTML requests the format being rendered (json for
|
|
194
231
|
# the default handler and for hash), as a Symbol, the only form Rails 7.0 and higher match
|
|
195
232
|
def rails_source_format
|
|
@@ -252,26 +289,18 @@ module Rabl
|
|
|
252
289
|
File.join(view_path.first.to_s, (file_path.to_s + ".rabl"))
|
|
253
290
|
end
|
|
254
291
|
|
|
255
|
-
# Returns the rabl template path for Rails,
|
|
292
|
+
# Returns the rabl template path for Rails, using Rails' own lookup
|
|
256
293
|
def fetch_rails_source(file, options = {})
|
|
257
|
-
# use Rails template resolution mechanism if possible (find_template)
|
|
258
294
|
source_format = request_format if defined?(request_format)
|
|
259
295
|
|
|
260
|
-
if source_format && context_scope.respond_to?(:lookup_context)
|
|
296
|
+
if source_format && context_scope.respond_to?(:lookup_context)
|
|
261
297
|
lookup_proc = lambda do |partial|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
else # Rails 3.2 and higher
|
|
265
|
-
# pull format directly from rails unless it is html
|
|
266
|
-
context_scope.lookup_context.find(file, [], partial, [], { :formats => [rails_source_format] })
|
|
267
|
-
end
|
|
298
|
+
# pull format directly from rails unless it is html
|
|
299
|
+
context_scope.lookup_context.find(file, [], partial, [], { :formats => [rails_source_format] })
|
|
268
300
|
end
|
|
269
301
|
template = lookup_proc.call(false) rescue nil
|
|
270
302
|
template ||= lookup_proc.call(true) rescue nil
|
|
271
303
|
template.identifier if template
|
|
272
|
-
elsif source_format && context_scope.respond_to?(:view_paths) # Rails 2
|
|
273
|
-
template = context_scope.view_paths.find_template(file, source_format, false)
|
|
274
|
-
template.filename if template
|
|
275
304
|
end
|
|
276
305
|
end
|
|
277
306
|
|
data/lib/rabl/template.rb
CHANGED
|
@@ -22,51 +22,8 @@ if defined?(Tilt)
|
|
|
22
22
|
Tilt.register 'rabl', RablTemplate
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
# Rails
|
|
26
|
-
if defined?(ActionView) && defined?(Rails) && Rails.respond_to?(:version)
|
|
27
|
-
require 'action_view/base'
|
|
28
|
-
require 'action_view/template'
|
|
29
|
-
|
|
30
|
-
module ActionView
|
|
31
|
-
module TemplateHandlers
|
|
32
|
-
class RablHandler < TemplateHandler
|
|
33
|
-
include Compilable
|
|
34
|
-
|
|
35
|
-
def compile(template) %{
|
|
36
|
-
::Rabl::Engine.new(#{template.source.inspect}, { :format => #{template.format.inspect} }).
|
|
37
|
-
render(self, assigns.merge(local_assigns))
|
|
38
|
-
} end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
ActionView::Template.register_template_handler :rabl, ActionView::TemplateHandlers::RablHandler
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Rails 3.X / 4.X / 5.X Template
|
|
47
|
-
if defined?(ActionView) && defined?(Rails) && Rails.respond_to?(:version) && Rails.version.to_s =~ /^[345]/
|
|
48
|
-
module ActionView
|
|
49
|
-
module Template::Handlers
|
|
50
|
-
class Rabl
|
|
51
|
-
class_attribute :default_format
|
|
52
|
-
self.default_format = Mime[:json]
|
|
53
|
-
|
|
54
|
-
def self.call(template)
|
|
55
|
-
source = template.source
|
|
56
|
-
|
|
57
|
-
%{ ::Rabl::Engine.new(#{source.inspect}, :source_location => #{template.identifier.inspect}).
|
|
58
|
-
apply(self, assigns.merge(local_assigns)).
|
|
59
|
-
render }
|
|
60
|
-
end # call
|
|
61
|
-
end # rabl class
|
|
62
|
-
end # handlers
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
ActionView::Template.register_template_handler :rabl, ActionView::Template::Handlers::Rabl
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Rails 6.X / 7.X / 8.X Template
|
|
69
|
-
if defined?(ActionView) && defined?(Rails) && Rails.respond_to?(:version) && Rails.version.to_s =~ /^[678]/
|
|
25
|
+
# Rails Template
|
|
26
|
+
if defined?(ActionView) && defined?(Rails) && Rails.respond_to?(:version)
|
|
70
27
|
module ActionView
|
|
71
28
|
module Template::Handlers
|
|
72
29
|
class Rabl
|
data/lib/rabl/version.rb
CHANGED
data/lib/rabl.rb
CHANGED
|
@@ -21,13 +21,17 @@ require 'rabl/cache_engine'
|
|
|
21
21
|
require 'rabl/template_cache_key'
|
|
22
22
|
|
|
23
23
|
if defined?(Rails) && Rails.respond_to?(:version)
|
|
24
|
-
require 'rabl/tracker'
|
|
25
|
-
require 'rabl/digestor'
|
|
26
|
-
require 'rabl/railtie'
|
|
24
|
+
require 'rabl/tracker'
|
|
25
|
+
require 'rabl/digestor'
|
|
26
|
+
require 'rabl/railtie'
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
# Rabl.register!
|
|
30
30
|
module Rabl
|
|
31
|
+
# Fiber-local key of the sources read by the render in progress (see Rabl.begin_render_source_cache)
|
|
32
|
+
RENDER_SOURCE_CACHE = :_rabl_render_source_cache
|
|
33
|
+
private_constant :RENDER_SOURCE_CACHE
|
|
34
|
+
|
|
31
35
|
class << self
|
|
32
36
|
|
|
33
37
|
# Initialize RABL within an application
|
|
@@ -62,14 +66,15 @@ module Rabl
|
|
|
62
66
|
# everything the lookup depends on (the file, the view path and the lookup details)
|
|
63
67
|
# source_cache("users/index", ["path/to/view"], [:json]) { "/full/path/to/template/users/index" }
|
|
64
68
|
def source_cache(file, view_path, lookup = nil)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
# With cache_sources off, a template the collection uses for each item (extends, partial) is
|
|
70
|
+
# still read and resolved once per render rather than once per item. That cache belongs to
|
|
71
|
+
# the render, so a template changed between renders is still read again.
|
|
72
|
+
cache = Rabl.configuration.cache_sources ? (@_source_cache ||= {}) : render_source_cache
|
|
73
|
+
return yield unless cache
|
|
68
74
|
|
|
69
75
|
# stores a frozen copy of the key, so later changes to the caller's strings don't reach it
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
end
|
|
76
|
+
key = [file, view_path, lookup]
|
|
77
|
+
cache.fetch(key) { cache[frozen_copy(key)] = yield }
|
|
73
78
|
end
|
|
74
79
|
|
|
75
80
|
# Resets the RABL source cache
|
|
@@ -77,6 +82,23 @@ module Rabl
|
|
|
77
82
|
@_source_cache = {}
|
|
78
83
|
end
|
|
79
84
|
|
|
85
|
+
# The template sources read so far by the render running in this fiber, if any
|
|
86
|
+
def render_source_cache
|
|
87
|
+
Thread.current[RENDER_SOURCE_CACHE]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Internal: Starts collecting the sources the render about to run reads (see source_cache).
|
|
91
|
+
# The render that starts a collection ends it, so a render started inside another collects
|
|
92
|
+
# its own sources for as long as it runs and the render around it reads again afterwards.
|
|
93
|
+
def begin_render_source_cache
|
|
94
|
+
Thread.current[RENDER_SOURCE_CACHE] = {}
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Internal: Drops the collection the render that returns from it started.
|
|
98
|
+
def end_render_source_cache
|
|
99
|
+
Thread.current[RENDER_SOURCE_CACHE] = nil
|
|
100
|
+
end
|
|
101
|
+
|
|
80
102
|
# Returns a copy of value whose Strings and Arrays are frozen, for cache keys that
|
|
81
103
|
# must not change when the caller's objects do
|
|
82
104
|
def frozen_copy(value)
|
|
@@ -99,6 +121,4 @@ end
|
|
|
99
121
|
if defined?(Padrino)
|
|
100
122
|
require 'padrino-core'
|
|
101
123
|
Padrino.after_load { Rabl.register! }
|
|
102
|
-
elsif defined?(Rails.version) && Rails.version =~ /^2/
|
|
103
|
-
Rabl.register!
|
|
104
124
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rabl
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.19.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nathan Esquenazi
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-11-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
19
|
+
version: '6.1'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
26
|
+
version: '6.1'
|
|
27
27
|
description: General ruby templating with json, bson, xml and msgpack support
|
|
28
28
|
email:
|
|
29
29
|
- nesquena@gmail.com
|
|
@@ -35,14 +35,12 @@ files:
|
|
|
35
35
|
- CONTRIBUTING.md
|
|
36
36
|
- MIT-LICENSE
|
|
37
37
|
- README.md
|
|
38
|
+
- SECURITY.md
|
|
38
39
|
- lib/rabl.rb
|
|
39
40
|
- lib/rabl/builder.rb
|
|
40
41
|
- lib/rabl/cache_engine.rb
|
|
41
42
|
- lib/rabl/configuration.rb
|
|
42
43
|
- lib/rabl/digestor.rb
|
|
43
|
-
- lib/rabl/digestor/rails3.rb
|
|
44
|
-
- lib/rabl/digestor/rails41.rb
|
|
45
|
-
- lib/rabl/digestor/rails5.rb
|
|
46
44
|
- lib/rabl/engine.rb
|
|
47
45
|
- lib/rabl/helpers.rb
|
|
48
46
|
- lib/rabl/multi_builder.rb
|
|
@@ -57,7 +55,11 @@ files:
|
|
|
57
55
|
homepage: https://github.com/nesquena/rabl
|
|
58
56
|
licenses:
|
|
59
57
|
- MIT
|
|
60
|
-
metadata:
|
|
58
|
+
metadata:
|
|
59
|
+
changelog_uri: https://github.com/nesquena/rabl/blob/master/CHANGELOG.md
|
|
60
|
+
source_code_uri: https://github.com/nesquena/rabl
|
|
61
|
+
bug_tracker_uri: https://github.com/nesquena/rabl/issues
|
|
62
|
+
rubygems_mfa_required: 'true'
|
|
61
63
|
post_install_message:
|
|
62
64
|
rdoc_options: []
|
|
63
65
|
require_paths:
|
data/lib/rabl/digestor/rails3.rb
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'action_view'
|
|
4
|
-
|
|
5
|
-
module Rabl
|
|
6
|
-
class Digestor < ActionView::Digestor
|
|
7
|
-
def self.digest(name, format, finder, options = {})
|
|
8
|
-
cache_key = [name, format] + Array.wrap(options[:dependencies])
|
|
9
|
-
@@cache[cache_key.join('.')] ||= begin
|
|
10
|
-
Digestor.new(name, format, finder, options).digest
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'action_view'
|
|
4
|
-
|
|
5
|
-
module Rabl
|
|
6
|
-
class Digestor < ActionView::Digestor
|
|
7
|
-
def self.digest(options = {})
|
|
8
|
-
cache_key = [options[:name]] + Array.wrap(options[:dependencies])
|
|
9
|
-
@@cache[cache_key.join('.')] ||= begin
|
|
10
|
-
Digestor.new({ :name => options[:name], :finder => options[:finder] }.merge!(options)).digest
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|