lazy_chain 0.0.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +86 -0
- data/Rakefile +6 -0
- data/config/routes.rb +2 -0
- data/lib/lazy_chain/engine.rb +4 -0
- data/lib/lazy_chain/version.rb +3 -0
- data/lib/lazy_chain.rb +6 -0
- data/lib/tasks/lazy_chain_tasks.rake +4 -0
- metadata +71 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 490f13aa3fc5619f80736a1e222dff5a98f7ae36264d828e27c5bc322fdd7e04
|
|
4
|
+
data.tar.gz: a06fe7509586c68ec26b9ec3d31c5313bd9391cb9e076cb62a50ad154fdfccfe
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 69ee1ffcf8236e402b4b36dc9fe20c7664ccda22d984052f241445b3039fafbf9c6e09c9c84526b29d9c15f897c6b057ca6c7802f25bdc23dd9591653c29a12b
|
|
7
|
+
data.tar.gz: f9a684c1721e42005310b22f2300fa56ba0ab00e27359941557140d8688c779a50e3f336e69753f4743903bb7b6919be8705dcc051b7c7d2c56bd0a346f7aac8
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright Andy Cohen
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# LazyChain :chains:
|
|
2
|
+
|
|
3
|
+
**Render every page immediately, even if your code is slow.**
|
|
4
|
+
|
|
5
|
+
> ⚠️ **Not usable yet.** `0.0.1` reserves the gem name and ships an empty engine — installing it does nothing. The API described below is the direction, not a promise, and it will change before `1.0`. Watch the repo if you want to know when it's real.
|
|
6
|
+
|
|
7
|
+
## The problem
|
|
8
|
+
|
|
9
|
+
Every mature Rails app has a page like this:
|
|
10
|
+
|
|
11
|
+
```erb
|
|
12
|
+
<td><%= number_to_currency estimate.pricing.all.total_amount %></td>
|
|
13
|
+
<td><%= number_to_currency estimate.pricing.all.margin_amount %></td>
|
|
14
|
+
<td><%= number_to_currency estimate.pricing.all.tax_amount %></td>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Three innocent-looking method calls. Each one walks a tree of associations, and each one takes 800ms. The page takes nine seconds to paint, and the user stares at a blank screen the whole time — including the header, the navigation, and the twenty other values that were ready instantly.
|
|
18
|
+
|
|
19
|
+
You know how to fix it. Memoize it, denormalize it, precompute it in a background job, rewrite it as one heroic SQL query. All of that is real work, and all of it is work you were not planning to do this week.
|
|
20
|
+
|
|
21
|
+
## The idea
|
|
22
|
+
|
|
23
|
+
The page is not slow. A handful of method calls on the page are slow. LazyChain lets you say which ones, and gets them out of the critical path:
|
|
24
|
+
|
|
25
|
+
```erb
|
|
26
|
+
<td><%= lazy_chain estimate, "pricing.all.total_amount" %></td>
|
|
27
|
+
<td><%= lazy_chain estimate, "pricing.all.margin_amount" %></td>
|
|
28
|
+
<td><%= lazy_chain estimate, "pricing.all.tax_amount" %></td>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The page renders now. Each deferred value arrives on its own, as soon as it's ready, in the spot where it belongs. All you need to do is opt-in to the provided route concern and use the view helper. Everything else is magic :magic_wand:.
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
resources :estimates, concerns: %i[lazy_chain]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
That's the whole pitch. No new service objects, no background job infrastructure, no cache to invalidate by hand, no JavaScript to write. You point at the slow call and move on with your day.
|
|
38
|
+
|
|
39
|
+
## What you get
|
|
40
|
+
|
|
41
|
+
- **A one-line change at the call site.** Wrap the expensive call. Delete the wrapper later when you get around to making it fast. Nothing else in your app needs to know.
|
|
42
|
+
- **Nine seconds becomes nine parallel requests.** The browser fetches deferred values concurrently, so the slowest one sets your page time — not the sum of all of them.
|
|
43
|
+
- **Values that show up where they belong.** Each one lands in its own slot in the markup. No spinners bolted onto the layout, no reflow, no partial rewrites.
|
|
44
|
+
- **Below-the-fold work that never happens.** Values further down the page aren't computed until the user scrolls to them.
|
|
45
|
+
- **Safe by construction.** Method chains are signed automatically, and signatures are validated before execution.
|
|
46
|
+
- **Turbo-native and boring.** Standard Turbo Frames, standard Rails caching, standard controllers. Nothing you'd be surprised to find in a `curl`.
|
|
47
|
+
|
|
48
|
+
## What it isn't
|
|
49
|
+
|
|
50
|
+
LazyChain is not a performance fix — it's a way to stop paying for one up front. The slow method is still slow, and it will still be slow after you install this. What changes is that your user isn't waiting on it to see the rest of the page.
|
|
51
|
+
|
|
52
|
+
If a value is slow *and* the user is blocked on reading it, make it fast. If it's slow and merely nice to have on screen eventually — which describes an enormous number of totals, rollups, and derived figures in real applications — defer it.
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
gem "lazy_chain"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
$ bundle install
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Requires Rails 8.1+ and Turbo.
|
|
65
|
+
|
|
66
|
+
## Status & roadmap
|
|
67
|
+
|
|
68
|
+
LazyChain is being extracted from a production estimating application, where it defers several hundred pricing rollups across the app and has been in daily use by a real sales team.
|
|
69
|
+
|
|
70
|
+
The extraction is in progress. Coming before the first usable release:
|
|
71
|
+
|
|
72
|
+
- [ ] The `lazy_chain` view helper and its underlying view object
|
|
73
|
+
- [ ] The controller and routing concern that resolve a deferred chain
|
|
74
|
+
- [ ] Signed method chains, verified before anything is called
|
|
75
|
+
- [ ] A pluggable cache strategy, so a computed value is computed once
|
|
76
|
+
- [ ] Pluggable formatting, with sensible defaults
|
|
77
|
+
- [ ] Test helpers for making deferred values behave in system specs
|
|
78
|
+
- [ ] Documentation worth the name
|
|
79
|
+
|
|
80
|
+
## Contributing
|
|
81
|
+
|
|
82
|
+
Not yet — there's nothing to contribute to. Once the extraction lands, issues and pull requests are welcome. Until then, if the pitch above describes a page in your app, open an issue and tell me about it. That's genuinely useful.
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
Released under the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/config/routes.rb
ADDED
data/lib/lazy_chain.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lazy_chain
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Andy Cohen
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 8.1.3.1
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 8.1.3.1
|
|
26
|
+
description: |-
|
|
27
|
+
Most slow Rails pages are not slow -- a handful of method calls on them are. LazyChain lets you
|
|
28
|
+
mark those calls in your templates and moves them off the critical path. The page renders right
|
|
29
|
+
away, and each deferred value loads on its own in a Turbo Frame as soon as it is ready. Values
|
|
30
|
+
below the fold are never computed at all unless the user scrolls to them. No background job
|
|
31
|
+
infrastructure, no cache to invalidate by hand, and no JavaScript to write -- just standard
|
|
32
|
+
Turbo Frames and standard Rails caching.
|
|
33
|
+
email:
|
|
34
|
+
- outlawandy@gmail.com
|
|
35
|
+
executables: []
|
|
36
|
+
extensions: []
|
|
37
|
+
extra_rdoc_files: []
|
|
38
|
+
files:
|
|
39
|
+
- MIT-LICENSE
|
|
40
|
+
- README.md
|
|
41
|
+
- Rakefile
|
|
42
|
+
- config/routes.rb
|
|
43
|
+
- lib/lazy_chain.rb
|
|
44
|
+
- lib/lazy_chain/engine.rb
|
|
45
|
+
- lib/lazy_chain/version.rb
|
|
46
|
+
- lib/tasks/lazy_chain_tasks.rake
|
|
47
|
+
homepage: https://github.com/OutlawAndy/lazy_chain
|
|
48
|
+
licenses:
|
|
49
|
+
- MIT
|
|
50
|
+
metadata:
|
|
51
|
+
source_code_uri: https://github.com/OutlawAndy/lazy_chain
|
|
52
|
+
changelog_uri: https://github.com/OutlawAndy/lazy_chain/blob/main/CHANGELOG.md
|
|
53
|
+
bug_tracker_uri: https://github.com/OutlawAndy/lazy_chain/issues
|
|
54
|
+
rdoc_options: []
|
|
55
|
+
require_paths:
|
|
56
|
+
- lib
|
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 3.2.0
|
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - ">="
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '0'
|
|
67
|
+
requirements: []
|
|
68
|
+
rubygems_version: 4.0.16
|
|
69
|
+
specification_version: 4
|
|
70
|
+
summary: Defer expensive method calls into Turbo Frames so every page renders immediately.
|
|
71
|
+
test_files: []
|