futurism 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +86 -84
  3. data/README.md +26 -0
  4. data/app/assets/javascripts/futurism.min.js.map +1 -1
  5. data/app/assets/javascripts/futurism.umd.min.js.map +1 -1
  6. data/futurism.gemspec +0 -3
  7. data/futurism.gemspec~ +1 -1
  8. data/lib/futurism/engine.rb +4 -0
  9. data/lib/futurism/helpers.rb +5 -5
  10. data/lib/futurism/options_transformer.rb +1 -1
  11. data/lib/futurism/resolver/controller/instrumentation.rb +33 -0
  12. data/lib/futurism/resolver/controller/renderer.rb +5 -1
  13. data/lib/futurism/resolver/resources.rb +3 -3
  14. data/lib/futurism/version.rb +1 -1
  15. data/lib/futurism.rb +11 -3
  16. data/package.json +1 -1
  17. data/package.json~ +2 -2
  18. data/yarn.lock +61 -21
  19. metadata +5 -94
  20. data/test/cable/channel_test.rb +0 -319
  21. data/test/dummy/app/channels/application_cable/channel.rb +0 -4
  22. data/test/dummy/app/channels/application_cable/connection.rb +0 -4
  23. data/test/dummy/app/controllers/application_controller.rb +0 -2
  24. data/test/dummy/app/controllers/home_controller.rb +0 -6
  25. data/test/dummy/app/controllers/posts_controller.rb +0 -59
  26. data/test/dummy/app/helpers/application_helper.rb +0 -2
  27. data/test/dummy/app/helpers/posts_helper.rb +0 -2
  28. data/test/dummy/app/jobs/application_job.rb +0 -7
  29. data/test/dummy/app/models/action_item.rb +0 -2
  30. data/test/dummy/app/models/application_record.rb +0 -3
  31. data/test/dummy/app/models/post.rb +0 -2
  32. data/test/dummy/config/application.rb +0 -29
  33. data/test/dummy/config/boot.rb +0 -5
  34. data/test/dummy/config/environment.rb +0 -5
  35. data/test/dummy/config/environments/development.rb +0 -40
  36. data/test/dummy/config/environments/production.rb +0 -94
  37. data/test/dummy/config/environments/test.rb +0 -39
  38. data/test/dummy/config/initializers/application_controller_renderer.rb +0 -8
  39. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  40. data/test/dummy/config/initializers/content_security_policy.rb +0 -28
  41. data/test/dummy/config/initializers/cookies_serializer.rb +0 -5
  42. data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  43. data/test/dummy/config/initializers/inflections.rb +0 -16
  44. data/test/dummy/config/initializers/mime_types.rb +0 -4
  45. data/test/dummy/config/initializers/wrap_parameters.rb +0 -9
  46. data/test/dummy/config/puma.rb +0 -38
  47. data/test/dummy/config/routes.rb +0 -12
  48. data/test/dummy/config/spring.rb +0 -6
  49. data/test/dummy/db/migrate/20200711122838_create_posts.rb +0 -9
  50. data/test/dummy/db/migrate/2021042923813_create_action_items.rb +0 -9
  51. data/test/dummy/db/schema.rb +0 -27
  52. data/test/futurism_test.rb +0 -28
  53. data/test/helper/helper_test.rb +0 -206
  54. data/test/integration/navigation_test.rb +0 -7
  55. data/test/resolver/controller/renderer_test.rb +0 -120
  56. data/test/resolver/controller_test.rb +0 -26
  57. data/test/test_helper.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ccfe52300a950587d804aaa4979ebe1ba9b48f3d945d1c1ab2580f8b4e6adc7
4
- data.tar.gz: 283e234ddb65fadfba97b47305ac3951fd9df5f173ff714564b379170becb493
3
+ metadata.gz: 4476a6d522e4145058c729ace6e4cf7adf19a11778e0c0537912bd512e7623df
4
+ data.tar.gz: 12446b79d1739073c4c21c3d95e8eafdf4b4e5ec110a04aa7c2e9b007b97310a
5
5
  SHA512:
6
- metadata.gz: f95d80439c90984025223ec802e520434079a00e84e98d7c14f79495d6b7b19d8dba072a0e26ba1e1f34c751821bce398bcbe0fed005aea4a95a8fe12d067ada
7
- data.tar.gz: 9cd61de7158bd555b31ee302d3fec6b39590b4a434ca0bd4432e14964315c20c914802822febdc693339f1cbd1c4b36d0d2a7a818d6cac9ce2b906688d1a1adb
6
+ metadata.gz: cabf9ea5de23694fbbc1cdcb06f857bc6e5d2bd066966def655e4f1c3cc94c64787ea268cd4d2a7026b0ccaf4a5b0fe43d0b695dd6739b7696671b505b20b5a2
7
+ data.tar.gz: 35734fb8141a9abb9c76dad89ae42b3f1858a1d0e165a6eaef1782d382d72e6f31d8e7be3d88887c8c323660ee742cd689ad129cacdcfe729330a451c6f7c1e5
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- futurism (1.2.0)
4
+ futurism (1.3.0)
5
5
  cable_ready (>= 5.0)
6
6
  rack (~> 2.0)
7
7
  rails (>= 5.2)
@@ -9,67 +9,67 @@ PATH
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- actioncable (7.0.4.3)
13
- actionpack (= 7.0.4.3)
14
- activesupport (= 7.0.4.3)
12
+ actioncable (7.0.8)
13
+ actionpack (= 7.0.8)
14
+ activesupport (= 7.0.8)
15
15
  nio4r (~> 2.0)
16
16
  websocket-driver (>= 0.6.1)
17
- actionmailbox (7.0.4.3)
18
- actionpack (= 7.0.4.3)
19
- activejob (= 7.0.4.3)
20
- activerecord (= 7.0.4.3)
21
- activestorage (= 7.0.4.3)
22
- activesupport (= 7.0.4.3)
17
+ actionmailbox (7.0.8)
18
+ actionpack (= 7.0.8)
19
+ activejob (= 7.0.8)
20
+ activerecord (= 7.0.8)
21
+ activestorage (= 7.0.8)
22
+ activesupport (= 7.0.8)
23
23
  mail (>= 2.7.1)
24
24
  net-imap
25
25
  net-pop
26
26
  net-smtp
27
- actionmailer (7.0.4.3)
28
- actionpack (= 7.0.4.3)
29
- actionview (= 7.0.4.3)
30
- activejob (= 7.0.4.3)
31
- activesupport (= 7.0.4.3)
27
+ actionmailer (7.0.8)
28
+ actionpack (= 7.0.8)
29
+ actionview (= 7.0.8)
30
+ activejob (= 7.0.8)
31
+ activesupport (= 7.0.8)
32
32
  mail (~> 2.5, >= 2.5.4)
33
33
  net-imap
34
34
  net-pop
35
35
  net-smtp
36
36
  rails-dom-testing (~> 2.0)
37
- actionpack (7.0.4.3)
38
- actionview (= 7.0.4.3)
39
- activesupport (= 7.0.4.3)
40
- rack (~> 2.0, >= 2.2.0)
37
+ actionpack (7.0.8)
38
+ actionview (= 7.0.8)
39
+ activesupport (= 7.0.8)
40
+ rack (~> 2.0, >= 2.2.4)
41
41
  rack-test (>= 0.6.3)
42
42
  rails-dom-testing (~> 2.0)
43
43
  rails-html-sanitizer (~> 1.0, >= 1.2.0)
44
- actiontext (7.0.4.3)
45
- actionpack (= 7.0.4.3)
46
- activerecord (= 7.0.4.3)
47
- activestorage (= 7.0.4.3)
48
- activesupport (= 7.0.4.3)
44
+ actiontext (7.0.8)
45
+ actionpack (= 7.0.8)
46
+ activerecord (= 7.0.8)
47
+ activestorage (= 7.0.8)
48
+ activesupport (= 7.0.8)
49
49
  globalid (>= 0.6.0)
50
50
  nokogiri (>= 1.8.5)
51
- actionview (7.0.4.3)
52
- activesupport (= 7.0.4.3)
51
+ actionview (7.0.8)
52
+ activesupport (= 7.0.8)
53
53
  builder (~> 3.1)
54
54
  erubi (~> 1.4)
55
55
  rails-dom-testing (~> 2.0)
56
56
  rails-html-sanitizer (~> 1.1, >= 1.2.0)
57
- activejob (7.0.4.3)
58
- activesupport (= 7.0.4.3)
57
+ activejob (7.0.8)
58
+ activesupport (= 7.0.8)
59
59
  globalid (>= 0.3.6)
60
- activemodel (7.0.4.3)
61
- activesupport (= 7.0.4.3)
62
- activerecord (7.0.4.3)
63
- activemodel (= 7.0.4.3)
64
- activesupport (= 7.0.4.3)
65
- activestorage (7.0.4.3)
66
- actionpack (= 7.0.4.3)
67
- activejob (= 7.0.4.3)
68
- activerecord (= 7.0.4.3)
69
- activesupport (= 7.0.4.3)
60
+ activemodel (7.0.8)
61
+ activesupport (= 7.0.8)
62
+ activerecord (7.0.8)
63
+ activemodel (= 7.0.8)
64
+ activesupport (= 7.0.8)
65
+ activestorage (7.0.8)
66
+ actionpack (= 7.0.8)
67
+ activejob (= 7.0.8)
68
+ activerecord (= 7.0.8)
69
+ activesupport (= 7.0.8)
70
70
  marcel (~> 1.0)
71
71
  mini_mime (>= 1.1.0)
72
- activesupport (7.0.4.3)
72
+ activesupport (7.0.8)
73
73
  concurrent-ruby (~> 1.0, >= 1.0.2)
74
74
  i18n (>= 1.6, < 2)
75
75
  minitest (>= 5.1)
@@ -80,79 +80,79 @@ GEM
80
80
  thor (>= 0.14.0)
81
81
  ast (2.4.2)
82
82
  builder (3.2.4)
83
- cable_ready (5.0.0)
83
+ cable_ready (5.0.3)
84
84
  actionpack (>= 5.2)
85
85
  actionview (>= 5.2)
86
86
  activesupport (>= 5.2)
87
87
  railties (>= 5.2)
88
88
  thread-local (>= 1.1.0)
89
- coderay (1.1.3)
90
89
  concurrent-ruby (1.2.2)
91
90
  crass (1.0.6)
92
- date (3.3.3)
91
+ date (3.3.4)
93
92
  erubi (1.12.0)
94
- globalid (1.1.0)
95
- activesupport (>= 5.0)
96
- i18n (1.12.0)
93
+ globalid (1.2.1)
94
+ activesupport (>= 6.1)
95
+ i18n (1.14.1)
97
96
  concurrent-ruby (~> 1.0)
98
97
  json (2.6.3)
99
98
  language_server-protocol (3.17.0.3)
100
- loofah (2.20.0)
99
+ loofah (2.22.0)
101
100
  crass (~> 1.0.2)
102
- nokogiri (>= 1.5.9)
101
+ nokogiri (>= 1.12.0)
103
102
  mail (2.8.1)
104
103
  mini_mime (>= 0.1.1)
105
104
  net-imap
106
105
  net-pop
107
106
  net-smtp
108
107
  marcel (1.0.2)
109
- method_source (0.9.2)
110
- mini_mime (1.1.2)
111
- minitest (5.18.0)
112
- net-imap (0.3.4)
108
+ method_source (1.0.0)
109
+ mini_mime (1.1.5)
110
+ minitest (5.20.0)
111
+ net-imap (0.4.7)
113
112
  date
114
113
  net-protocol
115
114
  net-pop (0.1.2)
116
115
  net-protocol
117
- net-protocol (0.2.1)
116
+ net-protocol (0.2.2)
118
117
  timeout
119
- net-smtp (0.3.3)
118
+ net-smtp (0.4.0)
120
119
  net-protocol
121
- nio4r (2.5.9)
122
- nokogiri (1.14.2-arm64-darwin)
120
+ nio4r (2.7.0)
121
+ nokogiri (1.15.4-arm64-darwin)
122
+ racc (~> 1.4)
123
+ nokogiri (1.15.4-x86_64-linux)
123
124
  racc (~> 1.4)
124
125
  parallel (1.22.1)
125
126
  parser (3.2.1.1)
126
127
  ast (~> 2.4.1)
127
- pry (0.12.2)
128
- coderay (~> 1.1.0)
129
- method_source (~> 0.9.0)
130
- racc (1.6.2)
131
- rack (2.2.6.4)
128
+ racc (1.7.1)
129
+ rack (2.2.8)
132
130
  rack-test (2.1.0)
133
131
  rack (>= 1.3)
134
- rails (7.0.4.3)
135
- actioncable (= 7.0.4.3)
136
- actionmailbox (= 7.0.4.3)
137
- actionmailer (= 7.0.4.3)
138
- actionpack (= 7.0.4.3)
139
- actiontext (= 7.0.4.3)
140
- actionview (= 7.0.4.3)
141
- activejob (= 7.0.4.3)
142
- activemodel (= 7.0.4.3)
143
- activerecord (= 7.0.4.3)
144
- activestorage (= 7.0.4.3)
145
- activesupport (= 7.0.4.3)
132
+ rails (7.0.8)
133
+ actioncable (= 7.0.8)
134
+ actionmailbox (= 7.0.8)
135
+ actionmailer (= 7.0.8)
136
+ actionpack (= 7.0.8)
137
+ actiontext (= 7.0.8)
138
+ actionview (= 7.0.8)
139
+ activejob (= 7.0.8)
140
+ activemodel (= 7.0.8)
141
+ activerecord (= 7.0.8)
142
+ activestorage (= 7.0.8)
143
+ activesupport (= 7.0.8)
146
144
  bundler (>= 1.15.0)
147
- railties (= 7.0.4.3)
148
- rails-dom-testing (2.0.3)
149
- activesupport (>= 4.2.0)
145
+ railties (= 7.0.8)
146
+ rails-dom-testing (2.2.0)
147
+ activesupport (>= 5.0.0)
148
+ minitest
150
149
  nokogiri (>= 1.6)
151
- rails-html-sanitizer (1.5.0)
152
- loofah (~> 2.19, >= 2.19.1)
153
- railties (7.0.4.3)
154
- actionpack (= 7.0.4.3)
155
- activesupport (= 7.0.4.3)
150
+ rails-html-sanitizer (1.6.0)
151
+ loofah (~> 2.21)
152
+ nokogiri (~> 1.14)
153
+ railties (7.0.8)
154
+ actionpack (= 7.0.8)
155
+ activesupport (= 7.0.8)
156
156
  method_source
157
157
  rake (>= 12.2)
158
158
  thor (~> 1.0)
@@ -178,6 +178,7 @@ GEM
178
178
  rubocop-ast (>= 0.4.0)
179
179
  ruby-progressbar (1.13.0)
180
180
  sqlite3 (1.6.1-arm64-darwin)
181
+ sqlite3 (1.6.1-x86_64-linux)
181
182
  standard (1.25.3)
182
183
  language_server-protocol (~> 3.17.0.2)
183
184
  rubocop (~> 1.48.1)
@@ -186,24 +187,25 @@ GEM
186
187
  standard
187
188
  thor (1.2.1)
188
189
  thread-local (1.1.0)
189
- timeout (0.3.2)
190
+ timeout (0.4.1)
190
191
  tzinfo (2.0.6)
191
192
  concurrent-ruby (~> 1.0)
192
193
  unicode-display_width (2.4.2)
193
- websocket-driver (0.7.5)
194
+ websocket-driver (0.7.6)
194
195
  websocket-extensions (>= 0.1.0)
195
196
  websocket-extensions (0.1.5)
196
- zeitwerk (2.6.7)
197
+ zeitwerk (2.6.12)
197
198
 
198
199
  PLATFORMS
199
200
  arm64-darwin-21
201
+ arm64-darwin-22
202
+ x86_64-linux
200
203
 
201
204
  DEPENDENCIES
202
205
  appraisal
203
206
  bundler (~> 2.0)
204
207
  futurism!
205
208
  nokogiri
206
- pry (~> 0.12.2)
207
209
  rake (~> 13.0)
208
210
  sqlite3
209
211
  standardrb
data/README.md CHANGED
@@ -25,6 +25,7 @@ Lazy-load Rails partials via CableReady
25
25
  - [Broadcast Partials Individually](#broadcast-partials-individually)
26
26
  - [Contextual Placeholder Arguments](#contextual-placeholder-arguments)
27
27
  - [Events](#events)
28
+ - [Instrumentation](#instrumentation)
28
29
  - [Installation](#installation)
29
30
  - [Manual Installation](#manual-installation)
30
31
  - [Authentication](#authentication)
@@ -228,6 +229,31 @@ For individual models or arbitrary collections, you can pass `record` and `index
228
229
 
229
230
  Once your futurize element has been rendered, the `futurism:appeared` custom event will be called.
230
231
 
232
+ ## Instrumentation
233
+
234
+ Futurism includes support for instrumenting rendering events.
235
+
236
+ To enable ActiveSupport notifications, use the `instrumentation` option:
237
+
238
+ ```ruby
239
+ Futurism.instrumentation = true
240
+ ```
241
+
242
+ Then subscribe to the `render.futurism` event:
243
+
244
+ ```ruby
245
+ ActiveSupport::Notifications.subscribe("render.futurism") do |*args|
246
+ event = ActiveSupport::Notifications::Event.new(*args)
247
+ event.name # => "render.futurism"
248
+ event.payload[:channel] # => "Futurism::Channel" # ActionCable channel to broadcast
249
+ event.payload[:controller] # => "posts" # The controller that invokes `futurize` call
250
+ event.payload[:action] # => "show" # The action that invokes `futurize` call
251
+ event.payload[:partial] # => "posts/card" # The partial that was rendered
252
+ end
253
+ ```
254
+
255
+ This is useful for performance monitoring, specifically for tracking the source of `futurize` calls.
256
+
231
257
  ## Installation
232
258
  Add this line to your application's Gemfile:
233
259
 
@@ -1 +1 @@
1
- {"version":3,"file":"futurism.min.js","sources":["../../../javascript/futurism_channel.js","../../../javascript/elements/futurism_utils.js","../../../javascript/elements/futurism_element.js","../../../javascript/elements/futurism_table_row.js","../../../javascript/elements/futurism_li.js","../../../javascript/elements/index.js","../../../javascript/utils/crypto.js"],"sourcesContent":["/* global CustomEvent, setTimeout */\n\nimport CableReady from 'cable_ready'\n\nconst debounceEvents = (callback, delay = 20) => {\n let timeoutId\n let events = []\n return (...args) => {\n clearTimeout(timeoutId)\n events = [...events, ...args]\n timeoutId = setTimeout(() => {\n timeoutId = null\n callback(events)\n events = []\n }, delay)\n }\n}\n\nexport const createSubscription = consumer => {\n consumer.subscriptions.create('Futurism::Channel', {\n connected () {\n window.Futurism = this\n document.addEventListener(\n 'futurism:appear',\n debounceEvents(events => {\n this.send({\n signed_params: events.map(e => e.target.dataset.signedParams),\n sgids: events.map(e => e.target.dataset.sgid),\n signed_controllers: events.map(\n e => e.target.dataset.signedController\n ),\n urls: events.map(_ => window.location.href),\n broadcast_each: events.map(e => e.target.dataset.broadcastEach)\n })\n })\n )\n },\n\n received (data) {\n if (data.cableReady) {\n CableReady.perform(data.operations, {\n emitMissingElementWarnings: false\n })\n\n document.dispatchEvent(\n new CustomEvent('futurism:appeared', {\n bubbles: true,\n cancelable: true\n })\n )\n }\n }\n })\n}\n","/* global IntersectionObserver, CustomEvent, setTimeout */\n\nconst dispatchAppearEvent = (entry, observer = null) => {\n if (!window.Futurism) {\n return () => {\n setTimeout(() => dispatchAppearEvent(entry, observer)(), 1)\n }\n }\n\n const target = entry.target ? entry.target : entry\n\n const evt = new CustomEvent('futurism:appear', {\n bubbles: true,\n detail: {\n target,\n observer\n }\n })\n\n return () => {\n target.dispatchEvent(evt)\n }\n}\n\n// from https://advancedweb.hu/how-to-implement-an-exponential-backoff-retry-strategy-in-javascript/#rejection-based-retrying\nconst wait = ms => new Promise(resolve => setTimeout(resolve, ms))\n\nconst callWithRetry = async (fn, depth = 0) => {\n try {\n return await fn()\n } catch (e) {\n if (depth > 10) {\n throw e\n }\n await wait(1.15 ** depth * 2000)\n\n return callWithRetry(fn, depth + 1)\n }\n}\n\nconst observerCallback = (entries, observer) => {\n entries.forEach(async entry => {\n if (!entry.isIntersecting) return\n\n await callWithRetry(dispatchAppearEvent(entry, observer))\n })\n}\n\nexport const extendElementWithIntersectionObserver = element => {\n Object.assign(element, {\n observer: new IntersectionObserver(observerCallback.bind(element), {})\n })\n\n if (!element.hasAttribute('keep')) {\n element.observer.observe(element)\n }\n}\n\nexport const extendElementWithEagerLoading = element => {\n if (element.dataset.eager === 'true') {\n if (element.observer) element.observer.disconnect()\n callWithRetry(dispatchAppearEvent(element))\n }\n}\n","/* global HTMLElement */\n\nimport {\n extendElementWithIntersectionObserver,\n extendElementWithEagerLoading\n} from './futurism_utils'\n\nexport default class FuturismElement extends HTMLElement {\n connectedCallback () {\n extendElementWithIntersectionObserver(this)\n extendElementWithEagerLoading(this)\n }\n}\n","/* global HTMLTableRowElement */\n\nimport {\n extendElementWithIntersectionObserver,\n extendElementWithEagerLoading\n} from './futurism_utils'\n\nexport default class FuturismTableRow extends HTMLTableRowElement {\n connectedCallback () {\n extendElementWithIntersectionObserver(this)\n extendElementWithEagerLoading(this)\n }\n}\n","/* global HTMLLIElement */\r\n\r\nimport {\r\n extendElementWithIntersectionObserver,\r\n extendElementWithEagerLoading\r\n} from './futurism_utils'\r\n\r\nexport default class FuturismLI extends HTMLLIElement {\r\n connectedCallback () {\r\n extendElementWithIntersectionObserver(this)\r\n extendElementWithEagerLoading(this)\r\n }\r\n}\r\n","/* global customElements, sessionStorage */\n\nimport FuturismElement from './futurism_element'\nimport FuturismTableRow from './futurism_table_row'\nimport FuturismLI from './futurism_li'\n\nimport { sha256 } from '../utils/crypto'\n\nconst polyfillCustomElements = () => {\n const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)\n\n if (customElements) {\n if (isSafari) {\n document.write(\n '<script src=\"//unpkg.com/@ungap/custom-elements-builtin\"><\\x2fscript>'\n )\n } else {\n try {\n customElements.define(\n 'built-in',\n document.createElement('tr').constructor,\n { extends: 'tr' }\n )\n } catch (_) {\n document.write(\n '<script src=\"//unpkg.com/@ungap/custom-elements-builtin\"><\\x2fscript>'\n )\n }\n }\n } else {\n document.write(\n '<script src=\"//unpkg.com/document-register-element\"><\\x2fscript>'\n )\n }\n}\n\nconst defineElements = e => {\n if (!customElements.get('futurism-element')) {\n customElements.define('futurism-element', FuturismElement)\n customElements.define('futurism-table-row', FuturismTableRow, {\n extends: 'tr'\n })\n customElements.define('futurism-li', FuturismLI, { extends: 'li' })\n }\n}\n\nconst cachePlaceholders = e => {\n sha256(e.detail.element.outerHTML).then(hashedContent => {\n e.detail.element.setAttribute('keep', '')\n sessionStorage.setItem(\n `futurism-${hashedContent}`,\n e.detail.element.outerHTML\n )\n e.target.dataset.futurismHash = hashedContent\n })\n}\n\nconst restorePlaceholders = e => {\n const inNamespace = ([key, _payload]) => key.startsWith('futurism-')\n Object.entries(sessionStorage)\n .filter(inNamespace)\n .forEach(([key, payload]) => {\n const match = /^futurism-(.*)/.exec(key)\n const targetElement = document.querySelector(\n `[data-futurism-hash=\"${match[1]}\"]`\n )\n\n if (targetElement) {\n targetElement.outerHTML = payload\n sessionStorage.removeItem(key)\n }\n })\n}\n\nexport const initializeElements = () => {\n polyfillCustomElements()\n document.addEventListener('DOMContentLoaded', defineElements)\n document.addEventListener('turbo:load', defineElements)\n document.addEventListener('turbo:before-cache', restorePlaceholders)\n document.addEventListener('turbolinks:load', defineElements)\n document.addEventListener('turbolinks:before-cache', restorePlaceholders)\n document.addEventListener('cable-ready:after-outer-html', cachePlaceholders)\n}\n","/* global crypto */\n\nexport async function sha256 (message) {\n // encode as UTF-8\n const msgBuffer = new TextEncoder('utf-8').encode(message)\n\n // hash the message\n const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer)\n\n // convert ArrayBuffer to Array\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n\n // convert bytes to hex string\n const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('')\n\n return hashHex\n}\n"],"names":["createSubscription","consumer","subscriptions","create","connected","window","Futurism","this","document","addEventListener","callback","delay","timeoutId","events","args","clearTimeout","setTimeout","debounceEvents","send","signed_params","map","e","target","dataset","signedParams","sgids","sgid","signed_controllers","signedController","urls","_","location","href","broadcast_each","broadcastEach","received","data","cableReady","CableReady","perform","operations","emitMissingElementWarnings","dispatchEvent","CustomEvent","bubbles","cancelable","dispatchAppearEvent","entry","observer","evt","detail","callWithRetry","async","fn","depth","ms","Promise","resolve","observerCallback","entries","forEach","isIntersecting","extendElementWithIntersectionObserver","element","Object","assign","IntersectionObserver","bind","hasAttribute","observe","extendElementWithEagerLoading","eager","disconnect","FuturismElement","HTMLElement","connectedCallback","FuturismTableRow","HTMLTableRowElement","FuturismLI","HTMLLIElement","defineElements","customElements","get","define","extends","cachePlaceholders","message","msgBuffer","TextEncoder","encode","hashBuffer","crypto","subtle","digest","Array","from","Uint8Array","b","toString","slice","join","sha256","outerHTML","then","hashedContent","setAttribute","sessionStorage","setItem","futurismHash","restorePlaceholders","filter","key","_payload","startsWith","payload","match","exec","targetElement","querySelector","removeItem","initializeElements","isSafari","test","navigator","userAgent","write","createElement","constructor","polyfillCustomElements"],"mappings":"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"}
1
+ {"version":3,"file":"futurism.min.js","sources":["../../../javascript/futurism_channel.js","../../../javascript/elements/futurism_utils.js","../../../javascript/elements/futurism_element.js","../../../javascript/elements/futurism_table_row.js","../../../javascript/elements/futurism_li.js","../../../javascript/elements/index.js","../../../javascript/utils/crypto.js"],"sourcesContent":["/* global CustomEvent, setTimeout */\n\nimport CableReady from 'cable_ready'\n\nconst debounceEvents = (callback, delay = 20) => {\n let timeoutId\n let events = []\n return (...args) => {\n clearTimeout(timeoutId)\n events = [...events, ...args]\n timeoutId = setTimeout(() => {\n timeoutId = null\n callback(events)\n events = []\n }, delay)\n }\n}\n\nexport const createSubscription = consumer => {\n consumer.subscriptions.create('Futurism::Channel', {\n connected () {\n window.Futurism = this\n document.addEventListener(\n 'futurism:appear',\n debounceEvents(events => {\n this.send({\n signed_params: events.map(e => e.target.dataset.signedParams),\n sgids: events.map(e => e.target.dataset.sgid),\n signed_controllers: events.map(\n e => e.target.dataset.signedController\n ),\n urls: events.map(_ => window.location.href),\n broadcast_each: events.map(e => e.target.dataset.broadcastEach)\n })\n })\n )\n },\n\n received (data) {\n if (data.cableReady) {\n CableReady.perform(data.operations, {\n emitMissingElementWarnings: false\n })\n\n document.dispatchEvent(\n new CustomEvent('futurism:appeared', {\n bubbles: true,\n cancelable: true\n })\n )\n }\n }\n })\n}\n","/* global IntersectionObserver, CustomEvent, setTimeout */\n\nconst dispatchAppearEvent = (entry, observer = null) => {\n if (!window.Futurism) {\n return () => {\n setTimeout(() => dispatchAppearEvent(entry, observer)(), 1)\n }\n }\n\n const target = entry.target ? entry.target : entry\n\n const evt = new CustomEvent('futurism:appear', {\n bubbles: true,\n detail: {\n target,\n observer\n }\n })\n\n return () => {\n target.dispatchEvent(evt)\n }\n}\n\n// from https://advancedweb.hu/how-to-implement-an-exponential-backoff-retry-strategy-in-javascript/#rejection-based-retrying\nconst wait = ms => new Promise(resolve => setTimeout(resolve, ms))\n\nconst callWithRetry = async (fn, depth = 0) => {\n try {\n return await fn()\n } catch (e) {\n if (depth > 10) {\n throw e\n }\n await wait(1.15 ** depth * 2000)\n\n return callWithRetry(fn, depth + 1)\n }\n}\n\nconst observerCallback = (entries, observer) => {\n entries.forEach(async entry => {\n if (!entry.isIntersecting) return\n\n await callWithRetry(dispatchAppearEvent(entry, observer))\n })\n}\n\nexport const extendElementWithIntersectionObserver = element => {\n Object.assign(element, {\n observer: new IntersectionObserver(observerCallback.bind(element), {})\n })\n\n if (!element.hasAttribute('keep')) {\n element.observer.observe(element)\n }\n}\n\nexport const extendElementWithEagerLoading = element => {\n if (element.dataset.eager === 'true') {\n if (element.observer) element.observer.disconnect()\n callWithRetry(dispatchAppearEvent(element))\n }\n}\n","/* global HTMLElement */\n\nimport {\n extendElementWithIntersectionObserver,\n extendElementWithEagerLoading\n} from './futurism_utils'\n\nexport default class FuturismElement extends HTMLElement {\n connectedCallback () {\n extendElementWithIntersectionObserver(this)\n extendElementWithEagerLoading(this)\n }\n}\n","/* global HTMLTableRowElement */\n\nimport {\n extendElementWithIntersectionObserver,\n extendElementWithEagerLoading\n} from './futurism_utils'\n\nexport default class FuturismTableRow extends HTMLTableRowElement {\n connectedCallback () {\n extendElementWithIntersectionObserver(this)\n extendElementWithEagerLoading(this)\n }\n}\n","/* global HTMLLIElement */\r\n\r\nimport {\r\n extendElementWithIntersectionObserver,\r\n extendElementWithEagerLoading\r\n} from './futurism_utils'\r\n\r\nexport default class FuturismLI extends HTMLLIElement {\r\n connectedCallback () {\r\n extendElementWithIntersectionObserver(this)\r\n extendElementWithEagerLoading(this)\r\n }\r\n}\r\n","/* global customElements, sessionStorage */\n\nimport FuturismElement from './futurism_element'\nimport FuturismTableRow from './futurism_table_row'\nimport FuturismLI from './futurism_li'\n\nimport { sha256 } from '../utils/crypto'\n\nconst polyfillCustomElements = () => {\n const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)\n\n if (customElements) {\n if (isSafari) {\n document.write(\n '<script src=\"//unpkg.com/@ungap/custom-elements-builtin\"><\\x2fscript>'\n )\n } else {\n try {\n customElements.define(\n 'built-in',\n document.createElement('tr').constructor,\n { extends: 'tr' }\n )\n } catch (_) {\n document.write(\n '<script src=\"//unpkg.com/@ungap/custom-elements-builtin\"><\\x2fscript>'\n )\n }\n }\n } else {\n document.write(\n '<script src=\"//unpkg.com/document-register-element\"><\\x2fscript>'\n )\n }\n}\n\nconst defineElements = e => {\n if (!customElements.get('futurism-element')) {\n customElements.define('futurism-element', FuturismElement)\n customElements.define('futurism-table-row', FuturismTableRow, {\n extends: 'tr'\n })\n customElements.define('futurism-li', FuturismLI, { extends: 'li' })\n }\n}\n\nconst cachePlaceholders = e => {\n sha256(e.detail.element.outerHTML).then(hashedContent => {\n e.detail.element.setAttribute('keep', '')\n sessionStorage.setItem(\n `futurism-${hashedContent}`,\n e.detail.element.outerHTML\n )\n e.target.dataset.futurismHash = hashedContent\n })\n}\n\nconst restorePlaceholders = e => {\n const inNamespace = ([key, _payload]) => key.startsWith('futurism-')\n Object.entries(sessionStorage)\n .filter(inNamespace)\n .forEach(([key, payload]) => {\n const match = /^futurism-(.*)/.exec(key)\n const targetElement = document.querySelector(\n `[data-futurism-hash=\"${match[1]}\"]`\n )\n\n if (targetElement) {\n targetElement.outerHTML = payload\n sessionStorage.removeItem(key)\n }\n })\n}\n\nexport const initializeElements = () => {\n polyfillCustomElements()\n document.addEventListener('DOMContentLoaded', defineElements)\n document.addEventListener('turbo:load', defineElements)\n document.addEventListener('turbo:before-cache', restorePlaceholders)\n document.addEventListener('turbolinks:load', defineElements)\n document.addEventListener('turbolinks:before-cache', restorePlaceholders)\n document.addEventListener('cable-ready:after-outer-html', cachePlaceholders)\n}\n","/* global crypto */\n\nexport async function sha256 (message) {\n // encode as UTF-8\n const msgBuffer = new TextEncoder('utf-8').encode(message)\n\n // hash the message\n const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer)\n\n // convert ArrayBuffer to Array\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n\n // convert bytes to hex string\n const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('')\n\n return hashHex\n}\n"],"names":["createSubscription","consumer","subscriptions","create","connected","window","Futurism","this","document","addEventListener","callback","delay","timeoutId","events","args","clearTimeout","setTimeout","debounceEvents","send","signed_params","map","e","target","dataset","signedParams","sgids","sgid","signed_controllers","signedController","urls","_","location","href","broadcast_each","broadcastEach","received","data","cableReady","CableReady","perform","operations","emitMissingElementWarnings","dispatchEvent","CustomEvent","bubbles","cancelable","dispatchAppearEvent","entry","observer","evt","detail","callWithRetry","async","fn","depth","ms","Promise","resolve","observerCallback","entries","forEach","isIntersecting","extendElementWithIntersectionObserver","element","Object","assign","IntersectionObserver","bind","hasAttribute","observe","extendElementWithEagerLoading","eager","disconnect","FuturismElement","HTMLElement","connectedCallback","FuturismTableRow","HTMLTableRowElement","FuturismLI","HTMLLIElement","defineElements","customElements","get","define","extends","cachePlaceholders","message","msgBuffer","TextEncoder","encode","hashBuffer","crypto","subtle","digest","Array","from","Uint8Array","b","toString","slice","join","sha256","outerHTML","then","hashedContent","setAttribute","sessionStorage","setItem","futurismHash","restorePlaceholders","filter","key","_payload","startsWith","payload","match","exec","targetElement","querySelector","removeItem","initializeElements","isSafari","test","navigator","userAgent","write","createElement","constructor","polyfillCustomElements"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"futurism.umd.min.js","sources":["../../../javascript/futurism_channel.js","../../../javascript/elements/futurism_utils.js","../../../javascript/elements/futurism_element.js","../../../javascript/elements/futurism_table_row.js","../../../javascript/elements/futurism_li.js","../../../javascript/elements/index.js","../../../javascript/utils/crypto.js"],"sourcesContent":["/* global CustomEvent, setTimeout */\n\nimport CableReady from 'cable_ready'\n\nconst debounceEvents = (callback, delay = 20) => {\n let timeoutId\n let events = []\n return (...args) => {\n clearTimeout(timeoutId)\n events = [...events, ...args]\n timeoutId = setTimeout(() => {\n timeoutId = null\n callback(events)\n events = []\n }, delay)\n }\n}\n\nexport const createSubscription = consumer => {\n consumer.subscriptions.create('Futurism::Channel', {\n connected () {\n window.Futurism = this\n document.addEventListener(\n 'futurism:appear',\n debounceEvents(events => {\n this.send({\n signed_params: events.map(e => e.target.dataset.signedParams),\n sgids: events.map(e => e.target.dataset.sgid),\n signed_controllers: events.map(\n e => e.target.dataset.signedController\n ),\n urls: events.map(_ => window.location.href),\n broadcast_each: events.map(e => e.target.dataset.broadcastEach)\n })\n })\n )\n },\n\n received (data) {\n if (data.cableReady) {\n CableReady.perform(data.operations, {\n emitMissingElementWarnings: false\n })\n\n document.dispatchEvent(\n new CustomEvent('futurism:appeared', {\n bubbles: true,\n cancelable: true\n })\n )\n }\n }\n })\n}\n","/* global IntersectionObserver, CustomEvent, setTimeout */\n\nconst dispatchAppearEvent = (entry, observer = null) => {\n if (!window.Futurism) {\n return () => {\n setTimeout(() => dispatchAppearEvent(entry, observer)(), 1)\n }\n }\n\n const target = entry.target ? entry.target : entry\n\n const evt = new CustomEvent('futurism:appear', {\n bubbles: true,\n detail: {\n target,\n observer\n }\n })\n\n return () => {\n target.dispatchEvent(evt)\n }\n}\n\n// from https://advancedweb.hu/how-to-implement-an-exponential-backoff-retry-strategy-in-javascript/#rejection-based-retrying\nconst wait = ms => new Promise(resolve => setTimeout(resolve, ms))\n\nconst callWithRetry = async (fn, depth = 0) => {\n try {\n return await fn()\n } catch (e) {\n if (depth > 10) {\n throw e\n }\n await wait(1.15 ** depth * 2000)\n\n return callWithRetry(fn, depth + 1)\n }\n}\n\nconst observerCallback = (entries, observer) => {\n entries.forEach(async entry => {\n if (!entry.isIntersecting) return\n\n await callWithRetry(dispatchAppearEvent(entry, observer))\n })\n}\n\nexport const extendElementWithIntersectionObserver = element => {\n Object.assign(element, {\n observer: new IntersectionObserver(observerCallback.bind(element), {})\n })\n\n if (!element.hasAttribute('keep')) {\n element.observer.observe(element)\n }\n}\n\nexport const extendElementWithEagerLoading = element => {\n if (element.dataset.eager === 'true') {\n if (element.observer) element.observer.disconnect()\n callWithRetry(dispatchAppearEvent(element))\n }\n}\n","/* global HTMLElement */\n\nimport {\n extendElementWithIntersectionObserver,\n extendElementWithEagerLoading\n} from './futurism_utils'\n\nexport default class FuturismElement extends HTMLElement {\n connectedCallback () {\n extendElementWithIntersectionObserver(this)\n extendElementWithEagerLoading(this)\n }\n}\n","/* global HTMLTableRowElement */\n\nimport {\n extendElementWithIntersectionObserver,\n extendElementWithEagerLoading\n} from './futurism_utils'\n\nexport default class FuturismTableRow extends HTMLTableRowElement {\n connectedCallback () {\n extendElementWithIntersectionObserver(this)\n extendElementWithEagerLoading(this)\n }\n}\n","/* global HTMLLIElement */\r\n\r\nimport {\r\n extendElementWithIntersectionObserver,\r\n extendElementWithEagerLoading\r\n} from './futurism_utils'\r\n\r\nexport default class FuturismLI extends HTMLLIElement {\r\n connectedCallback () {\r\n extendElementWithIntersectionObserver(this)\r\n extendElementWithEagerLoading(this)\r\n }\r\n}\r\n","/* global customElements, sessionStorage */\n\nimport FuturismElement from './futurism_element'\nimport FuturismTableRow from './futurism_table_row'\nimport FuturismLI from './futurism_li'\n\nimport { sha256 } from '../utils/crypto'\n\nconst polyfillCustomElements = () => {\n const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)\n\n if (customElements) {\n if (isSafari) {\n document.write(\n '<script src=\"//unpkg.com/@ungap/custom-elements-builtin\"><\\x2fscript>'\n )\n } else {\n try {\n customElements.define(\n 'built-in',\n document.createElement('tr').constructor,\n { extends: 'tr' }\n )\n } catch (_) {\n document.write(\n '<script src=\"//unpkg.com/@ungap/custom-elements-builtin\"><\\x2fscript>'\n )\n }\n }\n } else {\n document.write(\n '<script src=\"//unpkg.com/document-register-element\"><\\x2fscript>'\n )\n }\n}\n\nconst defineElements = e => {\n if (!customElements.get('futurism-element')) {\n customElements.define('futurism-element', FuturismElement)\n customElements.define('futurism-table-row', FuturismTableRow, {\n extends: 'tr'\n })\n customElements.define('futurism-li', FuturismLI, { extends: 'li' })\n }\n}\n\nconst cachePlaceholders = e => {\n sha256(e.detail.element.outerHTML).then(hashedContent => {\n e.detail.element.setAttribute('keep', '')\n sessionStorage.setItem(\n `futurism-${hashedContent}`,\n e.detail.element.outerHTML\n )\n e.target.dataset.futurismHash = hashedContent\n })\n}\n\nconst restorePlaceholders = e => {\n const inNamespace = ([key, _payload]) => key.startsWith('futurism-')\n Object.entries(sessionStorage)\n .filter(inNamespace)\n .forEach(([key, payload]) => {\n const match = /^futurism-(.*)/.exec(key)\n const targetElement = document.querySelector(\n `[data-futurism-hash=\"${match[1]}\"]`\n )\n\n if (targetElement) {\n targetElement.outerHTML = payload\n sessionStorage.removeItem(key)\n }\n })\n}\n\nexport const initializeElements = () => {\n polyfillCustomElements()\n document.addEventListener('DOMContentLoaded', defineElements)\n document.addEventListener('turbo:load', defineElements)\n document.addEventListener('turbo:before-cache', restorePlaceholders)\n document.addEventListener('turbolinks:load', defineElements)\n document.addEventListener('turbolinks:before-cache', restorePlaceholders)\n document.addEventListener('cable-ready:after-outer-html', cachePlaceholders)\n}\n","/* global crypto */\n\nexport async function sha256 (message) {\n // encode as UTF-8\n const msgBuffer = new TextEncoder('utf-8').encode(message)\n\n // hash the message\n const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer)\n\n // convert ArrayBuffer to Array\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n\n // convert bytes to hex string\n const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('')\n\n return hashHex\n}\n"],"names":["dispatchAppearEvent","entry","observer","window","Futurism","setTimeout","target","evt","CustomEvent","bubbles","detail","dispatchEvent","callWithRetry","async","fn","depth","e","ms","Promise","resolve","observerCallback","entries","forEach","isIntersecting","extendElementWithIntersectionObserver","element","Object","assign","IntersectionObserver","bind","hasAttribute","observe","extendElementWithEagerLoading","dataset","eager","disconnect","FuturismElement","HTMLElement","connectedCallback","this","FuturismTableRow","HTMLTableRowElement","FuturismLI","HTMLLIElement","defineElements","customElements","get","define","extends","cachePlaceholders","message","msgBuffer","TextEncoder","encode","hashBuffer","crypto","subtle","digest","Array","from","Uint8Array","map","b","toString","slice","join","sha256","outerHTML","then","hashedContent","setAttribute","sessionStorage","setItem","futurismHash","restorePlaceholders","filter","key","_payload","startsWith","payload","match","exec","targetElement","document","querySelector","removeItem","consumer","subscriptions","create","connected","addEventListener","callback","delay","timeoutId","events","args","clearTimeout","debounceEvents","send","signed_params","signedParams","sgids","sgid","signed_controllers","signedController","urls","_","location","href","broadcast_each","broadcastEach","received","data","cableReady","CableReady","perform","operations","emitMissingElementWarnings","cancelable","isSafari","test","navigator","userAgent","write","createElement","constructor","polyfillCustomElements"],"mappings":"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"}
1
+ {"version":3,"file":"futurism.umd.min.js","sources":["../../../javascript/futurism_channel.js","../../../javascript/elements/futurism_utils.js","../../../javascript/elements/futurism_element.js","../../../javascript/elements/futurism_table_row.js","../../../javascript/elements/futurism_li.js","../../../javascript/elements/index.js","../../../javascript/utils/crypto.js"],"sourcesContent":["/* global CustomEvent, setTimeout */\n\nimport CableReady from 'cable_ready'\n\nconst debounceEvents = (callback, delay = 20) => {\n let timeoutId\n let events = []\n return (...args) => {\n clearTimeout(timeoutId)\n events = [...events, ...args]\n timeoutId = setTimeout(() => {\n timeoutId = null\n callback(events)\n events = []\n }, delay)\n }\n}\n\nexport const createSubscription = consumer => {\n consumer.subscriptions.create('Futurism::Channel', {\n connected () {\n window.Futurism = this\n document.addEventListener(\n 'futurism:appear',\n debounceEvents(events => {\n this.send({\n signed_params: events.map(e => e.target.dataset.signedParams),\n sgids: events.map(e => e.target.dataset.sgid),\n signed_controllers: events.map(\n e => e.target.dataset.signedController\n ),\n urls: events.map(_ => window.location.href),\n broadcast_each: events.map(e => e.target.dataset.broadcastEach)\n })\n })\n )\n },\n\n received (data) {\n if (data.cableReady) {\n CableReady.perform(data.operations, {\n emitMissingElementWarnings: false\n })\n\n document.dispatchEvent(\n new CustomEvent('futurism:appeared', {\n bubbles: true,\n cancelable: true\n })\n )\n }\n }\n })\n}\n","/* global IntersectionObserver, CustomEvent, setTimeout */\n\nconst dispatchAppearEvent = (entry, observer = null) => {\n if (!window.Futurism) {\n return () => {\n setTimeout(() => dispatchAppearEvent(entry, observer)(), 1)\n }\n }\n\n const target = entry.target ? entry.target : entry\n\n const evt = new CustomEvent('futurism:appear', {\n bubbles: true,\n detail: {\n target,\n observer\n }\n })\n\n return () => {\n target.dispatchEvent(evt)\n }\n}\n\n// from https://advancedweb.hu/how-to-implement-an-exponential-backoff-retry-strategy-in-javascript/#rejection-based-retrying\nconst wait = ms => new Promise(resolve => setTimeout(resolve, ms))\n\nconst callWithRetry = async (fn, depth = 0) => {\n try {\n return await fn()\n } catch (e) {\n if (depth > 10) {\n throw e\n }\n await wait(1.15 ** depth * 2000)\n\n return callWithRetry(fn, depth + 1)\n }\n}\n\nconst observerCallback = (entries, observer) => {\n entries.forEach(async entry => {\n if (!entry.isIntersecting) return\n\n await callWithRetry(dispatchAppearEvent(entry, observer))\n })\n}\n\nexport const extendElementWithIntersectionObserver = element => {\n Object.assign(element, {\n observer: new IntersectionObserver(observerCallback.bind(element), {})\n })\n\n if (!element.hasAttribute('keep')) {\n element.observer.observe(element)\n }\n}\n\nexport const extendElementWithEagerLoading = element => {\n if (element.dataset.eager === 'true') {\n if (element.observer) element.observer.disconnect()\n callWithRetry(dispatchAppearEvent(element))\n }\n}\n","/* global HTMLElement */\n\nimport {\n extendElementWithIntersectionObserver,\n extendElementWithEagerLoading\n} from './futurism_utils'\n\nexport default class FuturismElement extends HTMLElement {\n connectedCallback () {\n extendElementWithIntersectionObserver(this)\n extendElementWithEagerLoading(this)\n }\n}\n","/* global HTMLTableRowElement */\n\nimport {\n extendElementWithIntersectionObserver,\n extendElementWithEagerLoading\n} from './futurism_utils'\n\nexport default class FuturismTableRow extends HTMLTableRowElement {\n connectedCallback () {\n extendElementWithIntersectionObserver(this)\n extendElementWithEagerLoading(this)\n }\n}\n","/* global HTMLLIElement */\r\n\r\nimport {\r\n extendElementWithIntersectionObserver,\r\n extendElementWithEagerLoading\r\n} from './futurism_utils'\r\n\r\nexport default class FuturismLI extends HTMLLIElement {\r\n connectedCallback () {\r\n extendElementWithIntersectionObserver(this)\r\n extendElementWithEagerLoading(this)\r\n }\r\n}\r\n","/* global customElements, sessionStorage */\n\nimport FuturismElement from './futurism_element'\nimport FuturismTableRow from './futurism_table_row'\nimport FuturismLI from './futurism_li'\n\nimport { sha256 } from '../utils/crypto'\n\nconst polyfillCustomElements = () => {\n const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)\n\n if (customElements) {\n if (isSafari) {\n document.write(\n '<script src=\"//unpkg.com/@ungap/custom-elements-builtin\"><\\x2fscript>'\n )\n } else {\n try {\n customElements.define(\n 'built-in',\n document.createElement('tr').constructor,\n { extends: 'tr' }\n )\n } catch (_) {\n document.write(\n '<script src=\"//unpkg.com/@ungap/custom-elements-builtin\"><\\x2fscript>'\n )\n }\n }\n } else {\n document.write(\n '<script src=\"//unpkg.com/document-register-element\"><\\x2fscript>'\n )\n }\n}\n\nconst defineElements = e => {\n if (!customElements.get('futurism-element')) {\n customElements.define('futurism-element', FuturismElement)\n customElements.define('futurism-table-row', FuturismTableRow, {\n extends: 'tr'\n })\n customElements.define('futurism-li', FuturismLI, { extends: 'li' })\n }\n}\n\nconst cachePlaceholders = e => {\n sha256(e.detail.element.outerHTML).then(hashedContent => {\n e.detail.element.setAttribute('keep', '')\n sessionStorage.setItem(\n `futurism-${hashedContent}`,\n e.detail.element.outerHTML\n )\n e.target.dataset.futurismHash = hashedContent\n })\n}\n\nconst restorePlaceholders = e => {\n const inNamespace = ([key, _payload]) => key.startsWith('futurism-')\n Object.entries(sessionStorage)\n .filter(inNamespace)\n .forEach(([key, payload]) => {\n const match = /^futurism-(.*)/.exec(key)\n const targetElement = document.querySelector(\n `[data-futurism-hash=\"${match[1]}\"]`\n )\n\n if (targetElement) {\n targetElement.outerHTML = payload\n sessionStorage.removeItem(key)\n }\n })\n}\n\nexport const initializeElements = () => {\n polyfillCustomElements()\n document.addEventListener('DOMContentLoaded', defineElements)\n document.addEventListener('turbo:load', defineElements)\n document.addEventListener('turbo:before-cache', restorePlaceholders)\n document.addEventListener('turbolinks:load', defineElements)\n document.addEventListener('turbolinks:before-cache', restorePlaceholders)\n document.addEventListener('cable-ready:after-outer-html', cachePlaceholders)\n}\n","/* global crypto */\n\nexport async function sha256 (message) {\n // encode as UTF-8\n const msgBuffer = new TextEncoder('utf-8').encode(message)\n\n // hash the message\n const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer)\n\n // convert ArrayBuffer to Array\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n\n // convert bytes to hex string\n const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('')\n\n return hashHex\n}\n"],"names":["dispatchAppearEvent","entry","observer","window","Futurism","setTimeout","target","evt","CustomEvent","bubbles","detail","dispatchEvent","callWithRetry","async","fn","depth","e","ms","Promise","resolve","observerCallback","entries","forEach","isIntersecting","extendElementWithIntersectionObserver","element","Object","assign","IntersectionObserver","bind","hasAttribute","observe","extendElementWithEagerLoading","dataset","eager","disconnect","FuturismElement","HTMLElement","connectedCallback","this","FuturismTableRow","HTMLTableRowElement","FuturismLI","HTMLLIElement","defineElements","customElements","get","define","extends","cachePlaceholders","message","msgBuffer","TextEncoder","encode","hashBuffer","crypto","subtle","digest","Array","from","Uint8Array","map","b","toString","slice","join","sha256","outerHTML","then","hashedContent","setAttribute","sessionStorage","setItem","futurismHash","restorePlaceholders","filter","key","_payload","startsWith","payload","match","exec","targetElement","document","querySelector","removeItem","consumer","subscriptions","create","connected","addEventListener","callback","delay","timeoutId","events","args","clearTimeout","debounceEvents","send","signed_params","signedParams","sgids","sgid","signed_controllers","signedController","urls","_","location","href","broadcast_each","broadcastEach","received","data","cableReady","CableReady","perform","operations","emitMissingElementWarnings","cancelable","isSafari","test","navigator","userAgent","write","createElement","constructor","polyfillCustomElements"],"mappings":"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"}
data/futurism.gemspec CHANGED
@@ -22,12 +22,9 @@ Gem::Specification.new do |spec|
22
22
  "[A-Z]*"
23
23
  ]
24
24
 
25
- spec.test_files = Dir["test/**/*.rb"]
26
-
27
25
  spec.add_development_dependency "appraisal"
28
26
  spec.add_development_dependency "bundler", "~> 2.0"
29
27
  spec.add_development_dependency "rake", "~> 13.0"
30
- spec.add_development_dependency "pry", "~> 0.12.2"
31
28
  spec.add_development_dependency "nokogiri"
32
29
  spec.add_development_dependency "standardrb"
33
30
  spec.add_development_dependency "sqlite3"
data/futurism.gemspec~ CHANGED
@@ -34,5 +34,5 @@ Gem::Specification.new do |spec|
34
34
 
35
35
  spec.add_dependency "rack", "~> 2.0"
36
36
  spec.add_dependency "rails", ">= 5.2"
37
- spec.add_dependency "cable_ready", "= 5.0.0.pre9"
37
+ spec.add_dependency "cable_ready", ">= 5.0"
38
38
  end
@@ -19,5 +19,9 @@ module Futurism
19
19
  app.config.importmap.cache_sweepers << Engine.root.join("app/assets/javascripts")
20
20
  end
21
21
  end
22
+
23
+ initializer "futurism.logger", after: "initialize_logger" do
24
+ Futurism.logger ||= Rails.logger || Logger.new($stdout)
25
+ end
22
26
  end
23
27
  end
@@ -1,7 +1,7 @@
1
1
  module Futurism
2
2
  module Helpers
3
3
  def futurize(records_or_string = nil, extends: :div, **options, &block)
4
- if (Rails.env.test? && Futurism.skip_in_test) || options[:unless]
4
+ if (Rails.env.test? && Futurism.skip_in_test?) || options[:unless]
5
5
  if records_or_string.nil?
6
6
  return render(**options)
7
7
  else
@@ -9,7 +9,7 @@ module Futurism
9
9
  end
10
10
  end
11
11
 
12
- options[:eager] = true unless block_given?
12
+ options[:eager] = true unless block
13
13
 
14
14
  # cannot serialize a proc
15
15
  options.delete(:cached) if options[:cached].is_a?(Proc)
@@ -27,7 +27,7 @@ module Futurism
27
27
  def futurize_with_options(extends:, **options, &block)
28
28
  collection = options.delete(:collection)
29
29
  if collection.nil?
30
- placeholder = capture(&block) if block_given?
30
+ placeholder = capture(&block) if block
31
31
 
32
32
  WrappingFuturismElement.new(extends: extends, placeholder: placeholder, options: options).render
33
33
  else
@@ -36,7 +36,7 @@ module Futurism
36
36
  broadcast_each = options.delete(:broadcast_each) || false
37
37
 
38
38
  collection.each_with_index.map { |record, index|
39
- placeholder = capture(record, index, &block) if block_given?
39
+ placeholder = capture(record, index, &block) if block
40
40
 
41
41
  WrappingFuturismElement.new(extends: extends, placeholder: placeholder, options: options.deep_merge(
42
42
  broadcast_each: broadcast_each,
@@ -48,7 +48,7 @@ module Futurism
48
48
 
49
49
  def futurize_active_record(records, extends:, **options, &block)
50
50
  Array(records).map.with_index { |record, index|
51
- placeholder = capture(record, index, &block) if block_given?
51
+ placeholder = capture(record, index, &block) if block
52
52
 
53
53
  WrappingFuturismElement.new(extends: extends, options: options.merge(model: record), placeholder: placeholder).render
54
54
  }.join.html_safe
@@ -14,7 +14,7 @@ module Futurism
14
14
  def load_options(options)
15
15
  require_relative "shims/deep_transform_values" unless options.respond_to? :deep_transform_values
16
16
 
17
- options.deep_transform_values { |value| value.is_a?(String) && value.start_with?("gid://") ? GlobalID::Locator.locate(value) : value }
17
+ options.deep_transform_values { |value| (value.is_a?(String) && value.start_with?("gid://")) ? GlobalID::Locator.locate(value) : value }
18
18
  end
19
19
  end
20
20
  end
@@ -0,0 +1,33 @@
1
+ require "active_support/notifications"
2
+
3
+ module Futurism
4
+ module Resolver
5
+ class Controller
6
+ class Instrumentation < SimpleDelegator
7
+ PARAMETERS_KEY = ActionDispatch::Http::Parameters::PARAMETERS_KEY
8
+
9
+ def render(*args)
10
+ ActiveSupport::Notifications.instrument(
11
+ "render.futurism",
12
+ channel: get_param(:channel),
13
+ controller: get_param(:controller),
14
+ action: get_param(:action),
15
+ partial: extract_partial_name(*args)
16
+ ) do
17
+ super(*args)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def get_param(key)
24
+ __getobj__.instance_variable_get(:@env).dig(PARAMETERS_KEY, key)
25
+ end
26
+
27
+ def extract_partial_name(opts_or_model, *args)
28
+ opts_or_model.is_a?(Hash) ? opts_or_model[:partial] : opts_or_model.to_partial_path
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -7,7 +7,11 @@ module Futurism
7
7
  HTTP_METHODS = [:get, :post, :put, :patch, :delete]
8
8
 
9
9
  def self.for(controller:, connection:, url:, params:)
10
- new(controller: controller, connection: connection, url: url, params: params).renderer
10
+ controller_renderer = new(
11
+ controller: controller, connection: connection, url: url, params: params
12
+ ).renderer
13
+
14
+ Futurism.instrumentation? ? Instrumentation.new(controller_renderer) : controller_renderer
11
15
  end
12
16
 
13
17
  def initialize(controller:, connection:, url:, params:)
@@ -83,9 +83,9 @@ module Futurism
83
83
 
84
84
  def renderer_for(resource_definition:)
85
85
  Resolver::Controller::Renderer.for(controller: resource_definition.controller,
86
- connection: @connection,
87
- url: resource_definition.url,
88
- params: @params)
86
+ connection: @connection,
87
+ url: resource_definition.url,
88
+ params: @params)
89
89
  end
90
90
 
91
91
  def resolved_models
@@ -1,3 +1,3 @@
1
1
  module Futurism
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.0"
3
3
  end
data/lib/futurism.rb CHANGED
@@ -9,6 +9,7 @@ require "futurism/options_transformer"
9
9
  require "futurism/resolver/resources"
10
10
  require "futurism/resolver/controller"
11
11
  require "futurism/resolver/controller/renderer"
12
+ require "futurism/resolver/controller/instrumentation"
12
13
  require "futurism/helpers"
13
14
 
14
15
  module Futurism
@@ -17,16 +18,23 @@ module Futurism
17
18
  autoload :Helpers, "futurism/helpers"
18
19
 
19
20
  mattr_accessor :skip_in_test, default: false
21
+ mattr_accessor :instrumentation, default: false
22
+ mattr_accessor :logger
20
23
 
21
24
  mattr_writer :default_controller
22
25
  def self.default_controller
23
26
  (@@default_controller || "::ApplicationController").to_s.constantize
24
27
  end
25
28
 
29
+ def self.skip_in_test?
30
+ skip_in_test.present?
31
+ end
32
+
33
+ def self.instrumentation?
34
+ instrumentation.present?
35
+ end
36
+
26
37
  ActiveSupport.on_load(:action_view) do
27
38
  include Futurism::Helpers
28
39
  end
29
-
30
- mattr_accessor :logger
31
- self.logger ||= Rails.logger ? Rails.logger.new : Logger.new($stdout)
32
40
  end