synced 1.0.9 → 1.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 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: