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.

Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/README.md +472 -51
  4. data/assets/unpoly/jasmine.css +60 -0
  5. data/assets/unpoly/jasmine.js +19300 -0
  6. data/assets/unpoly/specs.css +18 -0
  7. data/assets/unpoly/specs.es5.js +28121 -0
  8. data/assets/unpoly/specs.js +28265 -0
  9. data/{dist → assets/unpoly}/unpoly-bootstrap3.css +1 -0
  10. data/assets/unpoly/unpoly-bootstrap3.js +54 -0
  11. data/{dist → assets/unpoly}/unpoly-bootstrap3.min.css +1 -0
  12. data/assets/unpoly/unpoly-bootstrap3.min.js +1 -0
  13. data/{dist → assets/unpoly}/unpoly-bootstrap4.css +1 -0
  14. data/assets/unpoly/unpoly-bootstrap4.js +54 -0
  15. data/{dist → assets/unpoly}/unpoly-bootstrap4.min.css +1 -0
  16. data/assets/unpoly/unpoly-bootstrap4.min.js +1 -0
  17. data/{dist → assets/unpoly}/unpoly-bootstrap5.css +1 -0
  18. data/assets/unpoly/unpoly-bootstrap5.js +56 -0
  19. data/{dist → assets/unpoly}/unpoly-bootstrap5.min.css +1 -0
  20. data/assets/unpoly/unpoly-bootstrap5.min.js +1 -0
  21. data/assets/unpoly/unpoly-migrate.js +1281 -0
  22. data/assets/unpoly/unpoly-migrate.min.js +1 -0
  23. data/{dist → assets/unpoly}/unpoly.css +23 -14
  24. data/assets/unpoly/unpoly.es5.js +23195 -0
  25. data/assets/unpoly/unpoly.es5.min.js +1 -0
  26. data/assets/unpoly/unpoly.js +21457 -0
  27. data/assets/unpoly/unpoly.min.css +10 -0
  28. data/assets/unpoly/unpoly.min.js +1 -0
  29. data/lib/unpoly-rails.rb +12 -12
  30. data/lib/unpoly/rails/controller.rb +1 -1
  31. data/lib/unpoly/rails/engine.rb +13 -20
  32. data/lib/unpoly/rails/request_echo_headers.rb +1 -1
  33. data/lib/unpoly/rails/version.rb +1 -1
  34. metadata +61 -30
  35. data/CHANGELOG.md +0 -2373
  36. data/README_RAILS.md +0 -418
  37. data/dist/unpoly-bootstrap3.js +0 -16
  38. data/dist/unpoly-bootstrap3.min.js +0 -1
  39. data/dist/unpoly-bootstrap4.js +0 -16
  40. data/dist/unpoly-bootstrap4.min.js +0 -1
  41. data/dist/unpoly-bootstrap5.js +0 -14
  42. data/dist/unpoly-bootstrap5.min.js +0 -1
  43. data/dist/unpoly-migrate.js +0 -1339
  44. data/dist/unpoly-migrate.min.js +0 -1
  45. data/dist/unpoly.js +0 -22266
  46. data/dist/unpoly.min.css +0 -1
  47. data/dist/unpoly.min.js +0 -6
  48. data/lib/unpoly/tasks.rb +0 -55
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 498fefa239aae0cf36380af1a27d56a032a5fe3732268fdadc9961dd144af2d0
4
- data.tar.gz: 21a984c788247f8bc5f68fc752ef18f48e5eb226a5c85d079473879f49791559
3
+ metadata.gz: 464327851ae1ae81375bf266c2bfccee83ddd8f132d5601b3ecaa6a9a58493b2
4
+ data.tar.gz: 52953ca1397f0882b2eed6fe36057763e96aeb2877ac156b7399f5c03fd320d1
5
5
  SHA512:
6
- metadata.gz: 3ae83c5e9224620e5b594730b6baa488b5891dc2ec14351b071b079abfcab5e3eb01ffa2d0a288e984b7d1d20a00bdab19353a6f87120673682c0033dd21276e
7
- data.tar.gz: 030fc977340f6979e7f25727ad91d0d4aeae773b8aeeb54ccbed4f946ffd1b1017a7c22c6d1496fc42a1a69d150949c2356d1c236d4e9827238b975c942173ef
6
+ metadata.gz: ac76cdbed077885cb5b01ef093354e879e73126828dabb8d6667f22b8bbd81f22d4a4d8f8d0618b4949396f6cb9f8b77e42f768fcb7bd9bc417aaa3b0773cf7f
7
+ data.tar.gz: c91cb3b267b595aa0ac96eae2212f463f1ee527cf72ca48426ddf7629bf35c98a8180f05d4607cb3fe8891a437716af1a46ab26e7fe85ded53d4f058f7dbdf6c
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,83 +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 guides and documentation.
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
- Development
24
- -----------
21
+ Installing frontend assets
22
+ --------------------------
25
23
 
26
- ### Running tests
24
+ ### With Webpacker
27
25
 
28
- 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.
29
27
 
30
- - This currently requires Ruby
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
- Install dependencies for tests:
30
+ ```js
31
+ import 'unpoly/unpoly.js'
32
+ import 'unpoly/unpoly.css'
33
+ ```
36
34
 
37
- - Install Ruby 2.3.8
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
- - `cd` into `spec_app`
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
- 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>.
50
41
 
51
- - `cd` into `spec_app`
52
- - Run `rspec`
42
+ Add the following line to your `application.js` manifest:
53
43
 
44
+ ```js
45
+ //= require unpoly
46
+ ```
54
47
 
55
- ### 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
+ ```
56
266
 
57
- We are currently feeding two release channels:
267
+ Keys can be accessed as either strings or symbols:
58
268
 
59
- - npm
60
- - 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
+ ````
61
273
 
62
- We always release to all channel simultaneously.
274
+ You may delete a key from the frontend by calling `up.context.delete`:
63
275
 
64
- To prepare a new version:
276
+ ```ruby
277
+ up.context.delete(:foo)
278
+ ````
65
279
 
66
- 1. Edit `lib/unpoly/rails/version.rb` and bump the version number. Use [semantic versioning](http://semver.org/).
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
- 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
+ ```
72
286
 
73
- After you have published all release channels, remember to:
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
- Credits
80
- -------
292
+ Use the methods below to interact with the [layer](/up.layer) of the fragment being targeted.
81
293
 
82
- - [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
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`