unpoly-rails 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of unpoly-rails might be problematic. Click here for more details.

Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/README.md +472 -50
  4. data/{dist → assets/unpoly}/unpoly-bootstrap3.css +1 -0
  5. data/assets/unpoly/unpoly-bootstrap3.js +54 -0
  6. data/{dist → assets/unpoly}/unpoly-bootstrap3.min.css +1 -0
  7. data/assets/unpoly/unpoly-bootstrap3.min.js +1 -0
  8. data/{dist → assets/unpoly}/unpoly-bootstrap4.css +1 -0
  9. data/assets/unpoly/unpoly-bootstrap4.js +54 -0
  10. data/{dist → assets/unpoly}/unpoly-bootstrap4.min.css +1 -0
  11. data/assets/unpoly/unpoly-bootstrap4.min.js +1 -0
  12. data/{dist → assets/unpoly}/unpoly-bootstrap5.css +1 -0
  13. data/assets/unpoly/unpoly-bootstrap5.js +56 -0
  14. data/{dist → assets/unpoly}/unpoly-bootstrap5.min.css +1 -0
  15. data/assets/unpoly/unpoly-bootstrap5.min.js +1 -0
  16. data/assets/unpoly/unpoly-migrate.js +1281 -0
  17. data/assets/unpoly/unpoly-migrate.min.js +1 -0
  18. data/{dist → assets/unpoly}/unpoly.css +23 -6
  19. data/assets/unpoly/unpoly.es5.js +23195 -0
  20. data/assets/unpoly/unpoly.es5.min.js +1 -0
  21. data/assets/unpoly/unpoly.js +21457 -0
  22. data/assets/unpoly/unpoly.min.css +10 -0
  23. data/assets/unpoly/unpoly.min.js +1 -0
  24. data/lib/unpoly-rails.rb +12 -12
  25. data/lib/unpoly/rails/controller.rb +1 -1
  26. data/lib/unpoly/rails/engine.rb +13 -20
  27. data/lib/unpoly/rails/request_echo_headers.rb +1 -1
  28. data/lib/unpoly/rails/version.rb +1 -1
  29. metadata +52 -26
  30. data/CHANGELOG.md +0 -2345
  31. data/README_RAILS.md +0 -418
  32. data/dist/unpoly-bootstrap3.js +0 -16
  33. data/dist/unpoly-bootstrap3.min.js +0 -1
  34. data/dist/unpoly-bootstrap4.js +0 -16
  35. data/dist/unpoly-bootstrap4.min.js +0 -1
  36. data/dist/unpoly-bootstrap5.js +0 -14
  37. data/dist/unpoly-bootstrap5.min.js +0 -1
  38. data/dist/unpoly-migrate.js +0 -1306
  39. data/dist/unpoly-migrate.min.js +0 -1
  40. data/dist/unpoly.js +0 -22134
  41. data/dist/unpoly.min.css +0 -1
  42. data/dist/unpoly.min.js +0 -6
  43. data/lib/unpoly/tasks.rb +0 -55
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60a37ba382e3664b7224d805da146e4a7364e7880dbcdb80580a0e9ef4406672
4
- data.tar.gz: 8d899df9850761c7b07849b2ded8a241e334c9e4cbc3ac8dc5003af0629206ad
3
+ metadata.gz: e172477708dafbf52ebd7dcfb43c784f3e91d1c37f883bc278c2d37949ebe4e8
4
+ data.tar.gz: 3e4471b6380952a5d07df315fcd5274bd4848e9b1cda47283dd40e9bd4a60227
5
5
  SHA512:
6
- metadata.gz: 9caee93e9aa5f35eb7d7dcb276f1e48b1231b2f003e3bd56285105869f15759cbf7d2cfc57cf66dd2470b85df54775826352ea568e81f1151d1a981c04f0bc69
7
- data.tar.gz: e53cee4842b88880f71769552dee074e4f865bfbae43033daf6c6a13950c73f0e5bf284ab7b9babfaff5677400ae0811111a8fac9dbafdd6a53e8a67b8c67796
6
+ metadata.gz: 1048e6c8ae8dbf2c7bec8477b61cd83e5921f33838bc83152eee17a5d09ced7b0ad5fbba9945e6eb61a265fd528373338075b2b43d7b7f24b3cec3f4ac9cdd35
7
+ data.tar.gz: a20857dd9a6b2288edef2f2199f6fe3dcc9d35700946919598d2cc483459e96a6cd51e6d7b1d56edc631f60d8b05777add4cbfe4e6e5e8859482e441862bc345
data/.yardopts CHANGED
@@ -1 +1 @@
1
- --markup=markdown --main=README_RAILS.md --exclude .
1
+ --markup=markdown --main=README.md --exclude .
data/README.md CHANGED
@@ -1,82 +1,504 @@
1
- [Unpoly 2](https://unpoly.com)
2
- ======
1
+ unpoly-rails: Ruby on Rails bindings for Unpoly
2
+ ===============================================
3
3
 
4
- Unobtrusive JavaScript framework for server-side applications
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
- [Unpoly](https://unpoly.com) enables fast and flexible frontends with minimal changes to your server-side code.
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
- If you're looking for the code of Unpoly 0.x or 1.0, use the [`1.x-stable`](https://github.com/unpoly/unpoly/tree/1.x-stable) branch.
9
+ Installing the gem
10
+ ------------------
12
11
 
12
+ Add the following line to your `Gemfile`:
13
13
 
14
- Getting started
15
- ---------------
14
+ ```ruby
15
+ gem 'unpoly-rails'
16
+ ```
16
17
 
17
- - See [unpoly.com](https://unpoly.com) for more information and JavaScript API documentation.
18
- - See [`CHANGELOG.md`](https://github.com/unpoly/unpoly/blob/master/CHANGELOG.md) for notable changes.
19
- - 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.
20
19
 
21
20
 
22
- Development
23
- -----------
21
+ Installing frontend assets
22
+ --------------------------
24
23
 
25
- ### Running tests
24
+ ### With Webpacker
26
25
 
27
- Overview:
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.
28
27
 
29
- - This currently requires Ruby
30
- - There's a Rails app in `spec_app`
31
- - Jasmine tests for Unpoly live in `spec_app/spec/javascripts`
32
- - RSpec tests for the `unpoly-rails` gem live in `spec_app/spec/controllers`
28
+ Now `import` Unpoly from your `application.js` pack:
33
29
 
34
- Install dependencies for tests:
30
+ ```js
31
+ import 'unpoly/unpoly.js'
32
+ import 'unpoly/unpoly.css'
33
+ ```
35
34
 
36
- - Install Ruby 2.3.8
37
- - Install Bundler by running `gem install bundler`
38
- - Install Node.js (required for building the library)
39
- - `cd` into `spec_app`
40
- - 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.
41
36
 
42
- To run Jasmine tests for Unpoly:
43
37
 
44
- - `cd` into `spec_app`
45
- - Start the Rails server by running `rails server`
46
- - Access `http://localhost:3000/specs` to see the Jasmine test runner
38
+ ### With the Asset Pipeline
47
39
 
48
- To run RSpec tests for the `unpoly-rails` gem:
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>.
49
41
 
50
- - `cd` into `spec_app`
51
- - Run `rspec`
42
+ Add the following line to your `application.js` manifest:
52
43
 
44
+ ```js
45
+ //= require unpoly
46
+ ```
53
47
 
54
- ### Making a new release
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
+ ```
55
266
 
56
- We are currently feeding two release channels:
267
+ Keys can be accessed as either strings or symbols:
57
268
 
58
- - npm
59
- - Rubygems (as the `unpoly-rails` gem)
269
+ ```ruby
270
+ puts "You have " + up.layer.context[:lives] + " lives left"
271
+ puts "You have " + up.layer.context['lives'] + " lives left"
272
+ ````
60
273
 
61
- We always release to all channel simultaneously.
274
+ You may delete a key from the frontend by calling `up.context.delete`:
62
275
 
63
- To prepare a new version:
276
+ ```ruby
277
+ up.context.delete(:foo)
278
+ ````
64
279
 
65
- 1. Edit `lib/unpoly/rails/version.rb` and bump the version number. Use [semantic versioning](http://semver.org/).
66
- 2. Add an entry to `CHANGELOG.md`
67
- 3. Commit and push the version bump and `CHANGELOG.md`
68
- 4. Log into Rubygems and npm
280
+ You may replace the entire context by calling `up.context.replace`:
69
281
 
70
- Now you can call `rake release:all` to publish to Rubygems and npm.
282
+ ```ruby
283
+ context_from_file = JSON.parse(File.read('context.json))
284
+ up.context.replace(context_from_file)
285
+ ```
71
286
 
72
- After you have published all release channels, remember to:
287
+ `up.context` is an alias for `up.layer.context`.
73
288
 
74
- 1. Update [unpoly.com](https://unpoly.com/) so users see the new version, CDN link and CHANGELOG.
75
- 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).
76
289
 
290
+ ### Accessing the targeted layer
77
291
 
78
- Credits
79
- -------
292
+ Use the methods below to interact with the [layer](/up.layer) of the fragment being targeted.
80
293
 
81
- - [Henning Koch](mailto:henning.koch@makandra.de) from [makandra](http://www.makandra.com) ([@triskweline](https://twitter.com/triskweline) on Twitter)
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
82
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`