futurism 1.2.0.pre7 → 1.2.0.pre10
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/Appraisals +24 -0
- data/Appraisals~ +24 -0
- data/CHANGELOG.md +370 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +215 -0
- data/Gemfile~ +17 -0
- data/README.md +59 -10
- data/README.md~ +405 -0
- data/app/assets/javascripts/futurism.js +191 -0
- data/app/assets/javascripts/futurism.min.js +2 -0
- data/app/assets/javascripts/futurism.min.js.map +1 -0
- data/app/assets/javascripts/futurism.umd.js +188 -0
- data/app/assets/javascripts/futurism.umd.min.js +2 -0
- data/app/assets/javascripts/futurism.umd.min.js.map +1 -0
- data/{lib → app/channels}/futurism/channel.rb +1 -1
- data/bin/rails +25 -0
- data/bin/standardize +5 -0
- data/bin/test +5 -0
- data/futurism.gemspec +38 -0
- data/futurism.gemspec~ +30 -0
- data/lib/futurism/configuration.rb +21 -0
- data/lib/futurism/engine.rb +19 -0
- data/lib/futurism/helpers.rb +5 -2
- data/lib/futurism/importmap.rb +2 -0
- data/lib/futurism/version.rb +1 -1
- data/lib/futurism.rb +2 -1
- data/package.json +47 -0
- data/package.json~ +51 -0
- data/rollup.config.js +77 -0
- data/rollup.config.js~ +73 -0
- data/test/cable/channel_test.rb +319 -0
- data/test/dummy/app/channels/application_cable/channel.rb +4 -0
- data/test/dummy/app/channels/application_cable/connection.rb +4 -0
- data/test/dummy/app/controllers/application_controller.rb +2 -0
- data/test/dummy/app/controllers/home_controller.rb +6 -0
- data/test/dummy/app/controllers/posts_controller.rb +59 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/helpers/posts_helper.rb +2 -0
- data/test/dummy/app/jobs/application_job.rb +7 -0
- data/test/dummy/app/models/action_item.rb +2 -0
- data/test/dummy/app/models/application_record.rb +3 -0
- data/test/dummy/app/models/post.rb +2 -0
- data/test/dummy/config/application.rb +29 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +40 -0
- data/test/dummy/config/environments/production.rb +94 -0
- data/test/dummy/config/environments/test.rb +39 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/content_security_policy.rb +28 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +9 -0
- data/test/dummy/config/puma.rb +38 -0
- data/test/dummy/config/routes.rb +12 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/db/migrate/20200711122838_create_posts.rb +9 -0
- data/test/dummy/db/migrate/2021042923813_create_action_items.rb +9 -0
- data/test/dummy/db/schema.rb +27 -0
- data/test/futurism_test.rb +28 -0
- data/test/helper/helper_test.rb +206 -0
- data/test/integration/navigation_test.rb +7 -0
- data/test/resolver/controller/renderer_test.rb +120 -0
- data/test/resolver/controller_test.rb +26 -0
- data/test/test_helper.rb +14 -0
- data/yarn.lock +3263 -0
- metadata +122 -18
- data/config/routes.rb +0 -2
- data/lib/futurism/channel.rb~ +0 -31
- data/lib/futurism/helpers.rb~ +0 -118
- data/lib/futurism/options_transformer.rb~ +0 -0
- data/lib/futurism/resolver/controller/renderer.rb~ +0 -76
- data/lib/futurism/resolver/controller.rb~ +0 -17
- data/lib/futurism/resolver/resources.rb~ +0 -101
- data/lib/futurism/version.rb~ +0 -3
- data/lib/futurism.rb~ +0 -30
- data/lib/tasks/futurism_tasks.rake +0 -39
- data/lib/tasks/futurism_tasks.rake~ +0 -39
data/lib/futurism.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
require "rails"
|
2
|
+
|
2
3
|
require "action_cable"
|
3
4
|
require "cable_ready"
|
5
|
+
require "futurism/configuration"
|
4
6
|
require "futurism/engine"
|
5
7
|
require "futurism/message_verifier"
|
6
8
|
require "futurism/options_transformer"
|
7
9
|
require "futurism/resolver/resources"
|
8
10
|
require "futurism/resolver/controller"
|
9
11
|
require "futurism/resolver/controller/renderer"
|
10
|
-
require "futurism/channel"
|
11
12
|
require "futurism/helpers"
|
12
13
|
|
13
14
|
module Futurism
|
data/package.json
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
{
|
2
|
+
"name": "@stimulus_reflex/futurism",
|
3
|
+
"version": "1.2.0-pre10",
|
4
|
+
"description": "Lazy-load Rails partials via CableReady",
|
5
|
+
"main": "./dist/futurism.umd.min.js",
|
6
|
+
"module": "./dist/futurism.min.js",
|
7
|
+
"files": [
|
8
|
+
"dist/*",
|
9
|
+
"javascript/*"
|
10
|
+
],
|
11
|
+
"scripts": {
|
12
|
+
"test": "yarn run mocha",
|
13
|
+
"lint": "yarn run prettier-standard:check",
|
14
|
+
"format": "yarn run prettier-standard:format",
|
15
|
+
"prettier-standard:check": "yarn run prettier-standard --check ./javascript/**/*.js rollup.config.js",
|
16
|
+
"prettier-standard:format": "yarn run prettier-standard ./javascript/**/*.js rollup.config.js",
|
17
|
+
"build": "yarn rollup -c",
|
18
|
+
"watch": "yarn rollup -wc"
|
19
|
+
},
|
20
|
+
"repository": {
|
21
|
+
"type": "git",
|
22
|
+
"url": "git+https://github.com/stimulusreflex/futurism.git"
|
23
|
+
},
|
24
|
+
"keywords": [
|
25
|
+
"cable_ready",
|
26
|
+
"lazy",
|
27
|
+
"loading"
|
28
|
+
],
|
29
|
+
"author": "Julian Rubisch <julian.rubisch@hey.com>",
|
30
|
+
"license": "MIT",
|
31
|
+
"bugs": {
|
32
|
+
"url": "https://github.com/stimulusreflex/futurism/issues"
|
33
|
+
},
|
34
|
+
"homepage": "https://github.com/stimulusreflex/futurism#readme",
|
35
|
+
"dependencies": {
|
36
|
+
"cable_ready": "^5.0.0-pre9"
|
37
|
+
},
|
38
|
+
"devDependencies": {
|
39
|
+
"@rollup/plugin-commonjs": "^21.0.3",
|
40
|
+
"@rollup/plugin-json": "^4.1.0",
|
41
|
+
"@rollup/plugin-node-resolve": "^13.1.3",
|
42
|
+
"mocha": "^8.0.1",
|
43
|
+
"prettier-standard": "^16.4.1",
|
44
|
+
"rollup": "^2.70.1",
|
45
|
+
"rollup-plugin-terser": "^7.0.2"
|
46
|
+
}
|
47
|
+
}
|
data/package.json~
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
{
|
2
|
+
"name": "@stimulus_reflex/futurism",
|
3
|
+
"version": "1.2.0-pre10",
|
4
|
+
"description": "Lazy-load Rails partials via CableReady",
|
5
|
+
"main": "./dist/futurism.umd.min.js",
|
6
|
+
"module": "./dist/futurism.min.js",
|
7
|
+
"files": [
|
8
|
+
"dist/*",
|
9
|
+
"javascript/*"
|
10
|
+
],
|
11
|
+
"scripts": {
|
12
|
+
"test": "yarn run mocha",
|
13
|
+
"lint": "yarn run prettier-standard:check",
|
14
|
+
"format": "yarn run prettier-standard:format",
|
15
|
+
"prettier-standard:check": "yarn run prettier-standard --check ./javascript/**/*.js rollup.config.js",
|
16
|
+
"prettier-standard:format": "yarn run prettier-standard ./javascript/**/*.js rollup.config.js",
|
17
|
+
"build": "yarn rollup -c",
|
18
|
+
"watch": "yarn rollup -wc"
|
19
|
+
},
|
20
|
+
"repository": {
|
21
|
+
"type": "git",
|
22
|
+
"url": "git+https://github.com/stimulusreflex/futurism.git"
|
23
|
+
},
|
24
|
+
"keywords": [
|
25
|
+
"cable_ready",
|
26
|
+
"lazy",
|
27
|
+
"loading"
|
28
|
+
],
|
29
|
+
"author": "Julian Rubisch <julian.rubisch@hey.com>",
|
30
|
+
"license": "MIT",
|
31
|
+
"bugs": {
|
32
|
+
"url": "https://github.com/stimulusreflex/futurism/issues"
|
33
|
+
},
|
34
|
+
"homepage": "https://github.com/stimulusreflex/futurism#readme",
|
35
|
+
"dependencies": {
|
36
|
+
<<<<<<< Updated upstream
|
37
|
+
"cable_ready": "^5.0.0-pre9"
|
38
|
+
=======
|
39
|
+
"cable_ready": "5.0.0-pre9"
|
40
|
+
>>>>>>> Stashed changes
|
41
|
+
},
|
42
|
+
"devDependencies": {
|
43
|
+
"@rollup/plugin-commonjs": "^21.0.3",
|
44
|
+
"@rollup/plugin-json": "^4.1.0",
|
45
|
+
"@rollup/plugin-node-resolve": "^13.1.3",
|
46
|
+
"mocha": "^8.0.1",
|
47
|
+
"prettier-standard": "^16.4.1",
|
48
|
+
"rollup": "^2.70.1",
|
49
|
+
"rollup-plugin-terser": "^7.0.2"
|
50
|
+
}
|
51
|
+
}
|
data/rollup.config.js
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
import resolve from '@rollup/plugin-node-resolve'
|
2
|
+
import commonjs from '@rollup/plugin-commonjs'
|
3
|
+
import json from '@rollup/plugin-json'
|
4
|
+
import { terser } from 'rollup-plugin-terser'
|
5
|
+
|
6
|
+
const pretty = () => {
|
7
|
+
return terser({
|
8
|
+
mangle: false,
|
9
|
+
compress: false,
|
10
|
+
format: {
|
11
|
+
beautify: true,
|
12
|
+
indent_level: 2
|
13
|
+
}
|
14
|
+
})
|
15
|
+
}
|
16
|
+
|
17
|
+
const minify = () => {
|
18
|
+
return terser({
|
19
|
+
mangle: true,
|
20
|
+
compress: true
|
21
|
+
})
|
22
|
+
}
|
23
|
+
|
24
|
+
const esConfig = {
|
25
|
+
format: 'es',
|
26
|
+
inlineDynamicImports: true
|
27
|
+
}
|
28
|
+
|
29
|
+
const umdConfig = {
|
30
|
+
name: 'Futurism',
|
31
|
+
format: 'umd',
|
32
|
+
exports: 'named',
|
33
|
+
globals: {
|
34
|
+
cable_ready: 'CableReady'
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
const distFolders = ['dist/', 'app/assets/javascripts/']
|
39
|
+
|
40
|
+
const output = distFolders
|
41
|
+
.map(distFolder => [
|
42
|
+
{
|
43
|
+
...esConfig,
|
44
|
+
file: `${distFolder}/futurism.js`,
|
45
|
+
plugins: [pretty()]
|
46
|
+
},
|
47
|
+
{
|
48
|
+
...esConfig,
|
49
|
+
file: `${distFolder}/futurism.min.js`,
|
50
|
+
sourcemap: true,
|
51
|
+
plugins: [minify()]
|
52
|
+
},
|
53
|
+
{
|
54
|
+
...umdConfig,
|
55
|
+
file: `${distFolder}/futurism.umd.js`,
|
56
|
+
plugins: [pretty()]
|
57
|
+
},
|
58
|
+
{
|
59
|
+
...umdConfig,
|
60
|
+
file: `${distFolder}/futurism.umd.min.js`,
|
61
|
+
sourcemap: true,
|
62
|
+
plugins: [minify()]
|
63
|
+
}
|
64
|
+
])
|
65
|
+
.flat()
|
66
|
+
|
67
|
+
export default [
|
68
|
+
{
|
69
|
+
external: ['cable_ready'],
|
70
|
+
input: 'javascript/index.js',
|
71
|
+
output,
|
72
|
+
plugins: [commonjs(), resolve(), json()],
|
73
|
+
watch: {
|
74
|
+
include: 'javascript/**'
|
75
|
+
}
|
76
|
+
}
|
77
|
+
]
|
data/rollup.config.js~
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
import resolve from '@rollup/plugin-node-resolve'
|
2
|
+
import commonjs from '@rollup/plugin-commonjs'
|
3
|
+
import json from '@rollup/plugin-json'
|
4
|
+
import { terser } from 'rollup-plugin-terser'
|
5
|
+
|
6
|
+
const pretty = () => {
|
7
|
+
return terser({
|
8
|
+
mangle: false,
|
9
|
+
compress: false,
|
10
|
+
format: {
|
11
|
+
beautify: true,
|
12
|
+
indent_level: 2
|
13
|
+
}
|
14
|
+
})
|
15
|
+
}
|
16
|
+
|
17
|
+
const minify = () => {
|
18
|
+
return terser({
|
19
|
+
mangle: true,
|
20
|
+
compress: true
|
21
|
+
})
|
22
|
+
}
|
23
|
+
|
24
|
+
const esConfig = {
|
25
|
+
format: 'es',
|
26
|
+
inlineDynamicImports: true
|
27
|
+
}
|
28
|
+
|
29
|
+
const umdConfig = {
|
30
|
+
name: 'Futurism',
|
31
|
+
format: 'umd',
|
32
|
+
exports: 'named'
|
33
|
+
}
|
34
|
+
|
35
|
+
const distFolders = ['dist/', 'app/assets/javascripts/']
|
36
|
+
|
37
|
+
const output = distFolders
|
38
|
+
.map(distFolder => [
|
39
|
+
{
|
40
|
+
...esConfig,
|
41
|
+
file: `${distFolder}/futurism.js`,
|
42
|
+
plugins: [pretty()]
|
43
|
+
},
|
44
|
+
{
|
45
|
+
...esConfig,
|
46
|
+
file: `${distFolder}/futurism.min.js`,
|
47
|
+
sourcemap: true,
|
48
|
+
plugins: [minify()]
|
49
|
+
},
|
50
|
+
{
|
51
|
+
...umdConfig,
|
52
|
+
file: `${distFolder}/futurism.umd.js`,
|
53
|
+
plugins: [pretty()]
|
54
|
+
},
|
55
|
+
{
|
56
|
+
...umdConfig,
|
57
|
+
file: `${distFolder}/futurism.umd.min.js`,
|
58
|
+
sourcemap: true,
|
59
|
+
plugins: [minify()]
|
60
|
+
}
|
61
|
+
])
|
62
|
+
.flat()
|
63
|
+
|
64
|
+
export default [
|
65
|
+
{
|
66
|
+
input: 'javascript/index.js',
|
67
|
+
output,
|
68
|
+
plugins: [commonjs(), resolve(), json()],
|
69
|
+
watch: {
|
70
|
+
include: 'javascript/**'
|
71
|
+
}
|
72
|
+
}
|
73
|
+
]
|
@@ -0,0 +1,319 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
def with_mocked_renderer
|
4
|
+
renderer = Minitest::Mock.new
|
5
|
+
|
6
|
+
Futurism::Resolver::Controller::Renderer.stub(:for, renderer) do
|
7
|
+
yield(renderer)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def with_mocked_cable_ready
|
12
|
+
cable_ready_mock = MiniTest::Mock.new
|
13
|
+
cable_ready_channel = MiniTest::Mock.new
|
14
|
+
cable_ready_channel.expect(:outer_html, nil, [Hash])
|
15
|
+
cable_ready_channel.expect(:outer_html, nil, [Hash])
|
16
|
+
|
17
|
+
cable_ready_mock.expect(:[], cable_ready_channel, ["1"])
|
18
|
+
cable_ready_mock.expect(:[], cable_ready_channel, ["1"])
|
19
|
+
cable_ready_mock.expect(:broadcast, nil)
|
20
|
+
cable_ready_mock.expect(:broadcast, nil)
|
21
|
+
cable_ready_mock.expect(:broadcast, nil)
|
22
|
+
|
23
|
+
CableReady::Broadcaster.alias_method(:orig_cable_ready, :cable_ready)
|
24
|
+
|
25
|
+
CableReady::Broadcaster.define_method(:cable_ready) do
|
26
|
+
cable_ready_mock
|
27
|
+
end
|
28
|
+
|
29
|
+
yield cable_ready_mock
|
30
|
+
|
31
|
+
CableReady::Broadcaster.undef_method(:cable_ready)
|
32
|
+
|
33
|
+
CableReady::Broadcaster.alias_method(:cable_ready, :orig_cable_ready)
|
34
|
+
end
|
35
|
+
|
36
|
+
class Futurism::ChannelTest < ActionCable::Channel::TestCase
|
37
|
+
include Futurism::Helpers
|
38
|
+
include ActionView::Helpers
|
39
|
+
include ActionView::Context
|
40
|
+
include CableReady::Broadcaster
|
41
|
+
|
42
|
+
setup do
|
43
|
+
stub_connection(env: {"SCRIPT_NAME" => "/cable"}, identifiers: [:current_user], current_user: Struct.new(:id)[1])
|
44
|
+
end
|
45
|
+
|
46
|
+
test "subscribed" do
|
47
|
+
subscribe(channel: "Futurism::Channel")
|
48
|
+
|
49
|
+
assert subscription.confirmed?
|
50
|
+
|
51
|
+
assert_has_stream "Futurism::Channel:1"
|
52
|
+
end
|
53
|
+
|
54
|
+
test "broadcasts a rendered model after receiving signed params" do
|
55
|
+
with_mocked_renderer do |mock_renderer|
|
56
|
+
post = Post.create title: "Lorem"
|
57
|
+
fragment = Nokogiri::HTML.fragment(futurize(post) {})
|
58
|
+
signed_params_array = fragment.children.map { |element| element["data-signed-params"] }
|
59
|
+
sgids = fragment.children.map { |element| element["data-sgid"] }
|
60
|
+
subscribe
|
61
|
+
|
62
|
+
mock_renderer.expect :render, "<tag></tag>", [post]
|
63
|
+
|
64
|
+
perform :receive, {"signed_params" => signed_params_array, "sgids" => sgids}
|
65
|
+
|
66
|
+
assert_mock mock_renderer
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
test "broadcasts an ActiveRecord::Relation" do
|
71
|
+
with_mocked_renderer do |mock_renderer|
|
72
|
+
post1 = Post.create(title: "Lorem")
|
73
|
+
post2 = Post.create(title: "Ipsum")
|
74
|
+
|
75
|
+
fragment = Nokogiri::HTML.fragment(futurize(Post.all) {})
|
76
|
+
signed_params_array = fragment.children.map { |element| element["data-signed-params"] }
|
77
|
+
sgids = fragment.children.map { |element| element["data-sgid"] }
|
78
|
+
subscribe
|
79
|
+
|
80
|
+
mock_renderer
|
81
|
+
.expect(:render, "<tag></tag>", [post1])
|
82
|
+
.expect :render, "<tag></tag>", [post2]
|
83
|
+
|
84
|
+
perform :receive, {"signed_params" => signed_params_array, "sgids" => sgids}
|
85
|
+
|
86
|
+
assert_mock mock_renderer
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
test "broadcasts a rendered partial after receiving signed params" do
|
91
|
+
with_mocked_renderer do |mock_renderer|
|
92
|
+
post = Post.create title: "Lorem"
|
93
|
+
fragment = Nokogiri::HTML.fragment(futurize(partial: "posts/card", locals: {post: post}) {})
|
94
|
+
signed_params = fragment.children.first["data-signed-params"]
|
95
|
+
subscribe
|
96
|
+
|
97
|
+
mock_renderer
|
98
|
+
.expect(:render, "<tag></tag>", [partial: "posts/card", locals: {post: post}])
|
99
|
+
|
100
|
+
perform :receive, {"signed_params" => [signed_params]}
|
101
|
+
|
102
|
+
assert_mock mock_renderer
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
test "broadcasts a rendered partial after receiving the shorthand syntax" do
|
107
|
+
with_mocked_renderer do |mock_renderer|
|
108
|
+
post = Post.create title: "Lorem"
|
109
|
+
fragment = Nokogiri::HTML.fragment(futurize("posts/card", post: post) {})
|
110
|
+
signed_params = fragment.children.first["data-signed-params"]
|
111
|
+
subscribe
|
112
|
+
|
113
|
+
mock_renderer.expect(:render, "<tag></tag>", [partial: "posts/card", locals: {post: post}])
|
114
|
+
perform :receive, {"signed_params" => [signed_params]}
|
115
|
+
|
116
|
+
assert_mock mock_renderer
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
test "broadcasts a rendered partial after receiving the shorthand syntax with html options" do
|
121
|
+
with_mocked_renderer do |mock_renderer|
|
122
|
+
post = Post.create title: "Lorem"
|
123
|
+
fragment = Nokogiri::HTML.fragment(futurize("posts/card", post: post, html_options: {style: "color: green"}) {})
|
124
|
+
signed_params = fragment.children.first["data-signed-params"]
|
125
|
+
subscribe
|
126
|
+
|
127
|
+
mock_renderer.expect(:render, "<tag></tag>", [partial: "posts/card", locals: {post: post}])
|
128
|
+
|
129
|
+
perform :receive, {"signed_params" => [signed_params]}
|
130
|
+
|
131
|
+
assert_mock mock_renderer
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
test "broadcasts a collection" do
|
136
|
+
with_mocked_renderer do |mock_renderer|
|
137
|
+
Post.create title: "Lorem"
|
138
|
+
Post.create title: "Ipsum"
|
139
|
+
fragment = Nokogiri::HTML.fragment(futurize(partial: "posts/card", collection: Post.all, locals: {important_local: "needed to render"}) {})
|
140
|
+
subscribe
|
141
|
+
|
142
|
+
mock_renderer
|
143
|
+
.expect(:render, "<tag></tag>", [partial: "posts/card", locals: {post: Post.first, important_local: "needed to render", post_counter: 0}])
|
144
|
+
.expect(:render, "<tag></tag>", [partial: "posts/card", locals: {post: Post.last, important_local: "needed to render", post_counter: 1}])
|
145
|
+
|
146
|
+
signed_params = fragment.children.first["data-signed-params"]
|
147
|
+
perform :receive, {"signed_params" => [signed_params]}
|
148
|
+
|
149
|
+
signed_params = fragment.children.last["data-signed-params"]
|
150
|
+
perform :receive, {"signed_params" => [signed_params]}
|
151
|
+
|
152
|
+
assert_mock mock_renderer
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
test "broadcasts a collection (with multi-word class)" do
|
157
|
+
with_mocked_renderer do |mock_renderer|
|
158
|
+
ActionItem.create description: "Do this"
|
159
|
+
ActionItem.create description: "Do that"
|
160
|
+
fragment = Nokogiri::HTML.fragment(futurize(partial: "posts/card", collection: ActionItem.all, locals: {important_local: "needed to render"}) {})
|
161
|
+
|
162
|
+
subscribe
|
163
|
+
|
164
|
+
mock_renderer
|
165
|
+
.expect(:render, "<tag></tag>", [partial: "posts/card", locals: {action_item: ActionItem.first, important_local: "needed to render", action_item_counter: 0}])
|
166
|
+
.expect(:render, "<tag></tag>", [partial: "posts/card", locals: {action_item: ActionItem.last, important_local: "needed to render", action_item_counter: 1}])
|
167
|
+
|
168
|
+
signed_params = fragment.children.first["data-signed-params"]
|
169
|
+
perform :receive, {"signed_params" => [signed_params]}
|
170
|
+
|
171
|
+
signed_params = fragment.children.last["data-signed-params"]
|
172
|
+
perform :receive, {"signed_params" => [signed_params]}
|
173
|
+
|
174
|
+
assert_mock mock_renderer
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
test "broadcasts a collection with :as" do
|
179
|
+
with_mocked_renderer do |mock_renderer|
|
180
|
+
Post.create title: "Lorem"
|
181
|
+
Post.create title: "Ipsum"
|
182
|
+
fragment = Nokogiri::HTML.fragment(futurize(partial: "posts/card", collection: Post.all, as: :post_item) {})
|
183
|
+
subscribe
|
184
|
+
|
185
|
+
mock_renderer.expect(:render, "<tag></tag>", [partial: "posts/card", locals: {post_item: Post.first, post_item_counter: 0}])
|
186
|
+
signed_params = fragment.children.first["data-signed-params"]
|
187
|
+
perform :receive, {"signed_params" => [signed_params]}
|
188
|
+
|
189
|
+
mock_renderer.expect(:render, "<tag></tag>", [partial: "posts/card", locals: {post_item: Post.last, post_item_counter: 1}])
|
190
|
+
signed_params = fragment.children.last["data-signed-params"]
|
191
|
+
perform :receive, {"signed_params" => [signed_params]}
|
192
|
+
|
193
|
+
assert_mock mock_renderer
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
test "broadcasts elements of a collection immediately" do
|
198
|
+
with_mocked_cable_ready do |cable_ready_mock|
|
199
|
+
Post.create title: "Lorem"
|
200
|
+
Post.create title: "Ipsum"
|
201
|
+
fragment = Nokogiri::HTML.fragment(futurize(partial: "posts/card", collection: Post.all, broadcast_each: true, locals: {important_local: "needed to render"}) {})
|
202
|
+
subscribe
|
203
|
+
|
204
|
+
signed_params_1 = fragment.children.first["data-signed-params"]
|
205
|
+
broadcast_each_1 = fragment.children.first["data-broadcast-each"]
|
206
|
+
signed_params_2 = fragment.children.last["data-signed-params"]
|
207
|
+
broadcast_each_2 = fragment.children.last["data-broadcast-each"]
|
208
|
+
perform :receive, {"signed_params" => [signed_params_1, signed_params_2], "broadcast_each" => [broadcast_each_1, broadcast_each_2]}
|
209
|
+
|
210
|
+
assert_mock cable_ready_mock
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
test "broadcasts an inline rendered text" do
|
215
|
+
fragment = Nokogiri::HTML.fragment(futurize(inline: "<%= 1 + 2 %>") {})
|
216
|
+
signed_params = fragment.children.first["data-signed-params"]
|
217
|
+
subscribe(channel: "Futurism::Channel")
|
218
|
+
|
219
|
+
assert_cable_ready_operation_on("Futurism::Channel:1", operation: "outerHtml", selector: "[data-signed-params='#{signed_params}']", html: "3") do
|
220
|
+
perform :receive, {"signed_params" => [signed_params]}
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
test "broadcasts a correctly formed path" do
|
225
|
+
post = Post.create title: "Lorem"
|
226
|
+
fragment = Nokogiri::HTML.fragment(futurize(partial: "posts/card", locals: {post: post}) {})
|
227
|
+
signed_params = fragment.children.first["data-signed-params"]
|
228
|
+
subscribe(channel: "Futurism::Channel")
|
229
|
+
|
230
|
+
assert_cable_ready_operation_on("Futurism::Channel:1", operation: "outerHtml",
|
231
|
+
selector: "[data-signed-params='#{signed_params}']",
|
232
|
+
html: "<div class=\"card\">\n Lorem\n <a href=\"/posts/1/edit\">Edit</a>\n</div>\n") do
|
233
|
+
perform :receive, {"signed_params" => [signed_params]}
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
test "passes parsed params to controller render" do
|
238
|
+
with_mocked_renderer do |mock_renderer|
|
239
|
+
post = Post.create title: "Lorem"
|
240
|
+
fragment = Nokogiri::HTML.fragment(futurize(post) {})
|
241
|
+
signed_params_array = fragment.children.map { |element| element["data-signed-params"] }
|
242
|
+
sgids = fragment.children.map { |element| element["data-sgid"] }
|
243
|
+
urls = Array.new(fragment.children.length, "http://www.example.org/route?param1=true¶m2=1234")
|
244
|
+
subscribe
|
245
|
+
|
246
|
+
mock_renderer.expect(:render, "<tag></tag>", [post])
|
247
|
+
|
248
|
+
perform :receive, {"signed_params" => signed_params_array, "sgids" => sgids, "urls" => urls}
|
249
|
+
|
250
|
+
assert_mock mock_renderer
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
test "renders error message when rendering invalid partial error" do
|
255
|
+
fragment = Nokogiri::HTML.fragment(futurize(partial: "INVALID/PARTIAL") {})
|
256
|
+
signed_params = fragment.children.first["data-signed-params"]
|
257
|
+
subscribe(channel: "Futurism::Channel")
|
258
|
+
|
259
|
+
assert_cable_ready_operation_on("Futurism::Channel:1", operation: "outerHtml",
|
260
|
+
selector: "[data-signed-params='#{signed_params}']",
|
261
|
+
html: /Missing partial INVALID\/_PARTIAL/) do
|
262
|
+
perform :receive, {"signed_params" => [signed_params]}
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
test "renders error message when wrong variable name" do
|
267
|
+
Post.create title: "Lorem"
|
268
|
+
fragment = Nokogiri::HTML.fragment(futurize(partial: "posts/card", collection: Post.all, as: :wrong_variable_name) {})
|
269
|
+
signed_params = fragment.children.first["data-signed-params"]
|
270
|
+
subscribe(channel: "Futurism::Channel")
|
271
|
+
|
272
|
+
assert_cable_ready_operation_on("Futurism::Channel:1", operation: "outerHtml",
|
273
|
+
selector: "[data-signed-params='#{signed_params}']",
|
274
|
+
html: /undefined local variable or method/) do
|
275
|
+
perform :receive, {"signed_params" => [signed_params]}
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def assert_cable_ready_operation_on(stream, operation:, selector:, html:, &block)
|
280
|
+
data = {
|
281
|
+
"cableReady" => true,
|
282
|
+
"operations" => [{
|
283
|
+
"selector" => selector,
|
284
|
+
"html" => html,
|
285
|
+
"operation" => operation
|
286
|
+
}]
|
287
|
+
}
|
288
|
+
|
289
|
+
old_messages = broadcasts(stream)
|
290
|
+
clear_messages(stream)
|
291
|
+
|
292
|
+
assert_nothing_raised(&block)
|
293
|
+
|
294
|
+
new_messages = broadcasts(stream)
|
295
|
+
clear_messages(stream)
|
296
|
+
|
297
|
+
# Restore all sent messages
|
298
|
+
(old_messages + new_messages).each { |m| pubsub_adapter.broadcast(stream, m) }
|
299
|
+
|
300
|
+
message = new_messages.find { |msg| cable_ready_match?(ActiveSupport::JSON.decode(msg), data) }
|
301
|
+
|
302
|
+
assert message, "No messages sent with #{data} to #{stream}"
|
303
|
+
end
|
304
|
+
|
305
|
+
def cable_ready_match?(message, matcher)
|
306
|
+
return true if message == matcher
|
307
|
+
|
308
|
+
first_matching_operation = ["operations", 0]
|
309
|
+
|
310
|
+
matcher_operation = matcher.dig(*first_matching_operation)
|
311
|
+
message_operation = message.dig(*first_matching_operation)
|
312
|
+
|
313
|
+
message.dig("cableReady") == true &&
|
314
|
+
(matcher_operation.dig("selector") === message_operation.dig("selector") ||
|
315
|
+
matcher_operation.dig("selector").match(message_operation.dig("selector"))) &&
|
316
|
+
(matcher_operation.dig("html") === message_operation.dig("html") ||
|
317
|
+
matcher_operation.dig("html").match(message_operation.dig("html")))
|
318
|
+
end
|
319
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class PostsController < ApplicationController
|
2
|
+
before_action :set_post, only: [:show, :edit, :update, :destroy]
|
3
|
+
|
4
|
+
# GET /posts
|
5
|
+
def index
|
6
|
+
@posts = Post.all
|
7
|
+
end
|
8
|
+
|
9
|
+
# GET /posts/1
|
10
|
+
def show
|
11
|
+
end
|
12
|
+
|
13
|
+
# GET /posts/new
|
14
|
+
def new
|
15
|
+
@post = Post.new
|
16
|
+
end
|
17
|
+
|
18
|
+
# GET /posts/1/edit
|
19
|
+
def edit
|
20
|
+
end
|
21
|
+
|
22
|
+
# POST /posts
|
23
|
+
def create
|
24
|
+
@post = Post.new(post_params)
|
25
|
+
|
26
|
+
if @post.save
|
27
|
+
redirect_to @post, notice: "Post was successfully created."
|
28
|
+
else
|
29
|
+
render :new
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# PATCH/PUT /posts/1
|
34
|
+
def update
|
35
|
+
if @post.update(post_params)
|
36
|
+
redirect_to @post, notice: "Post was successfully updated."
|
37
|
+
else
|
38
|
+
render :edit
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# DELETE /posts/1
|
43
|
+
def destroy
|
44
|
+
@post.destroy
|
45
|
+
redirect_to posts_url, notice: "Post was successfully destroyed."
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Use callbacks to share common setup or constraints between actions.
|
51
|
+
def set_post
|
52
|
+
@post = Post.find(params[:id])
|
53
|
+
end
|
54
|
+
|
55
|
+
# Only allow a trusted parameter "white list" through.
|
56
|
+
def post_params
|
57
|
+
params.require(:post).permit(:title)
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
class ApplicationJob < ActiveJob::Base
|
2
|
+
# Automatically retry jobs that encountered a deadlock
|
3
|
+
# retry_on ActiveRecord::Deadlocked
|
4
|
+
|
5
|
+
# Most jobs are safe to ignore if the underlying records are no longer available
|
6
|
+
# discard_on ActiveJob::DeserializationError
|
7
|
+
end
|