syncano 4.0.0.alpha1 → 4.0.0.alpha2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -157
  3. data/circle.yml +1 -1
  4. data/lib/syncano.rb +38 -11
  5. data/lib/syncano/api.rb +20 -2
  6. data/lib/syncano/api/endpoints.rb +17 -0
  7. data/lib/syncano/connection.rb +46 -54
  8. data/lib/syncano/poller.rb +55 -0
  9. data/lib/syncano/resources.rb +48 -16
  10. data/lib/syncano/resources/base.rb +46 -56
  11. data/lib/syncano/resources/paths.rb +48 -0
  12. data/lib/syncano/resources/resource_invalid.rb +15 -0
  13. data/lib/syncano/response.rb +55 -0
  14. data/lib/syncano/schema.rb +10 -29
  15. data/lib/syncano/schema/attribute_definition.rb +2 -2
  16. data/lib/syncano/schema/endpoints_whitelist.rb +40 -0
  17. data/lib/syncano/schema/resource_definition.rb +5 -0
  18. data/lib/syncano/upload_io.rb +7 -0
  19. data/lib/syncano/version.rb +1 -1
  20. data/spec/integration/syncano_spec.rb +220 -15
  21. data/spec/spec_helper.rb +3 -1
  22. data/spec/unit/connection_spec.rb +34 -97
  23. data/spec/unit/resources/paths_spec.rb +21 -0
  24. data/spec/unit/resources_base_spec.rb +77 -16
  25. data/spec/unit/response_spec.rb +75 -0
  26. data/spec/unit/schema/resource_definition_spec.rb +10 -0
  27. data/spec/unit/schema_spec.rb +5 -55
  28. data/syncano.gemspec +4 -0
  29. metadata +69 -13
  30. data/lib/active_attr/dirty.rb +0 -26
  31. data/lib/active_attr/typecasting/hash_typecaster.rb +0 -34
  32. data/lib/active_attr/typecasting_override.rb +0 -29
  33. data/lib/syncano/model/associations.rb +0 -121
  34. data/lib/syncano/model/associations/base.rb +0 -38
  35. data/lib/syncano/model/associations/belongs_to.rb +0 -30
  36. data/lib/syncano/model/associations/has_many.rb +0 -75
  37. data/lib/syncano/model/associations/has_one.rb +0 -22
  38. data/lib/syncano/model/base.rb +0 -257
  39. data/lib/syncano/model/callbacks.rb +0 -49
  40. data/lib/syncano/model/scope_builder.rb +0 -158
@@ -1,22 +0,0 @@
1
- require 'syncano/model/associations/base'
2
-
3
- module Syncano
4
- module Model
5
- module Association
6
- # Class for has one association
7
- class HasOne < Syncano::Model::Association::Base
8
- attr_reader :associated_model, :foreign_key, :source_model
9
-
10
- # Checks if association is has_one type
11
- # @return [TrueClass]
12
- def has_one?
13
- true
14
- end
15
-
16
- private
17
-
18
- attr_writer :associated_model, :foreign_key, :source_model
19
- end
20
- end
21
- end
22
- end
@@ -1,257 +0,0 @@
1
- require 'syncano/model/scope_builder'
2
- require 'syncano/model/associations'
3
- require 'syncano/model/callbacks'
4
-
5
- module Syncano
6
- # Scope for modules and classes integrating ActiveRecord functionality
7
- module Model
8
- # Class for integrating ActiveRecord functionality
9
- class Base
10
- include ActiveAttr::Model
11
- include ActiveAttr::Dirty
12
-
13
- include ActiveModel::ForbiddenAttributesProtection
14
- include Syncano::Model::Associations
15
- include Syncano::Model::Callbacks
16
-
17
- attribute :id, type: Integer
18
- attribute :created_at, type: DateTime
19
- attribute :updated_at, type: DateTime
20
-
21
- # Constructor for model
22
- # @param [Hash] params
23
- def initialize(params = {})
24
- if params.is_a?(Syncano::Resources::Object)
25
- self.syncano_object = params
26
- self.attributes = syncano_object_merged_attributes
27
- mark_as_saved!
28
- else
29
- self.syncano_object = self.class.syncano_class.objects.new
30
- self.attributes = params
31
- end
32
- end
33
-
34
- # Gets collection with all objects
35
- # @return [Array]
36
- def self.all
37
- scope_builder.all
38
- end
39
-
40
- # Returns first object or collection of first x objects
41
- # @param [Integer] amount
42
- # @return [Object, Array]
43
- def self.first(amount = nil)
44
- scope_builder.first(amount)
45
- end
46
-
47
- # Returns last object or collection of last x objects
48
- # @param [Integer] amount
49
- # @return [Object, Array]
50
- def self.last(amount = nil)
51
- scope_builder.last(amount)
52
- end
53
-
54
- # Returns scope builder with condition passed as arguments
55
- # @param [String] condition
56
- # @param [Array] params
57
- # @return [Syncano::ActiveRecord::ScopeBuilder]
58
- def self.where(condition, *params)
59
- scope_builder.where(condition, *params)
60
- end
61
-
62
- # Returns scope builder with order passed as first argument
63
- # @param [String] order
64
- # @return [Syncano::ActiveRecord::ScopeBuilder]
65
- def self.order(order)
66
- scope_builder.order(order)
67
- end
68
-
69
- # Returns one object found by id
70
- # @param [Integer] id
71
- # @return [Object]
72
- def self.find(id)
73
- scope_builder.find(id)
74
- end
75
-
76
- def reload!
77
- syncano_object.reload!
78
- self.attributes = syncano_object_merged_attributes
79
- mark_as_saved!
80
- self
81
- end
82
-
83
- # Creates new object with specified attributes
84
- # @param [Hash] attributes
85
- # @return [Object]
86
- def self.create(attributes)
87
- new_object = self.new(attributes)
88
- new_object.save
89
- new_object
90
- end
91
-
92
- # Saves object in Syncano
93
- # @return [TrueClass, FalseClass]
94
- def save
95
- if valid?
96
- was_persisted = persisted?
97
-
98
- process_callbacks(:before_save)
99
- process_callbacks(was_persisted ? :before_update : :before_create)
100
-
101
- syncano_object.custom_attributes = attributes_to_sync
102
- syncano_object.save
103
- self.attributes = syncano_object_merged_attributes
104
- mark_as_saved!
105
-
106
- process_callbacks(was_persisted ? :after_update : :after_create)
107
- process_callbacks(:after_save)
108
- end
109
-
110
- saved?
111
- end
112
-
113
- def self.syncano_class
114
- syncano_class
115
- end
116
-
117
- # Updates object with specified attributes
118
- # @param [Hash] attributes
119
- # @return [TrueClass, FalseClass]
120
- def update_attributes(attributes)
121
- self.attributes = attributes
122
- self.save
123
- end
124
-
125
- # Returns scope builder with limit parameter set to parameter
126
- # @param [Integer] amount
127
- # @return [Syncano::ActiveRecord::ScopeBuilder]
128
- def self.limit(amount)
129
- scope_builder.limit(amount)
130
- end
131
-
132
- # Returns hash with scopes
133
- # @return [HashWithIndifferentAccess]
134
- def self.scopes
135
- self._scopes ||= HashWithIndifferentAccess.new
136
- end
137
-
138
- # Overwritten equality operator
139
- # @param [Object] object
140
- # @return [TrueClass, FalseClass]
141
- def ==(object)
142
- self.class == object.class && id == object.id
143
- end
144
-
145
- # Performs validations
146
- # @return [TrueClass, FalseClass]
147
- def valid?
148
- process_callbacks(:before_validation)
149
- process_callbacks(:after_validation) if result = super
150
- result
151
- end
152
-
153
- # Deletes object from Syncano
154
- # @return [TrueClass, FalseClass]
155
- def destroy
156
- process_callbacks(:before_destroy)
157
- syncano_object.destroy
158
- process_callbacks(:after_destroy) if syncano_object.destroyed?
159
- end
160
-
161
- def destroyed?
162
- syncano_object.destroyed?
163
- end
164
-
165
- # Checks if object has not been saved in Syncano yet
166
- # @return [TrueClass, FalseClass]
167
- def new_record?
168
- !persisted?
169
- end
170
-
171
- # Checks if object has been already saved in Syncano
172
- # @return [TrueClass, FalseClass]
173
- def persisted?
174
- !syncano_object.new_record?
175
- end
176
-
177
- def saved?
178
- !new_record? && !changed?
179
- end
180
-
181
- private
182
-
183
- class_attribute :syncano_class, :_scopes
184
- attr_accessor :syncano_object
185
-
186
- def mark_as_saved!
187
- raise(Syncano::Error.new('primary key is blank')) if new_record?
188
-
189
- @previously_changed = changes
190
- @changed_attributes.clear
191
- self
192
- end
193
-
194
- # Setter for scopes attribute
195
- def self.scopes=(hash)
196
- self._scopes = hash
197
- end
198
-
199
- # Returns scope builder for current model
200
- # @return [Syncano::ActiveRecord::ScopeBuilder]
201
- def self.scope_builder
202
- Syncano::Model::ScopeBuilder.new(self)
203
- end
204
-
205
- # Defines model scope
206
- # @param [Symbol] name
207
- # @param [Proc] procedure
208
- def self.scope(name, procedure)
209
- scopes[name] = procedure
210
- end
211
-
212
- # Overwritten method_missing for handling calling defined scopes
213
- # @param [String] name
214
- # @param [Array] args
215
- def self.method_missing(name, *args)
216
- if scopes[name].nil?
217
- super
218
- else
219
- scope_builder.send(name.to_sym, *args)
220
- end
221
- end
222
-
223
- # Returns scope builder for specified class
224
- # @return [Syncano::ActiveRecord::ScopeBuilder]
225
- def scope_builder(object_class)
226
- Syncano::Model::ScopeBuilder.new(object_class)
227
- end
228
-
229
- def attributes_to_sync
230
- attributes_names = self.class.attributes_to_sync
231
- attributes.select{ |name, value| attributes_names.include?(name.to_sym) }
232
- end
233
-
234
- def self.attributes_to_sync
235
- syncano_class.schema.collect{ |attribute| attribute[:name].to_sym }
236
- end
237
-
238
- def syncano_object_merged_attributes
239
- syncano_object.attributes.except(:custom_attributes).merge(syncano_object.custom_attributes)
240
- end
241
-
242
- def self.inherited(child_class)
243
- # Load schema and generate attributes
244
- child_class_name = child_class.name.demodulize.tableize.singularize
245
- syncano_class = MODEL_SCHEMA.find{ |syncano_class| syncano_class.name == child_class_name }
246
-
247
- syncano_class.schema.each do |attribute_schema|
248
- attribute attribute_schema['name'], type: String
249
- end
250
-
251
- child_class.syncano_class = syncano_class
252
-
253
- super
254
- end
255
- end
256
- end
257
- end
@@ -1,49 +0,0 @@
1
- module Syncano
2
- module Model
3
- # Module with callbacks functionality for Syncano::ActiveRecord
4
- module Callbacks
5
- extend ActiveSupport::Concern
6
-
7
- # Class methods for Syncano::ActiveRecord::Callbacks module
8
- module ClassMethods
9
- private
10
-
11
- [:validation, :save, :create, :update, :destroy].each do |action|
12
- [:before, :after].each do |type|
13
- define_method("prepend_#{type}_#{action}") do |argument|
14
- send("#{type}_#{action}_callbacks").unshift(argument)
15
- end
16
-
17
- define_method("#{type}_#{action}") do |argument|
18
- send("#{type}_#{action}_callbacks") << argument
19
- end
20
- end
21
- end
22
-
23
- def inherited(subclass)
24
- # Initializes chains for all types of callbacks
25
- [:validation, :save, :create, :update, :destroy].each do |action|
26
- [:before, :after].each do |type|
27
- chain_name = "#{type}_#{action}_callbacks"
28
-
29
- subclass.class_attribute chain_name
30
- subclass.send("#{chain_name}=", [])
31
- end
32
- end
33
-
34
- super
35
- end
36
- end
37
-
38
- # Processes callbacks with specified type
39
- # @param [Symbol, String] type
40
- def process_callbacks(type)
41
- if respond_to?("#{type}_callbacks")
42
- send("#{type}_callbacks").each do |callback_name|
43
- send(callback_name)
44
- end
45
- end
46
- end
47
- end
48
- end
49
- end
@@ -1,158 +0,0 @@
1
- module Syncano
2
- module Model
3
- # ScopeBuilder class allows for creating and chaining more complex queries
4
- class ScopeBuilder
5
- # Constructor for ScopeBuilder
6
- # @param [Class] model
7
- def initialize(model)
8
- raise 'Model should be a class extending module Syncano::Model::Base' unless model <= Syncano::Model::Base
9
-
10
- self.model = model
11
- self.query = HashWithIndifferentAccess.new
12
- end
13
-
14
- # Returns collection of objects
15
- # @return [Array]
16
- def all
17
- model.syncano_class.objects.all(parameters).collect do |data_object|
18
- model.new(data_object)
19
- end
20
- end
21
-
22
- # Returns one object found by id
23
- # @param [Integer] id
24
- # @return [Object]
25
- def find(id)
26
- data_object = model.syncano_class.objects.find(id)
27
- data_object.present? ? model.new(data_object) : nil
28
- end
29
-
30
- # Returns first object or collection of first x objects
31
- # @param [Integer] amount
32
- # @return [Object, Array]
33
- def first(amount = nil)
34
- objects = all.first(amount || 1)
35
- amount.nil? ? objects.first : objects
36
- end
37
-
38
- # Returns last object or last x objects
39
- # @param [Integer] amount
40
- # @return [Object, Array]
41
- def last(amount)
42
- objects = all.last(amount || 1)
43
- amount.nil? ? objects.first : objects
44
- end
45
-
46
- # Adds to the current scope builder condition to the scope builder
47
- # @param [String] condition
48
- # @param [Array] params
49
- # @return [Syncano::ActiveRecord::ScopeBuilder]
50
- def where(conditions, *params)
51
- raise 'Invalid params count in where clause!' unless conditions.count('?') == params.count
52
-
53
- params = params.dup
54
-
55
- conditions.gsub(/\s+/, ' ').split(/and/i).each do |condition|
56
- if condition.ends_with?('?')
57
- value = params.shift
58
- condition.gsub!('?', '').strip!
59
- else
60
- value = true
61
- end
62
-
63
- attribute, operator = condition.split(' ', 2)
64
- operator.upcase!
65
-
66
- raise 'Invalid attribute in where clause!' unless model.attributes.keys.include?(attribute)
67
- raise 'Invalid operator in where clause!' unless self.class.where_mapping.keys.include?(operator)
68
-
69
- operator = self.class.where_mapping[operator]
70
-
71
- query[attribute] = HashWithIndifferentAccess.new if query[attribute].nil?
72
- query[attribute][operator] = value
73
- end
74
-
75
- self
76
- end
77
-
78
- # Adds to the current scope builder order clause
79
- # @param [String] order
80
- # @return [Syncano::ActiveRecord::ScopeBuilder]
81
- def order(order)
82
- if order.is_a?(Hash)
83
- attribute = order.keys.first
84
- order_type = order[attribute]
85
- else
86
- attribute, order_type = order.gsub(/\s+/, ' ').split(' ')
87
- end
88
-
89
- raise 'Invalid attribute in order clause' unless (model.attributes.keys).include?(attribute)
90
-
91
- self.order_clause = order_type.to_s.downcase == 'desc' ? "-#{attribute}" : attribute
92
-
93
- self
94
- end
95
-
96
- # # Adds to the current scope builder limit clause
97
- # # @param [Integer] amount
98
- # # @return [Syncano::ActiveRecord::ScopeBuilder]
99
- # def limit(amount)
100
- # self.parameters[:limit] = amount
101
- # self
102
- # end
103
- #
104
- private
105
-
106
- attr_accessor :order_clause, :query, :model, :scopes
107
-
108
- # Returns Syncano::Resource class for current model
109
- # @return [Syncano::Resources::Folder]
110
- def syncano_class
111
- model.syncano_class
112
- end
113
-
114
- # Returns scopes for current model
115
- # @return [HashWithIndifferentAccess]
116
- def scopes
117
- model.scopes
118
- end
119
-
120
- def parameters
121
- params = {}
122
-
123
- params[:order_by] = order_clause if order_clause.present?
124
- params[:query] = query.to_json if query.present?
125
-
126
- params
127
- end
128
-
129
- # Returns mapping for operators
130
- # @return [Hash]
131
- def self.where_mapping
132
- { '=' => '_eq', '!=' => '_neq', '<>' => '_neq', '>=' => '_gte', '>' => '_gt',
133
- '<=' => '_lte', '<' => '_lt', 'IS NOT NULL' => '_exists', 'IN' => '_in' }
134
- end
135
-
136
- # Applies scope to the current scope builder
137
- # @param [Symbol] name
138
- # @param [Array] args
139
- # @return [Syncano::ActiveRecord::ScopeBuilder]
140
- def execute_scope(name, *args)
141
- procedure = scopes[name]
142
- instance_exec(*args, &procedure)
143
- self
144
- end
145
-
146
- # Overwritten method_missing for handling calling defined scopes
147
- # @param [String] name
148
- # @param [Array] args
149
- def method_missing(name, *args)
150
- if scopes[name].nil?
151
- super
152
- else
153
- execute_scope(name, *args)
154
- end
155
- end
156
- end
157
- end
158
- end