render_turbo_stream 4.3.0 → 4.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 28e02fddd9493b46170e9e2767651c82748da1c0a0984940e57044489a24d472
4
- data.tar.gz: a75224ab07323e2eef5d8f6905776c849a852068074d5e10a356303178b7d30a
3
+ metadata.gz: 9f3b950cec2aadef6d1acea45b6d15e0bb10a2b6c47adaf49bad4e4ed909ffba
4
+ data.tar.gz: a1ec0bf02392b4cebf5416aed80723f9a2d2c34f2996262bc451733dc56c9442
5
5
  SHA512:
6
- metadata.gz: 45161e756cf12ebee2a2fd8941e93471ac001153bd39905597777f3fed48f5bb91e26fb254b41f0f30e340b4f47d1f743c2ec744a969e59e6b21baefdde40d5e
7
- data.tar.gz: 8e610e79ae595f3af6290b450fb7b61a97c0166e883db6a5cae71bb095dac3481644d2964218478176376c1d0461144235cb6f2526d9af2dba97e21df999e57d
6
+ metadata.gz: f9ca2767f8ac34efd99bdb31f3abb5bc5654c0d130482d3a999f815b435a08cb3eecce4eec88272919feefc0e4e5584fa7a61970b3e9535ba2550945c10bac9a
7
+ data.tar.gz: 97b79f7d79095632faa08dc7838e959188e3342ce9eb62168e0a2e2cacac1de67eb6279dd2fecb31b289aa4d6a84799f3a129c3ee6a7692db4c6a16f4e8ee9c5
data/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  # RenderTurboStream
2
2
 
3
- 2021 DHH annouced a great milestone in web development with [Rails 7, "Fulfilling a Vision"](https://rubyonrails.org/2021/12/15/Rails-7-fulfilling-a-vision).
4
-
5
3
  This gem has a second [README Turbo::StreamsChannel](https://gitlab.com/sedl/renderturbostream/-/blob/main/README-channels.md). Starting point is here. And I recommend that you download my [Quick-and-dirty test project](https://gitlab.com/sedl/renderturbostream_railsapp), set it up, see all the tests succeed, and then read through this README.
6
4
 
7
5
  As of v4.3, locals inside partials should work as expected. If you are working with turbo but without this gem, please read [readme-locals](https://gitlab.com/sedl/renderturbostream/-/blob/main/readme-locals.md) to avoid tedious details.
@@ -39,6 +37,12 @@ ApplicationController
39
37
  include RenderTurboStream::ControllerHelpers
40
38
  ```
41
39
 
40
+ Create a Initializer
41
+
42
+ ```ruby
43
+ ActionView::Base.send :include, RenderTurboStream::ChannelViewHelpers
44
+ ```
45
+
42
46
  spec/rails_helper.rb
43
47
 
44
48
  ```ruby
@@ -104,12 +108,28 @@ The Rails team has integrated `ActionCable` as `Turbo::StreamsChannel` into `Tur
104
108
  def update
105
109
  turbo_stream_save(
106
110
  @article.update(article_params),
107
- if_success_turbo_redirect_to: articles_path,
108
- target_id: 'customer-form'
111
+ if_success_redirect_to: articles_path,
112
+ partial: 'form'
109
113
  )
110
114
  end
111
115
  ```
112
- This will set a status, generate a flash message, and run `render_turbo_stream`. If it fails, it would result in something like this:
116
+
117
+ Assuming we want to build a form that works within a turbo-frame, including flash messages and redirects, we have to build all that inside a `turbo-frame`, and, within that we need a target-id, that must not be a turbo-frame. For that, there is a helper:
118
+
119
+ ```haml
120
+ = turbo_target_tag do
121
+ = simple_form ...
122
+ ```
123
+
124
+ This will generate an element like `<turbo-target id="new-article-form">`.
125
+
126
+ And the form should work!
127
+
128
+ If the update succeeds, it sends a flash message by channel(!), because stream cannot work alongside redirect, and redirects to articles#index. If it fails, it sends a flash message by stream and replaces the form tag by stream so that the invalid fields are marked red and you can see the errors inside the form.
129
+
130
+ Technical details? see later in this README.
131
+
132
+ As mentioned above, the turbo stream actions are handled by the `render_turbo_stream` controller helper:
113
133
 
114
134
  ```ruby
115
135
  render_turbo_stream(
@@ -129,19 +149,6 @@ render_turbo_stream(
129
149
 
130
150
  If the update succeeds, it will do a `redirect_to` action from turbo_power
131
151
 
132
- The `stream_partial` method is for rendering a partial alone.
133
-
134
- ```ruby
135
- stream_partial(
136
- 'flash-box',
137
- partial: nil, #=> default: id.gsub('-','_')
138
- locals: { title: 'my title' },
139
- action: nil #=> default: :replace
140
- )
141
- ```
142
-
143
- **locals**: The hash for locals goes through a `symbolize_keys`, so you need to use locals in used partials like this: `locals[:message]`.
144
-
145
152
  **More options**
146
153
 
147
154
  `render_turbo_stream` interprets a hash as a partial to be sent by `turbo_stream` and an array as a command to be sent. This allows you to perform most actions from, e.g., [turbo_power](https://github.com/marcoroth/turbo_power). An example of adding a css class to an html element and updating the browser history would look like this:
@@ -172,7 +179,24 @@ If this config is set to true, Turbo::StreamsChannel is installed and a current
172
179
 
173
180
  If an `if_success_redirect_to` argument is provided and the save action was successful, `turbo_stream_save` would send the partials by channel.
174
181
 
175
- **target-ID: Avoid the same definition in multiple places**
182
+ **Target-ID**
183
+
184
+ The target ID for turbo has to be safely unique, and it has to be nice, because at least during testing the developer has to deal with it. Since the default ID builder `dom_id` is too simple for this, there are some helpers. How it works is best shown by the `request-test helper target_id`:
185
+
186
+ ```ruby
187
+ # target_id(virtual_view_path, object)
188
+ target_id('customers/_form', Customer.new) #=> 'new-customer-form'
189
+ target_id('customers/_form', Customer.first) #=> 'customer-1-form'
190
+ target_id('customers/_form', nil) #=> 'customers-form'
191
+ ```
192
+ View-helpers, assuming we are inside `customers/_form`:
193
+
194
+ ```ruby
195
+ target_id #=> this checks automatically for @customer => if present => 'customer-1-form'
196
+ target_id(Customer.new) #=> 'new-customer-form'
197
+ ```
198
+
199
+ **Target-ID: Avoid the same definition in multiple places**
176
200
 
177
201
  Without this gem a turbo action would be wrapped within two frames, for example:
178
202
 
@@ -196,7 +220,7 @@ This means that the target id must be defined in several places: inside a partia
196
220
  In order to avoid this kind of tedious coding, the gem has a kind of fallback built in: If the argument `partial` is given, but the attribute `target_id` is not, the gem will get the target_id from the partial. The process is:
197
221
 
198
222
  1. Render the partial with the provided locals
199
- 2. Grabs into the partial by Nokogiri and looks for a turbo_frame_tag and fetches the target_id.
223
+ 2. Grabs into the partial by Nokogiri and looks for the first `turbo-frame` or `turbo-target` element, get the id and uses this as target_id.
200
224
  3. If all that not is found it raises a exception
201
225
  4. wraps the partial within the `turbo_stream.*` and sends this to the front.
202
226
 
@@ -252,15 +276,17 @@ config.x.render_turbo_stream.first_argument_is_html_id = %[replace append prepen
252
276
 
253
277
  This setting is relevant for testing helpers.
254
278
 
255
- # Conclusion
279
+ # Personal note
256
280
 
257
281
  The World Wide Web, founded around 1990 by [Tim Berners-Lee](https://en.wikipedia.org/wiki/Tim_Berners-Lee), was an html response from the server.
258
282
 
259
- Frameworks like Angular, Ember, React, Vue brought a much better user experience, so called "single page applications". But they changed something: Now Javascript was the processor of HTML, which is far from the [Progressive Enhancement] (https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement)
283
+ Frameworks like Angular, Ember, React, Vue brought a much better user experience, so called "single page applications". But they changed a paradigma: Now Javascript was the processor of HTML, which is far from the [Progressive Enhancement](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement).
284
+
285
+ 2021 DHH annouced a great milestone in web development with [Rails 7, "Fulfilling a Vision"](https://rubyonrails.org/2021/12/15/Rails-7-fulfilling-a-vision). The Rails core now has done a big step, for bringing a user experience like a single page app, but by reducing javascript. This is a modern back to the roots. Thank you Rails Team!
260
286
 
261
- The Rails core team has now achieved a milestone by turbo, for bringing a user experience like a single page app, but by reducing javascript. Somehow modern, back to the roots. Thank you.
287
+ But as of 2023, I searched for months and found nothing to solve all these details to make this Vision really work for rails. Now i am really happy and i cannot imagine working without Turbo!
262
288
 
263
- This gem is an attempt to make this approach more convenient for developers.
289
+ Let us build on this vision and make rails what it has always been: A framework ahead of its time!
264
290
 
265
291
  # Contributing
266
292
 
@@ -0,0 +1,4 @@
1
+ module RenderTurboStream
2
+ class Option < ApplicationRecord
3
+ end
4
+ end
@@ -16,8 +16,14 @@
16
16
 
17
17
 
18
18
  <% locals = args[:locals]&.symbolize_keys %>
19
- <% partial = args[:partial] %>
20
- <% rendered_html = render(partial: partial, locals: locals, formats: [:html]) %>
19
+
20
+ <% if args[:partial].present? %>
21
+ <% rendered_html = render(partial: args[:partial], locals: locals, formats: [:html]) %>
22
+ <% else %>
23
+ <% rendered_html = render(template: args[:template], locals: locals, formats: [:html]) %>
24
+ <% end %>
25
+
26
+
21
27
  <% unless args[:target].present? %>
22
28
  <% args[:target] = RenderTurboStream::Libs.fetch_arguments_from_rendered_string(rendered_html)[:target] %>
23
29
  <% unless args[:target].present? %>
@@ -12,8 +12,11 @@
12
12
 
13
13
 
14
14
  <% locals = s[:locals]&.symbolize_keys %>
15
- <% partial = s[:partial] %>
16
- <% html = (render partial: partial, locals: locals, formats: [:html]) %>
15
+ <% if s[:partial].present? %>
16
+ <% html = (render partial: s[:partial], locals: locals, formats: [:html]) %>
17
+ <% else %>
18
+ <% html = (render template: s[:template], locals: locals, formats: [:html]) %>
19
+ <% end %>
17
20
 
18
21
 
19
22
 
@@ -0,0 +1,9 @@
1
+ class CreateRenderTurboStreamOptions < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :render_turbo_stream_options do |t|
4
+ t.string :title
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -61,9 +61,9 @@ module RenderTurboStream
61
61
 
62
62
  disable_default = false
63
63
  if partial.present?
64
- _partial = RenderTurboStream::Libs.partial_path(nil, target_id, controller_path, partial)
64
+ _partial = RenderTurboStream::Libs.partial_path(controller_path, partial)
65
65
  elsif template.present?
66
- _template = RenderTurboStream::Libs.partial_path(nil, target_id, controller_path, template)
66
+ _template = RenderTurboStream::Libs.partial_path(controller_path, template)
67
67
  disable_default = true
68
68
  else
69
69
  _template = [controller_path, action_name].join('/')
@@ -12,10 +12,10 @@ module RenderTurboStream
12
12
  if_success_redirect_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
13
13
  if_success_turbo_redirect_to: nil, # does a full page redirect (break out of all frames by turbo_power redirect)
14
14
 
15
- target_id: nil, # IF NIL: no partial will be rendered AND IF allowed by config: it would send by channel *_to_me and not affect the default response-rendering
16
- partial: nil,
15
+ target_id: nil, # IF NIL: the gem grabs inside the rendered content for turbo-frame tag or turbo-target (element from helper of this gem) tag and takes the id from there.
16
+ partial: nil, # if nil: the gem renders the default template by turbo-stream
17
17
  action: 'replace', # options: append, prepend
18
- locals: {}, # locals used by the target_id
18
+ locals: {},
19
19
 
20
20
  if_success_add: nil, # hash for a partial to render or array with actions (as array) or hashes for partials within
21
21
  if_error_add: nil, # additional partials that should be rendered if save_action failed
@@ -29,12 +29,6 @@ module RenderTurboStream
29
29
  flash_controller_action_name: action_name # options: 'update', 'create', otherwise you have to declare a translation in config/locales like "activerecord.success.#{flash_controller_action_name}" and "activerecord.errors.#{flash_controller_action_name}"
30
30
  )
31
31
 
32
- # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
33
- # EXCEPTIONS
34
- # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
35
-
36
- raise 'render_turbo_stream: arguments target_id and if_success_redirect_to cannot be provided both' if target_id && if_success_redirect_to
37
-
38
32
  # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
39
33
  # LOGIC
40
34
  # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
@@ -49,7 +43,7 @@ module RenderTurboStream
49
43
  libs = RenderTurboStream::ControllerLibs.new(save_action)
50
44
  model_name = object.model_name.human
51
45
 
52
- streams = (target_id ? libs.generate_action(controller_path, target_id, action, partial, locals) : [])
46
+ streams = []
53
47
 
54
48
  flashes = libs.generate_flash(
55
49
  model_name,
@@ -93,10 +87,11 @@ module RenderTurboStream
93
87
  if_success_turbo_redirect_to
94
88
  ]
95
89
  ])
90
+
96
91
  elsif save_action && if_success_redirect_to.present?
97
- response.status = 302
92
+ response.status = 303
98
93
  if allow_channel
99
- Rails.logger.debug(" • Sent #{streams.length} actions through Turbo::StreamsChannel to enable defined redirection because allowed in configs.")
94
+ Rails.logger.debug(" • Sent #{streams.length} actions through Turbo::StreamsChannel because allowed in configs.")
100
95
  c_libs = RenderTurboStream::ChannelLibs.new(response)
101
96
  c_libs.send_actions_to_channel("authenticated-user-#{helpers.current_user.id}", streams, @render_turbo_stream_evaluate_instance_variables)
102
97
  else
@@ -107,13 +102,10 @@ module RenderTurboStream
107
102
  end
108
103
  redirect_to if_success_redirect_to
109
104
 
110
- elsif !target_id.present? && allow_channel
111
- Rails.logger.debug(" • Sent #{streams.length} actions through Turbo::StreamsChannel because no target_id defined and allowed in configs.")
112
- c_libs = RenderTurboStream::ChannelLibs.new(response)
113
- c_libs.send_actions_to_channel("authenticated-user-#{helpers.current_user.id}", streams, @render_turbo_stream_evaluate_instance_variables)
114
-
115
105
  else
106
+ streams += libs.generate_action(controller_path, target_id, action, partial, (partial ? nil : action_name), locals)
116
107
  render_turbo_stream(streams)
108
+
117
109
  end
118
110
  end
119
111
 
@@ -134,7 +126,11 @@ module RenderTurboStream
134
126
  end
135
127
  r.delete(:target_id)
136
128
  r[:action] = (props[:action].present? ? props[:action] : :replace)
137
- r[:partial] = RenderTurboStream::Libs.partial_path(props[:target], props[:target_id], controller_path, props[:partial])
129
+ if props[:partial].present?
130
+ r[:partial] = RenderTurboStream::Libs.partial_path(controller_path, props[:partial])
131
+ else
132
+ r[:template] = RenderTurboStream::Libs.partial_path(controller_path, props[:template])
133
+ end
138
134
  r[:type] = 'stream-partial'
139
135
 
140
136
  ary.push(r)
@@ -155,20 +151,5 @@ module RenderTurboStream
155
151
 
156
152
  end
157
153
 
158
- # renders a partial to turbo_stream
159
-
160
- def stream_partial(target_id = nil, partial: nil, action: :replace, locals: {})
161
- render_turbo_stream(
162
- [
163
- {
164
- target: RenderTurboStream::Libs.target_id_to_target(target_id),
165
- partial: partial,
166
- action: action,
167
- locals: locals
168
- }
169
- ]
170
- )
171
- end
172
-
173
154
  end
174
155
  end
@@ -76,11 +76,16 @@ module RenderTurboStream
76
76
 
77
77
  end
78
78
 
79
- def generate_action(controller_path, target_id, action, partial, locals)
79
+ def generate_action(controller_path, target_id, action, partial, template, locals)
80
80
  libs = RenderTurboStream::Libs
81
81
  target = libs.target_id_to_target(target_id)
82
- _partial = libs.partial_path(nil, target_id, controller_path, partial)
82
+ if partial
83
+ _partial = libs.partial_path(controller_path, partial)
83
84
  [target: target, partial: _partial, locals: locals, action: action]
85
+ else
86
+ _template = libs.template_path(controller_path, template)
87
+ [target: target, template: _template, locals: locals, action: action]
88
+ end
84
89
  end
85
90
 
86
91
  def action_errors(actions)
@@ -17,30 +17,64 @@ module RenderTurboStream
17
17
  end
18
18
  end
19
19
 
20
- def self.partial_path(target, target_id, controller_path, partial)
20
+ def self.partial_path(controller_path, partial)
21
21
  if partial && partial.to_s.include?('/')
22
22
  partial
23
23
  elsif partial.present?
24
24
  [controller_path, partial].join('/')
25
- elsif target_id.present?
26
- [controller_path, target_id.gsub('-', '_')].join('/')
27
- elsif target.present?
28
- [controller_path, RenderTurboStream::Libs.target_to_target_id(target).gsub('-', '_')].join('/')
25
+ end
26
+ end
27
+
28
+ def self.template_path(controller_path, template)
29
+ if template && template.to_s.include?('/')
30
+ template
31
+ elsif template.present?
32
+ [controller_path, template].join('/')
29
33
  end
30
34
  end
31
35
 
32
36
  def self.fetch_arguments_from_rendered_string(rendered_string)
33
37
  noko = Nokogiri::HTML(rendered_string)
34
38
  frame = noko.at_css('turbo-frame')
39
+ ele = noko.at_css('turbo-target')
35
40
  if frame.present?
36
41
  {
37
42
  target_id: frame[:id],
38
43
  target: target_id_to_target(frame[:id])
39
44
  }
45
+ elsif ele.present?
46
+ {
47
+ target_id: ele[:id],
48
+ target: target_id_to_target(ele[:id])
49
+ }
40
50
  else
41
51
  {}
42
52
  end
43
53
  end
44
54
 
55
+ def self.target_id(virt_view_path, id)
56
+ if id.is_a?(String)
57
+ id
58
+ elsif !id
59
+ [
60
+ virt_view_path.gsub('/', '-').gsub('-_', '-')
61
+ ].join('-')
62
+ else
63
+ unless id.methods.include?(:id)
64
+ raise("target_id / argument ID: Only String or object with method :id accepted")
65
+ end
66
+ (
67
+ (
68
+ id.id ?
69
+ [id.class.to_s.downcase, id.id] :
70
+ ['new', id.class.to_s.downcase]
71
+ ) +
72
+ [
73
+ virt_view_path.split('/').last.sub(/^_/, '')
74
+ ]
75
+ ).join('-')
76
+ end
77
+ end
78
+
45
79
  end
46
80
  end
@@ -84,6 +84,11 @@ module RenderTurboStream
84
84
  )
85
85
  end
86
86
 
87
+ def target_id(relative_view_path, object)
88
+ libs = RenderTurboStream::Libs
89
+ libs.target_id(relative_view_path, object)
90
+ end
91
+
87
92
  end
88
93
  end
89
94
  end
@@ -1,3 +1,3 @@
1
1
  module RenderTurboStream
2
- VERSION = "4.3.0"
2
+ VERSION = "4.3.2"
3
3
  end
@@ -0,0 +1,20 @@
1
+ module RenderTurboStream
2
+ module ViewHelpers
3
+
4
+ def turbo_target_tag(id = nil, &block)
5
+ content_tag :'turbo-target', id: target_id(id) do
6
+ if block_given?
7
+ capture(&block)
8
+ end
9
+ end
10
+ end
11
+
12
+ def target_id(id = nil)
13
+ libs = RenderTurboStream::Libs
14
+ virt_path = self.instance_variable_get('@current_template').instance_variable_get('@virtual_path')
15
+ obj = (id ? id : eval("@#{controller_name.singularize}"))
16
+ libs.target_id(virt_path, obj)
17
+ end
18
+
19
+ end
20
+ end
@@ -10,6 +10,7 @@ require 'render_turbo_stream/controller_helpers'
10
10
  require 'render_turbo_stream/controller_channel_helpers'
11
11
 
12
12
  require 'render_turbo_stream/channel_view_helpers'
13
+ require 'render_turbo_stream/view_helpers'
13
14
 
14
15
  require 'render_turbo_stream/channel_libs'
15
16
  require 'render_turbo_stream/controller_libs'
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: 4.3.0
4
+ version: 4.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - christian
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-25 00:00:00.000000000 Z
11
+ date: 2023-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -29,8 +29,8 @@ description: 'A set of helpers that allow a UNIFIED WORKFLOW for TurboStream and
29
29
  from the controller. There is no need to write *.turbo_stream.* templates anymore.
30
30
  Logic can stay on in the controller. Javascript actions can also be executed directly
31
31
  from the controller. TESTING: To reduce the amount of hard-to-maintain system tests,
32
- this gem allows detailed request tests. A DEMO PROJECT with all this built in, along
33
- with system and request tests, is linked in the README.'
32
+ request tests are possible. A DEMO PROJECT with all this built in, along with system
33
+ and request tests, is linked in the README.'
34
34
  email:
35
35
  - christian@sedlmair.ch
36
36
  executables: []
@@ -41,11 +41,13 @@ files:
41
41
  - Rakefile
42
42
  - app/controllers/render_turbo_stream/application_controller.rb
43
43
  - app/controllers/render_turbo_stream/render_controller.rb
44
+ - app/models/render_turbo_stream/option.rb
44
45
  - app/views/layouts/_add_turbo_frame_tag.html.erb
45
46
  - app/views/render_turbo_stream.turbo_stream.erb
46
47
  - app/views/render_turbo_stream_command.html.erb
47
48
  - app/views/render_turbo_stream_empty_template.html.erb
48
49
  - app/views/render_turbo_stream_request_test.html.erb
50
+ - db/migrate/20230616070450_create_render_turbo_stream_options.rb
49
51
  - lib/render_turbo_stream.rb
50
52
  - lib/render_turbo_stream/channel_libs.rb
51
53
  - lib/render_turbo_stream/channel_view_helpers.rb
@@ -60,6 +62,7 @@ files:
60
62
  - lib/render_turbo_stream/test/request/helpers.rb
61
63
  - lib/render_turbo_stream/test/request/libs.rb
62
64
  - lib/render_turbo_stream/version.rb
65
+ - lib/render_turbo_stream/view_helpers.rb
63
66
  - lib/tasks/render_turbo_stream_tasks.rake
64
67
  homepage: https://gitlab.com/sedl/renderturbostream
65
68
  licenses: