futurism 1.2.0 → 1.3.0

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.
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