unpoly-rails 2.1.0 → 2.2.0.pre.rc1
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 -51
- data/assets/unpoly/jasmine.css +60 -0
- data/assets/unpoly/jasmine.js +19300 -0
- data/assets/unpoly/specs.css +18 -0
- data/assets/unpoly/specs.es5.js +28121 -0
- data/assets/unpoly/specs.js +28265 -0
- 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 -14
- 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 +61 -30
- data/CHANGELOG.md +0 -2373
- 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 -1339
- data/dist/unpoly-migrate.min.js +0 -1
- data/dist/unpoly.js +0 -22266
- data/dist/unpoly.min.css +0 -1
- data/dist/unpoly.min.js +0 -6
- data/lib/unpoly/tasks.rb +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 464327851ae1ae81375bf266c2bfccee83ddd8f132d5601b3ecaa6a9a58493b2
|
4
|
+
data.tar.gz: 52953ca1397f0882b2eed6fe36057763e96aeb2877ac156b7399f5c03fd320d1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac76cdbed077885cb5b01ef093354e879e73126828dabb8d6667f22b8bbd81f22d4a4d8f8d0618b4949396f6cb9f8b77e42f768fcb7bd9bc417aaa3b0773cf7f
|
7
|
+
data.tar.gz: c91cb3b267b595aa0ac96eae2212f463f1ee527cf72ca48426ddf7629bf35c98a8180f05d4607cb3fe8891a437716af1a46ab26e7fe85ded53d4f058f7dbdf6c
|
data/.yardopts
CHANGED
@@ -1 +1 @@
|
|
1
|
-
--markup=markdown --main=
|
1
|
+
--markup=markdown --main=README.md --exclude .
|
data/README.md
CHANGED
@@ -1,83 +1,504 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
unpoly-rails: Ruby on Rails bindings for Unpoly
|
2
|
+
===============================================
|
3
3
|
|
4
|
-
|
5
|
-
-------------------------------------------------------------
|
4
|
+
[Unpoly](https://unpoly.com) is an [unobtrusive JavaScript](https://en.wikipedia.org/wiki/Unobtrusive_JavaScript) framework for server-side web applications.
|
6
5
|
|
7
|
-
|
6
|
+
The `unpoly-rails` gem helps integrating Unpoly with [Ruby on Rails](https://rubyonrails.org/) applications.
|
8
7
|
|
9
|
-
This repository is home to both the Unpoly 2 JavaScript code and its (optional) bindings for Ruby on Rails (`unpoly-rails` gem).
|
10
8
|
|
11
|
-
|
9
|
+
Installing the gem
|
10
|
+
------------------
|
12
11
|
|
12
|
+
Add the following line to your `Gemfile`:
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
```ruby
|
15
|
+
gem 'unpoly-rails'
|
16
|
+
```
|
16
17
|
|
17
|
-
|
18
|
-
- See [installation instructions](https://unpoly.com/install) for many different package managers and languages.
|
19
|
-
- See [`CHANGELOG.md`](https://github.com/unpoly/unpoly/blob/master/CHANGELOG.md) for notable changes.
|
20
|
-
- See [`README_RAILS.md`](https://github.com/unpoly/unpoly/blob/master/README_RAILS.md) documentation of the Rails bindings.
|
18
|
+
Now run `bundle install` and restart your development server.
|
21
19
|
|
22
20
|
|
23
|
-
|
24
|
-
|
21
|
+
Installing frontend assets
|
22
|
+
--------------------------
|
25
23
|
|
26
|
-
###
|
24
|
+
### With Webpacker
|
27
25
|
|
28
|
-
|
26
|
+
If you're using [Webpacker](https://edgeguides.rubyonrails.org/webpacker.html), install the [`unpoly` npm package](https://unpoly.com/install/npm) to get Unpoly's frontend files.
|
29
27
|
|
30
|
-
|
31
|
-
- There's a Rails app in `spec_app`
|
32
|
-
- Jasmine tests for Unpoly live in `spec_app/spec/javascripts`
|
33
|
-
- RSpec tests for the `unpoly-rails` gem live in `spec_app/spec/controllers`
|
28
|
+
Now `import` Unpoly from your `application.js` pack:
|
34
29
|
|
35
|
-
|
30
|
+
```js
|
31
|
+
import 'unpoly/unpoly.js'
|
32
|
+
import 'unpoly/unpoly.css'
|
33
|
+
```
|
36
34
|
|
37
|
-
|
38
|
-
- Install Bundler by running `gem install bundler`
|
39
|
-
- Install Node.js (required for building the library)
|
40
|
-
- `cd` into `spec_app`
|
41
|
-
- Install dependencies by running `bundle install`
|
35
|
+
You may need to import [additional files](https://unpoly.com/install), e.g. when migrating from an old Unpoly version.
|
42
36
|
|
43
|
-
To run Jasmine tests for Unpoly:
|
44
37
|
|
45
|
-
|
46
|
-
- Start the Rails server by running `rails server`
|
47
|
-
- Access `http://localhost:3000/specs` to see the Jasmine test runner
|
38
|
+
### With the Asset Pipeline
|
48
39
|
|
49
|
-
|
40
|
+
If you're using the [Asset Pipeline](https://guides.rubyonrails.org/asset_pipeline.html), this `unpoly-rails` gem also contains Unpoly's frontend files. The files are automatically added to the Asset Pipeline's <a href="https://guides.rubyonrails.org/asset_pipeline.html#search-paths">search path</a>.
|
50
41
|
|
51
|
-
|
52
|
-
- Run `rspec`
|
42
|
+
Add the following line to your `application.js` manifest:
|
53
43
|
|
44
|
+
```js
|
45
|
+
//= require unpoly
|
46
|
+
```
|
54
47
|
|
55
|
-
|
48
|
+
Also add the following line to your `application.css` manifest:
|
49
|
+
|
50
|
+
```css
|
51
|
+
/*
|
52
|
+
*= require unpoly
|
53
|
+
*/
|
54
|
+
```
|
55
|
+
|
56
|
+
You may need to require [additional files](https://unpoly.com/install), e.g. when migrating from an old Unpoly version.
|
57
|
+
|
58
|
+
|
59
|
+
Server protocol helpers
|
60
|
+
-----------------------
|
61
|
+
|
62
|
+
This `unpoly-rails` gem implements the <a href="https://unpoly.com/up.protocol">optional server protocol</a> by providing the following helper methods to your controllers, views and helpers.
|
63
|
+
|
64
|
+
|
65
|
+
### Detecting a fragment update
|
66
|
+
|
67
|
+
Use `up?` to test whether the current request is a [fragment update](https://unpoly.com/up.link):
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
up? # => true or false
|
71
|
+
```
|
72
|
+
|
73
|
+
To retrieve the CSS selector that is being [updated](https://unpoly.com/up.link), use `up.target`:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
up.target # => '.content'
|
77
|
+
```
|
78
|
+
|
79
|
+
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:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
if up.target?('.sidebar')
|
83
|
+
render('expensive_sidebar_partial')
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
Fragment updates may target different selectors for successful (HTTP status `200 OK`) and failed (status `4xx` or `5xx`) responses.
|
88
|
+
Use these methods to inspect the target for failed responses:
|
89
|
+
|
90
|
+
- `up.fail_target`: The CSS selector targeted for a failed response
|
91
|
+
- `up.fail_target?(selector)`: Whether the given selector is targeted for a failed response
|
92
|
+
- `up.any_target?(selector)`: Whether the given selector is targeted for either a successful or a failed response
|
93
|
+
|
94
|
+
### Changing the render target
|
95
|
+
|
96
|
+
The server may instruct the frontend to render a different target by assigning a new CSS selector to the `up.target` property:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
unless signed_in?
|
100
|
+
up.target = 'body'
|
101
|
+
render 'sign_in'
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
The frontend will use the server-provided target for both successful (HTTP status `200 OK`) and failed (status `4xx` or `5xx`) responses.
|
106
|
+
|
107
|
+
|
108
|
+
### Rendering nothing
|
109
|
+
|
110
|
+
Sometimes it's OK to render nothing, e.g. when you know that the current layer is to be closed.
|
111
|
+
|
112
|
+
In this case you may call `up.render_nothing`:
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
class NotesController < ApplicationController
|
116
|
+
def create
|
117
|
+
@note = Note.new(note_params)
|
118
|
+
if @note.save
|
119
|
+
if up.layer.overlay?
|
120
|
+
up.accept_layer(@note.id)
|
121
|
+
up.render_nothing
|
122
|
+
else
|
123
|
+
redirect_to @note
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
```
|
129
|
+
|
130
|
+
This will render a 200 OK response with a header `X-Up-Target: none` and an empty body.
|
131
|
+
|
132
|
+
You may render nothing with a different HTTP status by passing a `:status` option:
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
up.render_nothing(status: :bad_request)
|
136
|
+
```
|
137
|
+
|
138
|
+
|
139
|
+
### Pushing a document title to the client
|
140
|
+
|
141
|
+
To force Unpoly to set a document title when processing the response:
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
up.title = 'Title from server'
|
145
|
+
```
|
146
|
+
|
147
|
+
This is useful when you skip rendering the `<head>` in an Unpoly request.
|
148
|
+
|
149
|
+
### Emitting events on the frontend
|
150
|
+
|
151
|
+
You may use `up.emit` to emit an event on the `document` after the
|
152
|
+
fragment was updated:
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
class UsersController < ApplicationController
|
156
|
+
|
157
|
+
def show
|
158
|
+
@user = User.find(params[:id])
|
159
|
+
up.emit('user:selected', id: @user.id)
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
If you wish to emit an event on the current [layer](https://unpoly.com/up.layer)
|
166
|
+
instead of the `document`, use `up.layer.emit`:
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
class UsersController < ApplicationController
|
170
|
+
|
171
|
+
def show
|
172
|
+
@user = User.find(params[:id])
|
173
|
+
up.layer.emit('user:selected', id: @user.id)
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
```
|
178
|
+
|
179
|
+
### Detecting an Unpoly form validation
|
180
|
+
|
181
|
+
To test whether the current request is a [form validation](https://unpoly.com/input-up-validate):
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
up.validate?
|
185
|
+
```
|
186
|
+
|
187
|
+
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:
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
class UsersController < ApplicationController
|
191
|
+
|
192
|
+
def create
|
193
|
+
user_params = params[:user].permit(:email, :password)
|
194
|
+
@user = User.new(user_params)
|
195
|
+
if up.validate?
|
196
|
+
@user.valid? # run validations, but don't save to the database
|
197
|
+
render 'form' # render form with error messages
|
198
|
+
elsif @user.save?
|
199
|
+
sign_in @user
|
200
|
+
else
|
201
|
+
render 'form', status: :bad_request
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
```
|
207
|
+
|
208
|
+
### Detecting a fragment reload
|
209
|
+
|
210
|
+
To test whether the current request was made to [reload](https://unpoly.com/up.reload) or [poll](https://unpoly.com/up-poll) a fragment:
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
up.reload?
|
214
|
+
```
|
215
|
+
|
216
|
+
You also retrieve the time when the fragment being reloaded was previously inserted into the DOM:
|
217
|
+
|
218
|
+
```ruby
|
219
|
+
up.reload_from_time # returns a Time object
|
220
|
+
```
|
221
|
+
|
222
|
+
The server can compare the time from the request with the time of the last data update.
|
223
|
+
If no more recent data is available, the server can [render nothing](/X-Up-Target):
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
class MessagesController < ApplicationController
|
227
|
+
|
228
|
+
def index
|
229
|
+
if up.reload_from_time == current_user.last_message_at
|
230
|
+
up.render_nothing
|
231
|
+
else
|
232
|
+
@messages = current_user.messages.order(time: :desc).to_a
|
233
|
+
render 'index'
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
```
|
239
|
+
|
240
|
+
Only rendering when needed saves <b>CPU time</b> on your server, which spends most of its response time rendering HTML.
|
241
|
+
|
242
|
+
This also reduces the <b>bandwidth cost</b> for a request/response exchange to **~1 KB**.
|
243
|
+
|
244
|
+
|
245
|
+
### Working with context
|
246
|
+
|
247
|
+
Calling `up.context` will return the [context](https://unpoly.com/up.context) object of the targeted layer.
|
248
|
+
|
249
|
+
The context is a JSON object shared between the frontend and the server.
|
250
|
+
It persists for a series of Unpoly navigation, but is cleared when the user makes a full page load.
|
251
|
+
Different Unpoly [layers](https://unpoly.com/up.layer) will usually have separate context objects,
|
252
|
+
although layers may choose to share their context scope.
|
253
|
+
|
254
|
+
You may read and change the context object. Changes will be sent to the frontend with your response.
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
class GamesController < ApplicationController
|
258
|
+
|
259
|
+
def restart
|
260
|
+
up.context[:lives] = 3
|
261
|
+
render 'stage1'
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
265
|
+
```
|
56
266
|
|
57
|
-
|
267
|
+
Keys can be accessed as either strings or symbols:
|
58
268
|
|
59
|
-
|
60
|
-
|
269
|
+
```ruby
|
270
|
+
puts "You have " + up.layer.context[:lives] + " lives left"
|
271
|
+
puts "You have " + up.layer.context['lives'] + " lives left"
|
272
|
+
````
|
61
273
|
|
62
|
-
|
274
|
+
You may delete a key from the frontend by calling `up.context.delete`:
|
63
275
|
|
64
|
-
|
276
|
+
```ruby
|
277
|
+
up.context.delete(:foo)
|
278
|
+
````
|
65
279
|
|
66
|
-
|
67
|
-
2. Add an entry to `CHANGELOG.md`
|
68
|
-
3. Commit and push the version bump and `CHANGELOG.md`
|
69
|
-
4. Log into Rubygems and npm
|
280
|
+
You may replace the entire context by calling `up.context.replace`:
|
70
281
|
|
71
|
-
|
282
|
+
```ruby
|
283
|
+
context_from_file = JSON.parse(File.read('context.json))
|
284
|
+
up.context.replace(context_from_file)
|
285
|
+
```
|
72
286
|
|
73
|
-
|
287
|
+
`up.context` is an alias for `up.layer.context`.
|
74
288
|
|
75
|
-
1. Update [unpoly.com](https://unpoly.com/) so users see the new version, CDN link and CHANGELOG.
|
76
|
-
2. Send a message to the [E-mail group](https://groups.google.com/group/unpoly) with the title "Unpoly X.Y.Z released". You can copy the relevant CHANGELOG part from [here](http://localhost:4567/changes_google_groups).
|
77
289
|
|
290
|
+
### Accessing the targeted layer
|
78
291
|
|
79
|
-
|
80
|
-
-------
|
292
|
+
Use the methods below to interact with the [layer](/up.layer) of the fragment being targeted.
|
81
293
|
|
82
|
-
|
294
|
+
Note that fragment updates may target different layers for successful (HTTP status `200 OK`) and failed (status `4xx` or `5xx`) responses.
|
295
|
+
|
296
|
+
#### `up.layer.mode`
|
297
|
+
|
298
|
+
Returns the [mode](https://unpoly.com/up.layer.mode) of the targeted layer (e.g. `"root"` or `"modal"`).
|
299
|
+
|
300
|
+
#### `up.layer.root?`
|
301
|
+
|
302
|
+
Returns whether the targeted layer is the root layer.
|
303
|
+
|
304
|
+
#### `up.layer.overlay?`
|
305
|
+
|
306
|
+
Returns whether the targeted layer is an overlay (not the root layer).
|
307
|
+
|
308
|
+
#### `up.layer.context`
|
309
|
+
|
310
|
+
Returns the [context](https://unpoly.com/up.context) object of the targeted layer.
|
311
|
+
See documentation for `up.context`, which is an alias for `up.layer.context`.
|
312
|
+
|
313
|
+
#### `up.layer.accept(value)`
|
314
|
+
|
315
|
+
[Accepts](https://unpoly.com/up.layer.accept) the current overlay.
|
316
|
+
|
317
|
+
Does nothing if the root layer is targeted.
|
318
|
+
|
319
|
+
Note that Rails expects every controller action to render or redirect.
|
320
|
+
Your action should either call `up.render_nothing` or respond with `text/html` content matching the requested target.
|
321
|
+
|
322
|
+
#### `up.layer.dismiss(value)`
|
323
|
+
|
324
|
+
[Dismisses](https://unpoly.com/up.layer.dismisses) the current overlay.
|
325
|
+
|
326
|
+
Does nothing if the root layer is targeted.
|
327
|
+
|
328
|
+
Note that Rails expects every controller action to render or redirect.
|
329
|
+
Your action should either call `up.render_nothing` or respond with `text/html` content matching the requested target.
|
330
|
+
|
331
|
+
#### `up.layer.emit(type, options)`
|
332
|
+
|
333
|
+
[Emits an event](https://unpoly.com/up.layer.emit) on the targeted layer.
|
334
|
+
|
335
|
+
#### `up.fail_layer.mode`
|
336
|
+
|
337
|
+
Returns the [mode](https://unpoly.com/up.layer.mode) of the layer targeted for a failed response.
|
338
|
+
|
339
|
+
#### `up.fail_layer.root?`
|
340
|
+
|
341
|
+
Returns whether the layer targeted for a failed response is the root layer.
|
342
|
+
|
343
|
+
#### `up.fail_layer.overlay?`
|
344
|
+
|
345
|
+
Returns whether the layer targeted for a failed response is an overlay.
|
346
|
+
|
347
|
+
#### `up.fail_layer.context`
|
348
|
+
|
349
|
+
Returns the [context](https://unpoly.com/up.context) object of the layer targeted for a failed response.
|
350
|
+
|
351
|
+
|
352
|
+
### Managing the client-side cache
|
353
|
+
|
354
|
+
The Unpoly frontend caches server responses for a few minutes, making requests to these URLs return instantly.
|
355
|
+
Only `GET` requests are cached. The *entire* cache is cleared after every non-`GET` request (like `POST` or `PUT`).
|
356
|
+
|
357
|
+
The server may override these defaults. For instance, the server can clear Unpoly's client-side response cache, even for `GET` requests:
|
358
|
+
|
359
|
+
```ruby
|
360
|
+
up.cache.clear
|
361
|
+
```
|
362
|
+
|
363
|
+
You may also clear a single page:
|
364
|
+
|
365
|
+
```ruby
|
366
|
+
up.cache.clear('/notes/1034')
|
367
|
+
```
|
368
|
+
|
369
|
+
You may also clear all entries matching a URL pattern:
|
370
|
+
|
371
|
+
```ruby
|
372
|
+
up.cache.clear('/notes/*')
|
373
|
+
```
|
374
|
+
|
375
|
+
You may also prevent cache clearing for an unsafe request:
|
376
|
+
|
377
|
+
```ruby
|
378
|
+
up.cache.keep
|
379
|
+
```
|
380
|
+
|
381
|
+
Here is an longer example where the server uses careful cache management to keep as much of the client-side cache as possible:
|
382
|
+
|
383
|
+
```ruby
|
384
|
+
def NotesController < ApplicationController
|
385
|
+
|
386
|
+
def create
|
387
|
+
@note = Note.create!(params[:note].permit(...))
|
388
|
+
if @note.save
|
389
|
+
up.cache.clear('/notes/*') # Only clear affected entries
|
390
|
+
redirect_to(@note)
|
391
|
+
else
|
392
|
+
up.cache.keep # Keep the cache because we haven't saved
|
393
|
+
render 'new'
|
394
|
+
end
|
395
|
+
end
|
396
|
+
...
|
397
|
+
end
|
398
|
+
```
|
399
|
+
|
400
|
+
|
401
|
+
### Preserving Unpoly-related request information through redirects
|
402
|
+
|
403
|
+
`unpoly-rails` patches [`redirect_to`](https://api.rubyonrails.org/classes/ActionController/Redirecting.html#method-i-redirect_to)
|
404
|
+
so Unpoly-related request information (like the CSS selector being targeted for a fragment
|
405
|
+
update) will be preserved for the action you redirect to.
|
406
|
+
|
407
|
+
|
408
|
+
### Automatic redirect detection
|
409
|
+
|
410
|
+
`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
|
411
|
+
HTTP method as `X-Up-Method`.
|
412
|
+
|
413
|
+
|
414
|
+
### Automatic method detection for initial page load
|
415
|
+
|
416
|
+
`unpoly-rails` sets an `_up_method` cookie that Unpoly needs to detect the request method for the initial page load.
|
417
|
+
|
418
|
+
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`.
|
419
|
+
|
420
|
+
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.
|
421
|
+
|
422
|
+
|
423
|
+
What you still need to do manually
|
424
|
+
----------------------------------
|
425
|
+
|
426
|
+
### Failed form submissions must return a non-200 status code
|
427
|
+
|
428
|
+
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.
|
429
|
+
|
430
|
+
For Unpoly to be able to detect a failed form submission,
|
431
|
+
the form must be re-rendered with a non-200 HTTP status code.
|
432
|
+
We recommend to use either 400 (bad request) or 422 (unprocessable entity).
|
433
|
+
|
434
|
+
To do so in Rails, pass a [`:status` option to `render`](http://guides.rubyonrails.org/layouts_and_rendering.html#the-status-option):
|
435
|
+
|
436
|
+
```ruby
|
437
|
+
class UsersController < ApplicationController
|
438
|
+
|
439
|
+
def create
|
440
|
+
user_params = params[:user].permit(:email, :password)
|
441
|
+
@user = User.new(user_params)
|
442
|
+
if @user.save?
|
443
|
+
sign_in @user
|
444
|
+
else
|
445
|
+
render 'form', status: :bad_request
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
end
|
450
|
+
```
|
451
|
+
|
452
|
+
Development
|
453
|
+
-----------
|
454
|
+
|
455
|
+
### Before you make a PR
|
456
|
+
|
457
|
+
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).
|
458
|
+
|
459
|
+
### Running tests
|
460
|
+
|
461
|
+
- Install the Ruby version from `.ruby-version` (currently 2.3.8)
|
462
|
+
- Install Bundler by running `gem install bundler`
|
463
|
+
- Install dependencies by running `bundle install`
|
464
|
+
- Run `bundle exec rspec`
|
465
|
+
|
466
|
+
The tests run against a minimal Rails app that lives in `spec/dummy`.
|
467
|
+
|
468
|
+
### Making a new release
|
83
469
|
|
470
|
+
Install the `unpoly-rails` and [`unpoly`](https://github.com/unpoly/unpoly) repositories into the same parent folder:
|
471
|
+
|
472
|
+
```
|
473
|
+
projects/
|
474
|
+
unpoly/
|
475
|
+
unpoly-rails/
|
476
|
+
```
|
477
|
+
|
478
|
+
During development `unpoly-rails` will use assets from the folder `assets/unpoly-dev`, which is symlinked against the `dist` folder of the ``unpoly` repo.
|
479
|
+
|
480
|
+
Before packaging the gem, a rake task will copy symlinked files `assets/unpoly-dev/*` to `assets/unpoly/*`. The latter is packaged into the gem and distributed.
|
481
|
+
|
482
|
+
```
|
483
|
+
projects/
|
484
|
+
unpoly/
|
485
|
+
dist/
|
486
|
+
unpoly.js
|
487
|
+
unpoly.css
|
488
|
+
unpoly-rails
|
489
|
+
assets/
|
490
|
+
unpoly-dev -> ../../unpoly/dist
|
491
|
+
unpoly.js -> ../../unpoly/dist/unpoly.js
|
492
|
+
unpoly.css -> ../../unpoly/dist/unpoly.css
|
493
|
+
unpoly
|
494
|
+
unpoly.js
|
495
|
+
unpoly.css
|
496
|
+
```
|
497
|
+
|
498
|
+
Making a new release of `unpoly-rails` involves the following steps:
|
499
|
+
|
500
|
+
- Make a new build of unpoly (`npm run build`)
|
501
|
+
- Make a new release of the unpoly npm package
|
502
|
+
- Bump the version in `lib/unpoly/rails/version.rb` to match that in Unpoly's `package.json`
|
503
|
+
- Commit and push the changes
|
504
|
+
- Run `rake gem:release`
|