unpoly-rails 2.0.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of unpoly-rails might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.yardopts +1 -1
- data/README.md +472 -50
- data/{dist → assets/unpoly}/unpoly-bootstrap3.css +1 -0
- data/assets/unpoly/unpoly-bootstrap3.js +54 -0
- data/{dist → assets/unpoly}/unpoly-bootstrap3.min.css +1 -0
- data/assets/unpoly/unpoly-bootstrap3.min.js +1 -0
- data/{dist → assets/unpoly}/unpoly-bootstrap4.css +1 -0
- data/assets/unpoly/unpoly-bootstrap4.js +54 -0
- data/{dist → assets/unpoly}/unpoly-bootstrap4.min.css +1 -0
- data/assets/unpoly/unpoly-bootstrap4.min.js +1 -0
- data/{dist → assets/unpoly}/unpoly-bootstrap5.css +1 -0
- data/assets/unpoly/unpoly-bootstrap5.js +56 -0
- data/{dist → assets/unpoly}/unpoly-bootstrap5.min.css +1 -0
- data/assets/unpoly/unpoly-bootstrap5.min.js +1 -0
- data/assets/unpoly/unpoly-migrate.js +1281 -0
- data/assets/unpoly/unpoly-migrate.min.js +1 -0
- data/{dist → assets/unpoly}/unpoly.css +23 -6
- data/assets/unpoly/unpoly.es5.js +23195 -0
- data/assets/unpoly/unpoly.es5.min.js +1 -0
- data/assets/unpoly/unpoly.js +21457 -0
- data/assets/unpoly/unpoly.min.css +10 -0
- data/assets/unpoly/unpoly.min.js +1 -0
- data/lib/unpoly-rails.rb +12 -12
- data/lib/unpoly/rails/controller.rb +1 -1
- data/lib/unpoly/rails/engine.rb +13 -20
- data/lib/unpoly/rails/request_echo_headers.rb +1 -1
- data/lib/unpoly/rails/version.rb +1 -1
- metadata +52 -26
- data/CHANGELOG.md +0 -2345
- data/README_RAILS.md +0 -418
- data/dist/unpoly-bootstrap3.js +0 -16
- data/dist/unpoly-bootstrap3.min.js +0 -1
- data/dist/unpoly-bootstrap4.js +0 -16
- data/dist/unpoly-bootstrap4.min.js +0 -1
- data/dist/unpoly-bootstrap5.js +0 -14
- data/dist/unpoly-bootstrap5.min.js +0 -1
- data/dist/unpoly-migrate.js +0 -1306
- data/dist/unpoly-migrate.min.js +0 -1
- data/dist/unpoly.js +0 -22134
- data/dist/unpoly.min.css +0 -1
- data/dist/unpoly.min.js +0 -6
- data/lib/unpoly/tasks.rb +0 -55
data/README_RAILS.md
DELETED
@@ -1,418 +0,0 @@
|
|
1
|
-
unpoly-rails: Ruby on Rails bindings for Unpoly
|
2
|
-
===============================================
|
3
|
-
|
4
|
-
[Unpoly](https://unpoly.com) is a backend-agnostic [unobtrusive JavaScript](https://en.wikipedia.org/wiki/Unobtrusive_JavaScript) framework. `unpoly-rails` gives a [Ruby on Rails](http://rubyonrails.org/) application some convenience methods to communicate with an Unpoly-enhanced frontend.
|
5
|
-
|
6
|
-
Note that the bindings provided by `unpoly-rails` are entirely optional. You are free to use Unpoly with Rails without the `unpoly-rails` gem.
|
7
|
-
|
8
|
-
|
9
|
-
Features
|
10
|
-
--------
|
11
|
-
|
12
|
-
The methods documented below are available in all controllers, views and helpers.
|
13
|
-
|
14
|
-
### Detecting a fragment update
|
15
|
-
|
16
|
-
Use `up?` to test whether the current request is a [fragment update](https://unpoly.com/up.link):
|
17
|
-
|
18
|
-
```ruby
|
19
|
-
up? # => true or false
|
20
|
-
```
|
21
|
-
|
22
|
-
To retrieve the CSS selector that is being [updated](https://unpoly.com/up.link), use `up.target`:
|
23
|
-
|
24
|
-
```ruby
|
25
|
-
up.target # => '.content'
|
26
|
-
```
|
27
|
-
|
28
|
-
The Unpoly frontend will expect an HTML response containing an element that matches this selector. Your Rails app is free to render a smaller response that only contains HTML matching the targeted selector. You may call `up.target?` to test whether a given CSS selector has been targeted:
|
29
|
-
|
30
|
-
```ruby
|
31
|
-
if up.target?('.sidebar')
|
32
|
-
render('expensive_sidebar_partial')
|
33
|
-
end
|
34
|
-
```
|
35
|
-
|
36
|
-
Fragment updates may target different selectors for successful (HTTP status `200 OK`) and failed (status `4xx` or `5xx`) responses.
|
37
|
-
Use these methods to inspect the target for failed responses:
|
38
|
-
|
39
|
-
- `up.fail_target`: The CSS selector targeted for a failed response
|
40
|
-
- `up.fail_target?(selector)`: Whether the given selector is targeted for a failed response
|
41
|
-
- `up.any_target?(selector)`: Whether the given selector is targeted for either a successful or a failed response
|
42
|
-
|
43
|
-
### Changing the render target
|
44
|
-
|
45
|
-
The server may instruct the frontend to render a different target by assigning a new CSS selector to the `up.target` property:
|
46
|
-
|
47
|
-
```ruby
|
48
|
-
unless signed_in?
|
49
|
-
up.target = 'body'
|
50
|
-
render 'sign_in'
|
51
|
-
end
|
52
|
-
```
|
53
|
-
|
54
|
-
The frontend will use the server-provided target for both successful (HTTP status `200 OK`) and failed (status `4xx` or `5xx`) responses.
|
55
|
-
|
56
|
-
|
57
|
-
### Rendering nothing
|
58
|
-
|
59
|
-
Sometimes it's OK to render nothing, e.g. when you know that the current layer is to be closed.
|
60
|
-
|
61
|
-
In this case you may call `up.render_nothing`:
|
62
|
-
|
63
|
-
```ruby
|
64
|
-
class NotesController < ApplicationController
|
65
|
-
def create
|
66
|
-
@note = Note.new(note_params)
|
67
|
-
if @note.save
|
68
|
-
if up.layer.overlay?
|
69
|
-
up.accept_layer(@note.id)
|
70
|
-
up.render_nothing
|
71
|
-
else
|
72
|
-
redirect_to @note
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
```
|
78
|
-
|
79
|
-
This will render a 200 OK response with a header `X-Up-Target: none` and an empty body.
|
80
|
-
|
81
|
-
You may render nothing with a different HTTP status by passing a `:status` option:
|
82
|
-
|
83
|
-
```
|
84
|
-
up.render_nothing(status: :bad_request)
|
85
|
-
```
|
86
|
-
|
87
|
-
|
88
|
-
### Pushing a document title to the client
|
89
|
-
|
90
|
-
To force Unpoly to set a document title when processing the response:
|
91
|
-
|
92
|
-
```ruby
|
93
|
-
up.title = 'Title from server'
|
94
|
-
```
|
95
|
-
|
96
|
-
This is useful when you skip rendering the `<head>` in an Unpoly request.
|
97
|
-
|
98
|
-
### Emitting events on the frontend
|
99
|
-
|
100
|
-
You may use `up.emit` to emit an event on the `document` after the
|
101
|
-
fragment was updated:
|
102
|
-
|
103
|
-
```ruby
|
104
|
-
class UsersController < ApplicationController
|
105
|
-
|
106
|
-
def show
|
107
|
-
@user = User.find(params[:id])
|
108
|
-
up.emit('user:selected', id: @user.id)
|
109
|
-
end
|
110
|
-
|
111
|
-
end
|
112
|
-
```
|
113
|
-
|
114
|
-
If you wish to emit an event on the current [layer](https://unpoly.com/up.layer)
|
115
|
-
instead of the `document`, use `up.layer.emit`:
|
116
|
-
|
117
|
-
```ruby
|
118
|
-
class UsersController < ApplicationController
|
119
|
-
|
120
|
-
def show
|
121
|
-
@user = User.find(params[:id])
|
122
|
-
up.layer.emit('user:selected', id: @user.id)
|
123
|
-
end
|
124
|
-
|
125
|
-
end
|
126
|
-
```
|
127
|
-
|
128
|
-
### Detecting an Unpoly form validation
|
129
|
-
|
130
|
-
To test whether the current request is a [form validation](https://unpoly.com/input-up-validate):
|
131
|
-
|
132
|
-
```ruby
|
133
|
-
up.validate?
|
134
|
-
```
|
135
|
-
|
136
|
-
When detecting a validation request, the server is expected to validate (but not save) the form submission and render a new copy of the form with validation errors. A typical saving action should behave like this:
|
137
|
-
|
138
|
-
```ruby
|
139
|
-
class UsersController < ApplicationController
|
140
|
-
|
141
|
-
def create
|
142
|
-
user_params = params[:user].permit(:email, :password)
|
143
|
-
@user = User.new(user_params)
|
144
|
-
if up.validate?
|
145
|
-
@user.valid? # run validations, but don't save to the database
|
146
|
-
render 'form' # render form with error messages
|
147
|
-
elsif @user.save?
|
148
|
-
sign_in @user
|
149
|
-
else
|
150
|
-
render 'form', status: :bad_request
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
end
|
155
|
-
```
|
156
|
-
|
157
|
-
### Detecting a fragment reload
|
158
|
-
|
159
|
-
To test whether the current request was made to [reload](https://unpoly.com/up.reload) or [poll](https://unpoly.com/up-poll) a fragment:
|
160
|
-
|
161
|
-
```ruby
|
162
|
-
up.reload?
|
163
|
-
```
|
164
|
-
|
165
|
-
You also retrieve the time when the fragment being reloaded was previously inserted into the DOM:
|
166
|
-
|
167
|
-
```ruby
|
168
|
-
up.reload_from_time # returns a Time object
|
169
|
-
```
|
170
|
-
|
171
|
-
The server can compare the time from the request with the time of the last data update.
|
172
|
-
If no more recent data is available, the server can [render nothing](/X-Up-Target):
|
173
|
-
|
174
|
-
```ruby
|
175
|
-
class MessagesController < ApplicationController
|
176
|
-
|
177
|
-
def index
|
178
|
-
if up.reload_from_time == current_user.last_message_at
|
179
|
-
up.render_nothing
|
180
|
-
else
|
181
|
-
@messages = current_user.messages.order(time: :desc).to_a
|
182
|
-
render 'index'
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
end
|
187
|
-
```
|
188
|
-
|
189
|
-
Only rendering when needed saves <b>CPU time</b> on your server, which spends most of its response time rendering HTML.
|
190
|
-
|
191
|
-
This also reduces the <b>bandwidth cost</b> for a request/response exchange to **~1 KB**.
|
192
|
-
|
193
|
-
|
194
|
-
### Working with context
|
195
|
-
|
196
|
-
Calling `up.context` will return the [context](https://unpoly.com/up.context) object of the targeted layer.
|
197
|
-
|
198
|
-
The context is a JSON object shared between the frontend and the server.
|
199
|
-
It persists for a series of Unpoly navigation, but is cleared when the user makes a full page load.
|
200
|
-
Different Unpoly [layers](https://unpoly.com/up.layer) will usually have separate context objects,
|
201
|
-
although layers may choose to share their context scope.
|
202
|
-
|
203
|
-
You may read and change the context object. Changes will be sent to the frontend with your response.
|
204
|
-
|
205
|
-
```ruby
|
206
|
-
class GamesController < ApplicationController
|
207
|
-
|
208
|
-
def restart
|
209
|
-
up.context[:lives] = 3
|
210
|
-
render 'stage1'
|
211
|
-
end
|
212
|
-
|
213
|
-
end
|
214
|
-
```
|
215
|
-
|
216
|
-
Keys can be accessed as either strings or symbols:
|
217
|
-
|
218
|
-
```ruby
|
219
|
-
puts "You have " + up.layer.context[:lives] + " lives left"
|
220
|
-
puts "You have " + up.layer.context['lives'] + " lives left"
|
221
|
-
````
|
222
|
-
|
223
|
-
You may delete a key from the frontend by calling `up.context.delete`:
|
224
|
-
|
225
|
-
```ruby
|
226
|
-
up.context.delete(:foo)
|
227
|
-
````
|
228
|
-
|
229
|
-
You may replace the entire context by calling `up.context.replace`:
|
230
|
-
|
231
|
-
```
|
232
|
-
context_from_file = JSON.parse(File.read('context.json))
|
233
|
-
up.context.replace(context_from_file)
|
234
|
-
```
|
235
|
-
|
236
|
-
`up.context` is an alias for `up.layer.context`.
|
237
|
-
|
238
|
-
|
239
|
-
### Accessing the targeted layer
|
240
|
-
|
241
|
-
Use the methods below to interact with the [layer](/up.layer) of the fragment being targeted.
|
242
|
-
|
243
|
-
Note that fragment updates may target different layers for successful (HTTP status `200 OK`) and failed (status `4xx` or `5xx`) responses.
|
244
|
-
|
245
|
-
#### `up.layer.mode`
|
246
|
-
|
247
|
-
Returns the [mode](https://unpoly.com/up.layer.mode) of the targeted layer (e.g. `"root"` or `"modal"`).
|
248
|
-
|
249
|
-
#### `up.layer.root?`
|
250
|
-
|
251
|
-
Returns whether the targeted layer is the root layer.
|
252
|
-
|
253
|
-
#### `up.layer.overlay?`
|
254
|
-
|
255
|
-
Returns whether the targeted layer is an overlay (not the root layer).
|
256
|
-
|
257
|
-
#### `up.layer.context`
|
258
|
-
|
259
|
-
Returns the [context](https://unpoly.com/up.context) object of the targeted layer.
|
260
|
-
See documentation for `up.context`, which is an alias for `up.layer.context`.
|
261
|
-
|
262
|
-
#### `up.layer.accept(value)`
|
263
|
-
|
264
|
-
[Accepts](https://unpoly.com/up.layer.accept) the current overlay.
|
265
|
-
|
266
|
-
Does nothing if the root layer is targeted.
|
267
|
-
|
268
|
-
Note that Rails expects every controller action to render or redirect.
|
269
|
-
Your action should either call `up.render_nothing` or respond with `text/html` content matching the requested target.
|
270
|
-
|
271
|
-
#### `up.layer.dismiss(value)`
|
272
|
-
|
273
|
-
[Dismisses](https://unpoly.com/up.layer.dismisses) the current overlay.
|
274
|
-
|
275
|
-
Does nothing if the root layer is targeted.
|
276
|
-
|
277
|
-
Note that Rails expects every controller action to render or redirect.
|
278
|
-
Your action should either call `up.render_nothing` or respond with `text/html` content matching the requested target.
|
279
|
-
|
280
|
-
#### `up.layer.emit(type, options)`
|
281
|
-
|
282
|
-
[Emits an event](https://unpoly.com/up.layer.emit) on the targeted layer.
|
283
|
-
|
284
|
-
#### `up.fail_layer.mode`
|
285
|
-
|
286
|
-
Returns the [mode](https://unpoly.com/up.layer.mode) of the layer targeted for a failed response.
|
287
|
-
|
288
|
-
#### `up.fail_layer.root?`
|
289
|
-
|
290
|
-
Returns whether the layer targeted for a failed response is the root layer.
|
291
|
-
|
292
|
-
#### `up.fail_layer.overlay?`
|
293
|
-
|
294
|
-
Returns whether the layer targeted for a failed response is an overlay.
|
295
|
-
|
296
|
-
#### `up.fail_layer.context`
|
297
|
-
|
298
|
-
Returns the [context](https://unpoly.com/up.context) object of the layer targeted for a failed response.
|
299
|
-
|
300
|
-
|
301
|
-
### Managing the client-side cache
|
302
|
-
|
303
|
-
The Unpoly frontend caches server responses for a few minutes, making requests to these URLs return instantly.
|
304
|
-
Only `GET` requests are cached. The *entire* cache is cleared after every non-`GET` request (like `POST` or `PUT`).
|
305
|
-
|
306
|
-
The server may override these defaults. For instance, the server can clear Unpoly's client-side response cache, even for `GET` requests:
|
307
|
-
|
308
|
-
```ruby
|
309
|
-
up.cache.clear
|
310
|
-
```
|
311
|
-
|
312
|
-
You may also clear a single page:
|
313
|
-
|
314
|
-
```ruby
|
315
|
-
up.cache.clear('/notes/1034')
|
316
|
-
```
|
317
|
-
|
318
|
-
You may also clear all entries matching a URL pattern:
|
319
|
-
|
320
|
-
```ruby
|
321
|
-
up.cache.clear('/notes/*')
|
322
|
-
```
|
323
|
-
|
324
|
-
You may also prevent cache clearing for an unsafe request:
|
325
|
-
|
326
|
-
```ruby
|
327
|
-
up.cache.keep
|
328
|
-
```
|
329
|
-
|
330
|
-
Here is an longer example where the server uses careful cache management to keep as much of the client-side cache as possible:
|
331
|
-
|
332
|
-
```ruby
|
333
|
-
def NotesController < ApplicationController
|
334
|
-
|
335
|
-
def create
|
336
|
-
@note = Note.create!(params[:note].permit(...))
|
337
|
-
if @note.save
|
338
|
-
up.cache.clear('/notes/*') # Only clear affected entries
|
339
|
-
redirect_to(@note)
|
340
|
-
else
|
341
|
-
up.cache.keep # Keep the cache because we haven't saved
|
342
|
-
render 'new'
|
343
|
-
end
|
344
|
-
end
|
345
|
-
...
|
346
|
-
end
|
347
|
-
```
|
348
|
-
|
349
|
-
|
350
|
-
### Preserving Unpoly-related request information through redirects
|
351
|
-
|
352
|
-
`unpoly-rails` patches [`redirect_to`](https://api.rubyonrails.org/classes/ActionController/Redirecting.html#method-i-redirect_to)
|
353
|
-
so Unpoly-related request information (like the CSS selector being targeted for a fragment
|
354
|
-
update) will be preserved for the action you redirect to.
|
355
|
-
|
356
|
-
|
357
|
-
### Automatic redirect detection
|
358
|
-
|
359
|
-
`unpoly-rails` installs a [`before_action`](https://api.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-before_action) into all controllers which echoes the request's URL as a response header `X-Up-Location` and the request's
|
360
|
-
HTTP method as `X-Up-Method`.
|
361
|
-
|
362
|
-
|
363
|
-
### Automatic method detection for initial page load
|
364
|
-
|
365
|
-
`unpoly-rails` sets an `_up_method` cookie that Unpoly needs to detect the request method for the initial page load.
|
366
|
-
|
367
|
-
If the initial page was loaded with a non-`GET` HTTP method, Unpoly will fall back to full page loads for all actions that require `pushState`.
|
368
|
-
|
369
|
-
The reason for this is that some browsers remember the method of the initial page load and don't let the application change it, even with `pushState`. Thus, when the user reloads the page much later, an affected browser might request a `POST`, `PUT`, etc. instead of the correct method.
|
370
|
-
|
371
|
-
|
372
|
-
What you still need to do manually
|
373
|
-
----------------------------------
|
374
|
-
|
375
|
-
### Failed form submissions must return a non-200 status code
|
376
|
-
|
377
|
-
Unpoly lets you submit forms via AJAX by using the [`form[up-follow]`](https://unpoly.com/form-up-submit) selector or [`up.submit()`](https://unpoly.com/up.submit) function.
|
378
|
-
|
379
|
-
For Unpoly to be able to detect a failed form submission,
|
380
|
-
the form must be re-rendered with a non-200 HTTP status code.
|
381
|
-
We recommend to use either 400 (bad request) or 422 (unprocessable entity).
|
382
|
-
|
383
|
-
To do so in Rails, pass a [`:status` option to `render`](http://guides.rubyonrails.org/layouts_and_rendering.html#the-status-option):
|
384
|
-
|
385
|
-
```ruby
|
386
|
-
class UsersController < ApplicationController
|
387
|
-
|
388
|
-
def create
|
389
|
-
user_params = params[:user].permit(:email, :password)
|
390
|
-
@user = User.new(user_params)
|
391
|
-
if @user.save?
|
392
|
-
sign_in @user
|
393
|
-
else
|
394
|
-
render 'form', status: :bad_request
|
395
|
-
end
|
396
|
-
end
|
397
|
-
|
398
|
-
end
|
399
|
-
```
|
400
|
-
|
401
|
-
Development
|
402
|
-
-----------
|
403
|
-
|
404
|
-
### Before you make a PR
|
405
|
-
|
406
|
-
Before you create a pull request, please have some discussion about the proposed change by [opening an issue on GitHub](https://github.com/unpoly/unpoly/issues/new).
|
407
|
-
|
408
|
-
### Running tests
|
409
|
-
|
410
|
-
- Install Ruby 2.3.8
|
411
|
-
- Install Bundler by running `gem install bundler`
|
412
|
-
- `cd` into `spec_app`
|
413
|
-
- Install dependencies by running `bundle install`
|
414
|
-
- Run `rspec`
|
415
|
-
|
416
|
-
### Making a new release
|
417
|
-
|
418
|
-
New versions of `unpoly-rails` are released as part of the [Unpoly release process](https://github.com/unpoly/unpoly/blob/master/README.md#making-a-new-release), which also feeds other package managers like Bower or npm.
|
data/dist/unpoly-bootstrap3.js
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
(function() {
|
2
|
-
up.feedback.config.currentClasses.push('active');
|
3
|
-
|
4
|
-
up.feedback.config.navSelectors.push('.nav', '.navbar');
|
5
|
-
|
6
|
-
up.form.config.validateTargets.unshift('.form-group:has(:origin)');
|
7
|
-
|
8
|
-
up.viewport.config.fixedTop.push('.navbar-fixed-top');
|
9
|
-
|
10
|
-
up.viewport.config.fixedBottom.push('.navbar-fixed-bottom');
|
11
|
-
|
12
|
-
up.viewport.config.anchoredRight.push('.navbar-fixed-top', '.navbar-fixed-bottom');
|
13
|
-
|
14
|
-
up.fragment.config.badTargetClasses.push('row', /^col(-xs|-sm|-md|-lg)?(-\d+)?$/);
|
15
|
-
|
16
|
-
}).call(this);
|
@@ -1 +0,0 @@
|
|
1
|
-
(function(){up.feedback.config.currentClasses.push("active"),up.feedback.config.navSelectors.push(".nav",".navbar"),up.form.config.validateTargets.unshift(".form-group:has(:origin)"),up.viewport.config.fixedTop.push(".navbar-fixed-top"),up.viewport.config.fixedBottom.push(".navbar-fixed-bottom"),up.viewport.config.anchoredRight.push(".navbar-fixed-top",".navbar-fixed-bottom"),up.fragment.config.badTargetClasses.push("row",/^col(-xs|-sm|-md|-lg)?(-\d+)?$/)}).call(this);
|
data/dist/unpoly-bootstrap4.js
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
(function() {
|
2
|
-
up.feedback.config.currentClasses.push('active');
|
3
|
-
|
4
|
-
up.feedback.config.navSelectors.push('.nav', '.navbar');
|
5
|
-
|
6
|
-
up.form.config.validateTargets.unshift('.form-group:has(:origin)');
|
7
|
-
|
8
|
-
up.viewport.config.fixedTop.push('.navbar.fixed-top');
|
9
|
-
|
10
|
-
up.viewport.config.fixedBottom.push('.navbar.fixed-bottom');
|
11
|
-
|
12
|
-
up.viewport.config.anchoredRight.push('.navbar.fixed-top', '.navbar.fixed-bottom');
|
13
|
-
|
14
|
-
up.fragment.config.badTargetClasses.push('row', /^col(-xs|-sm|-md|-lg|-xl)?(-\d+)?$/, /^[mp][tblrxy]?-\d+$/);
|
15
|
-
|
16
|
-
}).call(this);
|