synced 0.0.3 → 0.0.4

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: d8fd447ac4edfd7650c0cbc27b6dd390e01709b9
4
- data.tar.gz: e0bc89a27dacb6abd1039c0e0c86eb233d85266e
3
+ metadata.gz: 9269d91cf4e1c50718fb11cc8c3a6860ede34a3a
4
+ data.tar.gz: 07a7b737a0d403e3e78011a2dd9b49385e65bfa9
5
5
  SHA512:
6
- metadata.gz: b037b0f4e35a473b0c0d0ea4e9cc95161f362620af9ad3a5b6aabad5590080c3b6133fcfab6fc2568144e1e1b0b0d34ae7bc4fb58ed8fd79e52768a1838d6e48
7
- data.tar.gz: cc2c8a5fcdb35be25a7dd4ebba407f6b7be476066bd95a6b60a37d6cbb44a4b78dff786f5396638cfd17271d616e09c84f9bf8ae5b057b47f397581335492e45
6
+ metadata.gz: e787d2b0ccca79dd8cd4dbe21814bbef16664e88290a82185253a88e4e4088cac2c30b8c925b8e01e2048570d552b0696151f06e1d9dde0ddcf8b5edca767613
7
+ data.tar.gz: 5236ccc2bebfbd74810f21d29630075322426a6c5ffc9e60fed447b912637b05875a96f74536240c9212764217c600a907633bf87355364159400b25b522cec1
@@ -3,39 +3,41 @@ require 'hashie'
3
3
  # Provide a serialized attribute for models. This attribute is `synced_data_key`
4
4
  # which by default is `:synced_data`. This is a friendlier alternative to
5
5
  # `serialize` with respect to dirty attributes.
6
- module Synced::HasSyncedData
7
- extend ActiveSupport::Concern
8
- class SyncedData < Hashie::Mash; end
6
+ module Synced
7
+ module HasSyncedData
8
+ extend ActiveSupport::Concern
9
+ class SyncedData < Hashie::Mash; end
9
10
 
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
11
+ included do
12
+ if synced_data_key
13
+ define_method "#{synced_data_key}=" do |object|
14
+ write_attribute synced_data_key, dump(object)
15
+ end
15
16
 
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))
17
+ define_method synced_data_key do
18
+ instance_variable_get("@#{synced_data_key}") ||
19
+ instance_variable_set("@#{synced_data_key}",
20
+ SyncedData.new(loaded_synced_data))
21
+ end
20
22
  end
21
23
  end
22
- end
23
24
 
24
- private
25
+ private
25
26
 
26
- def loaded_synced_data
27
- if data = read_attribute(synced_data_key)
28
- load data
29
- else
30
- {}
27
+ def loaded_synced_data
28
+ if data = read_attribute(synced_data_key)
29
+ load data
30
+ else
31
+ {}
32
+ end
31
33
  end
32
- end
33
34
 
34
- def dump(object)
35
- JSON.dump object
36
- end
35
+ def dump(object)
36
+ JSON.dump object
37
+ end
37
38
 
38
- def load(source)
39
- JSON.load source
39
+ def load(source)
40
+ JSON.load source
41
+ end
40
42
  end
41
43
  end
@@ -1,75 +1,77 @@
1
1
  require "synced/synchronizer"
2
2
 
3
- module Synced::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::HasSyncedData
31
- end
3
+ module Synced
4
+ module Model
5
+ # Enables synced for ActiveRecord model.
6
+ #
7
+ # @param options [Hash] Configuration options for synced. They are inherited
8
+ # by subclasses, but can be overwritten in the subclass.
9
+ # @option options [Symbol] id_key: attribute name under which
10
+ # remote object's ID is stored, default is :synced_id.
11
+ # @option options [Symbol] synced_all_at_key: attribute name under which
12
+ # last synchronization time is stored, default is :synced_all_at. It's only
13
+ # used when only_updated option is enabled.
14
+ # @option options [Boolean] only_updated: If true requests to API will take
15
+ # advantage of updated_since param and fetch only created/changed/deleted
16
+ # remote objects
17
+ # @option options [Symbol] data_key: attribute name under which remote
18
+ # object's data is stored.
19
+ # @option options [Array] local_attributes: Array of attributes in the remote
20
+ # object which will be mapped to local object attributes.
21
+ def synced(options = {})
22
+ class_attribute :synced_id_key, :synced_all_at_key, :synced_data_key,
23
+ :synced_local_attributes, :synced_associations, :synced_only_updated
24
+ self.synced_id_key = options.fetch(:id_key, :synced_id)
25
+ self.synced_all_at_key = options.fetch(:synced_all_at_key,
26
+ :synced_all_at)
27
+ self.synced_data_key = options.fetch(:data_key, :synced_data)
28
+ self.synced_local_attributes = options.fetch(:local_attributes, [])
29
+ self.synced_associations = options.fetch(:associations, [])
30
+ self.synced_only_updated = options.fetch(:only_updated, false)
31
+ include Synced::HasSyncedData
32
+ end
32
33
 
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
- include: nil)
61
- options = {
62
- scope: scope,
63
- id_key: synced_id_key,
64
- synced_all_at_key: synced_all_at_key,
65
- data_key: synced_data_key,
66
- remove: remove,
67
- local_attributes: synced_local_attributes,
68
- associations: synced_associations,
69
- only_updated: synced_only_updated,
70
- include: include
71
- }
72
- synchronizer = Synced::Synchronizer.new(remote, model_class, options)
73
- synchronizer.perform
34
+ # Performs synchronization of given remote objects to local database.
35
+ #
36
+ # @param remote [Array] - Remote objects to be synchronized with local db. If
37
+ # it's nil then synchronizer will make request on it's own.
38
+ # @param model_class [Class] - ActiveRecord model class to which remote objects
39
+ # will be synchronized.
40
+ # @param scope [ActiveRecord::Base] - Within this object scope local objects
41
+ # will be synchronized. By default it's model_class.
42
+ # @param remove [Boolean] - If it's true all local objects within
43
+ # current scope which are not present in the remote array will be destroyed.
44
+ # If only_updated is enabled, ids of objects to be deleted will be taken
45
+ # from the meta part. By default if cancel_at column is present, all
46
+ # missing local objects will be canceled with cancel_all,
47
+ # if it's missing, all will be destroyed with destroy_all.
48
+ # You can also force method to remove local objects by passing it
49
+ # to remove: :mark_as_missing.
50
+ # @example Synchronizing amenities
51
+ #
52
+ # Amenity.synchronize(remote: [remote_amenity1, remote_amenity2])
53
+ #
54
+ # @example Synchronizing rentals within given website. This will
55
+ # create/remove/update rentals only within website.
56
+ # It requires relation website.rentals to exist.
57
+ #
58
+ # Rental.synchronize(remote: remote_rentals, scope: website)
59
+ #
60
+ def synchronize(remote: nil, model_class: self, scope: nil, remove: false,
61
+ include: nil)
62
+ options = {
63
+ scope: scope,
64
+ id_key: synced_id_key,
65
+ synced_all_at_key: synced_all_at_key,
66
+ data_key: synced_data_key,
67
+ remove: remove,
68
+ local_attributes: synced_local_attributes,
69
+ associations: synced_associations,
70
+ only_updated: synced_only_updated,
71
+ include: include
72
+ }
73
+ synchronizer = Synced::Synchronizer.new(remote, model_class, options)
74
+ synchronizer.perform
75
+ end
74
76
  end
75
77
  end
@@ -1,4 +1,3 @@
1
- require "synced/has_synced_data"
2
1
  require "synced/model"
3
2
 
4
3
  module Synced
@@ -9,6 +8,10 @@ module Synced
9
8
  g.test_framework :rspec
10
9
  end
11
10
 
11
+ config.to_prepare do
12
+ require "synced/has_synced_data"
13
+ end
14
+
12
15
  ActiveSupport.on_load :active_record do
13
16
  extend Synced::Model
14
17
  end
@@ -1,194 +1,196 @@
1
1
  # Synchronizer class which performs actual synchronization between
2
2
  # local database and given array of remote objects
3
- class Synced::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
- @include = options[:include]
44
- @local_attributes = Array(options[:local_attributes])
45
- @associations = Array(options[:associations])
46
- @remote_objects = Array(remote_objects) if remote_objects
47
- @request_performed = false
48
- end
3
+ module Synced
4
+ class Synchronizer
5
+ attr_reader :id_key
6
+
7
+ # Initializes a new Synchronizer
8
+ #
9
+ # @param remote_objects [Array|NilClass] Array of objects to be synchronized
10
+ # with local database. Objects need to respond to at least :id message.
11
+ # If it's nil, then synchronizer will fetch the remote objects on it's own.
12
+ # @param model_class [Class] ActiveRecord model class from which local objects
13
+ # will be created.
14
+ # @param options [Hash]
15
+ # @option options [Symbol] scope: Within this object scope local objects
16
+ # will be synchronized. By default it's model_class.
17
+ # @option options [Symbol] id_key: attribute name under which
18
+ # remote object's ID is stored, default is :synced_id.
19
+ # @option options [Symbol] synced_all_at_key: attribute name under which
20
+ # remote object's sync time is stored, default is :synced_all_at
21
+ # @option options [Symbol] data_key: attribute name under which remote
22
+ # object's data is stored.
23
+ # @option options [Array] local_attributes: Array of attributes in the remote
24
+ # object which will be mapped to local object attributes.
25
+ # @option options [Boolean] remove: If it's true all local objects within
26
+ # current scope which are not present in the remote array will be destroyed.
27
+ # If only_updated is enabled, ids of objects to be deleted will be taken
28
+ # from the meta part. By default if cancel_at column is present, all
29
+ # missing local objects will be canceled with cancel_all,
30
+ # if it's missing, all will be destroyed with destroy_all.
31
+ # You can also force method to remove local objects by passing it
32
+ # to remove: :mark_as_missing.
33
+ # @option options [Boolean] only_updated: If true requests to API will take
34
+ # advantage of updated_since param and fetch only created/changed/deleted
35
+ # remote objects
36
+ def initialize(remote_objects, model_class, options = {})
37
+ @model_class = model_class
38
+ @scope = options[:scope]
39
+ @id_key = options[:id_key]
40
+ @synced_all_at_key = options[:synced_all_at_key]
41
+ @data_key = options[:data_key]
42
+ @remove = options[:remove]
43
+ @only_updated = options[:only_updated]
44
+ @include = options[:include]
45
+ @local_attributes = Array(options[:local_attributes])
46
+ @associations = Array(options[:associations])
47
+ @remote_objects = Array(remote_objects) if remote_objects
48
+ @request_performed = false
49
+ end
49
50
 
50
- def perform
51
- relation_scope.transaction do
52
- remove_relation.send(remove_strategy) if @remove
53
-
54
- remote_objects.map do |remote|
55
- local_object = local_object_by_remote_id(remote.id) || relation_scope.new
56
- local_object.attributes = default_attributes_mapping(remote)
57
- local_object.attributes = local_attributes_mapping(remote)
58
- local_object.save! if local_object.changed?
59
- local_object.tap do |local_object|
60
- @associations.each do |association|
61
- klass = association.to_s.classify.constantize
62
- klass.synchronize(remote: remote[association], scope: local_object,
63
- remove: @remove)
51
+ def perform
52
+ relation_scope.transaction do
53
+ remove_relation.send(remove_strategy) if @remove
54
+
55
+ remote_objects.map do |remote|
56
+ local_object = local_object_by_remote_id(remote.id) || relation_scope.new
57
+ local_object.attributes = default_attributes_mapping(remote)
58
+ local_object.attributes = local_attributes_mapping(remote)
59
+ local_object.save! if local_object.changed?
60
+ local_object.tap do |local_object|
61
+ @associations.each do |association|
62
+ klass = association.to_s.classify.constantize
63
+ klass.synchronize(remote: remote[association], scope: local_object,
64
+ remove: @remove)
65
+ end
66
+ end
67
+ end.tap do |local_objects|
68
+ if updated_since_enabled? && @request_performed
69
+ relation_scope.update_all(@synced_all_at_key => Time.now)
64
70
  end
65
- end
66
- end.tap do |local_objects|
67
- if updated_since_enabled? && @request_performed
68
- relation_scope.update_all(@synced_all_at_key => Time.now)
69
71
  end
70
72
  end
71
73
  end
72
- end
73
-
74
- private
75
74
 
76
- def local_attributes_mapping(remote)
77
- Hash[@local_attributes.map { |k| [k, remote[k]] }]
78
- end
75
+ private
79
76
 
80
- def default_attributes_mapping(remote)
81
- {}.tap do |attributes|
82
- attributes[@id_key] = remote.id
83
- attributes[@data_key] = remote if @data_key
77
+ def local_attributes_mapping(remote)
78
+ Hash[@local_attributes.map { |k| [k, remote[k]] }]
84
79
  end
85
- end
86
80
 
87
- # Returns relation within which local objects are created/edited and removed
88
- # If no scope is provided, the relation_scope will be class on which
89
- # .synchronize method is called.
90
- # If scope is provided, like: account, then relation_scope will be a relation
91
- # account.rentals (given we run .synchronize on Rental class)
92
- #
93
- # @return [ActiveRecord::Relation|Class]
94
- def relation_scope
95
- @scope ? @scope.send(resource_name) : @model_class
96
- end
81
+ def default_attributes_mapping(remote)
82
+ {}.tap do |attributes|
83
+ attributes[@id_key] = remote.id
84
+ attributes[@data_key] = remote if @data_key
85
+ end
86
+ end
97
87
 
98
- # Returns api client from the closest possible source.
99
- #
100
- # @raise [BookingSync::API::Unauthorized] - On unauthorized user
101
- # @return [BookingSync::API::Client] BookingSync API client
102
- def api
103
- closest = [@scope, @scope.class, @model_class].detect do |o|
104
- o.respond_to?(:api)
105
- end
106
- closest && closest.api || raise(MissingAPIClient.new(@scope, @model_class))
107
- end
88
+ # Returns relation within which local objects are created/edited and removed
89
+ # If no scope is provided, the relation_scope will be class on which
90
+ # .synchronize method is called.
91
+ # If scope is provided, like: account, then relation_scope will be a relation
92
+ # account.rentals (given we run .synchronize on Rental class)
93
+ #
94
+ # @return [ActiveRecord::Relation|Class]
95
+ def relation_scope
96
+ @scope ? @scope.send(resource_name) : @model_class
97
+ end
108
98
 
109
- def local_object_by_remote_id(remote_id)
110
- local_objects.find { |l| l.attributes[id_key.to_s] == remote_id }
111
- end
99
+ # Returns api client from the closest possible source.
100
+ #
101
+ # @raise [BookingSync::API::Unauthorized] - On unauthorized user
102
+ # @return [BookingSync::API::Client] BookingSync API client
103
+ def api
104
+ closest = [@scope, @scope.class, @model_class].detect do |o|
105
+ o.respond_to?(:api)
106
+ end
107
+ closest && closest.api || raise(MissingAPIClient.new(@scope, @model_class))
108
+ end
112
109
 
113
- def local_objects
114
- @local_objects ||= relation_scope.where(id_key => remote_objects_ids).to_a
115
- end
110
+ def local_object_by_remote_id(remote_id)
111
+ local_objects.find { |l| l.attributes[id_key.to_s] == remote_id }
112
+ end
116
113
 
117
- def remote_objects_ids
118
- @remote_objects_ids ||= remote_objects.map(&:id)
119
- end
114
+ def local_objects
115
+ @local_objects ||= relation_scope.where(id_key => remote_objects_ids).to_a
116
+ end
120
117
 
121
- def remote_objects
122
- @remote_objects ||= fetch_remote_objects
123
- end
118
+ def remote_objects_ids
119
+ @remote_objects_ids ||= remote_objects.map(&:id)
120
+ end
124
121
 
125
- def deleted_remote_objects_ids
126
- remote_objects unless @request_performed
127
- api.last_response.meta[:deleted_ids]
128
- end
122
+ def remote_objects
123
+ @remote_objects ||= fetch_remote_objects
124
+ end
129
125
 
130
- def fetch_remote_objects
131
- api.paginate(resource_name, api_request_options).tap do
132
- @request_performed = true
126
+ def deleted_remote_objects_ids
127
+ remote_objects unless @request_performed
128
+ api.last_response.meta[:deleted_ids]
133
129
  end
134
- end
135
130
 
136
- def api_request_options
137
- {}.tap do |options|
138
- options[:include] = @associations if @associations.present?
139
- if @include.present?
140
- options[:include] ||= []
141
- options[:include] += @include
131
+ def fetch_remote_objects
132
+ api.paginate(resource_name, api_request_options).tap do
133
+ @request_performed = true
142
134
  end
143
- options[:updated_since] = minimum_updated_at if updated_since_enabled?
144
- options[:auto_paginate] = true
145
135
  end
146
- end
147
-
148
- def minimum_updated_at
149
- relation_scope.minimum(@synced_all_at_key)
150
- end
151
136
 
152
- def updated_since_enabled?
153
- @only_updated && @synced_all_at_key
154
- end
137
+ def api_request_options
138
+ {}.tap do |options|
139
+ options[:include] = @associations if @associations.present?
140
+ if @include.present?
141
+ options[:include] ||= []
142
+ options[:include] += @include
143
+ end
144
+ options[:updated_since] = minimum_updated_at if updated_since_enabled?
145
+ options[:auto_paginate] = true
146
+ end
147
+ end
155
148
 
156
- def resource_name
157
- @model_class.to_s.tableize
158
- end
149
+ def minimum_updated_at
150
+ relation_scope.minimum(@synced_all_at_key)
151
+ end
159
152
 
160
- def remove_strategy
161
- @remove == true ? default_remove_strategy : @remove
162
- end
153
+ def updated_since_enabled?
154
+ @only_updated && @synced_all_at_key
155
+ end
163
156
 
164
- def default_remove_strategy
165
- if @model_class.column_names.include?("canceled_at")
166
- :cancel_all
167
- else
168
- :destroy_all
157
+ def resource_name
158
+ @model_class.to_s.tableize
169
159
  end
170
- end
171
160
 
172
- def remove_relation
173
- if @only_updated
174
- relation_scope.where(id_key => deleted_remote_objects_ids)
175
- else
176
- relation_scope.where.not(id_key => remote_objects_ids)
161
+ def remove_strategy
162
+ @remove == true ? default_remove_strategy : @remove
177
163
  end
178
- end
179
164
 
180
- class MissingAPIClient < StandardError
181
- def initialize(scope, model_class)
182
- @scope = scope
183
- @model_class = model_class
165
+ def default_remove_strategy
166
+ if @model_class.column_names.include?("canceled_at")
167
+ :cancel_all
168
+ else
169
+ :destroy_all
170
+ end
184
171
  end
185
172
 
186
- def message
187
- if @scope
188
- %Q{Missing BookingSync API client in #{@scope} object or
189
- #{@scope.class} class}
173
+ def remove_relation
174
+ if @only_updated
175
+ relation_scope.where(id_key => deleted_remote_objects_ids)
190
176
  else
191
- %Q{Missing BookingSync API client in #{@model_class} class}
177
+ relation_scope.where.not(id_key => remote_objects_ids)
178
+ end
179
+ end
180
+
181
+ class MissingAPIClient < StandardError
182
+ def initialize(scope, model_class)
183
+ @scope = scope
184
+ @model_class = model_class
185
+ end
186
+
187
+ def message
188
+ if @scope
189
+ %Q{Missing BookingSync API client in #{@scope} object or
190
+ #{@scope.class} class}
191
+ else
192
+ %Q{Missing BookingSync API client in #{@model_class} class}
193
+ end
192
194
  end
193
195
  end
194
196
  end
@@ -1,3 +1,3 @@
1
1
  module Synced
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
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: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastien Grosjean