syncano 4.0.0.alpha1 → 4.0.0.alpha2
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 +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
|