sync_bumper 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +50 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +408 -0
- data/Rakefile +0 -0
- data/lib/generators/sync/install_generator.rb +13 -0
- data/lib/generators/sync/templates/sync_bumper.rb +10 -0
- data/lib/sync_bumper.rb +24 -0
- data/lib/sync_bumper/model.rb +93 -0
- metadata +80 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: eeb67b4ccf61e6b2c827757823f6c3409b204a0f
|
4
|
+
data.tar.gz: 1c00aeefec000c068ed4198c161fd84b9d1827a1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4a38eb4c7caa2151750ef0f6de38f2dbe0afb392a5e3f97fea70d1d691ab8eb3f4a8029238818773b719f0997275a2964e881d6ab104e624942c174d8d76796d
|
7
|
+
data.tar.gz: 6bf29354269289a5ee3836aeeeddfde38eb11bd3296df2128a74fcd3f8b2231878302438dbd58f53633190cc7582e9da342940cf2880f6422128d9cacff271c5
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Version 0.2.3 - June 30, 2013
|
2
|
+
|
3
|
+
- Fixed Turbolinks issue where `page:restore` events no longer evaluate script tags in the body. The workaround re-evaluates all sync sript tags on page restore.
|
4
|
+
|
5
|
+
# Version 0.2.1 - May 27, 2013
|
6
|
+
|
7
|
+
- Add ability to narrow scope to custom channel for sync_new publishes
|
8
|
+
|
9
|
+
Example Usage:
|
10
|
+
|
11
|
+
View:
|
12
|
+
```erb
|
13
|
+
<%= sync_new partial: 'todo_list_row', resource: Todo.new, scope: [@project, :staff] %>
|
14
|
+
```
|
15
|
+
|
16
|
+
Controller/Model:
|
17
|
+
```ruby
|
18
|
+
sync_new @todo, scope: [@project, :staff]
|
19
|
+
```
|
20
|
+
|
21
|
+
|
22
|
+
# Version 0.2.0 - May 26, 2013
|
23
|
+
|
24
|
+
- Add ability to refetch partial from server to render within session context, ref: https://github.com/chrismccord/sync/issues/44
|
25
|
+
|
26
|
+
This solves the issues of syncing partials across different users when the partial requires the session's context (ie. current_user).
|
27
|
+
|
28
|
+
Ex:
|
29
|
+
View: Add `refetch: true` to sync calls, and place partial file in a 'refetch'
|
30
|
+
subdirectory in the model's sync view folder:
|
31
|
+
|
32
|
+
The partial file would be located in `app/views/sync/todos/refetch/_list_row.html.erb`
|
33
|
+
```erb
|
34
|
+
<% @project.todos.ordered.each do |todo| %>
|
35
|
+
<%= sync partial: 'list_row', resource: todo, refetch: true %>
|
36
|
+
<% end %>
|
37
|
+
<%= sync_new partial: 'list_row', resource: Todo.new, scope: @project, refetch: true %>
|
38
|
+
```
|
39
|
+
|
40
|
+
*Notes*
|
41
|
+
While this approach works very well for the cases it's needed, syncing without refetching should be used unless refetching is absolutely necessary for performance reasons. For example,
|
42
|
+
|
43
|
+
A sync update request is triggered on the server for a 'regular' sync'd partial with 100 listening clients:
|
44
|
+
- number of http requests 1
|
45
|
+
- number of renders 1, pushed out to all 100 clients via pubsub server.
|
46
|
+
|
47
|
+
|
48
|
+
A sync update request is triggered on the server for a 'refetch' sync'd partial with 100 listening clients:
|
49
|
+
- number of http requests 100
|
50
|
+
- number of renders 100, rendering each request in clients session context.
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Chris McCord
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
The Software shall be used for Good, not Evil.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,408 @@
|
|
1
|
+
# Sync
|
2
|
+
> This started as a thought experiment that is growing into a viable option for realtime Rails apps without ditching
|
3
|
+
the standard rails stack that we love and are so productive with for a heavy client side MVC framework.
|
4
|
+
|
5
|
+
|
6
|
+
Real-time partials with Rails. Sync lets you render partials for models that, with minimal code,
|
7
|
+
update in realtime in the browser when changes occur on the server.
|
8
|
+
|
9
|
+
#### Watch a screencast to see it in action
|
10
|
+
[![See it in action](http://chrismccord.com/images/sync/video_thumb.png)](http://chrismccord.com/blog/2013/04/21/sync-realtime-rails-partials/)
|
11
|
+
|
12
|
+
In practice, one simply only needs to replace:
|
13
|
+
|
14
|
+
```erb
|
15
|
+
<%= render partial: 'user_row', locals: {user: @user} %>
|
16
|
+
```
|
17
|
+
|
18
|
+
with:
|
19
|
+
|
20
|
+
```erb
|
21
|
+
<%= sync partial: 'user_row', resource: @user %>
|
22
|
+
```
|
23
|
+
|
24
|
+
Then update views realtime automatically with the `sync` DSL or with a with a simple `sync_update(@user)` in the controller without any extra javascript or
|
25
|
+
configuration.
|
26
|
+
|
27
|
+
In addition to real-time updates, Sync also provides:
|
28
|
+
|
29
|
+
- Realtime removal of partials from the DOM when the sync'd model is destroyed in the controller via `sync_destroy(@user)`
|
30
|
+
- Realtime appending of newly created model's on scoped channels
|
31
|
+
- JavaScript/CoffeeScript hooks to override and extend element updates/appends/removes for partials
|
32
|
+
- Support for [Faye](http://faye.jcoglan.com/) and [Pusher](http://pusher.com)
|
33
|
+
|
34
|
+
## Requirements
|
35
|
+
|
36
|
+
- Ruby >= 1.9.2
|
37
|
+
- Rails 3 >= 3.1 or Rails 4
|
38
|
+
- jQuery >= 1.9
|
39
|
+
|
40
|
+
|
41
|
+
## Installation
|
42
|
+
|
43
|
+
#### 1) Add the gem to your `Gemfile`
|
44
|
+
|
45
|
+
#### Using Faye
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
gem 'faye'
|
49
|
+
gem 'thin', require: false
|
50
|
+
gem 'sync'
|
51
|
+
```
|
52
|
+
|
53
|
+
#### Using Pusher
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
gem 'pusher'
|
57
|
+
gem 'sync'
|
58
|
+
```
|
59
|
+
|
60
|
+
#### Install
|
61
|
+
|
62
|
+
```bash
|
63
|
+
$ bundle
|
64
|
+
$ rails g sync:install
|
65
|
+
```
|
66
|
+
|
67
|
+
#### 2) Require sync in your asset javascript manifest `app/assets/javascripts/application.js`:
|
68
|
+
|
69
|
+
```javascript
|
70
|
+
//= require sync
|
71
|
+
```
|
72
|
+
|
73
|
+
#### 3) Add the pubsub adapter's javascript to your application layout `app/views/layouts/application.html.erb`
|
74
|
+
|
75
|
+
```erb
|
76
|
+
<%= javascript_include_tag Sync.adapter_javascript_url %>
|
77
|
+
```
|
78
|
+
|
79
|
+
#### 4) Configure your pubsub server (Faye or Pusher)
|
80
|
+
|
81
|
+
|
82
|
+
#### Using [Faye](http://faye.jcoglan.com/) (self hosted)
|
83
|
+
|
84
|
+
Set your configuration in the generated `config/sync.yml` file, using the Faye adapter. Then run Faye alongside your app.
|
85
|
+
|
86
|
+
```bash
|
87
|
+
rackup sync.ru -E production
|
88
|
+
```
|
89
|
+
|
90
|
+
#### Using [Pusher](http://pusher.com) (SaaS)
|
91
|
+
|
92
|
+
Set your configuration in the generated `config/sync.yml` file, using the Pusher adapter. No extra process/setup.
|
93
|
+
|
94
|
+
## Current Caveats
|
95
|
+
The current implementation uses a DOM range query (jQuery's `nextUntil`) to match your partial's "element" in
|
96
|
+
the DOM. The way this selector works requires your sync'd partial to be wrapped in a root level html tag for that partial file.
|
97
|
+
For example, this parent view/sync partial approach would *not* work:
|
98
|
+
|
99
|
+
Given the sync partial `_todo_row.html.erb`:
|
100
|
+
|
101
|
+
```erb
|
102
|
+
Title:
|
103
|
+
<%= link_to todo.title, todo %>
|
104
|
+
```
|
105
|
+
|
106
|
+
And the parent view:
|
107
|
+
|
108
|
+
```erb
|
109
|
+
<table>
|
110
|
+
<tbody>
|
111
|
+
<tr>
|
112
|
+
<%= sync partial: 'todo_row', resource: @todo %>
|
113
|
+
</tr>
|
114
|
+
</tbody>
|
115
|
+
</table>
|
116
|
+
```
|
117
|
+
|
118
|
+
##### The markup *would need to change to*:
|
119
|
+
|
120
|
+
|
121
|
+
sync partial `_todo_row.html.erb`:
|
122
|
+
|
123
|
+
```erb
|
124
|
+
<tr> <!-- root level container for the partial required here -->
|
125
|
+
Title:
|
126
|
+
<%= link_to todo.title, todo %>
|
127
|
+
</tr>
|
128
|
+
```
|
129
|
+
|
130
|
+
And the parent view changed to:
|
131
|
+
|
132
|
+
```erb
|
133
|
+
<table>
|
134
|
+
<tbody>
|
135
|
+
<%= sync partial: 'todo_row', resource: @todo %>
|
136
|
+
</tbody>
|
137
|
+
</table>
|
138
|
+
```
|
139
|
+
|
140
|
+
I'm currently investigating true DOM ranges via the [Range](https://developer.mozilla.org/en-US/docs/DOM/range) object.
|
141
|
+
|
142
|
+
|
143
|
+
## 'Automatic' syncing through the sync DSL
|
144
|
+
|
145
|
+
In addition to calling explicit sync actions within controller methods, a
|
146
|
+
`sync` and `enable_sync` DSL has been added to ActionController::Base and ActiveRecord::Base to automate the syncing
|
147
|
+
approach in a controlled, threadsafe way.
|
148
|
+
|
149
|
+
### Example Controller/Model
|
150
|
+
```ruby
|
151
|
+
class TodosController < ApplicationController
|
152
|
+
|
153
|
+
enable_sync only: [:create, :update, :destroy]
|
154
|
+
...
|
155
|
+
end
|
156
|
+
|
157
|
+
class Todo < ActiveRecord::Base
|
158
|
+
|
159
|
+
belongs_to :project, counter_cache: true
|
160
|
+
has_many :comments, dependent: :destroy
|
161
|
+
|
162
|
+
sync :all, scope: :project
|
163
|
+
|
164
|
+
end
|
165
|
+
```
|
166
|
+
|
167
|
+
### Syncing outside of the controller
|
168
|
+
|
169
|
+
`Sync::Actions` can be included into any object wishing to perform sync
|
170
|
+
publishes for a given resource. Instead of using the the controller as
|
171
|
+
context for rendering, a Sync::Renderer instance is used. Since the Renderer
|
172
|
+
is not part of the request/response/session, it has no knowledge of the
|
173
|
+
current session (ie. current_user), so syncing from outside the controller
|
174
|
+
context will require some care that the partial can be rendered within a
|
175
|
+
sessionless context.
|
176
|
+
|
177
|
+
### Example Syncing from a background worker or rails console
|
178
|
+
```ruby
|
179
|
+
# Inside some script/worker
|
180
|
+
Sync::Model.enable do
|
181
|
+
Todo.first.update title: "This todo will be sync'd on save"
|
182
|
+
end
|
183
|
+
Todo.first.update title: "This todo will NOT be sync'd on save"
|
184
|
+
|
185
|
+
Sync::Model.enable!
|
186
|
+
Todo.first.update title: "This todo will be sync'd on save"
|
187
|
+
Todo.first.update title: "This todo will be sync'd on save"
|
188
|
+
Todo.first.update title: "This todo will be sync'd on save"
|
189
|
+
Sync::Model.disable!
|
190
|
+
Todo.first.update title: "This todo will NOT be sync'd on save"
|
191
|
+
```
|
192
|
+
|
193
|
+
## Custom Sync Views and javascript hooks
|
194
|
+
|
195
|
+
Sync allows you to hook into and override or extend all of the actions it performs when updating partials on the client side. When a sync partial is rendered, sync will instantiate a javascript View class based on the following order of lookup:
|
196
|
+
|
197
|
+
1. The camelized version of the concatenated snake case resource
|
198
|
+
and partial names.
|
199
|
+
2. The camelized version of the snake cased partial name.
|
200
|
+
|
201
|
+
#### Examples
|
202
|
+
|
203
|
+
partial name 'list_row', resource name 'todo', order of lookup:
|
204
|
+
|
205
|
+
1. Sync.TodoListRow
|
206
|
+
2. Sync.ListRow
|
207
|
+
3. Sync.View (Default fallback)
|
208
|
+
|
209
|
+
|
210
|
+
For example, if you wanted to fade in/out a row in a sync'd todo list instead of the Sync.View default of instant insert/remove:
|
211
|
+
|
212
|
+
```coffeescript
|
213
|
+
class Sync.TodoListRow extends Sync.View
|
214
|
+
|
215
|
+
beforeInsert: ($el) ->
|
216
|
+
$el.hide()
|
217
|
+
@insert($el)
|
218
|
+
|
219
|
+
afterInsert: -> @$el.fadeIn 'slow'
|
220
|
+
|
221
|
+
beforeRemove: -> @$el.fadeOut 'slow', => @remove()
|
222
|
+
|
223
|
+
```
|
224
|
+
|
225
|
+
## Narrowing sync_new scope
|
226
|
+
|
227
|
+
Sometimes, you do not want your page to update with every new record. With the `scope` option, you can limit what is being updated on a given page.
|
228
|
+
|
229
|
+
One way of using `scope` is by supplying a String or a Symbol. This is useful for example when you want to only show new records for a given locale:
|
230
|
+
|
231
|
+
View:
|
232
|
+
```erb
|
233
|
+
<%= sync_new partial: 'todo_list_row', resource: Todo.new, scope: I18n.locale %>
|
234
|
+
```
|
235
|
+
|
236
|
+
Controller/Model:
|
237
|
+
```ruby
|
238
|
+
sync_new @todo, scope: @todo.locale
|
239
|
+
```
|
240
|
+
|
241
|
+
Another use of `scope` is with a parent resource. This way you can for example update a project page with new todos for this single project:
|
242
|
+
|
243
|
+
View:
|
244
|
+
```erb
|
245
|
+
<%= sync_new partial: 'todo_list_row', resource: Todo.new, scope: @project %>
|
246
|
+
```
|
247
|
+
|
248
|
+
Controller/Model:
|
249
|
+
```ruby
|
250
|
+
sync_new @todo, scope: @project
|
251
|
+
```
|
252
|
+
|
253
|
+
Both approaches can be combined. Just supply an Array of Strings/Symbols and/or parent resources to the `scope` option. Note that the order of elements matters. Be sure to use the same order in your view and in your controller/model.
|
254
|
+
|
255
|
+
## Refetching Partials
|
256
|
+
|
257
|
+
Refetching allows syncing partials across different users when the partial requires the session's context (ie. current_user).
|
258
|
+
|
259
|
+
Ex:
|
260
|
+
View: Add `refetch: true` to sync calls, and place partial file in a 'refetch'
|
261
|
+
subdirectory in the model's sync view folder:
|
262
|
+
|
263
|
+
The partial file would be located in `app/views/sync/todos/refetch/_list_row.html.erb`
|
264
|
+
```erb
|
265
|
+
<% @project.todos.ordered.each do |todo| %>
|
266
|
+
<%= sync partial: 'list_row', resource: todo, refetch: true %>
|
267
|
+
<% end %>
|
268
|
+
<%= sync_new partial: 'list_row', resource: Todo.new, scope: @project, refetch: true %>
|
269
|
+
```
|
270
|
+
|
271
|
+
*Notes*
|
272
|
+
|
273
|
+
While this approach works very well for the cases it's needed, syncing without refetching should be used unless refetching is absolutely necessary for performance reasons. For example,
|
274
|
+
|
275
|
+
A sync update request is triggered on the server for a 'regular' sync'd partial with 100 listening clients:
|
276
|
+
- number of http requests 1
|
277
|
+
- number of renders 1, pushed out to all 100 clients via pubsub server.
|
278
|
+
|
279
|
+
|
280
|
+
A sync update request is triggered on the server for a 'refetch' sync'd partial with 100 listening clients:
|
281
|
+
- number of http requests 100
|
282
|
+
- number of renders 100, rendering each request in clients session context.
|
283
|
+
|
284
|
+
## Using with cache_digests (Russian doll caching)
|
285
|
+
|
286
|
+
Sync has a custom `DependencyTracker::ERBTracker` that can handle `sync` render calls.
|
287
|
+
Because the full partial name is not included, it has to guess the location of
|
288
|
+
your partial based on the name of the `resource` or `collection` passed to it.
|
289
|
+
See the tests to see how it works. If it doesn't work for you, you can always
|
290
|
+
use the [explicit "Template Dependency"
|
291
|
+
markers](https://github.com/rails/cache_digests).
|
292
|
+
|
293
|
+
To enable, add to `config/initializers/cache_digests.rb`:
|
294
|
+
|
295
|
+
#### Rails 4
|
296
|
+
|
297
|
+
```ruby
|
298
|
+
require 'action_view/dependency_tracker'
|
299
|
+
|
300
|
+
ActionView::DependencyTracker.register_tracker :haml, Sync::ERBTracker
|
301
|
+
ActionView::DependencyTracker.register_tracker :erb, Sync::ERBTracker
|
302
|
+
```
|
303
|
+
|
304
|
+
#### Rails 3 with [cache_digests](https://github.com/rails/cache_digests) gem
|
305
|
+
|
306
|
+
```ruby
|
307
|
+
require 'cache_digests/dependency_tracker'
|
308
|
+
|
309
|
+
CacheDigests::DependencyTracker.register_tracker :haml, Sync::ERBTracker
|
310
|
+
CacheDigests::DependencyTracker.register_tracker :erb, Sync::ERBTracker
|
311
|
+
```
|
312
|
+
|
313
|
+
**Note:** haml support is limited, but it seems to work in most cases.
|
314
|
+
|
315
|
+
|
316
|
+
## Serving Faye over HTTPS (with Thin)
|
317
|
+
|
318
|
+
Create a thin configuration file `config/sync_thin.yml` similar to the following:
|
319
|
+
|
320
|
+
```yaml
|
321
|
+
---
|
322
|
+
port: 4443
|
323
|
+
ssl: true
|
324
|
+
ssl_key_file: /path/to/server.pem
|
325
|
+
ssl_cert_file: /path/to/certificate_chain.pem
|
326
|
+
environment: production
|
327
|
+
rackup: sync.ru
|
328
|
+
```
|
329
|
+
|
330
|
+
The `certificate_chain.pem` file should contain your signed certificate, followed by intermediate certificates (if any) and the root certificate of the CA that signed the key.
|
331
|
+
|
332
|
+
Next reconfigure the `server` and `adapter_javascript_url` in `config/sync.yml` to look like `https://your.hostname.com:4443/faye` and `https://your.hostname.com:4443/faye/faye.js` respectively.
|
333
|
+
|
334
|
+
Finally start up Thin from the project root.
|
335
|
+
|
336
|
+
```
|
337
|
+
thin -C config/sync_thin.yml start
|
338
|
+
```
|
339
|
+
|
340
|
+
|
341
|
+
## Brief Example or [checkout an example application](https://github.com/chrismccord/sync_example)
|
342
|
+
|
343
|
+
View `sync/users/_user_list_row.html.erb`
|
344
|
+
|
345
|
+
```erb
|
346
|
+
<tr>
|
347
|
+
<td><%= link_to user.name, user %></td>
|
348
|
+
<td><%= link_to 'Edit', edit_user_path(user) %></td>
|
349
|
+
<td><%= link_to 'Destroy', user, method: :delete, remote: true, data: { confirm: 'Are you sure?' } %></td>
|
350
|
+
</tr>
|
351
|
+
```
|
352
|
+
|
353
|
+
View `users/index.html.erb`
|
354
|
+
|
355
|
+
```erb
|
356
|
+
<h1>Some Users</h1>
|
357
|
+
<table>
|
358
|
+
<tbody>
|
359
|
+
<%= sync partial: 'user_list_row', collection: @users %>
|
360
|
+
<%= sync_new partial: 'user_list_row', resource: User.new, direction: :append %>
|
361
|
+
</tbody>
|
362
|
+
</table>
|
363
|
+
```
|
364
|
+
|
365
|
+
|
366
|
+
Controller
|
367
|
+
|
368
|
+
```ruby
|
369
|
+
def UsersController < ApplicationController
|
370
|
+
…
|
371
|
+
def create
|
372
|
+
@user = User.new(user_params)
|
373
|
+
if @user.save
|
374
|
+
sync_new @user
|
375
|
+
end
|
376
|
+
respond_to do |format|
|
377
|
+
format.html { redirect_to users_url }
|
378
|
+
format.json { head :no_content }
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
def update
|
383
|
+
@user = User.find(params[:id])
|
384
|
+
if user.save
|
385
|
+
…
|
386
|
+
end
|
387
|
+
|
388
|
+
# Sync updates to any partials listening for this user
|
389
|
+
sync_update @user
|
390
|
+
|
391
|
+
redirect_to users_path, notice: "Saved!"
|
392
|
+
end
|
393
|
+
|
394
|
+
def destroy
|
395
|
+
@user = User.find(params[:id])
|
396
|
+
@user.destroy
|
397
|
+
|
398
|
+
# Sync destroy, telling client to remove all dom elements containing this user
|
399
|
+
sync_destroy @user
|
400
|
+
|
401
|
+
respond_to do |format|
|
402
|
+
format.html { redirect_to users_url }
|
403
|
+
format.json { head :no_content }
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
407
|
+
```
|
408
|
+
|
data/Rakefile
ADDED
File without changes
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module SyncBumper
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
def self.source_root
|
5
|
+
File.dirname(__FILE__) + "/templates"
|
6
|
+
end
|
7
|
+
|
8
|
+
def copy_files
|
9
|
+
template "sync_bumper.rb", "config/initializers/sync_bumper.rb"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
SyncBumper.configure do |c|
|
2
|
+
c.url = case
|
3
|
+
when Rails.env.production?
|
4
|
+
lambda { |id| "http://example.com/todos/#{id}/bumper" }
|
5
|
+
when Rails.env.development?
|
6
|
+
lambda { |id| "http://example.com/todos/#{id}/bumper" }
|
7
|
+
else
|
8
|
+
lambda { |id| "http://example.com/todos/#{id}/bumper" }
|
9
|
+
end
|
10
|
+
end
|
data/lib/sync_bumper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
require 'digest/sha1'
|
3
|
+
require 'net/http'
|
4
|
+
require 'net/https'
|
5
|
+
|
6
|
+
require 'sync_bumper/model'
|
7
|
+
|
8
|
+
module SyncBumper
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :url
|
12
|
+
|
13
|
+
def configure(&blk); class_eval(&blk); end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
if defined? Rails
|
20
|
+
# path = Rails.root.join("config/sync_bumper.yml")
|
21
|
+
# SyncBumper.load_config(path, Rails.env) if path.exist?
|
22
|
+
|
23
|
+
ActiveRecord::Base.send :extend, SyncBumper::Model::ClassMethods
|
24
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module SyncBumper
|
2
|
+
module Model
|
3
|
+
|
4
|
+
def self.enabled?
|
5
|
+
Thread.current["model_sync_enabled"]
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.context
|
9
|
+
Thread.current["model_sync_context"]
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.enable!(context = nil)
|
13
|
+
Thread.current["model_sync_enabled"] = true
|
14
|
+
Thread.current["model_sync_context"] = context
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.disable!
|
18
|
+
Thread.current["model_sync_enabled"] = false
|
19
|
+
Thread.current["model_sync_context"] = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.enable(context = nil)
|
23
|
+
enable!(context)
|
24
|
+
yield
|
25
|
+
ensure
|
26
|
+
disable!
|
27
|
+
end
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
attr_accessor :sync_scope
|
31
|
+
|
32
|
+
def sync(*actions)
|
33
|
+
include ModelActions
|
34
|
+
if actions.last.is_a? Hash
|
35
|
+
@sync_scope = actions.last.fetch :scope
|
36
|
+
end
|
37
|
+
actions = [:create, :update, :destroy] if actions.include? :all
|
38
|
+
actions.flatten!
|
39
|
+
|
40
|
+
if actions.include? :create
|
41
|
+
after_create :publish_sync_create, :on => :create#, :if => lamda { Sync::Model.enabled? }
|
42
|
+
end
|
43
|
+
if actions.include? :update
|
44
|
+
after_update :publish_sync_update, :on => :update#, :if => lamda { Sync::Model.enabled? }
|
45
|
+
end
|
46
|
+
if actions.include? :destroy
|
47
|
+
after_destroy :publish_sync_destroy, :on => :destroy#, :if => lamda { Sync::Model.enabled? }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module ModelActions
|
53
|
+
def sync_scope
|
54
|
+
return nil unless self.class.sync_scope
|
55
|
+
send self.class.sync_scope
|
56
|
+
end
|
57
|
+
|
58
|
+
def publish_sync_create
|
59
|
+
Thread.new do
|
60
|
+
uri = URI(SyncBumper.url.call(id))
|
61
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
62
|
+
|
63
|
+
perform_request(req, uri)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def publish_sync_update
|
68
|
+
Thread.new do
|
69
|
+
uri = URI(SyncBumper.url.call(id))
|
70
|
+
req = Net::HTTP::Put.new(uri.request_uri)
|
71
|
+
|
72
|
+
perform_request(req, uri)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def publish_sync_destroy
|
77
|
+
Thread.new do
|
78
|
+
uri = URI(SyncBumper.url.call(id))
|
79
|
+
req = Net::HTTP::Delete.new(uri.request_uri)
|
80
|
+
|
81
|
+
perform_request(req, uri)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def perform_request(req, uri)
|
86
|
+
req["Content-Type"] = 'text/plain;charset=UTF-8'
|
87
|
+
req["Content-Length"] = '0'
|
88
|
+
|
89
|
+
res = Net::HTTP.start(uri.host, uri.port) { |http| http.request(req) }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sync_bumper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexey Dubovskoy
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: em-http-request
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.3.18
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.3.18
|
41
|
+
description: Bumper to involve Sync callbacks at external Rails App. Sync turns your
|
42
|
+
Rails partials realtime with automatic updates through Faye
|
43
|
+
email: dubovskoy.a@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- lib/generators/sync/install_generator.rb
|
49
|
+
- lib/generators/sync/templates/sync_bumper.rb
|
50
|
+
- lib/sync_bumper/model.rb
|
51
|
+
- lib/sync_bumper.rb
|
52
|
+
- CHANGELOG.md
|
53
|
+
- Gemfile
|
54
|
+
- LICENSE
|
55
|
+
- Rakefile
|
56
|
+
- README.md
|
57
|
+
homepage: http://github.com/dubadub/sync_bumper
|
58
|
+
licenses: []
|
59
|
+
metadata: {}
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 1.3.4
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 2.0.14
|
77
|
+
signing_key:
|
78
|
+
specification_version: 4
|
79
|
+
summary: Bumper to involve Sync callbacks at external Rails App
|
80
|
+
test_files: []
|