synced 1.0.9 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 08ba04139b6a3f35c0d72930ec44b13b65dff37b
4
- data.tar.gz: 30e35e40ad1dd24130a00be545e33a7c8c5bc083
3
+ metadata.gz: 4220ff2ed647669303e132040f72a13835a2ea11
4
+ data.tar.gz: 575e7b3ba5985f389655252f040b279ec80976fc
5
5
  SHA512:
6
- metadata.gz: c906440ffad6ff1974f8f9cbcee2f8efb2c80da92335e7a2a424a76cf5f56410b20ab1ec69b3471a12b4900d25a12abc917877dc794bfcf063ed8611e63e552a
7
- data.tar.gz: b4d93a8d7c8d60ff78f7ced2c9a2bab555fe5e26a01d0e5767debd1017f464b7bc4995adc37cfa697b62c806cdfa9cf4068fffa97a19c0529c2c6f3157686d1d
6
+ metadata.gz: 1168823d0315ebbeff7524139d86fe17defd940d2371ff6fa634471d7033d5d28eb38cca898871e009b0fe47b5e482eb215d799a0b7bc1721046ba2eea4147b5
7
+ data.tar.gz: 0f6eaf9fac8a72b73f2b001afef26521485b88fa3e3279b64dce0054b0eb33aed927379843d20de869c0daf82222b80e475a6128bd1e82199c576c7a28510202
data/lib/synced/model.rb CHANGED
@@ -98,7 +98,7 @@ module Synced
98
98
  def synchronize(options = {})
99
99
  options.symbolize_keys!
100
100
  options.assert_valid_keys(:api, :fields, :include, :remote, :remove,
101
- :scope)
101
+ :scope, :strategy)
102
102
  options[:remove] = synced_remove unless options.has_key?(:remove)
103
103
  options[:include] = Array(synced_include) unless options.has_key?(:include)
104
104
  options[:fields] = Array(synced_fields) unless options.has_key?(:fields)
@@ -0,0 +1,54 @@
1
+ module Synced
2
+ module Strategies
3
+ # This strategy doesn't do any synchronization it simply verifies if local objects are in sync
4
+ # with the remote ones (taken from the API).
5
+ class Check < Full
6
+ attr_reader :result
7
+
8
+ def initialize(model_class, options = {})
9
+ super
10
+ @result = Result.new
11
+ end
12
+
13
+ # Makes a DRY run of full synchronization. It checks and collects objects which
14
+ # * are present in the local database, but not in the API. Local AR object is
15
+ # returned - additional objects
16
+ # * are present in the API, but not in the local database, remote object is
17
+ # returned - missing objects
18
+ # * are changed in the API, but not in the local database,
19
+ # ActiveRecord::Model #changes hash is returned - changed objects
20
+ # @return [Synced::Strategies::Check::Result] Integrity check result
21
+ def perform
22
+ result.additional = remove_relation.to_a
23
+ remote_objects.map do |remote|
24
+ if local_object = local_object_by_remote_id(remote.id)
25
+ remote.extend(@mapper) if @mapper
26
+ local_object.attributes = default_attributes_mapping(remote)
27
+ local_object.attributes = local_attributes_mapping(remote)
28
+ if @globalized_attributes.present?
29
+ local_object.attributes = globalized_attributes_mapping(remote,
30
+ local_object.translations.translated_locales)
31
+ end
32
+ result.changed << local_object.changes if local_object.changed?
33
+ else
34
+ result.missing << remote
35
+ end
36
+ end
37
+ result
38
+ end
39
+
40
+ # Represents result of synchronization integrity check
41
+ class Result
42
+ attr_accessor :changed, :missing, :additional
43
+
44
+ def initialize
45
+ @changed, @missing, @additional = [], [], []
46
+ end
47
+
48
+ def passed?
49
+ changed.empty? && missing.empty? && additional.empty?
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,229 @@
1
+ module Synced
2
+ module Strategies
3
+ # This strategy performs full synchronization.
4
+ # It takes all the objects from the API and
5
+ # - creates missing in the local database
6
+ # - removes local objects which are missing the API
7
+ # - updates local objects which are changed in the API
8
+ # This is the base synchronization strategy.
9
+ class Full
10
+ include AttributesAsHash
11
+
12
+ # Initializes new Full sync strategy
13
+ #
14
+ # @param remote_objects [Array|NilClass] Array of objects to be synchronized
15
+ # with local database. Objects need to respond to at least :id message.
16
+ # If it's nil, then synchronizer will fetch the remote objects on it's own from the API.
17
+ # @param model_class [Class] ActiveRecord model class from which local objects
18
+ # will be created.
19
+ # @param options [Hash]
20
+ # @option options [Symbol] scope: Within this object scope local objects
21
+ # will be synchronized. By default it's model_class.
22
+ # @option options [Symbol] id_key: attribute name under which
23
+ # remote object's ID is stored, default is :synced_id.
24
+ # @option options [Symbol] synced_all_at_key: attribute name under which
25
+ # remote object's sync time is stored, default is :synced_all_at
26
+ # @option options [Symbol] data_key: attribute name under which remote
27
+ # object's data is stored.
28
+ # @option options [Array] local_attributes: Array of attributes in the remote
29
+ # object which will be mapped to local object attributes.
30
+ # @option options [Boolean] remove: If it's true all local objects within
31
+ # current scope which are not present in the remote array will be destroyed.
32
+ # If only_updated is enabled, ids of objects to be deleted will be taken
33
+ # from the meta part. By default if cancel_at column is present, all
34
+ # missing local objects will be canceled with cancel_all,
35
+ # if it's missing, all will be destroyed with destroy_all.
36
+ # You can also force method to remove local objects by passing it
37
+ # to remove: :mark_as_missing.
38
+ # @param api [BookingSync::API::Client] - API client to be used for fetching
39
+ # remote objects
40
+ # @option options [Boolean] only_updated: If true requests to API will take
41
+ # advantage of updated_since param and fetch only created/changed/deleted
42
+ # remote objects
43
+ # @option options [Module] mapper: Module class which will be used for
44
+ # mapping remote objects attributes into local object attributes
45
+ # @option options [Array|Hash] globalized_attributes: A list of attributes
46
+ # which will be mapped with their translations.
47
+ def initialize(model_class, options = {})
48
+ @model_class = model_class
49
+ @scope = options[:scope]
50
+ @id_key = options[:id_key]
51
+ @synced_all_at_key = options[:synced_all_at_key]
52
+ @data_key = options[:data_key]
53
+ @remove = options[:remove]
54
+ @only_updated = options[:only_updated]
55
+ @include = options[:include]
56
+ @local_attributes = synced_attributes_as_hash(options[:local_attributes])
57
+ @api = options[:api]
58
+ @mapper = options[:mapper].respond_to?(:call) ?
59
+ options[:mapper].call : options[:mapper]
60
+ @fields = options[:fields]
61
+ @remove = options[:remove]
62
+ @associations = Array(options[:associations])
63
+ @perform_request = options[:remote].nil?
64
+ @remote_objects = Array(options[:remote]) unless @perform_request
65
+ @globalized_attributes = synced_attributes_as_hash(options[:globalized_attributes])
66
+ end
67
+
68
+ def perform
69
+ instrument("perform.synced", model: @model_class) do
70
+ relation_scope.transaction do
71
+ instrument("remove_perform.synced", model: @model_class) do
72
+ remove_relation.send(remove_strategy) if @remove
73
+ end
74
+ instrument("sync_perform.synced", model: @model_class) do
75
+ remote_objects.map do |remote|
76
+ remote.extend(@mapper) if @mapper
77
+ local_object = local_object_by_remote_id(remote.id) || relation_scope.new
78
+ local_object.attributes = default_attributes_mapping(remote)
79
+ local_object.attributes = local_attributes_mapping(remote)
80
+ if @globalized_attributes.present?
81
+ local_object.attributes = globalized_attributes_mapping(remote,
82
+ local_object.translations.translated_locales)
83
+ end
84
+ local_object.save! if local_object.changed?
85
+ local_object.tap do |local_object|
86
+ synchronize_associations(remote, local_object)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def synchronize_associations(remote, local_object)
97
+ @associations.each do |association|
98
+ klass = association.to_s.classify.constantize
99
+ klass.synchronize(remote: remote[association], scope: local_object, remove: @remove)
100
+ end
101
+ end
102
+
103
+ def local_attributes_mapping(remote)
104
+ Hash[@local_attributes.map do |k, v|
105
+ [k, v.respond_to?(:call) ? v.call(remote) : remote.send(v)]
106
+ end]
107
+ end
108
+
109
+ def default_attributes_mapping(remote)
110
+ {}.tap do |attributes|
111
+ attributes[@id_key] = remote.id
112
+ attributes[@data_key] = remote if @data_key
113
+ end
114
+ end
115
+
116
+ def globalized_attributes_mapping(remote, used_locales)
117
+ empty = Hash[used_locales.map { |locale| [locale.to_s, nil] }]
118
+ {}.tap do |attributes|
119
+ @globalized_attributes.each do |local_attr, remote_attr|
120
+ translations = empty.merge(remote.send(remote_attr) || {})
121
+ attributes["#{local_attr}_translations"] = translations
122
+ end
123
+ end
124
+ end
125
+
126
+ # Returns relation within which local objects are created/edited and removed
127
+ # If no scope is provided, the relation_scope will be class on which
128
+ # .synchronize method is called.
129
+ # If scope is provided, like: account, then relation_scope will be a relation
130
+ # account.rentals (given we run .synchronize on Rental class)
131
+ #
132
+ # @return [ActiveRecord::Relation|Class]
133
+ def relation_scope
134
+ if @scope
135
+ @model_class.unscoped { @scope.send(resource_name).scope }
136
+ else
137
+ @model_class.unscoped
138
+ end
139
+ end
140
+
141
+ # Returns api client from the closest possible source.
142
+ #
143
+ # @raise [BookingSync::API::Unauthorized] - On unauthorized user
144
+ # @return [BookingSync::API::Client] BookingSync API client
145
+ def api
146
+ return @api if @api
147
+ closest = [@scope, @scope.class, @model_class].find do |object|
148
+ object.respond_to?(:api)
149
+ end
150
+ closest.try(:api) || raise(MissingAPIClient.new(@scope, @model_class))
151
+ end
152
+
153
+ def local_object_by_remote_id(remote_id)
154
+ local_objects.find { |l| l.public_send(@id_key) == remote_id }
155
+ end
156
+
157
+ def local_objects
158
+ @local_objects ||= relation_scope.where(@id_key => remote_objects_ids).to_a
159
+ end
160
+
161
+ def remote_objects_ids
162
+ @remote_objects_ids ||= remote_objects.map(&:id)
163
+ end
164
+
165
+ def remote_objects
166
+ @remote_objects ||= @perform_request ? fetch_remote_objects : nil
167
+ end
168
+
169
+ def fetch_remote_objects
170
+ instrument("fetch_remote_objects.synced", model: @model_class) do
171
+ api.paginate(resource_name, api_request_options)
172
+ end
173
+ end
174
+
175
+ def api_request_options
176
+ {}.tap do |options|
177
+ options[:include] = @associations if @associations.present?
178
+ if @include.present?
179
+ options[:include] ||= []
180
+ options[:include] += @include
181
+ end
182
+ options[:fields] = @fields if @fields.present?
183
+ options[:auto_paginate] = true
184
+ end
185
+ end
186
+
187
+ def resource_name
188
+ @model_class.to_s.tableize
189
+ end
190
+
191
+ def remove_strategy
192
+ @remove == true ? default_remove_strategy : @remove
193
+ end
194
+
195
+ def default_remove_strategy
196
+ if @model_class.column_names.include?("canceled_at")
197
+ :cancel_all
198
+ else
199
+ :destroy_all
200
+ end
201
+ end
202
+
203
+ # Remove all local objects which are not present in the remote objects
204
+ def remove_relation
205
+ relation_scope.where.not(@id_key => remote_objects_ids)
206
+ end
207
+
208
+ def instrument(*args, &block)
209
+ Synced.instrumenter.instrument(*args, &block)
210
+ end
211
+
212
+ class MissingAPIClient < StandardError
213
+ def initialize(scope, model_class)
214
+ @scope = scope
215
+ @model_class = model_class
216
+ end
217
+
218
+ def message
219
+ if @scope
220
+ %Q{Missing BookingSync API client in #{@scope} object or \
221
+ #{@scope.class} class when synchronizing #{@model_class} model}
222
+ else
223
+ %Q{Missing BookingSync API client in #{@model_class} class}
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,75 @@
1
+ module Synced
2
+ module Strategies
3
+ # This strategy performs partial synchronization.
4
+ # It fetches only changes (additions, modifications and deletions) from the API.
5
+ class UpdatedSince < Full
6
+ # @option options [Time|Proc] initial_sync_since: A point in time from which
7
+ # objects will be synchronized on first synchronization.
8
+ def initialize(model_class, options = {})
9
+ super
10
+ @initial_sync_since = options[:initial_sync_since]
11
+ end
12
+
13
+ def perform
14
+ super.tap do |local_objects|
15
+ instrument("update_synced_all_at_perform.synced", model: @model_class) do
16
+ # TODO: it can't be Time.now. this value has to be fetched from the API as well
17
+ # https://github.com/BookingSync/synced/issues/29
18
+ relation_scope.update_all(@synced_all_at_key => Time.now)
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def api_request_options
26
+ super.merge(updated_since: updated_since)
27
+ end
28
+
29
+ def initial_sync_since
30
+ if @initial_sync_since.respond_to?(:call)
31
+ @initial_sync_since.arity == 0 ? @initial_sync_since.call :
32
+ @initial_sync_since.call(@scope)
33
+ else
34
+ @initial_sync_since
35
+ end
36
+ end
37
+
38
+ def updated_since
39
+ instrument("updated_since.synced") do
40
+ [relation_scope.minimum(@synced_all_at_key), initial_sync_since].compact.max
41
+ end
42
+ end
43
+
44
+ def deleted_remote_objects_ids
45
+ meta && meta[:deleted_ids] or raise CannotDeleteDueToNoDeletedIdsError.new(@model_class)
46
+ end
47
+
48
+ def meta
49
+ remote_objects
50
+ @meta ||= api.last_response.meta
51
+ end
52
+
53
+ # Remove all objects with ids from deleted_ids field in the meta key
54
+ def remove_relation
55
+ relation_scope.where(@id_key => deleted_remote_objects_ids)
56
+ end
57
+
58
+ class CannotDeleteDueToNoDeletedIdsError < StandardError
59
+ def initialize(model_class)
60
+ @model_class = model_class
61
+ end
62
+
63
+ def message
64
+ "Cannot delete #{pluralized_model_class}. No deleted_ids were returned in API response."
65
+ end
66
+
67
+ private
68
+
69
+ def pluralized_model_class
70
+ @model_class.to_s.pluralize
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,17 +1,20 @@
1
1
  require 'synced/delegate_attributes'
2
2
  require 'synced/attributes_as_hash'
3
+ require 'synced/strategies/full'
4
+ require 'synced/strategies/check'
5
+ require 'synced/strategies/updated_since'
6
+
3
7
  # Synchronizer class which performs actual synchronization between
4
8
  # local database and given array of remote objects
5
9
  module Synced
6
10
  class Synchronizer
7
- include AttributesAsHash
8
- attr_reader :id_key
11
+ attr_reader :strategy
9
12
 
10
13
  # Initializes a new Synchronizer
11
14
  #
12
15
  # @param remote_objects [Array|NilClass] Array of objects to be synchronized
13
16
  # with local database. Objects need to respond to at least :id message.
14
- # If it's nil, then synchronizer will fetch the remote objects on it's own.
17
+ # If it's nil, then synchronizer will fetch the remote objects on it's own from the API.
15
18
  # @param model_class [Class] ActiveRecord model class from which local objects
16
19
  # will be created.
17
20
  # @param options [Hash]
@@ -42,221 +45,30 @@ module Synced
42
45
  # mapping remote objects attributes into local object attributes
43
46
  # @option options [Array|Hash] globalized_attributes: A list of attributes
44
47
  # which will be mapped with their translations.
45
- # @option options [Time|Proc] initial_sync_since: A point in time from which
46
- # objects will be synchronized on first synchronization.
47
- # Works only for partial (updated_since param) synchronizations.
48
+ # @option options [Symbol] strategy: Strategy to be used for synchronization
49
+ # process, possible values are :full, :updated_since, :check and nil. Default
50
+ # is nil, so strategy will be chosen automatically.
48
51
  def initialize(model_class, options = {})
49
- @model_class = model_class
50
- @scope = options[:scope]
51
- @id_key = options[:id_key]
52
- @synced_all_at_key = options[:synced_all_at_key]
53
- @data_key = options[:data_key]
54
- @remove = options[:remove]
55
- @only_updated = options[:only_updated]
56
- @include = options[:include]
57
- @local_attributes = synced_attributes_as_hash(options[:local_attributes])
58
- @api = options[:api]
59
- @mapper = options[:mapper].respond_to?(:call) ?
60
- options[:mapper].call : options[:mapper]
61
- @fields = options[:fields]
62
- @remove = options[:remove]
63
- @associations = Array(options[:associations])
64
- @perform_request = options[:remote].nil?
65
- @remote_objects = Array(options[:remote]) unless @perform_request
66
- @globalized_attributes = synced_attributes_as_hash(options[:globalized_attributes])
67
- @initial_sync_since = options[:initial_sync_since]
52
+ @model_class = model_class
53
+ @synced_all_at_key = options[:synced_all_at_key]
54
+ @only_updated = options[:only_updated]
55
+ @perform_request = options[:remote].nil?
56
+ @strategy = strategy_class(options[:strategy]).new(model_class, options)
68
57
  end
69
58
 
70
59
  def perform
71
- instrument("perform.synced", model: @model_class) do
72
- relation_scope.transaction do
73
- instrument("remove_perform.synced", model: @model_class) do
74
- remove_relation.send(remove_strategy) if @remove
75
- end
76
- instrument("sync_perform.synced", model: @model_class) do
77
- remote_objects.map do |remote|
78
- remote.extend(@mapper) if @mapper
79
- local_object = local_object_by_remote_id(remote.id) || relation_scope.new
80
- local_object.attributes = default_attributes_mapping(remote)
81
- local_object.attributes = local_attributes_mapping(remote)
82
- if @globalized_attributes.present?
83
- local_object.attributes = globalized_attributes_mapping(remote,
84
- local_object.translations.translated_locales)
85
- end
86
- local_object.save! if local_object.changed?
87
- local_object.tap do |local_object|
88
- @associations.each do |association|
89
- klass = association.to_s.classify.constantize
90
- klass.synchronize(remote: remote[association], scope: local_object,
91
- remove: @remove)
92
- end
93
- end
94
- end
95
- end.tap do |local_objects|
96
- if updated_since_enabled?
97
- instrument("update_synced_all_at_perform.synced", model: @model_class) do
98
- relation_scope.update_all(@synced_all_at_key => Time.now)
99
- end
100
- end
101
- end
102
- end
103
- end
60
+ @strategy.perform
104
61
  end
105
62
 
106
63
  private
107
64
 
108
- def local_attributes_mapping(remote)
109
- Hash[@local_attributes.map do |k, v|
110
- [k, v.respond_to?(:call) ? v.call(remote) : remote.send(v)]
111
- end]
112
- end
113
-
114
- def default_attributes_mapping(remote)
115
- {}.tap do |attributes|
116
- attributes[@id_key] = remote.id
117
- attributes[@data_key] = remote if @data_key
118
- end
119
- end
120
-
121
- def globalized_attributes_mapping(remote, used_locales)
122
- empty = Hash[used_locales.map { |locale| [locale.to_s, nil] }]
123
- {}.tap do |attributes|
124
- @globalized_attributes.each do |local_attr, remote_attr|
125
- translations = empty.merge(remote.send(remote_attr) || {})
126
- attributes["#{local_attr}_translations"] = translations
127
- end
128
- end
129
- end
130
-
131
- # Returns relation within which local objects are created/edited and removed
132
- # If no scope is provided, the relation_scope will be class on which
133
- # .synchronize method is called.
134
- # If scope is provided, like: account, then relation_scope will be a relation
135
- # account.rentals (given we run .synchronize on Rental class)
136
- #
137
- # @return [ActiveRecord::Relation|Class]
138
- def relation_scope
139
- if @scope
140
- @model_class.unscoped { @scope.send(resource_name).scope }
141
- else
142
- @model_class.unscoped
143
- end
144
- end
145
-
146
- # Returns api client from the closest possible source.
147
- #
148
- # @raise [BookingSync::API::Unauthorized] - On unauthorized user
149
- # @return [BookingSync::API::Client] BookingSync API client
150
- def api
151
- return @api if @api
152
- closest = [@scope, @scope.class, @model_class].detect do |o|
153
- o.respond_to?(:api)
154
- end
155
- closest && closest.api || raise(MissingAPIClient.new(@scope, @model_class))
156
- end
157
-
158
- def local_object_by_remote_id(remote_id)
159
- local_objects.find { |l| l.public_send(id_key) == remote_id }
160
- end
161
-
162
- def local_objects
163
- @local_objects ||= relation_scope.where(id_key => remote_objects_ids).to_a
164
- end
165
-
166
- def remote_objects_ids
167
- @remote_objects_ids ||= remote_objects.map(&:id)
168
- end
169
-
170
- def remote_objects
171
- @remote_objects ||= @perform_request ? fetch_remote_objects : nil
172
- end
173
-
174
- def deleted_remote_objects_ids
175
- remote_objects
176
- api.last_response.meta[:deleted_ids]
65
+ def strategy_class(name)
66
+ name ||= updated_since? ? :updated_since : :full
67
+ "Synced::Strategies::#{name.to_s.classify}".constantize
177
68
  end
178
69
 
179
- def fetch_remote_objects
180
- instrument("fetch_remote_objects.synced", model: @model_class) do
181
- api.paginate(resource_name, api_request_options)
182
- end
183
- end
184
-
185
- def api_request_options
186
- {}.tap do |options|
187
- options[:include] = @associations if @associations.present?
188
- if @include.present?
189
- options[:include] ||= []
190
- options[:include] += @include
191
- end
192
- options[:fields] = @fields if @fields.present?
193
- options[:updated_since] = updated_since if updated_since_enabled?
194
- options[:auto_paginate] = true
195
- end
196
- end
197
-
198
- def updated_since
199
- instrument("updated_since.synced") do
200
- [relation_scope.minimum(@synced_all_at_key),
201
- initial_sync_since].compact.max
202
- end
203
- end
204
-
205
- def initial_sync_since
206
- if @initial_sync_since.respond_to?(:call)
207
- @initial_sync_since.arity == 0 ? @initial_sync_since.call :
208
- @initial_sync_since.call(@scope)
209
- else
210
- @initial_sync_since
211
- end
212
- end
213
-
214
- def updated_since_enabled?
70
+ def updated_since?
215
71
  @only_updated && @synced_all_at_key && @perform_request
216
72
  end
217
-
218
- def resource_name
219
- @model_class.to_s.tableize
220
- end
221
-
222
- def remove_strategy
223
- @remove == true ? default_remove_strategy : @remove
224
- end
225
-
226
- def default_remove_strategy
227
- if @model_class.column_names.include?("canceled_at")
228
- :cancel_all
229
- else
230
- :destroy_all
231
- end
232
- end
233
-
234
- def remove_relation
235
- if updated_since_enabled?
236
- relation_scope.where(id_key => deleted_remote_objects_ids)
237
- else
238
- relation_scope.where.not(id_key => remote_objects_ids)
239
- end
240
- end
241
-
242
- def instrument(*args, &block)
243
- Synced.instrumenter.instrument(*args, &block)
244
- end
245
-
246
- class MissingAPIClient < StandardError
247
- def initialize(scope, model_class)
248
- @scope = scope
249
- @model_class = model_class
250
- end
251
-
252
- def message
253
- if @scope
254
- %Q{Missing BookingSync API client in #{@scope} object or \
255
- #{@scope.class} class when synchronizing #{@model_class} model}
256
- else
257
- %Q{Missing BookingSync API client in #{@model_class} class}
258
- end
259
- end
260
- end
261
73
  end
262
74
  end
@@ -1,3 +1,3 @@
1
1
  module Synced
2
- VERSION = "1.0.9"
2
+ VERSION = "1.1.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: synced
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.9
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastien Grosjean
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-03-24 00:00:00.000000000 Z
12
+ date: 2015-06-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -167,6 +167,9 @@ files:
167
167
  - lib/synced/has_synced_data.rb
168
168
  - lib/synced/model.rb
169
169
  - lib/synced/rails.rb
170
+ - lib/synced/strategies/check.rb
171
+ - lib/synced/strategies/full.rb
172
+ - lib/synced/strategies/updated_since.rb
170
173
  - lib/synced/synchronizer.rb
171
174
  - lib/synced/version.rb
172
175
  homepage: https://github.com/BookingSync/synced
@@ -189,8 +192,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
189
192
  version: '0'
190
193
  requirements: []
191
194
  rubyforge_project:
192
- rubygems_version: 2.4.3
195
+ rubygems_version: 2.4.5
193
196
  signing_key:
194
197
  specification_version: 4
195
198
  summary: Keep your BookingSync Application synced with BookingSync.
196
199
  test_files: []
200
+ has_rdoc: