ud_sync 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +9 -0
- data/Gemfile +4 -0
- data/README.md +124 -0
- data/Rakefile +25 -0
- data/app/controllers/ud_sync/operations_controller.rb +19 -0
- data/app/models/ud_sync/operation.rb +5 -0
- data/bin/console +14 -0
- data/bin/reset +7 -0
- data/bin/setup +8 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20160130183000_create_ud_sync_tables.rb +13 -0
- data/lib/ud_sync/engine.rb +13 -0
- data/lib/ud_sync/rails/model_extension.rb +23 -0
- data/lib/ud_sync/sentinel.rb +53 -0
- data/lib/ud_sync/version.rb +3 -0
- data/lib/ud_sync.rb +9 -0
- data/ud_sync.gemspec +29 -0
- metadata +174 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6bd624f7a50e53ab1cfaa2ddce8be9f50f7342b5
|
4
|
+
data.tar.gz: aa73de3d74848846325ab80eeba6d6f39cd9b810
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3a68af143ba1bf28704e3269ac26aa3017d3f087c457437d366331d3d53703f34c40e58f447d3c47e4f657825561101ff3709cdd8eca02f8a23121277a3b346d
|
7
|
+
data.tar.gz: f5c5d56349b628b3f0e6553f617c9450106b957476ff2c461db0515013377da0d9f2add2f0ada9b9c04ac173c06f47c77618b03cee826b3945b09fd4878f5dab
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
# ud_sync_rails [](https://travis-ci.org/ud-sync/ud-sync-rails)
|
2
|
+
|
3
|
+
ud-sync is a set of tools to allow client applications to work offline. This gem
|
4
|
+
will help you on the server.
|
5
|
+
|
6
|
+
This gem will plug into your Rails models and save every DB operation.
|
7
|
+
With that information, it will expose `GET /ud_sync/operations` so that your
|
8
|
+
mobile devices can come from offline and know what data was deleted in other
|
9
|
+
devices, synchronizing automatically.
|
10
|
+
|
11
|
+
We're aiming at building the client counterparts. Check the
|
12
|
+
[ud-sync](https://github.com/ud-sync) organization for other languages
|
13
|
+
(e.g [ud-sync-swift](Swift)).
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'ud_sync'
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
$ bundle
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
**Step 1: add the route**
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
Rails.application.routes.draw do
|
33
|
+
mount UdSync::Engine => "/ud_sync"
|
34
|
+
|
35
|
+
# ...
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
**Step 2: configure your models**
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
class Post < ActiveRecord::Base
|
43
|
+
ud_sync
|
44
|
+
|
45
|
+
# ...
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
Whenever you save or delete a post, this will save the operation automatically.
|
50
|
+
|
51
|
+
**Step 3: consume /ud_sync/operations**
|
52
|
+
|
53
|
+
When you access `GET /ud_sync/operations`, you will get a response such as the
|
54
|
+
following.
|
55
|
+
|
56
|
+
```json
|
57
|
+
{
|
58
|
+
"operations" => [{
|
59
|
+
"id" => 1,
|
60
|
+
"name" => "save",
|
61
|
+
"record_id" => "record-1",
|
62
|
+
"entity" => "User",
|
63
|
+
"date" => "2025-10-23T10:32:41Z"
|
64
|
+
}, {
|
65
|
+
"id" => 2,
|
66
|
+
"name" => "delete",
|
67
|
+
"record_id" => "record-2",
|
68
|
+
"entity" => "Post",
|
69
|
+
"date" => "2025-10-23T11:23:23Z"
|
70
|
+
}]
|
71
|
+
}
|
72
|
+
```
|
73
|
+
|
74
|
+
`name` stands for the name of the operation, which could be `save` or `delete`.
|
75
|
+
`record_id` is the id of the record that was processed. `entity` is the resource
|
76
|
+
name and `date` when it happened.
|
77
|
+
|
78
|
+
For example, when DeviceA running your offline ready mobile app deletes Post
|
79
|
+
with id `record-2`, this operation will be recorded. When DeviceB comes online,
|
80
|
+
it will request the operations endpoint and check that the Post was deleted
|
81
|
+
online. It will then delete it locally so that it's synchronized with DeviceA.
|
82
|
+
|
83
|
+
**Step 4: define current_user in your application controller**
|
84
|
+
|
85
|
+
If your `ApplicationController` has `current_user` defined, `GET /operations`
|
86
|
+
will only return Operations which `owner_id` equals `current_user.id`
|
87
|
+
|
88
|
+
### Customizing ud-sync-rails
|
89
|
+
|
90
|
+
You can customize it by changing `ud_sync` call:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
class Post < ActiveRecord::Base
|
94
|
+
belongs_to :user
|
95
|
+
|
96
|
+
ud_sync entity: 'Article', id: :uuid, owner: :user
|
97
|
+
# ...
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
`entity` is the name of the resource. If not specified, the name of the model
|
102
|
+
class will be used. `id` is the attribute you want to use as id - for example,
|
103
|
+
when you use uuids, you might not want to expose your internal ids. `owner`
|
104
|
+
is the association that represents the user that has the current resource. With
|
105
|
+
that, you can return only the operations belonging to the current user.
|
106
|
+
|
107
|
+
## Development
|
108
|
+
|
109
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
110
|
+
`rake` to run the tests.
|
111
|
+
|
112
|
+
### Testing
|
113
|
+
|
114
|
+
When adding a new attribute via migration, do the following to clean up the
|
115
|
+
cache:
|
116
|
+
|
117
|
+
```
|
118
|
+
bin/setup
|
119
|
+
```
|
120
|
+
|
121
|
+
## Contributing
|
122
|
+
|
123
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/ud-sync-rails.
|
124
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
#
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
8
|
+
|
9
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
10
|
+
load 'rails/tasks/engine.rake'
|
11
|
+
|
12
|
+
Bundler::GemHelper.install_tasks
|
13
|
+
|
14
|
+
Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
|
15
|
+
|
16
|
+
# This refreshes migration paths and no duplications occur
|
17
|
+
ActiveRecord::Tasks::DatabaseTasks.migrations_paths
|
18
|
+
|
19
|
+
require 'rspec/core'
|
20
|
+
require 'rspec/core/rake_task'
|
21
|
+
|
22
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
23
|
+
RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
|
24
|
+
|
25
|
+
task :default => :spec
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module UdSync
|
2
|
+
class OperationsController < ActionController::Base
|
3
|
+
def index
|
4
|
+
operations = UdSync::Operation.all
|
5
|
+
|
6
|
+
render json: {
|
7
|
+
operations: operations.map do |operation|
|
8
|
+
{
|
9
|
+
id: operation.id,
|
10
|
+
name: operation.name,
|
11
|
+
record_id: operation.record_id,
|
12
|
+
entity: operation.entity_name,
|
13
|
+
date: operation.created_at.iso8601,
|
14
|
+
}
|
15
|
+
end
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "ud/sync/rails"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/reset
ADDED
data/bin/setup
ADDED
data/config/routes.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateUdSyncTables < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :ud_sync_operations do |t|
|
4
|
+
t.string :name, null: false
|
5
|
+
t.string :record_id
|
6
|
+
t.string :external_id, null: false
|
7
|
+
t.string :owner_id
|
8
|
+
t.string :entity_name, null: false
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module UdSync
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace UdSync
|
4
|
+
|
5
|
+
initializer 'account.append_migrations' do |app|
|
6
|
+
unless app.root.to_s == root.to_s
|
7
|
+
config.paths["db/migrate"].expanded.each do |path|
|
8
|
+
app.config.paths["db/migrate"].push(path)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Base
|
3
|
+
class << self
|
4
|
+
def ud_sync(entity: nil, id: :id, owner: nil)
|
5
|
+
@@ud_sync_options ||= {}
|
6
|
+
|
7
|
+
class_name = self.name
|
8
|
+
@@ud_sync_options[class_name] = {
|
9
|
+
entity: entity,
|
10
|
+
id: id,
|
11
|
+
owner: owner
|
12
|
+
}
|
13
|
+
|
14
|
+
self.after_save :save_operation
|
15
|
+
self.after_destroy :save_operation
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def save_operation
|
20
|
+
UdSync::Sentinel.new(self, @@ud_sync_options[self.class.name]).save_operation
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module UdSync
|
2
|
+
class Sentinel
|
3
|
+
|
4
|
+
def initialize(model, options)
|
5
|
+
@model = model
|
6
|
+
@options = options
|
7
|
+
end
|
8
|
+
|
9
|
+
def save_operation
|
10
|
+
operation = if model.destroyed?
|
11
|
+
:delete
|
12
|
+
else
|
13
|
+
:save
|
14
|
+
end
|
15
|
+
|
16
|
+
UdSync::Operation.create(
|
17
|
+
name: operation,
|
18
|
+
record_id: model.id,
|
19
|
+
external_id: external_id,
|
20
|
+
owner_id: owner_id,
|
21
|
+
entity_name: operation_name
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_accessor :model, :options
|
28
|
+
|
29
|
+
def external_id
|
30
|
+
attribute = options[:id]
|
31
|
+
model.public_send(attribute)
|
32
|
+
end
|
33
|
+
|
34
|
+
def owner_id
|
35
|
+
if options[:owner].present?
|
36
|
+
attribute = "#{options[:owner]}".to_sym
|
37
|
+
|
38
|
+
if model.respond_to?(attribute) && model.public_send(attribute).present?
|
39
|
+
association = model.public_send(attribute)
|
40
|
+
if association.respond_to?(:id)
|
41
|
+
association.id.to_s
|
42
|
+
else
|
43
|
+
raise OwnerAssociationHasNoIdError, "The owner association has no id column"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def operation_name
|
50
|
+
options[:entity] || model.class.name
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/ud_sync.rb
ADDED
data/ud_sync.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'ud_sync/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ud_sync"
|
8
|
+
spec.version = UdSync::VERSION
|
9
|
+
spec.authors = ["Alexandre de Oliveira"]
|
10
|
+
spec.email = ["chavedomundo@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = ""
|
13
|
+
spec.description = ""
|
14
|
+
spec.homepage = "https://github.com/kurko/ud-sync-rails"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rspec-rails"
|
24
|
+
spec.add_development_dependency "sqlite3"
|
25
|
+
spec.add_development_dependency "awesome_print"
|
26
|
+
spec.add_development_dependency "database_cleaner"
|
27
|
+
spec.add_dependency "rails", ">=4.2"
|
28
|
+
spec.add_dependency "railties", ">=4.2"
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ud_sync
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexandre de Oliveira
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sqlite3
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: awesome_print
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: database_cleaner
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rails
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '4.2'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '4.2'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: railties
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '4.2'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '4.2'
|
125
|
+
description: ''
|
126
|
+
email:
|
127
|
+
- chavedomundo@gmail.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- ".gitignore"
|
133
|
+
- ".rspec"
|
134
|
+
- ".travis.yml"
|
135
|
+
- Gemfile
|
136
|
+
- README.md
|
137
|
+
- Rakefile
|
138
|
+
- app/controllers/ud_sync/operations_controller.rb
|
139
|
+
- app/models/ud_sync/operation.rb
|
140
|
+
- bin/console
|
141
|
+
- bin/reset
|
142
|
+
- bin/setup
|
143
|
+
- config/routes.rb
|
144
|
+
- db/migrate/20160130183000_create_ud_sync_tables.rb
|
145
|
+
- lib/ud_sync.rb
|
146
|
+
- lib/ud_sync/engine.rb
|
147
|
+
- lib/ud_sync/rails/model_extension.rb
|
148
|
+
- lib/ud_sync/sentinel.rb
|
149
|
+
- lib/ud_sync/version.rb
|
150
|
+
- ud_sync.gemspec
|
151
|
+
homepage: https://github.com/kurko/ud-sync-rails
|
152
|
+
licenses: []
|
153
|
+
metadata: {}
|
154
|
+
post_install_message:
|
155
|
+
rdoc_options: []
|
156
|
+
require_paths:
|
157
|
+
- lib
|
158
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
159
|
+
requirements:
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: '0'
|
163
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - ">="
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: '0'
|
168
|
+
requirements: []
|
169
|
+
rubyforge_project:
|
170
|
+
rubygems_version: 2.4.5
|
171
|
+
signing_key:
|
172
|
+
specification_version: 4
|
173
|
+
summary: ''
|
174
|
+
test_files: []
|