ruby_ddp_client 0.1

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 (7) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +8 -0
  3. data/README.md +0 -0
  4. data/lib/client.rb +181 -0
  5. data/lib/ddp.md +264 -0
  6. data/lib/ruby_ddp.rb +198 -0
  7. metadata +98 -0
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ lib/server.rb
2
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "faye-websocket"
4
+ gem "json"
5
+ gem "mongo"
6
+ gem "bson"
7
+ gem "bson_ext"
8
+
data/README.md ADDED
File without changes
data/lib/client.rb ADDED
@@ -0,0 +1,181 @@
1
+ #!usr/bin/env/ruby
2
+
3
+ #import gems
4
+ require "bundler"
5
+ Bundler.require
6
+
7
+ #ddp is just an extension of a websocket protocol
8
+ class DDP::Client < Faye::WebSocket::Client
9
+ attr_accessor :collections, :connect_m
10
+
11
+ #sets up basic server for ddp connections
12
+ def initialize( host, port = 8888, path = "websocket",
13
+ version, support )
14
+ super("http://#{host}:#{port}/#{path}") #setup websocket connection
15
+
16
+ #handler for incoming respones from the server
17
+ self.init_handlers()
18
+
19
+ @sub_callbacks = {}
20
+ @collections = {}
21
+ @sub_ids = {}
22
+ @version = version
23
+ @last_suc_version = nil
24
+ @session = nil
25
+ @curr_id = 0
26
+ @support = support
27
+ end
28
+
29
+ #sends a method call
30
+ def call_method(method, params = [], &blk)
31
+ id = self.next_id()
32
+ self.send(msg: 'method', id: id, method: method, params: params)
33
+ @sub_callbacks[id] = blk
34
+ end
35
+
36
+ #subscripe to the data 'name' on the server
37
+ #this function will assign an arbitrary id to the subscription so that
38
+ #it may be tracked by the client
39
+ def subscribe(name, params, &blk)
40
+ id = self.next_id()
41
+ self.send(msg: 'sub', id: id, name: name, params: params)
42
+
43
+ @sub_ids[name] = id
44
+ @sub_callbacks[id] = blk
45
+ end
46
+
47
+ #client unsibscribes from the specified subscription
48
+ def unsubscribe(name)
49
+ id = @sub_ids[name]
50
+ self.send(msg: 'unsub', id: id)
51
+ end
52
+
53
+ private
54
+
55
+ #sends connection message to server
56
+ def connect(version = @version)
57
+
58
+ #send initial connection request
59
+ self.send(msg: :connect, version: @version, support: @support)
60
+
61
+ #handle incoming response form the server
62
+ self.onmessage = lambda do |event|
63
+
64
+ res = JSON.parse(event.data)
65
+
66
+ if res.has_key? 'session'
67
+ #connection is successful - update session and record version
68
+ @session = res['session'].to_s
69
+ @last_suc_version = @version
70
+
71
+ #if the connection fails the suggested version should be retrieved
72
+ #from the response and the connection should be made
73
+ else #there was a failed connection
74
+ sug_version = res['version']
75
+ #retry the send request with the version specified by the server
76
+ self.connect(sug_version)
77
+
78
+ end
79
+ end
80
+ end
81
+
82
+ #updates and returns the next available ID number
83
+ def next_id
84
+ (@curr_id +=1).to_s
85
+ end
86
+
87
+ def send data
88
+ self.send(data.to_json)
89
+ end
90
+
91
+ #handlers for server sents
92
+ def init_handlers
93
+ #begin the client session by attempting to connect to the server
94
+ self.onopen = lamda { self.connect() }
95
+
96
+ self.onmessage = lambda do |event|
97
+
98
+ data = JSON.parse(event.data)
99
+
100
+ if data.has_key? 'msg'
101
+
102
+ case(data['msg'])
103
+ when 'connected'
104
+ self.connect_m.send_method event
105
+
106
+ #inserts the given data into the client datastore
107
+ when 'added'
108
+ if data.has_key? 'collection'
109
+ c_name = data['collection']
110
+ c_id = data['id']
111
+ @collections[c_name] ||= {}
112
+ data['params'].each { |k,v| @collections[c_name][c_id][k] = v }
113
+ end
114
+
115
+ #update the given data in the clients datastore
116
+ when 'changed'
117
+ if data.has_key 'collection'
118
+ c_name = data['collection']
119
+ c_id = data['id']
120
+ c_fields = data['fields']
121
+ to_clear = data['cleared']
122
+
123
+ #clear out fields specified at 'cleared'
124
+ to_clear.each { |f| @collections[c_name][c_id][f].delete f }
125
+ #insert/udpdate new data
126
+ c_fields.each { |k,v| @collections[c_name][c_id][k] = v }
127
+ end
128
+
129
+ #remove the given data from the client's datastore
130
+ when 'removed'
131
+ if data.has_key 'collection'
132
+ c_name = data['collection']
133
+ c_id = data['id']
134
+ @collections[c_name][c_id] = nil
135
+ end
136
+
137
+ #adds the document *before* that whose id is specified in the id
138
+ #field of addedBefore
139
+ when 'addedBefore'
140
+ if data.has_key? 'collection'
141
+ c_name = data['collection']
142
+ c_id = data['id']
143
+ before = data['before']
144
+
145
+ @collections[c_name] ||= {}
146
+ #adds before if non-null
147
+ if before
148
+ data['params'].each { |k,v| @collections[c_name][c_id-1][k] = v }
149
+ else
150
+ data['params'].each { |k,v| @collections[c_name][c_id][k] = v }
151
+ end
152
+ end
153
+
154
+ when 'movedBefore'
155
+ if data.has_key? 'collection'
156
+ c_name = data['collection']
157
+ c_id = data['id']
158
+ before = data['before']
159
+
160
+ @collections[c_name] ||= {}
161
+ #adds before if non-null
162
+ if before
163
+ data['params'].each { |k,v| @collections[c_name][c_id-1][k] = v }
164
+ else
165
+ data['params'].each { |k,v| @collections[c_name][c_id][k] = v }
166
+ end
167
+ end
168
+
169
+ when 'ready'
170
+ puts "Server acks READY"
171
+
172
+ end #case
173
+ end # msg?
174
+
175
+ self.on(:close) = lambda {|e| puts "Connection Closed"}
176
+ end #message handler
177
+ end #init_handlrers
178
+ end
179
+
180
+
181
+
data/lib/ddp.md ADDED
@@ -0,0 +1,264 @@
1
+ # DDP Specification (As provided by meteor)
2
+
3
+ DDP is a protocol between a client and a server that supports two operations:
4
+
5
+ * Remote procedure calls by the client to the server.
6
+ * The client subscribing to a set of documents, and the server keeping the
7
+ client informed about the contents of those documents as they change over
8
+ time.
9
+
10
+ This document specifies the version "pre1" of DDP. It's a rough description of
11
+ the protocol and not intended to be entirely definitive.
12
+
13
+ ## General Message Structure:
14
+
15
+ DDP may use either SockJS or WebSockets as a lower-level message transport. (For
16
+ now, you connect via SockJS at the URL `/sockjs` and via WebSockets at the URL
17
+ `/websocket`. The latter is likely to change to be the main app URL specifying a
18
+ WebSocket subprotocol.)
19
+
20
+ DDP messages are JSON objects, with some fields specified to be EJSON. Each one
21
+ has a `msg` field that specifies the message type, as well as other fields
22
+ depending on message type.
23
+
24
+ ## Establishing a DDP Connection:
25
+
26
+ ### Messages:
27
+
28
+ * `connect` (client -> server)
29
+ - `session`: string (if trying to reconnect to an existing DDP session)
30
+ - `version`: string (the proposed protocol version)
31
+ - `support`: array of strings (protocol versions supported by the client,
32
+ in order of preference)
33
+
34
+ * `connected` (server->client)
35
+ - `session`: string (an identifier for the DDP session)
36
+
37
+ * `failed` (server->client)
38
+ - `version`: string (a suggested protocol version to connect with)
39
+
40
+ ### Procedure:
41
+
42
+ The server may send an initial message which is a JSON object lacking a `msg`
43
+ key. If so, the client should ignore it. The client does not have to wait for
44
+ this message. (This message is used to help implement hot code reload over our
45
+ SockJS transport. It is currently sent over websockets as well, but probably
46
+ should not be.)
47
+
48
+ * The client sends a `connect` message.
49
+ * If the server is willing to speak the `version` of the protocol specified in
50
+ the `connect` message, it sends back a `connected` message.
51
+ * Otherwise the server sends back a `failed` message with a version of DDP it
52
+ would rather speak, informed by the `connect` message's `support` field, and
53
+ closes the underlying transport.
54
+ * The client is then free to attempt to connect again speaking a different
55
+ version of DDP. It can do that by sending another `connect` message on a new
56
+ connection. The client may optimistically send more messages after the
57
+ `connect` message, assuming that the server will support the proposed
58
+ protocol version. If the server does not support that version, it must ignore
59
+ those additional messages.
60
+
61
+ The versions in the `support` field of the client's `connect` message
62
+ are ordered according to the client's preference, most preferred
63
+ first. If, according to this ordering, the `version` proposed by the
64
+ client is not the best version that the server supports, the server
65
+ must force the client to switch to the better version by sending a
66
+ `failed` message.
67
+
68
+ When a client is connecting to a server for the first time it will
69
+ typically set `version` equal to its most preferred version. If
70
+ desired, the client can then remember the version that is ultimately
71
+ negotiated with the server and begin with that version in future
72
+ connections. The client can rely on the server sending a `failed`
73
+ message if a better version is possible as a result of the client or
74
+ the server having been upgraded.
75
+
76
+ ## Managing Data:
77
+
78
+ ### Messages:
79
+
80
+ * `sub` (client -> server):
81
+ - `id`: string (an arbitrary client-determined identifier for this subscription)
82
+ - `name`: string (the name of the subscription)
83
+ - `params`: optional array of EJSON items (parameters to the subscription)
84
+ * `unsub` (client -> server):
85
+ - `id`: string (the id passed to 'sub')
86
+ * `nosub` (server -> client):
87
+ - `id`: string (the id passed to 'sub')
88
+ * `error`: optional Error (an error raised by the subscription as it
89
+ concludes, or sub-not-found)
90
+ * `added` (server -> client):
91
+ - `collection`: string (collection name)
92
+ - `id`: string (document ID)
93
+ - `fields`: optional object with EJSON values
94
+ * `changed` (server -> client):
95
+ - `collection`: string (collection name)
96
+ - `id`: string (document ID)
97
+ - `fields`: optional object with EJSON values
98
+ - `cleared`: optional array of strings (field names to delete)
99
+ * `removed` (server -> client):
100
+ - `collection`: string (collection name)
101
+ - `id`: string (document ID)
102
+ * `ready` (server -> client):
103
+ - `subs`: array of strings (ids passed to 'sub' which have sent their
104
+ initial batch of data)
105
+ * `addedBefore` (server -> client):
106
+ - `collection`: string (collection name)
107
+ - `id`: string (document ID)
108
+ - `fields`: optional object with EJSON values
109
+ - `before`: string or null (the document ID to add the document before,
110
+ or null to add at the end)
111
+ * `movedBefore` (server -> client):
112
+ - `collection`: string
113
+ - `id`: string (the document ID)
114
+ - `before`: string or null (the document ID to move the document before, or
115
+ null to move to the end)
116
+
117
+ ### Procedure:
118
+
119
+ * The client specifies sets of information it is interested in by sending
120
+ `sub` messages to the server.
121
+
122
+ * At any time, but generally informed by the `sub` messages, the server can
123
+ send data messages to the client. Data consist of `added`, `changed`, and
124
+ `removed` messages. These messages model a local set of data the client
125
+ should keep track of.
126
+
127
+ - An `added` message indicates a document was added to the local set. The ID
128
+ of the document is specified in the `id` field, and the fields of the
129
+ document are specified in the `fields` field. Minimongo interperets the
130
+ string id field in a special way that transforms it to the _id field of
131
+ Mongo documents.
132
+
133
+ - A `changed` message indicates a document in the local set has new values
134
+ for some fields or has had some fields removed. The `id` field is the ID of
135
+ the document that has changed. The `fields` object, if present, indicates
136
+ fields in the document that should be replaced with new values. The
137
+ `cleared` field contains an array of fields that are no longer in the
138
+ document.
139
+
140
+ - A `removed` message indicates a document was removed from the local
141
+ set. The `id` field is the ID of the document.
142
+
143
+ * A collection is either ordered, or not. If a collection is ordered,
144
+ the `added` message is replaced by `addedBefore`, which
145
+ additionally contains the ID of the document after the one being
146
+ added in the `before` field. If the document is being added at the
147
+ end, `before` is set to null. For a given collection, the server
148
+ should only send `added` messages or `addedBefore` messages, not a
149
+ mixture of both, and should only send `movedBefore` messages for a
150
+ collection with `addedBefore` messages.
151
+
152
+ NOTE: The ordered collection DDP messages are not currently used by Meteor.
153
+ They will likely be used by Meteor in the future.
154
+
155
+ * The client maintains one set of data per collection. Each subscription does
156
+ not get its own datastore, but rather overlapping subscriptions cause the
157
+ server to send the union of facts about the one collection's data. For
158
+ example, if subscription A says document `x` has fields `{foo: 1, bar: 2}`
159
+ and subscription B says document `x` has fields `{foo: 1, baz:3}`, then the
160
+ client will be informed that document `x` has fields `{foo: 1, bar: 2, baz:
161
+ 3}`. If field values from different subscriptions conflict with each other,
162
+ the server should send one of the possible field values.
163
+
164
+ * When one or more subscriptions have finished sending their initial batch of
165
+ data, the server will send a `ready` message with their IDs.
166
+
167
+ ## Remote Procedure Calls:
168
+
169
+ ### Messages:
170
+
171
+ * `method` (client -> server):
172
+ - `method`: string (method name)
173
+ - `params`: optional array of EJSON items (parameters to the method)
174
+ - `id`: string (an arbitrary client-determined identifier for this method call)
175
+ * `result` (server -> client):
176
+ - `id`: string (the id passed to 'method')
177
+ - `error`: optional Error (an error thrown by the method (or method-not-found)
178
+ - `result`: optional EJSON item (the return value of the method, if any)
179
+ * `updated` (server -> client):
180
+ - `methods`: array of strings (ids passed to 'method', all of whose writes
181
+ have been reflected in data messages)
182
+
183
+ ### Procedure:
184
+
185
+ * The client sends a `method` message to the server
186
+
187
+ * The server responds with a `result` message to the client, carrying either
188
+ the result of the method call, or an appropriate error.
189
+
190
+ * Method calls can affect data that the client is subscribed to. Once the
191
+ server has finished sending the client all the relevant data messages based
192
+ on this procedure call, the server should send an `updated` message to the
193
+ client with this method's ID.
194
+
195
+ * There is no particular required ordering between `result` and `updated`
196
+ messages for a method call.
197
+
198
+ ## Errors:
199
+
200
+ Errors appear in `result` and `nosub` messages in an optional error field. An
201
+ error is an Object with the following fields:
202
+
203
+ * `error`: number
204
+ * `reason`: optional string
205
+ * `details`: optional string
206
+
207
+ Such an Error is used to represent errors raised by the method or subscription,
208
+ as well as an attempt to subscribe to an unknown subscription or call an unknown
209
+ method.
210
+
211
+ Other erroneous messages sent from the client to the server can result in
212
+ receiving a top-level `msg: 'error'` message in response. These conditions
213
+ include:
214
+
215
+ * sending messages which are not valid JSON objects
216
+ * unknown `msg` type
217
+ * other malformed client requests (not including required fields)
218
+ * sending anything other than `connect` as the first message, or sending
219
+ `connect` as a non-initial message
220
+
221
+ The error message contains the following fields:
222
+
223
+ * `reason`: string describing the error
224
+ * `offendingMessage`: if the original message parsed properly, it is included
225
+ here
226
+
227
+ ## Appendix: EJSON
228
+
229
+ EJSON is a way of embedding more than the built-in JSON types in JSON. It
230
+ supports all types built into JSON as plain JSON, plus the following:
231
+
232
+ **Dates:**
233
+
234
+ {"$date": MILLISECONDS_SINCE_EPOCH}
235
+
236
+ **Binary data:**
237
+
238
+ {"$binary": BASE_64_STRING}
239
+
240
+ (The base 64 string has `+` and `/` as characters 62 and 63, and has no maximum line length)
241
+
242
+ **Escaped things** that might otherwise look like EJSON types:
243
+
244
+ {"$escape": THING}
245
+
246
+ For example, here is the JSON value `{$date: 10000}` stored in EJSON:
247
+
248
+ {"$escape": {"$date": 10000}}
249
+
250
+ Note that escaping only causes keys to be literal for one level down; you can
251
+ have further EJSON inside. For example, the following is the key `$date` mapped
252
+ to a Date object:
253
+
254
+ {"$escape": {"$date": {"$date": 32491}}}
255
+
256
+ **User-specified types:**
257
+
258
+ {"$type": TYPENAME, "$value": VALUE}
259
+
260
+ Implementations of EJSON should try to preserve key order where they can. Users
261
+ of EJSON should not rely on key order, if possible.
262
+
263
+ > MongoDB relies on key order. When using EJSON with MongoDB, the
264
+ > implementation of EJSON must preserve key order.
data/lib/ruby_ddp.rb ADDED
@@ -0,0 +1,198 @@
1
+ #!usr/bin/env/ruby
2
+
3
+ #import gems
4
+ require "bundler"
5
+ Bundler.require
6
+
7
+ #ddp is just an extension of a websocket protocol
8
+ class DDP::Client < Faye::WebSocket::Client
9
+ attr_accessor :collections, :onconnect
10
+
11
+ #sets up basic server for ddp connections
12
+ def initialize( host, port = 8888, path = "websocket",
13
+ version = self.version, support )
14
+ super("http://#{host}:#{port}/#{path}") #setup websocket connection
15
+
16
+ self.init_event_handlers()
17
+
18
+ @callbacks = {}
19
+ @collections = {}
20
+ @subs = {}
21
+ @version = version
22
+ @session = nil
23
+ @curr_id = 0
24
+ @support = support
25
+
26
+ end
27
+
28
+ #sends connection message to server
29
+ def connect
30
+ self.send({
31
+ msg: :connect,
32
+ version: @version,
33
+ support: @support
34
+ })
35
+
36
+ #handle incoming response form the server
37
+ self.onmessage = lambda do |event|
38
+
39
+ res = JSON.parse(event.data)
40
+
41
+ if res.has_key? 'session'
42
+ #connection is successful - update session and record version
43
+ @session = res['session'].to_s
44
+ @@last_suc_version = @version
45
+
46
+ else #there was a failed connection
47
+ @version = res['version']
48
+ #retry the send request with the version specified by the server
49
+ self.send({
50
+ msg: :connect
51
+ version: @version,
52
+ support: @support
53
+ })
54
+ end
55
+
56
+ end
57
+ end
58
+
59
+ #sends a method call
60
+ def call(method, params = [], &blk)
61
+ id = self.next_id()
62
+ self.send(msg: 'method', id: id, method: method, params: params)
63
+ @callbacks[id] = blk
64
+ end
65
+
66
+ #subscripe to the data 'name' on the server
67
+ #this function will assign an arbitrary id to the subscription so that
68
+ #it may be tracked by the client
69
+ def subscribe(name, params, &blk)
70
+ id = self.next_id()
71
+ self.send({msg: 'sub', id: id, name: name, params: params})
72
+
73
+ @subs[name] = id
74
+ @callbacks[id] = blk
75
+ end
76
+
77
+ #client unsibscribes from the specified subscription
78
+ def unsubscribe(name)
79
+ id = @subs[name]
80
+
81
+ self.send(msg: 'unsub', id: id)
82
+ end
83
+
84
+ private
85
+ #updates and returns the next available ID number
86
+ def next_id
87
+ (@curr_id +=1).to_s
88
+ end
89
+
90
+ def send data
91
+ self.send(data.to_json)
92
+ end
93
+
94
+ #handlers for sever sents
95
+ def init_event_handlers
96
+ #begin the client session by attempting to connect to the server
97
+ self.onopen = lamda {self.connect()}
98
+
99
+ self.onmessage = lambda do |event|
100
+
101
+ data = JSON.parse(event.data)
102
+
103
+ if data.has_key? 'msg'
104
+
105
+ case(data['msg'])
106
+
107
+ #successfull connection!
108
+ when 'connected'
109
+ self.connect.call event
110
+
111
+ #inserts the given data into the client datastore
112
+ when 'addad'
113
+ if data.has_key? 'collection'
114
+ c_name = data['collection']
115
+ c_id = data['id']
116
+ @collections[c_name] ||= {}
117
+ data['params'].each { |k,v| @collections[c_name][c_id][k] = v }
118
+ end
119
+
120
+ #update the given data in the clients datastore
121
+ when 'changed'
122
+ if data.has_key 'collection'
123
+ c_name = data['collection']
124
+ c_id = data['id']
125
+ c_fields = data['fields']
126
+ to_clear = data['cleared']
127
+
128
+ #clear out fields specified at 'cleared'
129
+ to_clear.each do |f|
130
+ @collections[c_name][c_id][f].delete f
131
+ end
132
+ #insert/udpdate new data
133
+ c_fields.each do |k,v|
134
+ @collections[c_name][c_id][k] = v
135
+ end
136
+
137
+ end
138
+
139
+ #remove the given data from the client's datastore
140
+ when 'removed'
141
+ if data.has_key 'collection'
142
+ c_name = data['collection']
143
+ c_id = data['id']
144
+ @collections[c_name][c_id] = nil
145
+ end
146
+
147
+ when 'ready'
148
+ #adds the document *before* that whose id is specified in the id
149
+ #field of addedBefore
150
+ when 'addedBefore'
151
+
152
+ if data.has_key? 'collection'
153
+ c_name = data['collection']
154
+ c_id = data['id']
155
+ before = data['before']
156
+
157
+ @collections[c_name] ||= {}
158
+ #adds before if non-null
159
+ if before
160
+ data['params'].each { |k,v| @collections[c_name][c_id-1][k] = v }
161
+ else
162
+ data['params'].each { |k,v| @collections[c_name][c_id][k] = v }
163
+ end
164
+ end
165
+
166
+ when 'movedBefore'
167
+ if data.has_key? 'collection'
168
+ c_name = data['collection']
169
+ c_id = data['id']
170
+ before = data['before']
171
+
172
+ @collections[c_name] ||= {}
173
+ #adds before if non-null
174
+ if before
175
+ data['params'].each { |k,v| @collections[c_name][c_id-1][k] = v }
176
+ else
177
+ data['params'].each { |k,v| @collections[c_name][c_id][k] = v }
178
+ end
179
+ end
180
+
181
+ when 'ready'
182
+ puts "Server acks READY"
183
+
184
+ end #case
185
+ end # msg?
186
+
187
+ #there is no message ???
188
+
189
+ self.on(:close) = lambda {|e| puts "Connection Closed"}
190
+ end #message handler
191
+ end #init_event_handlrers
192
+
193
+ end #class
194
+
195
+
196
+
197
+
198
+
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby_ddp_client
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Brendan Ryan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: faye-websocket
16
+ requirement: &70316560 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70316560
25
+ - !ruby/object:Gem::Dependency
26
+ name: json
27
+ requirement: &70316320 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70316320
36
+ - !ruby/object:Gem::Dependency
37
+ name: bundler
38
+ requirement: &70316060 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: '1.3'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70316060
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: &70315850 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70315850
58
+ description: A simple ddp client (a la Meteor) written in Ruby
59
+ email:
60
+ - ryan.brendanjohn@gmail.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - Gemfile
67
+ - Gemfile.lock
68
+ - README.md
69
+ - lib/client.rb
70
+ - lib/ddp.md
71
+ - lib/ruby_ddp.rb
72
+ - lib/server.rb
73
+ homepage: http://github.com/bjryan2/ruby_ddp_client
74
+ licenses:
75
+ - MIT
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 1.8.11
95
+ signing_key:
96
+ specification_version: 3
97
+ summary: ''
98
+ test_files: []