syncano 3.1.1.beta

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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +25 -0
  3. data/Gemfile +4 -0
  4. data/Guardfile +5 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +304 -0
  7. data/Rakefile +7 -0
  8. data/lib/generators/syncano/install_generator.rb +17 -0
  9. data/lib/generators/syncano/templates/initializers/syncano.rb +4 -0
  10. data/lib/syncano.rb +92 -0
  11. data/lib/syncano/batch_queue.rb +58 -0
  12. data/lib/syncano/batch_queue_element.rb +33 -0
  13. data/lib/syncano/clients/base.rb +115 -0
  14. data/lib/syncano/clients/rest.rb +69 -0
  15. data/lib/syncano/clients/sync.rb +132 -0
  16. data/lib/syncano/errors.rb +13 -0
  17. data/lib/syncano/jimson_client.rb +34 -0
  18. data/lib/syncano/packets/auth.rb +7 -0
  19. data/lib/syncano/packets/base.rb +64 -0
  20. data/lib/syncano/packets/call.rb +34 -0
  21. data/lib/syncano/packets/call_response.rb +33 -0
  22. data/lib/syncano/packets/error.rb +19 -0
  23. data/lib/syncano/packets/message.rb +30 -0
  24. data/lib/syncano/packets/notification.rb +39 -0
  25. data/lib/syncano/packets/ping.rb +12 -0
  26. data/lib/syncano/query_builder.rb +144 -0
  27. data/lib/syncano/resources/admin.rb +26 -0
  28. data/lib/syncano/resources/api_key.rb +46 -0
  29. data/lib/syncano/resources/base.rb +375 -0
  30. data/lib/syncano/resources/collection.rb +186 -0
  31. data/lib/syncano/resources/data_object.rb +304 -0
  32. data/lib/syncano/resources/folder.rb +34 -0
  33. data/lib/syncano/resources/notifications/base.rb +103 -0
  34. data/lib/syncano/resources/notifications/create.rb +20 -0
  35. data/lib/syncano/resources/notifications/destroy.rb +20 -0
  36. data/lib/syncano/resources/notifications/message.rb +9 -0
  37. data/lib/syncano/resources/notifications/update.rb +24 -0
  38. data/lib/syncano/resources/project.rb +42 -0
  39. data/lib/syncano/resources/role.rb +11 -0
  40. data/lib/syncano/resources/subscription.rb +12 -0
  41. data/lib/syncano/resources/user.rb +47 -0
  42. data/lib/syncano/response.rb +22 -0
  43. data/lib/syncano/sync_connection.rb +110 -0
  44. data/lib/syncano/version.rb +4 -0
  45. data/spec/admins_spec.rb +16 -0
  46. data/spec/api_keys_spec.rb +34 -0
  47. data/spec/collections_spec.rb +67 -0
  48. data/spec/data_objects_spec.rb +113 -0
  49. data/spec/folders_spec.rb +39 -0
  50. data/spec/notifications_spec.rb +43 -0
  51. data/spec/projects_spec.rb +35 -0
  52. data/spec/roles_spec.rb +13 -0
  53. data/spec/spec_helper.rb +13 -0
  54. data/spec/sync_resources_spec.rb +35 -0
  55. data/spec/syncano_spec.rb +9 -0
  56. data/syncano.gemspec +32 -0
  57. metadata +250 -0
@@ -0,0 +1,20 @@
1
+ class Syncano
2
+ module Resources
3
+ module Notifications
4
+ # Notification resource about destroying data object - represents notification with type "delete"
5
+ class Destroy < Syncano::Resources::Notifications::Base
6
+
7
+ # Constructor for Syncano::Notifications::Create object
8
+ # @param [Syncano::Clients::Base] client
9
+ # @param [Hash] attributes
10
+ def initialize(client, attributes)
11
+ super(client, attributes)
12
+
13
+ if attributes.is_a?(::Syncano::Packets::Base)
14
+ self[:target] = attributes.target
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,9 @@
1
+ class Syncano
2
+ module Resources
3
+ module Notifications
4
+ # Notification resource representing general notifications
5
+ class Message < Syncano::Resources::Notifications::Base
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ class Syncano
2
+ module Resources
3
+ module Notifications
4
+ # Notification resource about updating data object - represents notification with type "change"
5
+ class Update < Syncano::Resources::Notifications::Base
6
+ # Constructor for Syncano::Notifications::Update object
7
+ # @param [Syncano::Clients::Base] client
8
+ # @param [Hash] attributes
9
+ def initialize(client, attributes)
10
+ super(client, attributes)
11
+
12
+ if attributes.is_a?(::Syncano::Packets::Base)
13
+ self.attributes = {
14
+ added: attributes.data[:added],
15
+ updated: attributes.data[:updated],
16
+ deleted: attributes.data[:deleted],
17
+ target: attributes.target
18
+ }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,42 @@
1
+ class Syncano
2
+ module Resources
3
+ # Project resource
4
+ class Project < ::Syncano::Resources::Base
5
+ # Association has_many :collections
6
+ # @return [Syncano::QueryBuilder] query builder for resource Syncano::Resources::Collection
7
+ def collections
8
+ ::Syncano::QueryBuilder.new(client, ::Syncano::Resources::Collection, project_id: id)
9
+ end
10
+
11
+ # Wrapper for api "subscription.subscribe_project" method
12
+ # @return [Syncano::Resource::Project]
13
+ def subscribe
14
+ perform_subscribe
15
+ reload!
16
+ end
17
+
18
+ # Wrapper for api "subscription.unsubscribe_project" method
19
+ # @return [Syncano::Resource::Project]
20
+ def unsubscribe
21
+ perform_unsubscribe
22
+ reload!
23
+ end
24
+
25
+ private
26
+
27
+ # Executes proper subscribe request
28
+ # @return [Syncano::Response]
29
+ def perform_subscribe
30
+ check_if_sync_client!
31
+ client.make_request(:subscription, :subscribe_project, { project_id: id })
32
+ end
33
+
34
+ # Executes proper unsubscribe request
35
+ # @return [Syncano::Response]
36
+ def perform_unsubscribe
37
+ check_if_sync_client!
38
+ client.make_request(:subscription, :unsubscribe_project, { project_id: id })
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,11 @@
1
+ class Syncano
2
+ module Resources
3
+ # Role resource
4
+ class Role < ::Syncano::Resources::Base
5
+ private
6
+
7
+ self.crud_class_methods = [:all]
8
+ self.crud_instance_methods = []
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ class Syncano
2
+ module Resources
3
+ # Subscription resource
4
+ class Subscription < ::Syncano::Resources::Base
5
+
6
+ private
7
+
8
+ self.crud_class_methods = [:all]
9
+ self.crud_instance_methods = []
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,47 @@
1
+ class Syncano
2
+ module Resources
3
+ # User resource
4
+ class User < ::Syncano::Resources::Base
5
+ # Wrapper for api "count" method
6
+ # @param [Syncano::Clients::Base] client
7
+ # @param [Hash] scope_parameters
8
+ # @param [Hash] conditions
9
+ # @return [Integer]
10
+ def self.count(client, scope_parameters = {}, conditions = {})
11
+ response = perform_count(client, scope_parameters, conditions)
12
+ response.data if response.status
13
+ end
14
+
15
+ private
16
+
17
+ self.scope_parameters = [:project_id, :collection_id]
18
+
19
+ # Prepares hash with attributes used in synchronization with Syncano
20
+ # @return [Hash]
21
+ def self.attributes_to_sync(attributes)
22
+ attributes = attributes.dup
23
+
24
+ if attributes.keys.map(&:to_sym).include?(:avatar)
25
+ if attributes[:avatar].blank?
26
+ attributes[:avatar] = ''
27
+ elsif attributes[:avatar].is_a?(String)
28
+ attributes[:avatar] = Base64.encode64(File.read(attributes[:avatar]))
29
+ else
30
+ attributes.delete(:image)
31
+ end
32
+ end
33
+
34
+ attributes
35
+ end
36
+
37
+ # Executes proper count request
38
+ # @param [Syncano::Clients::Base] client
39
+ # @param [Hash] scope_parameters
40
+ # @param [Hash] conditions
41
+ # @return [Syncano::Response]
42
+ def self.perform_count(client, scope_parameters, conditions)
43
+ make_request(client, nil, :count, conditions.merge(scope_parameters))
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,22 @@
1
+ class Syncano
2
+ # Represents response from Syncano API
3
+ class Response
4
+ attr_reader :status, :data, :errors
5
+
6
+ # Constructor for Syncano::Response
7
+ # @param [Boolean] status
8
+ # @param [Hash] data
9
+ # @param [Array] errors
10
+ def initialize(status = false, data = nil, errors = [])
11
+ super()
12
+
13
+ self.status = status
14
+ self.data = data
15
+ self.errors = errors
16
+ end
17
+
18
+ private
19
+
20
+ attr_writer :status, :data, :errors
21
+ end
22
+ end
@@ -0,0 +1,110 @@
1
+ class Syncano
2
+ # Represents connection with Sync Server
3
+ class SyncConnection < EventMachine::Connection
4
+ attr_reader :client
5
+
6
+ # Constructor for Syncano::SyncConnection object
7
+ def initialize
8
+ super
9
+
10
+ self.callbacks = ::ActiveSupport::HashWithIndifferentAccess.new
11
+ self.callbacks_queue = []
12
+
13
+ self.responses = ::ActiveSupport::HashWithIndifferentAccess.new
14
+ self.responses_queue = []
15
+
16
+ self.client = ::Syncano::Clients::Sync.instance
17
+ end
18
+
19
+ # Eventmachine callback invoked after completing connection
20
+ def connection_completed
21
+ start_tls
22
+ end
23
+
24
+ # Eventmachine callback invoked completing ssl handshake
25
+ def ssl_handshake_completed
26
+ auth_data = {
27
+ api_key: client.api_key,
28
+ instance: client.instance_name
29
+ }
30
+
31
+ client.connection = self
32
+
33
+ send_data "#{auth_data.to_json}\n"
34
+ end
35
+
36
+ # Eventmachine callback invoked after receiving data from socket
37
+ # Data are parsed here and processed by callbacks chain
38
+ def receive_data(data)
39
+ data = ::ActiveSupport::HashWithIndifferentAccess.new(JSON.parse(data))
40
+ packet = ::Syncano::Packets::Base.instantize_packet(data)
41
+
42
+ if packet.notification?
43
+ notification = ::Syncano::Resources::Notifications::Base.instantize_notification(client, packet)
44
+
45
+ callbacks_queue.each do |callback_name|
46
+ callbacks[callback_name].call(notification)
47
+ end
48
+ elsif packet.call_response?
49
+ queue_response(packet)
50
+ end
51
+ end
52
+
53
+ # Appends callback method to the end of callbacks chain
54
+ # @param [Symbol, String] callback_name
55
+ # @param [Block] callback
56
+ def append_callback(callback_name, callback)
57
+ callbacks[callback_name] = callback
58
+ callbacks_queue << callback_name
59
+ end
60
+
61
+ # Prepends callback method to the beginning of callbacks chain
62
+ # @param [Symbol, String] callback_name
63
+ # @param [Block] callback
64
+ def prepend_callback(callback_name, callback)
65
+ callbacks[callback_name] = callback
66
+ callbacks_queue.unshift(callback_name)
67
+ end
68
+
69
+ # Removes callback from callbacks chain
70
+ # @param [Symbol, String] callback_name
71
+ def remove_callback(callback_name)
72
+ callbacks.delete(callback_name)
73
+ callbacks_queue.delete(callback_name)
74
+ end
75
+
76
+ # Gets call response packet from the responses queue
77
+ # @param [Integer, String] message_id
78
+ # @return [Syncano::Packets::CallResponse]
79
+ def get_response(message_id)
80
+ responses.delete(message_id)
81
+ end
82
+
83
+ private
84
+
85
+ attr_accessor :client, :callbacks, :callbacks_queue, :responses, :responses_queue
86
+
87
+ # Adds call response packet to the responses queue
88
+ # @param [Syncano::Packets::CallResponse] packet
89
+ def queue_response(packet)
90
+ prune_responses_queue
91
+ message_id = packet.message_id.to_i
92
+ responses[message_id] = packet
93
+ responses_queue << message_id
94
+ end
95
+
96
+ # Removes old call response packets from the responses queue
97
+ def prune_responses_queue
98
+ while !responses_queue.empty?
99
+ message_id = responses_queue.first
100
+
101
+ if responses_queue[message_id].nil? || Time.now - responses[message_id].timestamp.to_time > 2.minutes
102
+ responses_queue.shift
103
+ responses.delete(message_id)
104
+ else
105
+ break
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,4 @@
1
+ class Syncano
2
+ # Syncano version number
3
+ VERSION = '3.1.1.beta'
4
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Syncano::Resource::Admin' do
4
+ it 'should get admins' do
5
+ @client.admins.all.each do |admin|
6
+ admin.id.should_not be_nil
7
+ admin[:email].should_not be_nil
8
+ end
9
+ end
10
+
11
+ it 'should get one admin' do
12
+ admins = @client.admins.all
13
+
14
+ @client.admins.find(admins.last.id)[:name].should == admins.last[:name]
15
+ end
16
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Syncano::Resource::ApiKey' do
4
+ it 'should create new api key in Syncano' do
5
+ count_before = @client.api_keys.count
6
+ api_key = @client.api_keys.create(role_id: '2', description: 'Just testing')
7
+ count_after = @client.api_keys.count
8
+
9
+ (count_after - count_before).should == 1
10
+ @client.api_keys.last[:description].should == 'Just testing'
11
+ end
12
+
13
+ it 'should get api keys' do
14
+ @client.api_keys.all.each do |api_key|
15
+ api_key.id.should_not be_nil
16
+ api_key[:description].should_not be_nil
17
+ end
18
+ end
19
+
20
+ it 'should get one project' do
21
+ api_keys = @client.api_keys.all
22
+
23
+ api_key = @client.api_keys.find(api_keys.last.id)
24
+ api_key[:description].should == api_keys.last[:description]
25
+ end
26
+
27
+ it 'should destroy project' do
28
+ count_before = @client.api_keys.count
29
+ @client.api_keys.last.destroy
30
+ count_after = @client.api_keys.count
31
+
32
+ (count_before - count_after).should == 1
33
+ end
34
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Syncano::Resource::Collection' do
4
+ before(:all) do
5
+ @project = @client.projects.last || @client.projects.create(name: 'Test project')
6
+ end
7
+
8
+ it 'should create a new collection in Syncano' do
9
+ count_before = @project.collections.count
10
+
11
+ collection = @project.collections.create(name: 'Test collection', description: 'Just testing')
12
+ collection.id.should_not be_nil
13
+
14
+ count_after = @project.collections.count
15
+
16
+ (count_after - count_before).should == 1
17
+ @project.collections.last[:name].should == 'Test collection'
18
+ end
19
+
20
+ it 'should get all collections' do
21
+ @project.collections.all.each do |collection|
22
+ collection.id.should_not be_nil
23
+ collection[:name].should_not be_nil
24
+ end
25
+ end
26
+
27
+ it 'should get a one collection' do
28
+ collection = @project.collections.last
29
+ @project.collections.find(collection.id)[:name].should == collection[:name]
30
+ end
31
+
32
+ it 'should activate inactive collection' do
33
+ collection = @project.collections.last
34
+
35
+ if collection[:status] == 'active'
36
+ collection.deactivate
37
+ collection.reload!
38
+ end
39
+ collection[:status].should == 'inactive'
40
+
41
+ collection.activate
42
+ collection.reload!
43
+ collection['status'].should == 'active'
44
+ end
45
+
46
+ it 'should deactivate active collection' do
47
+ collection = @project.collections.last
48
+
49
+ if collection[:status] == 'inactive'
50
+ collection.activate
51
+ collection.reload!
52
+ end
53
+ collection[:status].should == 'active'
54
+
55
+ collection.deactivate
56
+ collection.reload!
57
+ collection[:status].should == 'inactive'
58
+ end
59
+
60
+ it 'should destroy a collection' do
61
+ count_before = @project.collections.count
62
+ @project.collections.last.destroy
63
+ count_after = @project.collections.count
64
+
65
+ (count_before - count_after).should == 1
66
+ end
67
+ end
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Syncano::Resource::DataObject' do
4
+ before(:all) do
5
+ @project = @client.projects.last || @client.projects.create(name: 'Test project')
6
+ @collection = @project.collections.last || @project.collections.create(name: 'Test collection')
7
+ end
8
+
9
+ after(:all) do
10
+ @client.batch do |q|
11
+ @collection.data_objects.all.each do |data_object|
12
+ q << data_object.batch.destroy
13
+ end
14
+ end
15
+ end
16
+
17
+ it 'should create new data object in Syncano' do
18
+ count_before = @collection.data_objects.count
19
+ @collection.data_objects.create(title: 'Test data object', test_attribute: 'test_value')
20
+ count_after = @collection.data_objects.count
21
+
22
+ (count_after - count_before).should == 1
23
+ data_object = @collection.data_objects.last
24
+ data_object[:title].should == 'Test data object'
25
+ data_object[:test_attribute].should == 'test_value'
26
+ end
27
+
28
+ it 'should copy data object in Syncano' do
29
+ count_before = @collection.data_objects.count
30
+ data_object = @collection.data_objects.last
31
+ data_object.copy
32
+ data_object_copy = @collection.data_objects.last
33
+ count_after = @collection.data_objects.count
34
+
35
+ (count_after - count_before).should == 1
36
+ data_object_copy[:title].should == data_object[:title]
37
+ data_object_copy.id.should_not == data_object.id
38
+ end
39
+
40
+ it 'should get data objects' do
41
+ @collection.data_objects.all.each do |data_object|
42
+ data_object.id.should_not be_nil
43
+ data_object[:state].should_not be_nil
44
+ end
45
+ end
46
+
47
+ it 'should check amount of objects' do
48
+ @collection.data_objects.count.should == @collection.data_objects.all.count
49
+ end
50
+
51
+ it 'should get one data object' do
52
+ data_objects = @collection.data_objects.all
53
+
54
+ data_object = @collection.data_objects.find(data_objects.last.id)
55
+ data_object[:title].should == data_objects.last[:title]
56
+ end
57
+
58
+ context 'managing parents and children' do
59
+ before(:all) do
60
+ @parent = @collection.data_objects.first(include_children: true)
61
+ @parent = @collection.data_objects.create(title: "Parent object")
62
+ @child = @collection.data_objects.last
63
+ @child = @collection.data_objects.create(title: 'Child object') if @child.id == @parent.id
64
+ @parent.id.should_not == @child.id
65
+ end
66
+
67
+ it 'should add parent to the data object' do
68
+ @parent.remove_child(@child.id) if @parent[:children].present? && @parent[:children].map(&:id).include?(@child.id)
69
+
70
+ @child.add_parent(@parent.id)
71
+ @parent.reload!(include_children: true)
72
+
73
+ @parent[:children].should_not be_nil
74
+ @parent[:children].select{ |c| c.id == @child.id }.first.should_not be_nil
75
+ end
76
+
77
+ it 'should remove parent from the data object' do
78
+ @parent.add_child(@child.id) unless @parent[:children].present? && @parent[:children].map(&:id).include?(@child.id)
79
+
80
+ @child.remove_parent(@parent.id)
81
+ @parent.reload!(include_children: true)
82
+
83
+ @parent[:children].should be_nil
84
+ end
85
+
86
+ it 'should add child to the data object' do
87
+ @parent.remove_child(@child.id) if @parent[:children].present? && @parent[:children].map(&:id).include?(@child.id)
88
+
89
+ @parent.add_child(@child.id)
90
+ @parent.reload!(include_children: true)
91
+
92
+ @parent[:children].should_not be_nil
93
+ @parent[:children].select{ |c| c.id == @child.id }.first.should_not be_nil
94
+ end
95
+
96
+ it 'should remove child from the data object' do
97
+ @parent.add_child(@child.id) unless @parent[:children].present? && @parent[:children].map(&:id).include?(@child.id)
98
+
99
+ @parent.remove_child(@child.id)
100
+ @parent.reload!(include_children: true)
101
+
102
+ @parent[:children].should be_nil
103
+ end
104
+ end
105
+
106
+ it 'should destroy data object' do
107
+ count_before = @collection.data_objects.count
108
+ @collection.data_objects.last.destroy
109
+ count_after = @collection.data_objects.count
110
+
111
+ (count_before - count_after).should == 1
112
+ end
113
+ end