render_turbo_stream 1.4.4 → 1.4.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab99348bf3118d88b5b5d1ae6bb12eca5c3b2a433c53ce8d7777a7519130f69c
4
- data.tar.gz: 57958c45f249bab4fb1c1d4c118a5a30db4956f2991c04bca9ac36b50739e78f
3
+ metadata.gz: 187ae01e898e93a7b6a09936abc83f15e96172f4a76759844ff54c585d0a58bb
4
+ data.tar.gz: 8b361d4745339fb6bd83dd05ad784be75e82bf47f596e635e8bf8c567afeeff7
5
5
  SHA512:
6
- metadata.gz: 725ca7fb7f726dbb70c796ed8438ebf74ea63310055188c635bd94d59f5297f99c974d44677fc8b7bf2d033c07960fcc5ccb7c68cf1a0ebbbe5d868b0c9f2af4
7
- data.tar.gz: 7d2639a7744be4b9f9e28a620a7d2c55adedc7a2948b197ef4ced797d2ff585041cb9ffba61b72a55c87766fc803581098b7ad65595708dd76378d80e39b69e5
6
+ metadata.gz: 7d2fab8e12360c1756d53fc5dcd82479fd34a0fed8a6398f745449460325a28b173b22bdb805e0319815b2fa3745f52c15a7e0a09e9faeebedbcf5cdbaaa2bf7
7
+ data.tar.gz: 2f767606657e5f98281834676fb1a159aa8f799907dd43ec7dce770c6d2a2f7cdbc08f4e0d983964c231b5fbb466efee0659875a25cb25e8fc3c37fd75cb10d0
data/README.md CHANGED
@@ -8,6 +8,8 @@ And build it dynamically: Most [turbo_power](https://github.com/marcoroth/turbo_
8
8
 
9
9
  ATTENTION: This plugin is in a early state.
10
10
 
11
+ Hope this gem can help you.
12
+
11
13
  An overview of how we design a rails-7 application with turbo
12
14
  is [published on dev.to](https://dev.to/chmich/rails-7-vite-wrapping-up-1pia).
13
15
 
@@ -101,7 +103,7 @@ render_turbo_stream(
101
103
  )
102
104
  ```
103
105
 
104
- The `stream_partial` method is nothing else than a singular of `render_turbo_stream`.
106
+ The `stream_partial` method is just for rendering a partial alone.
105
107
 
106
108
  ```ruby
107
109
  stream_partial(
@@ -129,9 +131,6 @@ render_turbo_stream(
129
131
  :add_css_class,
130
132
  '#colored-element',
131
133
  'red'
132
- ],
133
- [
134
-
135
134
  ]
136
135
  ]
137
136
  )
@@ -141,14 +140,34 @@ Under the hood, inside a `*.turbo_stream.erb` template, it does the following: `
141
140
 
142
141
  **WORKAROUND for redirects inside frame**
143
142
 
144
- How to handle a classical redirect_to, together with a flash message, within a frame for a CRUD controller? see [workaround](https://gitlab.com/sedl/renderturbostream/-/issues/3)
143
+ Suppose you have a CRUD controller that should do all its actions inside a turbo frame. Classic redirect_to, along with a Flash message, would raise "Render and/or redirect were called multiple times in this action". See [issue] (https://gitlab.com/sedl/renderturbostream/-/issues/3).
144
+
145
+ You can handle this by reloading the parent frame's src attribute. The code might look like this:
146
+
147
+ ```ruby
148
+ def update
149
+ @car = Car.find(params['id'])
150
+ turbo_stream_save(
151
+ @car.update(car_params),
152
+ streams_on_success: [
153
+ [
154
+ :turbo_frame_set_src,
155
+ 'cars-box',
156
+ cars_path
157
+ ]
158
+ ]
159
+ )
160
+ end
161
+
162
+ ```
145
163
 
146
164
  **Parameters for turbo_stream_save**
147
165
 
148
166
  save_action,
149
- redirect_on_success_to: nil,
167
+ redirect_on_success_to: nil, # does a regular redirect. Works if you are inside a turbo_frame and just want to redirect inside that frame BUT CANNOT STREAM OTHERS ACTIONS ON THE SAME RESPONSE https://github.com/rails/rails/issues/48056
168
+ turbo_redirect_on_success_to: nil, # does a full page redirect (break out of all frames by turbo_power redirect)
150
169
  object: nil, # object used in save_action, example: @customer
151
- id: 'form', # if nil: no partial is rendered
170
+ id: nil, # if nil: no partial is rendered
152
171
  partial: nil, # example: 'customers/form' default: "#{controller_path}/#{id}"
153
172
  action: 'replace', # options: append, prepend
154
173
  locals: {}, # locals used by the partial
@@ -255,6 +274,17 @@ For more detailed testing of partials, there are view tests.
255
274
  Testing the plugin itself: There is a [quick-and-dirty app](https://gitlab.com/sedl/renderturbostream_railsapp) which
256
275
  includes the plugin and has tests done by rspec/request and capybara.
257
276
 
277
+ ## More Configs
278
+
279
+ On test helpers, the marker for a turbo-stream target is in most cases the id of the target element. This is true for the standard turbo-stream functions. On `turbo_power` it is the same in most cases. `RenderTurboStream::Libs.first_attr_is_html_id()` checks for which methods this is true. You can override this:
280
+
281
+ ```ruby
282
+ config.x.render_turbo_stream.first_attribute_is_html_id = %[replace append prepend turbo_frame_set_src]
283
+ ```
284
+
285
+ This setting is relevant for testing helpers.
286
+
287
+
258
288
  ## Contributing
259
289
 
260
290
  Contribution welcome.
@@ -262,3 +292,7 @@ Contribution welcome.
262
292
  ## License
263
293
 
264
294
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
295
+
296
+ ## Thanks
297
+
298
+ Thanks to Marco Roth for turbo_power and thanks to DHH and the rails team for taking a bold new step by creating turbo.
@@ -1,13 +1,19 @@
1
1
  <% rendered_partials = [] %>
2
2
 
3
+
3
4
  <% streams.each do |s| %>
4
5
 
5
6
  <% if s.is_a?(Array) %>
6
- <% rendered_partials.push({ command: "turbo_stream.#{s.first}, #{s[1..-1].join(', ')}", method: s.first, attributes: s[1..-1] }) %>
7
+ <% attr_id = RenderTurboStream::Libs.first_attr_is_html_id(s.first) %>
8
+ <% h = { attributes: s, type: 'command' } %>
9
+ <% h[:target] = "##{s.second}" if attr_id %>
10
+ <% rendered_partials.push(h) %>
7
11
  <% else %>
8
12
  <% html = (render s[:partial], locals: s[:locals]&.symbolize_keys, formats: [:html]) %>
9
13
  <% s.delete(:partial) %>
10
- <% rendered_partials.push({ html_response: html }.merge(s)) %>
14
+ <% s[:target] = "##{s[:id]}" %>
15
+ <% s.delete(:id) %>
16
+ <% rendered_partials.push({ html_response: html, type: 'partial' }.merge(s)) %>
11
17
  <% end %>
12
18
 
13
19
 
@@ -4,24 +4,7 @@ module RenderTurboStream
4
4
 
5
5
  # log as helper for the developer to see which flash is set and which partials are rendered to wich ids
6
6
  def streams_log
7
- all_responses = RenderTurboStream::Libs.all_responses(response)
8
- r = []
9
- if response.status == 302
10
- r.push("redirect to #{turbo_redirect_to}")
11
- else
12
- all_responses.map do |a|
13
- str = [
14
- a['action'],
15
- 'id',
16
- "«#{a['id']}»",
17
- 'by partial',
18
- "«#{a['partial']}»",
19
- (a['locals'].present? ? "locals: «#{a['locals']}»" : nil)
20
- ].compact.join(' ')
21
- r.push(str)
22
- end
23
- end
24
- r
7
+ RenderTurboStream::Libs.all_responses(response)
25
8
  end
26
9
 
27
10
  # Returns the path to which turbo_stream.redirect_to would be applied.
@@ -29,11 +12,13 @@ module RenderTurboStream
29
12
  resps = RenderTurboStream::Libs.all_responses(response)
30
13
  url = nil
31
14
  resps.each do |r|
32
- if r['method'] == 'redirect_to'
33
- if url
34
- url = 'ERROR: REDIRECT CALLED MORE THAN ONCE'
35
- else
36
- url = r['attributes'].first
15
+ if r['type'] == 'command'
16
+ if r['attributes'].first == 'redirect_to'
17
+ if url
18
+ url = 'ERROR: REDIRECT CALLED MORE THAN ONCE'
19
+ else
20
+ url = r['attributes'].second
21
+ end
37
22
  end
38
23
  end
39
24
  end
@@ -66,6 +51,50 @@ module RenderTurboStream
66
51
  def stream_targets_count
67
52
  stream_targets.length
68
53
  end
54
+
55
+ # Returns false if a given action such as "replace" (for: turbo_stream.replace) is not performed exactly once on a given target id. Otherwise, it returns an array of attributes that were used to call the function.
56
+ # For stream actions, this only works if the given action has the HTML-ID as its first element.
57
+ def stream_action_once(action, id)
58
+ all_responses = RenderTurboStream::Libs.all_responses(response)
59
+ counts = {}
60
+ res = nil
61
+ all_responses.each do |r|
62
+
63
+ if r['type'] == 'command'
64
+ act = r['attributes'].first
65
+ is_id = RenderTurboStream::Libs.first_attr_is_html_id(act)
66
+ if action.to_s == act && is_id && r['attributes'].second == id.to_s
67
+ k = [act, r['attributes'].second].join('-')
68
+ counts[k] ||= 0
69
+ counts[k] += 1
70
+ if counts[k] == 1
71
+ res = r['attributes']
72
+ else
73
+ res = false
74
+ end
75
+ end
76
+
77
+ elsif r['type'] == 'partial'
78
+
79
+ act = r['action']
80
+ is_id = r['target'][0] == '#'
81
+ _id = r['target'][1..-1]
82
+ if action.to_s == act && is_id && _id == id.to_s
83
+ k = [act, _id].join('-')
84
+ counts[k] ||= 0
85
+ counts[k] += 1
86
+ if counts[k] == 1
87
+ res = [act, id, { locals: r['locals'] }]
88
+ else
89
+ res = false
90
+ end
91
+ end
92
+
93
+ end
94
+ end
95
+ res
96
+ end
97
+
69
98
  end
70
99
  end
71
100
  end
@@ -2,22 +2,30 @@ module RenderTurboStream
2
2
  module Test
3
3
  module RspecRequestHelpers
4
4
 
5
-
6
5
  # expect status 200 and a each one time response to given ids
7
6
 
8
7
  def expect_successful_saved(*ids)
9
8
  responses = RenderTurboStream::Libs.all_responses(response)
10
- counts = {}
9
+ id_counts = {}
11
10
  responses.each do |r|
12
- counts[r['id']] ||= 0
13
- counts[r['id']] +=1
11
+ if r['target'].is_a?(Array)
12
+ r['target'].each do |t|
13
+ id = (t[0] == '#' ? t[1..-1] : t)
14
+ id_counts[t.to_s] ||= 0
15
+ id_counts[t.to_s] += 1
16
+ end
17
+ else
18
+ id = (r['target'][0] == '#' ? r['target'][1..-1] : r['target'])
19
+ id_counts[id] ||= 0
20
+ id_counts[id] += 1
21
+ end
14
22
  end
15
23
 
16
24
  expect(response.status).to eq(200)
17
- expect(counts.keys.length).to eq(ids.length)
25
+ expect(id_counts.keys.length).to eq(ids.length)
18
26
  ids.each do |id|
19
- expect(counts.key?(id)).to be_truthy
20
- expect(counts[id]).to eq(1)
27
+ expect(id_counts.key?(id)).to be_truthy
28
+ expect(id_counts[id]).to eq(1)
21
29
  end
22
30
  end
23
31
 
@@ -1,3 +1,3 @@
1
1
  module RenderTurboStream
2
- VERSION = "1.4.4"
2
+ VERSION = "1.4.6"
3
3
  end
@@ -8,7 +8,7 @@ module RenderTurboStream
8
8
 
9
9
  def turbo_stream_save(
10
10
  save_action,
11
- redirect_on_success_to: nil, # does a regular redirect. Works if you are inside a turbo_frame and just want to redirect inside that frame BUT CANNOT STREAM OTHERS ACTIONS AT SAME TIME https://github.com/rails/rails/issues/48056
11
+ redirect_on_success_to: nil, # does a regular redirect. Works if you are inside a turbo_frame and just want to redirect inside that frame BUT CANNOT STREAM OTHERS ACTIONS ON THE SAME RESPONSE https://github.com/rails/rails/issues/48056
12
12
  turbo_redirect_on_success_to: nil, # does a full page redirect (break out of all frames by turbo_power redirect)
13
13
  object: nil, # object used in save_action, example: @customer
14
14
  id: nil, # if nil: no partial is rendered
@@ -43,7 +43,7 @@ module RenderTurboStream
43
43
 
44
44
  #== Streams / Partials
45
45
 
46
- streams = [(id ? { id: id, partial: partial, locals: locals, action: action } : nil)]
46
+ streams = (id ? [id: id, partial: partial, locals: locals, action: action] : [])
47
47
 
48
48
  if save_action
49
49
  response.status = 200
@@ -160,7 +160,7 @@ module RenderTurboStream
160
160
  ary = []
161
161
  array.each do |pr|
162
162
  if !pr.present?
163
- a=1
163
+ a = 1
164
164
  elsif pr.is_a?(Hash)
165
165
  props = pr.symbolize_keys
166
166
  part = (props[:partial].present? ? props[:partial] : props[:id]).gsub('-', '_')
@@ -214,7 +214,7 @@ module RenderTurboStream
214
214
 
215
215
  all.select do |a|
216
216
  if id.present?
217
- a['id'] == id
217
+ a['target'] == "##{id}"
218
218
  else
219
219
  false
220
220
  end
@@ -223,15 +223,19 @@ module RenderTurboStream
223
223
  end
224
224
 
225
225
  def self.stream_targets(response)
226
- all = all_responses(response)
227
- ids = []
228
- all.each do |p|
229
- _p = p['id']
230
- unless ids.include?(_p)
231
- ids.push(_p)
226
+ responses = all_responses(response)
227
+ targets = []
228
+ responses.each do |r|
229
+ if r['target'].is_a?(Array)
230
+ r['target'].each do |t|
231
+ targets.push(t) unless targets.include?(t)
232
+ end
233
+ else
234
+ targets.push(r['target']) unless targets.include?(r['target'])
232
235
  end
233
236
  end
234
- ids
237
+
238
+ targets
235
239
  end
236
240
 
237
241
  def self.stream_target_response_count(response, id, total, &block)
@@ -252,5 +256,41 @@ module RenderTurboStream
252
256
 
253
257
  end
254
258
 
259
+ # on most methods the first attribute is the target.
260
+ # This method checks for that
261
+ # it includes the methods from turbo-power
262
+ # used for test helpers
263
+ def self.first_attr_is_html_id(method)
264
+ config = Rails.configuration.x.render_turbo_stream.first_attribute_is_html_id
265
+ default = [
266
+ :graft,
267
+ :morph,
268
+ :inner_html,
269
+ :insert_adjacent_text,
270
+ :outer_html,
271
+ :text_content,
272
+ :add_css_class,
273
+ :remove_attribute,
274
+ :remove_css_class,
275
+ :set_attribute,
276
+ :set_dataset_attribute,
277
+ :set_property,
278
+ :set_style,
279
+ :set_styles,
280
+ :set_value,
281
+ :dispatch_event,
282
+ :reset_form,
283
+ :clear_storage,
284
+ :scroll_into_view,
285
+ :set_focus,
286
+ :turbo_frame_reload,
287
+ :turbo_frame_set_src,
288
+ :replace,
289
+ :append,
290
+ :prepend
291
+ ]
292
+ (config.present? ? config : default).map{|m|m.to_sym}.include?(method.to_sym)
293
+ end
294
+
255
295
  end
256
296
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: render_turbo_stream
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.4
4
+ version: 1.4.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - christian
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-24 00:00:00.000000000 Z
11
+ date: 2023-04-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -38,7 +38,6 @@ files:
38
38
  - README.md
39
39
  - Rakefile
40
40
  - app/controllers/render_turbo_stream/application_controller.rb
41
- - app/views/_render_turbo_stream.turbo_stream.erb
42
41
  - app/views/render_turbo_stream.html.erb
43
42
  - app/views/render_turbo_stream.turbo_stream.erb
44
43
  - lib/render_turbo_stream.rb
@@ -73,5 +72,6 @@ requirements: []
73
72
  rubygems_version: 3.4.12
74
73
  signing_key:
75
74
  specification_version: 4
76
- summary: Render partials, run javascript like redirect by controller. With test helpers.
75
+ summary: Render partials and run javascripts directly from the controller. With test
76
+ helpers.
77
77
  test_files: []
@@ -1,49 +0,0 @@
1
- <% error = false %>
2
- <% control = [] %>
3
- <% streams = local_assigns[:streams] %>
4
- <% streams.each do |args| %>
5
-
6
-
7
- <% if args.is_a?(Array) %>
8
-
9
- <% ctl = "turbo_stream.#{args.first}, #{args[1..-1].join(', ')}" %>
10
-
11
- <% if args.last.is_a?(Proc) %>
12
- <% Rails.logger.error(" RENDER TURBO STREAM BLOCK => #{ctl}") %>
13
- <%#= turbo_stream.replace 'form' do %>
14
- <%#= render partial: 'articles/form' %>
15
- <%# end %>
16
- <%= turbo_stream.send args.first, *(args[1..-2]) { args.last.call } %>
17
- <% else %>
18
- <% Rails.logger.error(" RENDER TURBO POWER => #{ctl}") %>
19
- <%= turbo_stream.send args.first, *(args[1..-1]) %>
20
- <% end %>
21
-
22
- <% else %>
23
- <% ctl = { partial: args[:partial], id: args[:id], action: args[:action] } %>
24
- <% info = { id: args[:id], partial: args[:partial], locals: args[:locals] } %>
25
- <% if !args[:action].present? %>
26
- <% Rails.logger.error(" ERROR RENDER TURBO STREAM => MISSING ACTION => #{args}") %>
27
- <% error = true %>
28
-
29
- <% elsif args[:action].present? %>
30
- <% Rails.logger.debug(" • render-turbo-stream #{args[:action].upcase} => #{info}") %>
31
- <%= turbo_stream.send args[:action].to_sym, args[:id] do %>
32
- <%= render args[:partial], locals: args[:locals]&.symbolize_keys %>
33
- <% end %>
34
-
35
- <% else %>
36
- <% Rails.logger.error(" ERROR RENDER TURBO STREAM => NOTHING DONE! => #{args}") %>
37
- <% end %>
38
- <% end %>
39
-
40
- <% control.push(ctl) %>
41
-
42
- <% end %>
43
-
44
- <% if error %>
45
- <% Rails.logger.error(" RENDER TURBO STREAM HAD ERRORS, REPEATING WHOLE ARRAY: ") %>
46
- <% control.each do |c| %>
47
- <% Rails.logger.error(" #{c}") %>
48
- <% end %>
49
- <% end %>