render_turbo_stream 1.3.3 → 1.4.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: b32041ebda04bf085c6199ba1e29e5274f9fa86c2a210c7ee6a32e9937721037
4
- data.tar.gz: 32b05f420b1ce6c6733615f2be5059a265ee58bf2c55269363aa14e0d0f41c2e
3
+ metadata.gz: cf65f7ac19c9387a4ee6a42f9e2227082ec7996aef74e672cc45aa04f75becf7
4
+ data.tar.gz: 558ecbee422b8eab39c292f82ae0df3df54cc5b439b0e8a7b83008dc863e00ec
5
5
  SHA512:
6
- metadata.gz: bfdd0158be7131cdd05e7527b7005b40d90621d9a95919955bdeb58dd0e75cb41d5e7799ad43626f05d690a814600f267b9476e25e3a5b7400be6a8d36a481dd
7
- data.tar.gz: f04d228c519cd617ff53e7292144673eaaf610e879f87fc4a95a6902e487bffab7a84bd0bbff3068a94f138b2c32a17ea91872e45f117c133f1f846029cf3ede
6
+ metadata.gz: 11549d8a9b8145ca04e7e025516a74edbeeb726bfa9d3bfca0fa819c2d8bf76da7d354c860ec01ec461b4d50b80e0fb197aaf34b30502b1d29d72718982cf149
7
+ data.tar.gz: 8405143aeae7793da5011b56e5993843dac2f0f97c5d0cce712d5fac0eca5a1e4045dd7680b72db76f6c5c761dd35f7f3fa24fb71e029dbde6bda8b49b140281
data/README.md CHANGED
@@ -6,6 +6,8 @@ Working consistently with turbo_stream means shooting lots of partials from the
6
6
 
7
7
  And build it dynamically: Most [turbo_power](https://github.com/marcoroth/turbo_power) commands, such as adding a css class to an html element or pushing a state to the browser history, work directly from the controller. Since turbo allows it, custom javascript functions are also possible.
8
8
 
9
+ ATTENTION: This plugin is in a early state.
10
+
9
11
  An overview of how we design a rails-7 application with turbo
10
12
  is [published on dev.to](https://dev.to/chmich/rails-7-vite-wrapping-up-1pia).
11
13
 
@@ -34,11 +36,6 @@ RSpec.configure do |config|
34
36
  end
35
37
  ```
36
38
 
37
- **Redirection and more**
38
-
39
- For redirection to work, you must follow the installation steps
40
- from [turbo_power](https://github.com/marcoroth/turbo_power).
41
-
42
39
  **Flash**
43
40
 
44
41
  Required Configurations for Flash Partial
@@ -51,16 +48,37 @@ config.x.render_turbo_stream.flash_action = 'prepend'
51
48
 
52
49
  The corresponding partials for flashes could look [like this](https://gitlab.com/sedl/renderturbostream/-/wikis/flashes)
53
50
 
51
+ **Translations**
52
+
53
+ ```
54
+ activerecord.success.successfully_created
55
+ activerecord.success.successfully_updated
56
+ activerecord.errors.messages.could_not_create
57
+ activerecord.errors.messages.could_not_update
58
+ ```
59
+
60
+ example value: `"%<model_name>s successfully created"`
61
+
62
+ Model name translations, see: Rails Docs.
63
+
64
+ **Turbo power**
65
+
66
+ To get redirection and many other options working, you need to follow the installation steps from [turbo_power](https://github.com/marcoroth/turbo_power).
67
+
68
+ **Turbo itself**
69
+
70
+ A comprehensive tutorial on turbo and how to check that it is working properly can be found at [hotrails.dev](https://www.hotrails.dev/turbo-rails).
71
+
54
72
  ## Usage
55
73
 
56
- `turbo_stream_save` is a special method for streamlining `update' or `create' functions with `turbo_stream'. A controller action for update might look like this:
74
+ `turbo_stream_save` is a special method for streamlining `update` or `create` functions with `turbo_stream`. A controller action for update might look like this:
57
75
 
58
76
  ```ruby
59
77
 
60
78
  def update
61
79
  turbo_stream_save(
62
80
  @customer.update(customer_params),
63
- redirect_on_success_to: edit_customer_path(@customer)
81
+ redirect_on_success_to: edit_customer_path(@customer),
64
82
  )
65
83
  end
66
84
  ```
@@ -72,7 +90,7 @@ render_turbo_stream(
72
90
  [
73
91
  {
74
92
  id: 'form',
75
- partial: 'form'
93
+ partial: 'customers/form'
76
94
  },
77
95
  {
78
96
  id: 'flash-wrapper',
@@ -96,7 +114,7 @@ stream_partial(
96
114
 
97
115
  **locals**: The hash for locals goes through a `symbolize_keys`, so you need to use locals in used partials like this: `locals[:message]`.
98
116
 
99
- ## More options
117
+ **More options**
100
118
 
101
119
  `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:
102
120
 
@@ -111,12 +129,45 @@ render_turbo_stream(
111
129
  :add_css_class,
112
130
  '#colored-element',
113
131
  'red'
132
+ ],
133
+ [
134
+
114
135
  ]
115
136
  ]
116
137
  )
117
138
  ```
118
139
 
119
- which is done under the hood, for the array: `= turbo_stream.send args.first, *(args[1..-1])`
140
+ Under the hood, inside a `*.turbo_stream.erb` template, it does the following: `= turbo_stream.send args.first, *(args[1..-1])`
141
+
142
+ **Parameters for turbo_stream_save**
143
+
144
+ save_action,
145
+ redirect_on_success_to: nil,
146
+ object: nil, # object used in save_action, example: @customer
147
+ id: 'form', # if nil: no partial is rendered
148
+ partial: nil, # example: 'customers/form' default: "#{controller_path}/#{id}"
149
+ action: 'replace', # options: append, prepend
150
+ locals: {}, # locals used by the partial
151
+ streams_on_success: [
152
+ {
153
+ id: nil,
154
+ partial: 'form',
155
+ locals: {},
156
+ action: 'replace'
157
+ }
158
+ ], # additional partials that should be rendered if save_action succeeded
159
+ streams_on_error: [
160
+ {
161
+ id: nil,
162
+ partial: 'form',
163
+ locals: {},
164
+ action: 'replace'
165
+ }
166
+ ], # additional partials that should be rendered if save_action failed
167
+ add_flash_alerts: [], #=> array of strings
168
+ add_flash_notices: [], #=> array of strings
169
+ flashes_on_success: [], #=> array of strings
170
+ flashes_on_error: [] #=> array of strings
120
171
 
121
172
 
122
173
  ## Testing Scenarios
@@ -126,7 +177,7 @@ For system testing there is Capybara. Its the only way to check if frontend and
126
177
  If the request format is not `turbo_stream`, which is the case on request specs, the method responds in a special html
127
178
  that contains the medadata that is interesting for our tests and is parsed by included test helpers.
128
179
 
129
- There is a helper for writing the test: In the debugger, within the test, check the output of `partials_log`.
180
+ There is a helper for writing the test: In the debugger, within the test, check the output of `streams_log`.
130
181
 
131
182
  Test scenarios are:
132
183
 
@@ -141,10 +192,10 @@ For rspec there is a special helper, for a successful save action:
141
192
  ```ruby
142
193
  it 'update failed' do
143
194
  patch article_path(article, params: valid_params)
144
- expect_successful_saved('form', 'flash-box')
195
+ expect_successful_saved('customer-form', 'flash-box')
145
196
  # expects response.status 200
146
197
  # Make sure that the responses point to exactly these 2 IDs ('form' and 'flash-box').
147
- # Make sure that each ID is responded to exactly once.
198
+ # Make sure that to each ID is responded to exactly once.
148
199
  end
149
200
  ```
150
201
 
@@ -160,14 +211,14 @@ end
160
211
 
161
212
  For checking a little more inside the partial responses, but with a simple syntax that checks for a one-time response from a specific partial and may be sufficient for most cases:
162
213
  ```ruby
163
- expect(partials_count).to eq(2)
164
- # Check the total number of responding partials, in most cases it will be one form and one flash.
214
+ expect(stream_targets_count).to eq(2)
215
+ # Check the total number of targeted html-ids, in most cases it will be one form and one flash.
165
216
 
166
- expect(partial_response('form')).to eq(true)
167
- # Make sure that the form part (controller_path is not checked) has responded exactly once.
217
+ expect(stream_response('customer-form')).to eq(true)
218
+ # Make sure that the id «customer-form» is targeted exactly once.
168
219
 
169
- expect(partial_response('articles/form', id: 'form', css: '.field_with_errors', include_string: 'Title')).to eq(true)
170
- # Check the content inside at least one in any of the elements that match the given css and check the id that turbo has pointed to..
220
+ expect(stream_response('customer-form', css: '.field_with_errors', include_string: 'Title')).to eq(true)
221
+ # Verify that the response targeted to "customer-form" contains the specified html.
171
222
  ```
172
223
 
173
224
  **More detailed**
@@ -175,77 +226,31 @@ expect(partial_response('articles/form', id: 'form', css: '.field_with_errors',
175
226
  Consider a controller action that should respond in 2 flashes:
176
227
 
177
228
  ```ruby
178
- expect(partials_count).to eq(1)
229
+ expect(stream_targets_count).to eq(1)
179
230
 
180
231
  expect(
181
- partial_response_count('flash', total: 2) do |p|
232
+ stream_target_response_count('flash-box', total: 2) do |p|
182
233
  p.css('.callout.success').inner_html.include?('All perfect')
183
234
  end
184
235
  ).to eq(1)
185
236
 
186
237
  expect(
187
- partial_response_count('layouts/flash', id: 'flash-box', total: 2) do |p|
238
+ stream_target_response_count('flash-box', total: 2) do |p|
188
239
  p.css('.callout.alert').inner_html.include?('Something went wrong')
189
240
  end
190
241
  ).to eq(1)
191
242
  ```
192
- `partial_response_count` always returns the number of matched responses.
243
+ `stream_target_response_count` returns the number of matched responses.
244
+
245
+ Possible matchers can be found at [Nokogiri](https://nokogiri.org/tutorials/searching_a_xml_html_document.html).
193
246
 
194
- Possible matchers can be found at [Nokogiri](https://nokogiri.org/tutorials/searching_a_xml_html_document.html). That should be enough for this level. For more detailed testing of partials, there are view tests. And many of the partials are regularly accessed in two ways, for example a `form': it is pushed by turbo_stream as well as by calling edit_customer_path.
247
+ For more detailed testing of partials, there are view tests.
195
248
 
196
- P.S.:
249
+ **P.S.:**
197
250
 
198
251
  Testing the plugin itself: There is a [quick-and-dirty app](https://gitlab.com/sedl/renderturbostream_railsapp) which
199
252
  includes the plugin and has tests done by rspec/request and capybara.
200
253
 
201
- ## Parameters for turbo_stream_save
202
-
203
- save_action,
204
- redirect_on_success_to: nil,
205
- object: nil, # object used in save_action, example: @customer
206
- id: 'form', # if nil: no partial is rendered
207
- partial: nil, # example: 'customers/form' default: "#{controller_path}/#{id}"
208
- action: 'replace', # options: append, prepend
209
- locals: {}, # locals used by the partial
210
- streams_on_success: [
211
- {
212
- id: nil,
213
- partial: 'form',
214
- locals: {},
215
- action: 'replace'
216
- }
217
- ], # additional partials that should be rendered if save_action succeeded
218
- streams_on_error: [
219
- {
220
- id: nil,
221
- partial: 'form',
222
- locals: {},
223
- action: 'replace'
224
- }
225
- ], # additional partials that should be rendered if save_action failed
226
- add_flash_alerts: [], #=> array of strings
227
- add_flash_notices: [], #=> array of strings
228
- flashes_on_success: [], #=> array of strings
229
- flashes_on_error: [] #=> array of strings
230
-
231
- ## Requirements
232
-
233
- gem `turbo_power` (is included, used for redirection)
234
-
235
- **Translations**
236
-
237
- `activerecord.success.successfully_created`
238
-
239
- `activerecord.success.successfully_updated`
240
-
241
- `activerecord.errors.messages.could_not_create`
242
-
243
- `activerecord.errors.messages.could_not_update`
244
-
245
- example value: `"%<model_name>s successfully created"`
246
-
247
- .. and Model Name Translations
248
-
249
254
  ## Contributing
250
255
 
251
256
  Contribution welcome.
@@ -0,0 +1,49 @@
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 %>
@@ -6,6 +6,7 @@
6
6
  <% rendered_partials.push({ command: "turbo_stream.#{s.first}, #{s[1..-1].join(', ')}", method: s.first, attributes: s[1..-1] }) %>
7
7
  <% else %>
8
8
  <% html = (render s[:partial], locals: s[:locals]&.symbolize_keys, formats: [:html]) %>
9
+ <% s.delete(:partial) %>
9
10
  <% rendered_partials.push({ html_response: html }.merge(s)) %>
10
11
  <% end %>
11
12
 
@@ -2,16 +2,8 @@ module RenderTurboStream
2
2
  module Test
3
3
  module RequestHelpers
4
4
 
5
- # count of rendered partials
6
- # count rendering different partials (example: «customer/form»)
7
- # doesnt check how often a specific partial is rendered
8
-
9
- def partials_count
10
- RenderTurboStream::Libs.partials_count(response)
11
- end
12
-
13
5
  # log as helper for the developer to see which flash is set and which partials are rendered to wich ids
14
- def partials_log
6
+ def streams_log
15
7
  all_responses = RenderTurboStream::Libs.all_responses(response)
16
8
  r = []
17
9
  if response.status == 302
@@ -48,12 +40,12 @@ module RenderTurboStream
48
40
  url
49
41
  end
50
42
 
51
- def partial_response_count(partial, id: nil, total: 1, &block)
52
- RenderTurboStream::Libs.partial_response_count(response, partial, id, total, &block)
43
+ def stream_target_response_count(id, total: 1, &block)
44
+ RenderTurboStream::Libs.stream_target_response_count(response, id, total, &block)
53
45
  end
54
46
 
55
- def partial_response(partial, id: nil, css: nil, include_string: nil)
56
- c = partial_response_count(partial, id: id, total: 1) do |r|
47
+ def stream_response(id, css: nil, include_string: nil)
48
+ c = stream_target_response_count(id, total: 1) do |r|
57
49
  if css && include_string
58
50
  r.css(css).inner_html.include?(include_string)
59
51
  elsif include_string
@@ -67,8 +59,12 @@ module RenderTurboStream
67
59
  c == 1
68
60
  end
69
61
 
70
- def partial_ids
71
- RenderTurboStream::Libs.partial_ids(response)
62
+ def stream_targets
63
+ RenderTurboStream::Libs.stream_targets(response)
64
+ end
65
+
66
+ def stream_targets_count
67
+ stream_targets.length
72
68
  end
73
69
  end
74
70
  end
@@ -1,3 +1,3 @@
1
1
  module RenderTurboStream
2
- VERSION = "1.3.3"
2
+ VERSION = "1.4.2"
3
3
  end
@@ -8,7 +8,8 @@ module RenderTurboStream
8
8
 
9
9
  def turbo_stream_save(
10
10
  save_action,
11
- redirect_on_success_to: nil,
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
12
+ turbo_redirect_on_success_to: nil, # does a full page redirect (break out of all frames by turbo_power redirect)
12
13
  object: nil, # object used in save_action, example: @customer
13
14
  id: 'form', # if nil: no partial is rendered
14
15
  partial: nil, # example: 'customers/form' default: "#{controller_path}/#{id}"
@@ -122,16 +123,27 @@ module RenderTurboStream
122
123
 
123
124
  #== render
124
125
 
125
- if save_action && redirect_on_success_to.present?
126
+ if save_action && turbo_redirect_on_success_to.present?
126
127
  response.status = 302
127
128
  flash[:alert] = flash_alerts
128
129
  flash[:notice] = flash_notices
129
- render_turbo_stream([
130
- [
131
- :redirect_to,
132
- redirect_on_success_to
133
- ]
134
- ])
130
+ render_turbo_stream([
131
+ [
132
+ :redirect_to,
133
+ turbo_redirect_on_success_to
134
+ ]
135
+ ])
136
+ elsif save_action && redirect_on_success_to.present?
137
+ response.status = 302
138
+ flash.now[:alert] = flash_alerts
139
+ flash.now[:notice] = flash_notices
140
+ render_turbo_stream([
141
+ [
142
+ :redirect_to,
143
+ turbo_redirect_on_success_to
144
+ ]
145
+ ])
146
+
135
147
  else
136
148
  flash.now[:alert] = flash_alerts
137
149
  flash.now[:notice] = flash_notices
@@ -191,40 +203,20 @@ module RenderTurboStream
191
203
 
192
204
  # if partial is one word, its checked against the last behind the slash, example: 'articles/form' matches 'form'
193
205
 
194
- def self.select_responses(response, partial, id)
206
+ def self.select_responses(response, id)
195
207
  all = all_responses(response)
196
208
 
197
209
  all.select do |a|
198
- partial_matched = if !partial.present?
199
- false
200
- elsif partial.include?('/')
201
- a['partial'] == partial
202
- else
203
- a['partial'].split('/').last == partial
204
- end
205
- id_matched = if !id.present?
206
- false
207
- else
208
- a['id'] == id
209
- end
210
- partial_matched && (id.present? ? id_matched : true)
211
- end
212
-
213
- end
214
-
215
- def self.partials_count(response)
216
- all = all_responses(response)
217
- part = []
218
- all.each do |p|
219
- _p = p['partial']
220
- unless part.include?(_p)
221
- part.push(_p)
210
+ if id.present?
211
+ a['id'] == id
212
+ else
213
+ false
222
214
  end
223
215
  end
224
- part.length
216
+
225
217
  end
226
218
 
227
- def self.partial_ids(response)
219
+ def self.stream_targets(response)
228
220
  all = all_responses(response)
229
221
  ids = []
230
222
  all.each do |p|
@@ -236,8 +228,8 @@ module RenderTurboStream
236
228
  ids
237
229
  end
238
230
 
239
- def self.partial_response_count(response, partial, id, total, &block)
240
- responses = select_responses(response, partial, id)
231
+ def self.stream_target_response_count(response, id, total, &block)
232
+ responses = select_responses(response, id)
241
233
 
242
234
  if total && responses.count != total
243
235
  false
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.3.3
4
+ version: 1.4.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-04-23 00:00:00.000000000 Z
11
+ date: 2023-04-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -38,6 +38,7 @@ 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
41
42
  - app/views/render_turbo_stream.html.erb
42
43
  - app/views/render_turbo_stream.turbo_stream.erb
43
44
  - lib/render_turbo_stream.rb