meteor-motion 0.1.0
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 +7 -0
- data/.gitignore +21 -0
- data/.repl_history +0 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +166 -0
- data/Rakefile +17 -0
- data/app/app_delegate.rb +11 -0
- data/app/controllers/book_controller.rb +92 -0
- data/app/controllers/book_list_controller.rb +105 -0
- data/app/controllers/connection_controller.rb +83 -0
- data/app/controllers/login_controller.rb +35 -0
- data/lib/meteor-motion.rb +12 -0
- data/lib/meteor-motion/version.rb +3 -0
- data/meteor-motion.gemspec +27 -0
- data/motion/adapters/motion_model.rb +61 -0
- data/motion/client.rb +179 -0
- data/motion/collection.rb +50 -0
- data/motion/collections/default.rb +56 -0
- data/motion/collections/motion_model.rb +52 -0
- data/motion/ddp.rb +161 -0
- data/motion/srp/securerandom.rb +248 -0
- data/motion/srp/srp.rb +250 -0
- data/spec/adapters/motion_model_spec.rb +38 -0
- data/spec/client_spec.rb +104 -0
- data/spec/collection_spec.rb +63 -0
- data/spec/collections/default_spec.rb +46 -0
- data/spec/collections/motion_model_spec.rb +69 -0
- data/spec/ddp_spec.rb +123 -0
- data/spec/server/.meteor/.gitignore +1 -0
- data/spec/server/.meteor/packages +9 -0
- data/spec/server/.meteor/release +1 -0
- data/spec/server/collections/books.js +11 -0
- data/spec/server/server/fixtures.js +28 -0
- data/spec/server/server/publications.js +3 -0
- data/spec/server/smart.json +3 -0
- data/vendor/SocketRocket/NSData+SRB64Additions.h +24 -0
- data/vendor/SocketRocket/NSData+SRB64Additions.m +39 -0
- data/vendor/SocketRocket/SRWebSocket.h +114 -0
- data/vendor/SocketRocket/SRWebSocket.m +1757 -0
- data/vendor/SocketRocket/SocketRocket-Prefix.pch +27 -0
- data/vendor/SocketRocket/SocketRocket.bridgesupport +160 -0
- data/vendor/SocketRocket/base64.c +314 -0
- data/vendor/SocketRocket/base64.h +34 -0
- metadata +190 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
describe MeteorMotion::Collections::Default do
|
2
|
+
|
3
|
+
describe 'CRUD tasks' do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@coll = MeteorMotion::Collections::Default.new('temp')
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'adds an object to the collection and finds it' do
|
10
|
+
@coll.add('abc', {a: 1, b:2})
|
11
|
+
@coll.find('abc').should.not.be.equal nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'adds the correct fields to the object' do
|
15
|
+
@coll.add('abc', {a: 1, b:2})
|
16
|
+
|
17
|
+
@coll.find('abc')[:a].should.be.equal 1
|
18
|
+
@coll.find('abc')[:b].should.be.equal 2
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'updates an objects fields' do
|
22
|
+
@coll.add('abc', {a: 1, b:2})
|
23
|
+
@coll.find('abc')[:a].should.be.equal 1
|
24
|
+
|
25
|
+
@coll.update('abc', {a: 2}, nil)
|
26
|
+
@coll.find('abc')[:a].should.be.equal 2
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'clears an objects fields' do
|
30
|
+
@coll.add('abc', {a: 1, b:2})
|
31
|
+
@coll.find('abc')[:a].should.be.equal 1
|
32
|
+
|
33
|
+
@coll.update('abc', nil, [:a])
|
34
|
+
@coll.find('abc')[:a].should.be.equal nil
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'removes an object from the collection' do
|
38
|
+
@coll.add('abc', {a: 1, b:2})
|
39
|
+
|
40
|
+
@coll.remove('abc')
|
41
|
+
@coll.find('abc').should.be.equal nil
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class DummyModel
|
2
|
+
include MotionModel::Model
|
3
|
+
include MotionModel::ArrayModelAdapter
|
4
|
+
include MeteorMotion::Adapters::MotionModel
|
5
|
+
|
6
|
+
|
7
|
+
# Make sure to define :id as String so MotionModel does not create a default int
|
8
|
+
#
|
9
|
+
columns :id => :string,
|
10
|
+
:a => :int,
|
11
|
+
:b => :int
|
12
|
+
end
|
13
|
+
|
14
|
+
describe MeteorMotion::Collections::MotionModel do
|
15
|
+
describe 'Creation' do
|
16
|
+
it 'creates a collection with a compatible class' do
|
17
|
+
coll = MeteorMotion::Collections::MotionModel.new(DummyModel)
|
18
|
+
coll.should.not.be.equal nil
|
19
|
+
coll.name.should.be.equal 'dummymodel'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'sets a different name when it is provided' do
|
23
|
+
coll = MeteorMotion::Collections::MotionModel.new(DummyModel, 'dums')
|
24
|
+
coll.should.not.be.equal nil
|
25
|
+
coll.name.should.be.equal 'dums'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
describe 'CRUD tasks' do
|
31
|
+
|
32
|
+
before do
|
33
|
+
@client = MeteorMotion::Client.new
|
34
|
+
@client.connect
|
35
|
+
@coll = MeteorMotion::Collections::MotionModel.new(DummyModel)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'adds an object to the collection and finds it' do
|
39
|
+
@coll.add('abc', {a: 1, b:2})
|
40
|
+
@coll.find('abc').should.not.be.equal nil
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'adds the correct fields to the object' do
|
44
|
+
@coll.add('abc', {a: 1, b:2})
|
45
|
+
|
46
|
+
@coll.find('abc').a.should.be.equal 1
|
47
|
+
@coll.find('abc').b.should.be.equal 2
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'updates an objects fields' do
|
51
|
+
@coll.add('abc', {a: 1, b:2})
|
52
|
+
@coll.find('abc').a.should.be.equal 1
|
53
|
+
|
54
|
+
@coll.update('abc', {a: 2}, nil)
|
55
|
+
@coll.find('abc').a.should.be.equal 2
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'removes an object from the collection' do
|
59
|
+
@coll.add('abc', {a: 1, b:2})
|
60
|
+
|
61
|
+
wait 1.0 do
|
62
|
+
@coll.remove('abc')
|
63
|
+
@coll.find('abc').should.be.equal nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
data/spec/ddp_spec.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
describe MeteorMotion::DDP do
|
2
|
+
def handle_connect result
|
3
|
+
@result = result
|
4
|
+
end
|
5
|
+
|
6
|
+
describe 'connect' do
|
7
|
+
def error code, reason, details
|
8
|
+
return
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'connects with a valid hostname' do
|
12
|
+
ddp = MeteorMotion::DDP.new self
|
13
|
+
ddp.connect
|
14
|
+
|
15
|
+
wait 1.0 do
|
16
|
+
ddp.websocket_ready?.should.equal true
|
17
|
+
ddp.session.should.not.equal nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'subscriptions' do
|
23
|
+
|
24
|
+
def error code, reason, details
|
25
|
+
if reason != :unknown
|
26
|
+
@error_msg = reason
|
27
|
+
resume
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def collections
|
32
|
+
return @collections
|
33
|
+
end
|
34
|
+
|
35
|
+
def subscriptions
|
36
|
+
return @subscriptions
|
37
|
+
end
|
38
|
+
|
39
|
+
before do
|
40
|
+
@ddp = MeteorMotion::DDP.new self
|
41
|
+
@ddp.connect
|
42
|
+
@collections = {'books' => MeteorMotion::Collections::Default.new('books') }
|
43
|
+
@subscriptions = {}
|
44
|
+
|
45
|
+
wait 1.0 do
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'successfully subscribes to an existing publication' do
|
50
|
+
@sub_id = @ddp.sub('books')
|
51
|
+
@subscriptions[@sub_id] = {ready: false}
|
52
|
+
|
53
|
+
wait 1.0 do
|
54
|
+
@collections['books'].size.should.equal 3
|
55
|
+
@subscriptions[@sub_id][:ready].should.equal true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
it 'receives a no-sub message if no matching publication is found' do
|
61
|
+
@ddp.sub('posts')
|
62
|
+
|
63
|
+
wait do
|
64
|
+
@error_msg.should.equal "Subscription not found"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'successfully unsubscribes to an existing publication' do
|
69
|
+
@ddp.unsub( @sub_id )
|
70
|
+
|
71
|
+
wait 1.0 do
|
72
|
+
@collections['books'].size.should.equal 0
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
describe 'method calls' do
|
80
|
+
|
81
|
+
def error code, reason, details
|
82
|
+
return
|
83
|
+
end
|
84
|
+
|
85
|
+
def handle_method id, type, results
|
86
|
+
if results
|
87
|
+
@success = results
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
before do
|
92
|
+
@ddp = MeteorMotion::DDP.new self
|
93
|
+
@ddp.connect
|
94
|
+
wait 1.0 do
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'successfully executes an existing remote call without params' do
|
99
|
+
@ddp.call('ping')
|
100
|
+
|
101
|
+
wait 1.0 do
|
102
|
+
@success.should.equal 'pong'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'successfully executes an existing remote call with params' do
|
107
|
+
@ddp.call('echo', {message: 'echo'} )
|
108
|
+
|
109
|
+
wait 1.0 do
|
110
|
+
@success.should.equal 'echo'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'fails when calling a method that does not exist' do
|
115
|
+
@ddp.call('someMethod')
|
116
|
+
|
117
|
+
wait 1.0 do
|
118
|
+
@success[:reason].should.be.equal 'Method not found'
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
local
|
@@ -0,0 +1 @@
|
|
1
|
+
0.7.0.1
|
@@ -0,0 +1,28 @@
|
|
1
|
+
Books.remove({});
|
2
|
+
|
3
|
+
Books.insert({
|
4
|
+
title: 'Foundation',
|
5
|
+
author: 'Isaac Asimov',
|
6
|
+
year: '1951'
|
7
|
+
});
|
8
|
+
|
9
|
+
Books.insert({
|
10
|
+
title: 'Foundation and Empire',
|
11
|
+
author: 'Isaac Asimov',
|
12
|
+
year: '1952'
|
13
|
+
});
|
14
|
+
|
15
|
+
Books.insert({
|
16
|
+
title: 'Fahrenheit 451',
|
17
|
+
author: 'Ray Bradbury',
|
18
|
+
year: '1966'
|
19
|
+
});
|
20
|
+
|
21
|
+
|
22
|
+
Meteor.users.remove({});
|
23
|
+
|
24
|
+
Accounts.createUser({
|
25
|
+
username: 'user',
|
26
|
+
email: 'user@email.com',
|
27
|
+
password: 'pass'
|
28
|
+
});
|
@@ -0,0 +1,24 @@
|
|
1
|
+
//
|
2
|
+
// Copyright 2012 Square Inc.
|
3
|
+
//
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
// you may not use this file except in compliance with the License.
|
6
|
+
// You may obtain a copy of the License at
|
7
|
+
//
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
//
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
// See the License for the specific language governing permissions and
|
14
|
+
// limitations under the License.
|
15
|
+
//
|
16
|
+
|
17
|
+
#import <Foundation/Foundation.h>
|
18
|
+
|
19
|
+
|
20
|
+
@interface NSData (SRB64Additions)
|
21
|
+
|
22
|
+
- (NSString *)SR_stringByBase64Encoding;
|
23
|
+
|
24
|
+
@end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
//
|
2
|
+
// Copyright 2012 Square Inc.
|
3
|
+
//
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
// you may not use this file except in compliance with the License.
|
6
|
+
// You may obtain a copy of the License at
|
7
|
+
//
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
//
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
// See the License for the specific language governing permissions and
|
14
|
+
// limitations under the License.
|
15
|
+
//
|
16
|
+
|
17
|
+
#import "NSData+SRB64Additions.h"
|
18
|
+
#import "base64.h"
|
19
|
+
|
20
|
+
|
21
|
+
@implementation NSData (SRB64Additions)
|
22
|
+
|
23
|
+
- (NSString *)SR_stringByBase64Encoding;
|
24
|
+
{
|
25
|
+
size_t buffer_size = (([self length] * 3 + 2) / 2);
|
26
|
+
|
27
|
+
char *buffer = (char *)malloc(buffer_size);
|
28
|
+
|
29
|
+
int len = b64_ntop([self bytes], [self length], buffer, buffer_size);
|
30
|
+
|
31
|
+
if (len == -1) {
|
32
|
+
free(buffer);
|
33
|
+
return nil;
|
34
|
+
} else{
|
35
|
+
return [[NSString alloc] initWithBytesNoCopy:buffer length:len encoding:NSUTF8StringEncoding freeWhenDone:YES];
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
@end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
//
|
2
|
+
// Copyright 2012 Square Inc.
|
3
|
+
//
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
// you may not use this file except in compliance with the License.
|
6
|
+
// You may obtain a copy of the License at
|
7
|
+
//
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
//
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
// See the License for the specific language governing permissions and
|
14
|
+
// limitations under the License.
|
15
|
+
//
|
16
|
+
|
17
|
+
#import <Foundation/Foundation.h>
|
18
|
+
#import <Security/SecCertificate.h>
|
19
|
+
|
20
|
+
typedef enum {
|
21
|
+
SR_CONNECTING = 0,
|
22
|
+
SR_OPEN = 1,
|
23
|
+
SR_CLOSING = 2,
|
24
|
+
SR_CLOSED = 3,
|
25
|
+
} SRReadyState;
|
26
|
+
|
27
|
+
@class SRWebSocket;
|
28
|
+
|
29
|
+
extern NSString *const SRWebSocketErrorDomain;
|
30
|
+
|
31
|
+
#pragma mark - SRWebSocketDelegate
|
32
|
+
|
33
|
+
@protocol SRWebSocketDelegate;
|
34
|
+
|
35
|
+
#pragma mark - SRWebSocket
|
36
|
+
|
37
|
+
@interface SRWebSocket : NSObject <NSStreamDelegate>
|
38
|
+
|
39
|
+
@property (nonatomic, assign) id <SRWebSocketDelegate> delegate;
|
40
|
+
|
41
|
+
@property (nonatomic, readonly) SRReadyState readyState;
|
42
|
+
@property (nonatomic, readonly, retain) NSURL *url;
|
43
|
+
|
44
|
+
// This returns the negotiated protocol.
|
45
|
+
// It will be nil until after the handshake completes.
|
46
|
+
@property (nonatomic, readonly, copy) NSString *protocol;
|
47
|
+
|
48
|
+
// Protocols should be an array of strings that turn into Sec-WebSocket-Protocol.
|
49
|
+
- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols;
|
50
|
+
- (id)initWithURLRequest:(NSURLRequest *)request;
|
51
|
+
|
52
|
+
// Some helper constructors.
|
53
|
+
- (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols;
|
54
|
+
- (id)initWithURL:(NSURL *)url;
|
55
|
+
|
56
|
+
// Delegate queue will be dispatch_main_queue by default.
|
57
|
+
// You cannot set both OperationQueue and dispatch_queue.
|
58
|
+
- (void)setDelegateOperationQueue:(NSOperationQueue*) queue;
|
59
|
+
- (void)setDelegateDispatchQueue:(dispatch_queue_t) queue;
|
60
|
+
|
61
|
+
// By default, it will schedule itself on +[NSRunLoop SR_networkRunLoop] using defaultModes.
|
62
|
+
- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
|
63
|
+
- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
|
64
|
+
|
65
|
+
// SRWebSockets are intended for one-time-use only. Open should be called once and only once.
|
66
|
+
- (void)open;
|
67
|
+
|
68
|
+
- (void)close;
|
69
|
+
- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason;
|
70
|
+
|
71
|
+
// Send a UTF8 String or Data.
|
72
|
+
- (void)send:(id)data;
|
73
|
+
|
74
|
+
@end
|
75
|
+
|
76
|
+
#pragma mark - SRWebSocketDelegate
|
77
|
+
|
78
|
+
@protocol SRWebSocketDelegate <NSObject>
|
79
|
+
|
80
|
+
// message will either be an NSString if the server is using text
|
81
|
+
// or NSData if the server is using binary.
|
82
|
+
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
|
83
|
+
|
84
|
+
@optional
|
85
|
+
|
86
|
+
- (void)webSocketDidOpen:(SRWebSocket *)webSocket;
|
87
|
+
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
|
88
|
+
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
|
89
|
+
|
90
|
+
@end
|
91
|
+
|
92
|
+
#pragma mark - NSURLRequest (CertificateAdditions)
|
93
|
+
|
94
|
+
@interface NSURLRequest (CertificateAdditions)
|
95
|
+
|
96
|
+
@property (nonatomic, retain, readonly) NSArray *SR_SSLPinnedCertificates;
|
97
|
+
|
98
|
+
@end
|
99
|
+
|
100
|
+
#pragma mark - NSMutableURLRequest (CertificateAdditions)
|
101
|
+
|
102
|
+
@interface NSMutableURLRequest (CertificateAdditions)
|
103
|
+
|
104
|
+
@property (nonatomic, retain) NSArray *SR_SSLPinnedCertificates;
|
105
|
+
|
106
|
+
@end
|
107
|
+
|
108
|
+
#pragma mark - NSRunLoop (SRWebSocket)
|
109
|
+
|
110
|
+
@interface NSRunLoop (SRWebSocket)
|
111
|
+
|
112
|
+
+ (NSRunLoop *)SR_networkRunLoop;
|
113
|
+
|
114
|
+
@end
|