synced 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +107 -15
- data/lib/synced/engine.rb +7 -1
- data/lib/synced/engine/has_synced_data.rb +41 -0
- data/lib/synced/engine/model.rb +74 -0
- data/lib/synced/engine/synchronizer.rb +188 -0
- data/lib/synced/version.rb +1 -1
- metadata +19 -5
- data/app/models/synced/synchronization.rb +0 -4
- data/db/migrate/20140621143737_create_synced_synchronizations.rb +0 -10
- data/lib/synced/engine/lib/has_synced_data.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 129cd901e2a89c56c4854ebc844b1f37f57833e3
|
4
|
+
data.tar.gz: c52f215a8f82e22919adfc6925db0e9fbde894f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59b45c2d36ed714e375e2d34eabe2d57ba62453aad252a4a8c6cec61f134fc692c8692476bfa5c530334ad536a48199edbde2cf372f2ca3b746aa4ffbf1b75eb
|
7
|
+
data.tar.gz: 77a485f2007ce67c0f01be365622a770b41c01f96b240212fa87985e495cb0e2fbba594ca146c38cf0199d6e7ccc86a681ee58307684064512578f3e3cb30398
|
data/README.md
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
# Synced
|
2
2
|
|
3
|
-
Synced is a Rails Engine that helps you keep local models synchronized with
|
3
|
+
Synced is a Rails Engine that helps you keep local models synchronized with
|
4
|
+
their BookingSync representation.
|
4
5
|
|
5
|
-
Note: This synchronization is in one way only, from BookingSync to your
|
6
|
+
Note: This synchronization is in one way only, from BookingSync to your
|
7
|
+
application. If you want to do a 2 way synchronization, you will need to
|
8
|
+
implement it yourself using
|
9
|
+
[BookingSync-API](https://github.com/BookingSync/bookingsync-api)
|
6
10
|
|
7
11
|
## Requirements
|
8
12
|
|
9
|
-
This engine requires BookingSync API `>= 0.0.17`, Rails `>= 4.0.0` and
|
13
|
+
This engine requires BookingSync API `>= 0.0.17`, Rails `>= 4.0.0` and
|
14
|
+
Ruby `>= 2.0.0`.
|
10
15
|
|
11
16
|
## Documentation
|
12
17
|
|
@@ -14,24 +19,19 @@ This engine requires BookingSync API `>= 0.0.17`, Rails `>= 4.0.0` and Ruby `>=
|
|
14
19
|
|
15
20
|
## Installation
|
16
21
|
|
17
|
-
Synced works with BookingSync API 0.0.17 onwards, Rails 4.0 onwards and Ruby
|
22
|
+
Synced works with BookingSync API 0.0.17 onwards, Rails 4.0 onwards and Ruby
|
23
|
+
2.0 onwards. To get started, add it to your Gemfile with:
|
18
24
|
|
19
25
|
```ruby
|
20
26
|
gem 'synced'
|
21
27
|
```
|
22
28
|
|
23
|
-
|
24
|
-
|
25
|
-
```console
|
26
|
-
rake synced:install:migrations
|
27
|
-
```
|
28
|
-
|
29
|
-
Then, generate a migration to add Synced fields for the model you want to synchronize:
|
29
|
+
Generate a migration to add Synced fields for the model you want to synchronize:
|
30
30
|
|
31
31
|
Example:
|
32
32
|
```console
|
33
|
-
rails g migration AddSyncedFieldsToRentals synced_id:integer:index
|
34
|
-
synced_updated_at:datetime
|
33
|
+
rails g migration AddSyncedFieldsToRentals synced_id:integer:index \
|
34
|
+
synced_data:text synced_updated_at:datetime
|
35
35
|
```
|
36
36
|
|
37
37
|
and migrate:
|
@@ -40,11 +40,103 @@ and migrate:
|
|
40
40
|
rake db:migrate
|
41
41
|
```
|
42
42
|
|
43
|
-
And
|
43
|
+
And `synced` statement to the model you want to keep in sync.
|
44
|
+
|
45
|
+
Example:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
class Rental < ActiveRecord::Base
|
49
|
+
synced
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
Run synchronization with given remote rentals
|
54
|
+
|
55
|
+
Example:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
Rental.synchronize(remote: remote_rentals)
|
59
|
+
```
|
60
|
+
|
61
|
+
Run rentals synchronization in website scope
|
44
62
|
|
45
63
|
Example:
|
64
|
+
|
46
65
|
```ruby
|
66
|
+
Rental.synchronize(remote: remote_rentals, scope: website)
|
67
|
+
```
|
68
|
+
|
69
|
+
## Custom fields for storing remote object data.
|
70
|
+
|
71
|
+
By default synced stores remote object in the following db columns.
|
72
|
+
|
73
|
+
`synced_id` - ID of the remote object
|
74
|
+
`synced_data` - Whole remote object is serialized into this attribute
|
75
|
+
`synced_all_at` - Synchronization time of the local object when using
|
76
|
+
updated_since param
|
77
|
+
|
78
|
+
You can configure your own fields in `synced` declaration in your model.
|
79
|
+
|
80
|
+
```
|
47
81
|
class Rental < ActiveRecord::Base
|
48
|
-
|
82
|
+
synced id_key: :remote_id, data_key: :remote_data, synced_all_at_key: :remote_all_synced_at
|
49
83
|
end
|
50
84
|
```
|
85
|
+
|
86
|
+
## Local attributes
|
87
|
+
|
88
|
+
All remote data is stored in `synced_data`, however sometimes it's useful to have some attributes directly in your model. You can use `local_attributes` for that.
|
89
|
+
|
90
|
+
```
|
91
|
+
class Rental < ActiveRecord::Base
|
92
|
+
synced local_attributes: [:name, :size]
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
This assumes that model has name and size attributes. On every sychronization these two attributes will be assigned with value of `remote_object.name` and `remote_object.size` appropriately.
|
97
|
+
|
98
|
+
## Disabling synchronization for selected fields.
|
99
|
+
|
100
|
+
In some cases you only need one attribute to be synchronized and nothing more.
|
101
|
+
By default even when using local_attributes, whole remote object will be
|
102
|
+
saved in the `synced_data` and its updated_at in the `synced_all_at`.
|
103
|
+
This may take additonal space in the database.
|
104
|
+
In order to disable synchronizing these fields, set their names in the `synced` declaration to nil, as in the below example:
|
105
|
+
|
106
|
+
```
|
107
|
+
class Rental < ActiveRecord::Base
|
108
|
+
synced data_key: nil, synced_all_at_key: nil
|
109
|
+
end
|
110
|
+
```
|
111
|
+
|
112
|
+
You cannot disable synchronizing `synced_id` as it's required to match local
|
113
|
+
objects with the remote ones.
|
114
|
+
|
115
|
+
## Associations
|
116
|
+
|
117
|
+
It's possible to synchronize objects together with it's associations. For that
|
118
|
+
you need to
|
119
|
+
|
120
|
+
1. Specify associations you want to synchronize within `synced`
|
121
|
+
declaration of the parent model
|
122
|
+
2. Add `synced` declaration to the associated model
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
class Location < ActiveRecord::Base
|
126
|
+
synced associations: :photos
|
127
|
+
has_many :photos
|
128
|
+
end
|
129
|
+
|
130
|
+
class Photo < ActiveRecord::Base
|
131
|
+
synced
|
132
|
+
belongs_to :location
|
133
|
+
end
|
134
|
+
```
|
135
|
+
|
136
|
+
Then run synchronization of the parent objects. Every of the remote_locations
|
137
|
+
objects needs to respond to `remote_location[:photos]` from where data for
|
138
|
+
photos association will be taken.
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
Location.synchronize(remote: remote_locations)
|
142
|
+
```
|
data/lib/synced/engine.rb
CHANGED
@@ -7,7 +7,13 @@ module Synced
|
|
7
7
|
end
|
8
8
|
|
9
9
|
config.to_prepare do
|
10
|
-
require "synced/engine/
|
10
|
+
require "synced/engine/has_synced_data"
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveSupport.on_load :active_record do
|
14
|
+
extend Synced::Engine::Model
|
11
15
|
end
|
12
16
|
end
|
13
17
|
end
|
18
|
+
|
19
|
+
require "synced/engine/model"
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
# Provide a serialized attribute for models. This attribute is `synced_data_key`
|
4
|
+
# which by default is `:synced_data`. This is a friendlier alternative to
|
5
|
+
# `serialize` with respect to dirty attributes.
|
6
|
+
module Synced::Engine::HasSyncedData
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
class SyncedData < Hashie::Mash; end
|
9
|
+
|
10
|
+
included do
|
11
|
+
if synced_data_key
|
12
|
+
define_method "#{synced_data_key}=" do |object|
|
13
|
+
write_attribute synced_data_key, dump(object)
|
14
|
+
end
|
15
|
+
|
16
|
+
define_method synced_data_key do
|
17
|
+
instance_variable_get("@#{synced_data_key}") ||
|
18
|
+
instance_variable_set("@#{synced_data_key}",
|
19
|
+
SyncedData.new(loaded_synced_data))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def loaded_synced_data
|
27
|
+
if data = read_attribute(synced_data_key)
|
28
|
+
load data
|
29
|
+
else
|
30
|
+
{}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def dump(object)
|
35
|
+
JSON.dump object
|
36
|
+
end
|
37
|
+
|
38
|
+
def load(source)
|
39
|
+
JSON.load source
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require "synced/engine/synchronizer"
|
2
|
+
|
3
|
+
module Synced::Engine::Model
|
4
|
+
# Enables synced for ActiveRecord model.
|
5
|
+
#
|
6
|
+
# @param options [Hash] Configuration options for synced. They are inherited
|
7
|
+
# by subclasses, but can be overwritten in the subclass.
|
8
|
+
# @option options [Symbol] id_key: attribute name under which
|
9
|
+
# remote object's ID is stored, default is :synced_id.
|
10
|
+
# @option options [Symbol] synced_all_at_key: attribute name under which
|
11
|
+
# last synchronization time is stored, default is :synced_all_at. It's only
|
12
|
+
# used when only_updated option is enabled.
|
13
|
+
# @option options [Boolean] only_updated: If true requests to API will take
|
14
|
+
# advantage of updated_since param and fetch only created/changed/deleted
|
15
|
+
# remote objects
|
16
|
+
# @option options [Symbol] data_key: attribute name under which remote
|
17
|
+
# object's data is stored.
|
18
|
+
# @option options [Array] local_attributes: Array of attributes in the remote
|
19
|
+
# object which will be mapped to local object attributes.
|
20
|
+
def synced(options = {})
|
21
|
+
class_attribute :synced_id_key, :synced_all_at_key, :synced_data_key,
|
22
|
+
:synced_local_attributes, :synced_associations, :synced_only_updated
|
23
|
+
self.synced_id_key = options.fetch(:id_key, :synced_id)
|
24
|
+
self.synced_all_at_key = options.fetch(:synced_all_at_key,
|
25
|
+
:synced_all_at)
|
26
|
+
self.synced_data_key = options.fetch(:data_key, :synced_data)
|
27
|
+
self.synced_local_attributes = options.fetch(:local_attributes, [])
|
28
|
+
self.synced_associations = options.fetch(:associations, [])
|
29
|
+
self.synced_only_updated = options.fetch(:only_updated, false)
|
30
|
+
include Synced::Engine::HasSyncedData
|
31
|
+
end
|
32
|
+
|
33
|
+
# Performs synchronization of given remote objects to local database.
|
34
|
+
#
|
35
|
+
# @param remote [Array] - Remote objects to be synchronized with local db. If
|
36
|
+
# it's nil then synchronizer will make request on it's own.
|
37
|
+
# @param model_class [Class] - ActiveRecord model class to which remote objects
|
38
|
+
# will be synchronized.
|
39
|
+
# @param scope [ActiveRecord::Base] - Within this object scope local objects
|
40
|
+
# will be synchronized. By default it's model_class.
|
41
|
+
# @param remove [Boolean] - If it's true all local objects within
|
42
|
+
# current scope which are not present in the remote array will be destroyed.
|
43
|
+
# If only_updated is enabled, ids of objects to be deleted will be taken
|
44
|
+
# from the meta part. By default if cancel_at column is present, all
|
45
|
+
# missing local objects will be canceled with cancel_all,
|
46
|
+
# if it's missing, all will be destroyed with destroy_all.
|
47
|
+
# You can also force method to remove local objects by passing it
|
48
|
+
# to remove: :mark_as_missing.
|
49
|
+
# @example Synchronizing amenities
|
50
|
+
#
|
51
|
+
# Amenity.synchronize(remote: [remote_amenity1, remote_amenity2])
|
52
|
+
#
|
53
|
+
# @example Synchronizing rentals within given website. This will
|
54
|
+
# create/remove/update rentals only within website.
|
55
|
+
# It requires relation website.rentals to exist.
|
56
|
+
#
|
57
|
+
# Rental.synchronize(remote: remote_rentals, scope: website)
|
58
|
+
#
|
59
|
+
def synchronize(remote: nil, model_class: self, scope: nil, remove: false)
|
60
|
+
options = {
|
61
|
+
scope: scope,
|
62
|
+
id_key: synced_id_key,
|
63
|
+
synced_all_at_key: synced_all_at_key,
|
64
|
+
data_key: synced_data_key,
|
65
|
+
remove: remove,
|
66
|
+
local_attributes: synced_local_attributes,
|
67
|
+
associations: synced_associations,
|
68
|
+
only_updated: synced_only_updated
|
69
|
+
}
|
70
|
+
synchronizer = Synced::Engine::Synchronizer.new(remote, model_class,
|
71
|
+
options)
|
72
|
+
synchronizer.perform
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# Synchronizer class which performs actual synchronization between
|
2
|
+
# local database and given array of remote objects
|
3
|
+
class Synced::Engine::Synchronizer
|
4
|
+
attr_reader :id_key
|
5
|
+
|
6
|
+
# Initializes a new Synchronizer
|
7
|
+
#
|
8
|
+
# @param remote_objects [Array|NilClass] Array of objects to be synchronized
|
9
|
+
# with local database. Objects need to respond to at least :id message.
|
10
|
+
# If it's nil, then synchronizer will fetch the remote objects on it's own.
|
11
|
+
# @param model_class [Class] ActiveRecord model class from which local objects
|
12
|
+
# will be created.
|
13
|
+
# @param options [Hash]
|
14
|
+
# @option options [Symbol] scope: Within this object scope local objects
|
15
|
+
# will be synchronized. By default it's model_class.
|
16
|
+
# @option options [Symbol] id_key: attribute name under which
|
17
|
+
# remote object's ID is stored, default is :synced_id.
|
18
|
+
# @option options [Symbol] synced_all_at_key: attribute name under which
|
19
|
+
# remote object's sync time is stored, default is :synced_all_at
|
20
|
+
# @option options [Symbol] data_key: attribute name under which remote
|
21
|
+
# object's data is stored.
|
22
|
+
# @option options [Array] local_attributes: Array of attributes in the remote
|
23
|
+
# object which will be mapped to local object attributes.
|
24
|
+
# @option options [Boolean] remove: If it's true all local objects within
|
25
|
+
# current scope which are not present in the remote array will be destroyed.
|
26
|
+
# If only_updated is enabled, ids of objects to be deleted will be taken
|
27
|
+
# from the meta part. By default if cancel_at column is present, all
|
28
|
+
# missing local objects will be canceled with cancel_all,
|
29
|
+
# if it's missing, all will be destroyed with destroy_all.
|
30
|
+
# You can also force method to remove local objects by passing it
|
31
|
+
# to remove: :mark_as_missing.
|
32
|
+
# @option options [Boolean] only_updated: If true requests to API will take
|
33
|
+
# advantage of updated_since param and fetch only created/changed/deleted
|
34
|
+
# remote objects
|
35
|
+
def initialize(remote_objects, model_class, options = {})
|
36
|
+
@model_class = model_class
|
37
|
+
@scope = options[:scope]
|
38
|
+
@id_key = options[:id_key]
|
39
|
+
@synced_all_at_key = options[:synced_all_at_key]
|
40
|
+
@data_key = options[:data_key]
|
41
|
+
@remove = options[:remove]
|
42
|
+
@only_updated = options[:only_updated]
|
43
|
+
@local_attributes = Array(options[:local_attributes])
|
44
|
+
@associations = Array(options[:associations])
|
45
|
+
@remote_objects = Array(remote_objects) if remote_objects
|
46
|
+
@request_performed = false
|
47
|
+
end
|
48
|
+
|
49
|
+
def perform
|
50
|
+
relation_scope.transaction do
|
51
|
+
remove_relation.send(remove_strategy) if @remove
|
52
|
+
|
53
|
+
remote_objects.map do |remote|
|
54
|
+
local_object = local_object_by_remote_id(remote.id) || relation_scope.new
|
55
|
+
local_object.attributes = default_attributes_mapping(remote)
|
56
|
+
local_object.attributes = local_attributes_mapping(remote)
|
57
|
+
local_object.save! if local_object.changed?
|
58
|
+
local_object.tap do |local_object|
|
59
|
+
@associations.each do |association|
|
60
|
+
klass = association.to_s.classify.constantize
|
61
|
+
klass.synchronize(remote: remote[association], scope: local_object,
|
62
|
+
remove: @remove)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end.tap do |local_objects|
|
66
|
+
if updated_since_enabled? && @request_performed
|
67
|
+
relation_scope.update_all(@synced_all_at_key => Time.now)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def local_attributes_mapping(remote)
|
76
|
+
Hash[@local_attributes.map { |k| [k, remote[k]] }]
|
77
|
+
end
|
78
|
+
|
79
|
+
def default_attributes_mapping(remote)
|
80
|
+
{}.tap do |attributes|
|
81
|
+
attributes[@id_key] = remote.id
|
82
|
+
attributes[@data_key] = remote if @data_key
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns relation within which local objects are created/edited and removed
|
87
|
+
# If no scope is provided, the relation_scope will be class on which
|
88
|
+
# .synchronize method is called.
|
89
|
+
# If scope is provided, like: account, then relation_scope will be a relation
|
90
|
+
# account.rentals (given we run .synchronize on Rental class)
|
91
|
+
#
|
92
|
+
# @return [ActiveRecord::Relation|Class]
|
93
|
+
def relation_scope
|
94
|
+
@scope ? @scope.send(resource_name) : @model_class
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns api client from the closest possible source.
|
98
|
+
#
|
99
|
+
# @raise [BookingSync::API::Unauthorized] - On unauthorized user
|
100
|
+
# @return [BookingSync::API::Client] BookingSync API client
|
101
|
+
def api
|
102
|
+
closest = [@scope, @scope.class, @model_class].detect do |o|
|
103
|
+
o.respond_to?(:api)
|
104
|
+
end
|
105
|
+
closest && closest.api || raise(MissingAPIClient.new(@scope, @model_class))
|
106
|
+
end
|
107
|
+
|
108
|
+
def local_object_by_remote_id(remote_id)
|
109
|
+
local_objects.find { |l| l.attributes[id_key.to_s] == remote_id }
|
110
|
+
end
|
111
|
+
|
112
|
+
def local_objects
|
113
|
+
@local_objects ||= relation_scope.where(id_key => remote_objects_ids).to_a
|
114
|
+
end
|
115
|
+
|
116
|
+
def remote_objects_ids
|
117
|
+
@remote_objects_ids ||= remote_objects.map(&:id)
|
118
|
+
end
|
119
|
+
|
120
|
+
def remote_objects
|
121
|
+
@remote_objects ||= fetch_remote_objects
|
122
|
+
end
|
123
|
+
|
124
|
+
def deleted_remote_objects_ids
|
125
|
+
api.last_response.meta[:deleted_ids]
|
126
|
+
end
|
127
|
+
|
128
|
+
def fetch_remote_objects
|
129
|
+
api.get("/#{resource_name}", api_request_options).tap do
|
130
|
+
@request_performed = true
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def api_request_options
|
135
|
+
{}.tap do |options|
|
136
|
+
options[:include] = @associations if @associations.present?
|
137
|
+
options[:updated_since] = minimum_updated_at if updated_since_enabled?
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def minimum_updated_at
|
142
|
+
relation_scope.minimum(@synced_all_at_key)
|
143
|
+
end
|
144
|
+
|
145
|
+
def updated_since_enabled?
|
146
|
+
@only_updated && @synced_all_at_key
|
147
|
+
end
|
148
|
+
|
149
|
+
def resource_name
|
150
|
+
@model_class.to_s.tableize
|
151
|
+
end
|
152
|
+
|
153
|
+
def remove_strategy
|
154
|
+
@remove == true ? default_remove_strategy : @remove
|
155
|
+
end
|
156
|
+
|
157
|
+
def default_remove_strategy
|
158
|
+
if @model_class.column_names.include?("canceled_at")
|
159
|
+
:cancel_all
|
160
|
+
else
|
161
|
+
:destroy_all
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def remove_relation
|
166
|
+
if @only_updated
|
167
|
+
relation_scope.where(id_key => deleted_remote_objects_ids)
|
168
|
+
else
|
169
|
+
relation_scope.where.not(id_key => remote_objects_ids)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class MissingAPIClient < StandardError
|
174
|
+
def initialize(scope, model_class)
|
175
|
+
@scope = scope
|
176
|
+
@model_class = model_class
|
177
|
+
end
|
178
|
+
|
179
|
+
def message
|
180
|
+
if @scope
|
181
|
+
%Q{Missing BookingSync API client in #{@scope} object or
|
182
|
+
#{@scope.class} class}
|
183
|
+
else
|
184
|
+
%Q{Missing BookingSync API client in #{@model_class} class}
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
data/lib/synced/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: synced
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sebastien Grosjean
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-07-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: timecop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
description: Keep your BookingSync Application synced with BookingSync.
|
98
112
|
email:
|
99
113
|
- dev@bookingsync.com
|
@@ -104,12 +118,12 @@ files:
|
|
104
118
|
- MIT-LICENSE
|
105
119
|
- README.md
|
106
120
|
- Rakefile
|
107
|
-
- app/models/synced/synchronization.rb
|
108
121
|
- config/routes.rb
|
109
|
-
- db/migrate/20140621143737_create_synced_synchronizations.rb
|
110
122
|
- lib/synced.rb
|
111
123
|
- lib/synced/engine.rb
|
112
|
-
- lib/synced/engine/
|
124
|
+
- lib/synced/engine/has_synced_data.rb
|
125
|
+
- lib/synced/engine/model.rb
|
126
|
+
- lib/synced/engine/synchronizer.rb
|
113
127
|
- lib/synced/version.rb
|
114
128
|
homepage: https://github.com/BookingSync/synced
|
115
129
|
licenses:
|
@@ -1,37 +0,0 @@
|
|
1
|
-
require 'hashie'
|
2
|
-
|
3
|
-
module Synced
|
4
|
-
# Provide a serialized `bs_data` attribute for models. This is a friendlier
|
5
|
-
# alternative to `serialize` with respect to dirty attributes.
|
6
|
-
module HasSyncedData
|
7
|
-
class SyncedData < Hashie::Mash; end
|
8
|
-
|
9
|
-
# Serialize and set remote data from `object`.
|
10
|
-
def synced_data=(object)
|
11
|
-
write_attribute :synced_data, dump(object)
|
12
|
-
ensure
|
13
|
-
@synced_data = nil
|
14
|
-
end
|
15
|
-
|
16
|
-
# Return remote data as a cached instance.
|
17
|
-
def synced_data
|
18
|
-
@synced_data ||= SyncedData.new loaded_synced_data
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def loaded_synced_data
|
24
|
-
load read_attribute(:synced_data)
|
25
|
-
rescue
|
26
|
-
{}
|
27
|
-
end
|
28
|
-
|
29
|
-
def dump(object)
|
30
|
-
JSON.dump object
|
31
|
-
end
|
32
|
-
|
33
|
-
def load(source)
|
34
|
-
JSON.load source
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|