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.
- checksums.yaml +4 -4
- data/README.md +8 -157
- data/circle.yml +1 -1
- data/lib/syncano.rb +38 -11
- data/lib/syncano/api.rb +20 -2
- data/lib/syncano/api/endpoints.rb +17 -0
- data/lib/syncano/connection.rb +46 -54
- data/lib/syncano/poller.rb +55 -0
- data/lib/syncano/resources.rb +48 -16
- data/lib/syncano/resources/base.rb +46 -56
- data/lib/syncano/resources/paths.rb +48 -0
- data/lib/syncano/resources/resource_invalid.rb +15 -0
- data/lib/syncano/response.rb +55 -0
- data/lib/syncano/schema.rb +10 -29
- data/lib/syncano/schema/attribute_definition.rb +2 -2
- data/lib/syncano/schema/endpoints_whitelist.rb +40 -0
- data/lib/syncano/schema/resource_definition.rb +5 -0
- data/lib/syncano/upload_io.rb +7 -0
- data/lib/syncano/version.rb +1 -1
- data/spec/integration/syncano_spec.rb +220 -15
- data/spec/spec_helper.rb +3 -1
- data/spec/unit/connection_spec.rb +34 -97
- data/spec/unit/resources/paths_spec.rb +21 -0
- data/spec/unit/resources_base_spec.rb +77 -16
- data/spec/unit/response_spec.rb +75 -0
- data/spec/unit/schema/resource_definition_spec.rb +10 -0
- data/spec/unit/schema_spec.rb +5 -55
- data/syncano.gemspec +4 -0
- metadata +69 -13
- data/lib/active_attr/dirty.rb +0 -26
- data/lib/active_attr/typecasting/hash_typecaster.rb +0 -34
- data/lib/active_attr/typecasting_override.rb +0 -29
- data/lib/syncano/model/associations.rb +0 -121
- data/lib/syncano/model/associations/base.rb +0 -38
- data/lib/syncano/model/associations/belongs_to.rb +0 -30
- data/lib/syncano/model/associations/has_many.rb +0 -75
- data/lib/syncano/model/associations/has_one.rb +0 -22
- data/lib/syncano/model/base.rb +0 -257
- data/lib/syncano/model/callbacks.rb +0 -49
- 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
|
data/lib/syncano/model/base.rb
DELETED
@@ -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
|