meteor-motion 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|