render-later 1.1.0 → 1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 55370f11139243c05ff90f7daf2c71d46fe1f20c
4
- data.tar.gz: 70b2a0834e7502ea42de2525157eea36fc07fdb1
2
+ SHA256:
3
+ metadata.gz: c449ac1241d0e451f19d9ec51844d1fb3912aaac0e20ed145d2312f99e0404cc
4
+ data.tar.gz: 592d40821bb0a7c981e9105a5dc24ad48900daa8c400786adc75d59bfe33d4ac
5
5
  SHA512:
6
- metadata.gz: bfdd1c0a4dbda416582e61c88dbdf5da2d503c1908adff8f2ba2964020908514fe4d526d64105e069ad959ddd0d888eb345d1d0856e421646041d56b4bc3c508
7
- data.tar.gz: 9f02642e7c9cc19d81c3d233d22cb08b153e067ae8160fea10005f86b56de54ee276dd15181da16aec924544f877981a69987794b4f951dbcfc144ff1be4b4b2
6
+ metadata.gz: 428f8bd70f8f0056e2b84bf1e82c1b5c100713b20b7f95e01f7d2d2a5d474b46b5ed77244e5cf651fd50013d1b1696184b25edf5e7badd39bc22e0785b05e3b3
7
+ data.tar.gz: c7321d1e1190a62c9b2d332e6d516227b6c02597f923cf3c180ceab03d9c329d7d6445c62003d96f0dd08e0e13dcb566936f4f741fec11e8b8232e89e96b6785
@@ -1,13 +1,17 @@
1
1
  language: ruby
2
2
  cache: bundler
3
3
  rvm:
4
- - 2.3.4
5
- - 2.4.1
4
+ - 2.5.8
5
+ - 2.6.6
6
+ - 2.7.2
6
7
  gemfile:
7
- - test/gemfiles/rails-4.2.gemfile
8
8
  - test/gemfiles/rails-5.0.gemfile
9
9
  - test/gemfiles/rails-5.1.gemfile
10
+ - test/gemfiles/rails-5.2.gemfile
11
+ - test/gemfiles/rails-6.0.gemfile
10
12
  matrix:
11
13
  include:
12
- - rvm: 2.3.4
13
- gemfile: test/gemfiles/rails-4.1.gemfile
14
+ - rvm: 2.3.8
15
+ gemfile: test/gemfiles/rails-4.1.gemfile
16
+ - rvm: 2.4.9
17
+ gemfile: test/gemfiles/rails-4.2.gemfile
@@ -1,9 +1,23 @@
1
+ # v1.2
2
+
3
+ * Add support and documentation for new rack 2.2.x ETag bug
4
+ * Add new test for streaming capabilities of Rails stack.
5
+ * Update default rails server in Readme and in tests
6
+ * Loosen dev dependencies (rails and bundler)
7
+ * Removes sprockets from dumy app to better support Rails 6
8
+ * Update build matrix ruby version (+add 2.7)
9
+ * Update my last name <3
10
+
11
+ _Nov 23, 2020_
12
+
1
13
  # v1.1.0
2
14
 
3
15
  Add support for render-later in tables and other elements:
4
16
  * Add ability to customize placeholder tag (#2)
5
17
  * Fix javascript temporary element to match parent one (#2)
6
18
 
19
+ _Aug 3, 2017_
20
+
7
21
  # v1.0.0
8
22
 
9
23
  Initial production release
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2016 Adrien Jarthon
3
+ Copyright (c) 2016 Adrien Rey-Jarthon
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -47,6 +47,9 @@ And finally in your controller, you need to render with the `stream` option:
47
47
 
48
48
  ```ruby
49
49
  def index
50
+ # You may also need (see Gotcha section):
51
+ # form_authenticity_token; flash
52
+ # headers['Last-Modified'] = Time.now.httpdate
50
53
  render stream: true
51
54
  end
52
55
  ```
@@ -54,7 +57,7 @@ And finally in your controller, you need to render with the `stream` option:
54
57
  ## Performance
55
58
 
56
59
  Performance-wise, this approach is quite interesting, let me explain why:
57
- We know for a time that with bandwidth always increasing, web performance is getting more and more constrained by latency.
60
+ We know that bandwidth is always increasing, web performance is getting more and more constrained by latency.
58
61
 
59
62
  For cases like this, there is usually two ways:
60
63
 
@@ -65,18 +68,21 @@ The first solution is, IMO, decent **if using streaming**, because the browser c
65
68
 
66
69
  The second solution gets the first page rendered quickly but is much more complex to setup (needs another endpoint to expose the data, an Ajax call to fetch them, and some js to put the data back where it belongs, handle loading spinner, errors, etc.). It is also much **slower to load**, because the browser needs to load & parse all the javascript, wait to the entire page to be ready (domready), and only then it will start the ajax call, adding the whole round-trip time once again to the page loading.
67
70
 
68
- Whereas with async-render, you leverage the existing open socket currently downloading the document to stream the deferred data:
71
+ Whereas with render-later, you leverage the existing open socket currently downloading the document to stream the deferred data:
69
72
  - You don't have to handle errors, because it's the same request. So if it fails at this point, the browser will simply show it's native error page.
70
73
  - You don't have to show any kind of spinner because the browser is still showing it's native loading indicator.
71
- - You don't increase latency because the additional data start streaming right after the page is sent. And even better, the deferred data **starts rendering even if the browser didn't receive a single byte yet**, so if you're on a slow network (ex: 3G, 500ms rtt), instead of delaying even more (Ajax call), you'll actually receive the document **already complete**, because while the network lagged, the server was working :)
74
+ - You don't increase latency because the additional data start streaming right away after the page is sent. And even better, the deferred data **starts rendering even if the browser didn't receive a single byte yet**, so if you're on a slow network (ex: 3G, 500ms rtt), instead of delaying even more (Ajax call), you'll actually receive the document **already complete**, because while the network lagged, the server was working :)
72
75
 
73
76
  So in the end, this solutions is the best of both world: you get the first paint time of the Ajax version, but with the simplicity and time-to-full-document of the simple inline version.
74
77
 
75
- The only downside of this approach is that the domeady event will only be executed at the end of the request, once the body is complete. See the [Gotcha section](#gotcha) for more details.
78
+ The only downsides of this approach IMO are:
79
+ 1. The `domready` event will only be executed at the end of the end of the streaming, once the body is complete.
80
+ 2. Rails/rack support for streaming is bad and you may need to workaround some bugs.
81
+ See the [Gotcha section](#gotcha) for more details.
76
82
 
77
83
  ## Compatibility
78
84
 
79
- On the server side, the gem is tested against `ruby 2.1+` and `rails 4.1+`.
85
+ On the server side, the gem is tested against `ruby 2.3+` and `rails 4.1+`.
80
86
  The dependency is not strictly enforced though and it may work with others versions but we don't guaranty anything.
81
87
 
82
88
  The generated javascript is a simple inline function using nothing else than DOM Core level 2 properties, so it should work flawlessly on IE6+, Firefox 2+, Chrome 1+, Edge, Safari, etc.
@@ -97,11 +103,11 @@ Server | Supported | Comment
97
103
  ------------------|------------| -------
98
104
  Phusion Passenger | ✔️ |
99
105
  Unicorn | ✔️ | _with `tcp_nopush: false`_
100
- puma | ✔️ |
101
- WEBrick | ❌ | _default for `rails s`_
106
+ puma | ✔️ | _default for `rails s`_
107
+ WEBrick | ❌ |
102
108
  Thin | ❌ |
103
109
 
104
- To try it in development, I recommend adding `gem 'unicorn-rails'` to your `Gemfile`'s development group and use `UNICORN_WORKERS=4 rails s` to start it. We need multiple processes in development to avoid blocking CSS requests during the page load. It's totally fine to develop with a single process or a server which doesn't support streaming, you just won't see the effects of the gem.
110
+ To try it in development, I recommend using `puma` (the default Rails server) with `rails s`. We need the multiple threads in development to avoid blocking CSS/JS requests during the page streaming. It's totally fine to develop with a single thread/process or a server which doesn't support streaming, you just won't see the effects of the gem.
105
111
 
106
112
  ### gzip
107
113
 
@@ -129,6 +135,7 @@ Slim | ✔️ |
129
135
  Haml | ❌ |
130
136
 
131
137
  #### Flash message & CSRF token
138
+
132
139
  There is currently an [issue with Rails i'm trying to revive](https://github.com/rails/rails/issues/11476) about streaming causing trouble with flash messages and CSRF tokens. This is because rails waits the end of the request to check if you have used the flash message, if so it is removed from the store, otherwise it stays for the next request. But with streaming on, rails considers the requests as ended very early, before the flash message ever gets a change to be rendered. So this logic fails and keeps the flash message forever. The problem is exactly the same with the CSRF token.
133
140
 
134
141
  The best **workaround** we currently have is to fool rails by accessing the flash message and CSRF token before the render call:
@@ -141,7 +148,22 @@ The best **workaround** we currently have is to fool rails by accessing the flas
141
148
  end
142
149
  ```
143
150
 
151
+ #### Rack 2.2.x & ETag Middleware
152
+
153
+ There's a recent change in `rack 2.2.x` which broke streaming in Rails as can [be seen in this issue](https://github.com/rack/rack/issues/1619). The problem is that the ETag middleware used to ignore streaming response because of the `Cache-Control: no-cache` header (which is set by Rails for streaming response) and now it doesn't any more (because this behavior wasn't really valid). But the ETag middleware now has to process the whole body to generate the ETag header and thus blocks the response until everything is ready.
154
+
155
+ The best **workaround** we currently have is to fool the ETag middleware by setting the `Last-Modified` header (if set, the ETag middleware is skipped):
156
+
157
+ ```ruby
158
+ # in the controller, before every streaming render.
159
+ def index
160
+ headers['Last-Modified'] = Time.now.httpdate
161
+ render stream: true
162
+ end
163
+ ```
164
+
144
165
  #### Caching
166
+
145
167
  Finally, before your scratch your head, be careful not to put fragment caching **outside** a `render_later` block, because that would actually cache the empty span but not the inline script (which is generated by `render_now`), so it would be a waste and the real content will never be injected. It's totally **fine/recommended** to use fragment caching **inside** the `render_later` block.
146
168
 
147
169
  ## Contributing
@@ -150,6 +172,23 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/jartho
150
172
 
151
173
  After checking out the repo, run `bundle install` to install dependencies. Then, run `rake` to run the tests.
152
174
 
175
+ Start the demo server:
176
+ ```bash
177
+ cd test/dummy && rails s
178
+ ```
179
+
180
+ Verify the streaming behavior in browser (http://localhost:3000) or curl:
181
+ ```bash
182
+ curl http://localhost:3000
183
+ ```
184
+
185
+ Test with a specific Rails version:
186
+ ```bash
187
+ cd test/dummy
188
+ bundle install --gemfile=../gemfiles/rails-5.0.gemfile
189
+ bundle exec --gemfile=../gemfiles/rails-5.0.gemfile rails s
190
+ ```
191
+
153
192
  ## Ideas for improvement
154
193
 
155
194
  - Wider test coverage across browsers using [Sauce Labs](https://saucelabs.com/opensauce/)
@@ -1,3 +1,3 @@
1
1
  module RenderLater
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2"
3
3
  end
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'bundler/inline'
5
+ rescue LoadError => e
6
+ $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler'
7
+ raise e
8
+ end
9
+
10
+ gemfile(true) do
11
+ source 'https://rubygems.org'
12
+ gem 'rails', '4.2.5'
13
+ end
14
+
15
+ require 'rack/test'
16
+ require 'action_controller/railtie'
17
+
18
+ class TestApp < Rails::Application
19
+ config.root = File.dirname(__FILE__)
20
+ config.session_store :cookie_store, key: 'cookie_store_key'
21
+ secrets.secret_token = 'secret_token'
22
+ secrets.secret_key_base = 'secret_key_base'
23
+
24
+ config.logger = Logger.new($stdout)
25
+ Rails.logger = config.logger
26
+
27
+ routes.draw do
28
+ get '/' => 'test#index'
29
+ get '/set_flash' => 'test#set_flash'
30
+ end
31
+ end
32
+
33
+ class TestController < ActionController::Base
34
+ include Rails.application.routes.url_helpers
35
+
36
+ def index
37
+ render file: 'template.html.erb', stream: params[:stream]
38
+ end
39
+
40
+ def set_flash
41
+ flash.notice = 'notice'
42
+ render text: 'ok'
43
+ end
44
+ end
45
+
46
+ require 'minitest/autorun'
47
+
48
+ # Ensure backward compatibility with Minitest 4
49
+ Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
50
+
51
+ class BugTest < Minitest::Test
52
+ include Rack::Test::Methods
53
+
54
+ def setup
55
+ IO.write('template.html.erb', '<%= csrf_meta_tag %><%= flash.notice %>')
56
+ end
57
+
58
+ def teardown
59
+ File.unlink('template.html.erb')
60
+ end
61
+
62
+ def test_works_without_stream
63
+ get '/set_flash'
64
+ assert last_response.ok?
65
+ get '/'
66
+ assert last_response.ok?
67
+ assert_match /notice/, last_response.body
68
+ get '/'
69
+ assert last_response.ok?
70
+ refute_match /notice/, last_response.body
71
+ end
72
+
73
+ def test_works_with_stream
74
+ get '/set_flash'
75
+ assert last_response.ok?
76
+ get '/', stream: true
77
+ assert last_response.ok?
78
+ assert_match /notice/, last_response.body
79
+ get '/', stream: true
80
+ assert last_response.ok?
81
+ refute_match /notice/, last_response.body
82
+ end
83
+
84
+ private
85
+
86
+ def app
87
+ Rails.application
88
+ end
89
+ end
@@ -6,7 +6,7 @@ require 'render-later/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "render-later"
8
8
  spec.version = RenderLater::VERSION
9
- spec.authors = ["Adrien Jarthon"]
9
+ spec.authors = ["Adrien Rey-Jarthon"]
10
10
  spec.email = ["me@adrienjarthon.com"]
11
11
 
12
12
  spec.summary = %q{Defer rendering of slow parts to the end of the page}
@@ -19,10 +19,10 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.add_development_dependency "bundler", "~> 1.11"
22
+ spec.add_development_dependency "bundler"
23
23
  spec.add_development_dependency "rake"
24
24
  spec.add_development_dependency "minitest"
25
- spec.add_development_dependency "rails", "~> 5.1"
25
+ spec.add_development_dependency "rails", ">= 4.1"
26
26
  spec.add_development_dependency "puma"
27
27
  spec.add_development_dependency "poltergeist"
28
28
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: render-later
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: '1.2'
5
5
  platform: ruby
6
6
  authors:
7
- - Adrien Jarthon
7
+ - Adrien Rey-Jarthon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-08-03 00:00:00.000000000 Z
11
+ date: 2020-11-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.11'
19
+ version: '0'
20
20
  type: :development
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: '1.11'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -56,16 +56,16 @@ dependencies:
56
56
  name: rails
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '5.1'
61
+ version: '4.1'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '5.1'
68
+ version: '4.1'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: puma
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -114,6 +114,7 @@ files:
114
114
  - lib/render-later.rb
115
115
  - lib/render-later/engine.rb
116
116
  - lib/render-later/version.rb
117
+ - rails-streaming.rb
117
118
  - render-later.gemspec
118
119
  homepage: https://github.com/jarthod/render-later
119
120
  licenses:
@@ -134,8 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
135
  - !ruby/object:Gem::Version
135
136
  version: '0'
136
137
  requirements: []
137
- rubyforge_project:
138
- rubygems_version: 2.5.1
138
+ rubygems_version: 3.1.2
139
139
  signing_key:
140
140
  specification_version: 4
141
141
  summary: Defer rendering of slow parts to the end of the page