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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.repl_history +0 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +166 -0
  7. data/Rakefile +17 -0
  8. data/app/app_delegate.rb +11 -0
  9. data/app/controllers/book_controller.rb +92 -0
  10. data/app/controllers/book_list_controller.rb +105 -0
  11. data/app/controllers/connection_controller.rb +83 -0
  12. data/app/controllers/login_controller.rb +35 -0
  13. data/lib/meteor-motion.rb +12 -0
  14. data/lib/meteor-motion/version.rb +3 -0
  15. data/meteor-motion.gemspec +27 -0
  16. data/motion/adapters/motion_model.rb +61 -0
  17. data/motion/client.rb +179 -0
  18. data/motion/collection.rb +50 -0
  19. data/motion/collections/default.rb +56 -0
  20. data/motion/collections/motion_model.rb +52 -0
  21. data/motion/ddp.rb +161 -0
  22. data/motion/srp/securerandom.rb +248 -0
  23. data/motion/srp/srp.rb +250 -0
  24. data/spec/adapters/motion_model_spec.rb +38 -0
  25. data/spec/client_spec.rb +104 -0
  26. data/spec/collection_spec.rb +63 -0
  27. data/spec/collections/default_spec.rb +46 -0
  28. data/spec/collections/motion_model_spec.rb +69 -0
  29. data/spec/ddp_spec.rb +123 -0
  30. data/spec/server/.meteor/.gitignore +1 -0
  31. data/spec/server/.meteor/packages +9 -0
  32. data/spec/server/.meteor/release +1 -0
  33. data/spec/server/collections/books.js +11 -0
  34. data/spec/server/server/fixtures.js +28 -0
  35. data/spec/server/server/publications.js +3 -0
  36. data/spec/server/smart.json +3 -0
  37. data/vendor/SocketRocket/NSData+SRB64Additions.h +24 -0
  38. data/vendor/SocketRocket/NSData+SRB64Additions.m +39 -0
  39. data/vendor/SocketRocket/SRWebSocket.h +114 -0
  40. data/vendor/SocketRocket/SRWebSocket.m +1757 -0
  41. data/vendor/SocketRocket/SocketRocket-Prefix.pch +27 -0
  42. data/vendor/SocketRocket/SocketRocket.bridgesupport +160 -0
  43. data/vendor/SocketRocket/base64.c +314 -0
  44. data/vendor/SocketRocket/base64.h +34 -0
  45. 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
@@ -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,9 @@
1
+ # Meteor packages used by this project, one per line.
2
+ #
3
+ # 'meteor add' and 'meteor remove' will edit this file for you,
4
+ # but you can also edit it by hand.
5
+
6
+ standard-app-packages
7
+ insecure
8
+ preserve-inputs
9
+ accounts-password
@@ -0,0 +1 @@
1
+ 0.7.0.1
@@ -0,0 +1,11 @@
1
+ Books = new Meteor.Collection('books');
2
+
3
+ Meteor.methods({
4
+ echo: function(message) {
5
+ return message.message;
6
+ },
7
+
8
+ ping: function(message) {
9
+ return 'pong';
10
+ }
11
+ });
@@ -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,3 @@
1
+ Meteor.publish('books', function() {
2
+ return Books.find();
3
+ });
@@ -0,0 +1,3 @@
1
+ {
2
+ "packages": {}
3
+ }
@@ -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