rest-firebase 0.9.0 → 0.9.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.
- checksums.yaml +4 -4
- data/CHANGES.md +4 -0
- data/Rakefile +1 -1
- data/doc/intro.md +150 -0
- data/lib/rest-firebase.rb +10 -4
- data/rest-firebase.gemspec +4 -3
- data/test/test_api.rb +8 -2
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 38ac75dc25a2212b5493f30523502a0c524e5423
|
|
4
|
+
data.tar.gz: 194f4cafa79ce00c6d33dd9dbdf086ce0c30bda3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9e9dfc59ecf61bcdd560d72bfe851464f3a183bc514a98b1c5464d58f013cac9d2de2404be278005ed33f821f5becfd4a38984187fbfa83de96d95619ed04a53
|
|
7
|
+
data.tar.gz: 502858f091acd477c0e6ad2ed4c142845899ddaa8b16686ef2df5ff4e2134966bf7a54d3fd8c3e43b16a7f7bbfbc6c039a57d18d39ec66e235e01634195ab9e2
|
data/CHANGES.md
CHANGED
data/Rakefile
CHANGED
|
@@ -10,7 +10,7 @@ $LOAD_PATH.unshift(File.expand_path("#{dir}/rest-core/lib"))
|
|
|
10
10
|
|
|
11
11
|
Gemgem.init(dir) do |s|
|
|
12
12
|
s.name = 'rest-firebase'
|
|
13
|
-
s.version = '0.9.
|
|
13
|
+
s.version = '0.9.1'
|
|
14
14
|
s.homepage = 'https://github.com/CodementorIO/rest-firebase'
|
|
15
15
|
|
|
16
16
|
s.authors = ['Codementor', 'Lin Jen-Shin (godfat)']
|
data/doc/intro.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
|
|
2
|
+
# Codementor introduces you a new Firebase client for Ruby
|
|
3
|
+
|
|
4
|
+
## Why we pick Firebase
|
|
5
|
+
|
|
6
|
+
Here at Codementor we implemented all the realtime facilities with
|
|
7
|
+
[Firebase][], which is a great tool and service for realtime communication,
|
|
8
|
+
especially for their JavaScript library which could handle all those edge
|
|
9
|
+
cases like whenever the clients disconnected unexpectedly, how we could
|
|
10
|
+
process the data offline and when we have a chance to reconnect, reconnect
|
|
11
|
+
and resend the offline data, etc, which are definitely common enough and we
|
|
12
|
+
shall not ignore them.
|
|
13
|
+
|
|
14
|
+
[Firebase]: https://www.firebase.com/
|
|
15
|
+
|
|
16
|
+
## Why we need a Firebase client for Ruby
|
|
17
|
+
|
|
18
|
+
However, our server is written in Ruby, and we definitely need someway to let
|
|
19
|
+
the server communicate with the clients (browsers). For example, whenever
|
|
20
|
+
we want to programmatically broadcast some messages to certain users, it
|
|
21
|
+
would be much easier to do this from the server. Picking a Firebase client
|
|
22
|
+
for Ruby would be the most straightforward choice.
|
|
23
|
+
|
|
24
|
+
## Existing Firebase client for Ruby did not fit our need
|
|
25
|
+
|
|
26
|
+
Unfortunately, eventually we realized that the existing Firebase client for
|
|
27
|
+
Ruby, namely [firebase-ruby][], did not fit our need. The main reason is that
|
|
28
|
+
it did not support the [streaming feature from Firebase][streaming], which is
|
|
29
|
+
extremely important whenever we want the clients periodically notify the
|
|
30
|
+
server, (e.g. online presence) since the server needs to know the status in
|
|
31
|
+
order to do some other stuffs underneath in realtime. We could probably
|
|
32
|
+
implement this on our server, but why not just use Firebase whenever it's
|
|
33
|
+
already implemented, and we're using it?
|
|
34
|
+
|
|
35
|
+
[firebase-ruby]: https://github.com/oscardelben/firebase-ruby
|
|
36
|
+
[streaming]: https://www.firebase.com/docs/rest-api.html#streaming-from-the-rest-api
|
|
37
|
+
|
|
38
|
+
## [rest-firebase][]
|
|
39
|
+
|
|
40
|
+
Therefore we implemented our own Firebase client for Ruby, that is
|
|
41
|
+
[rest-firebase][]. It was built on top of [rest-core][], thus it has all
|
|
42
|
+
the advantages from rest-core, just like firebase-ruby was built on top
|
|
43
|
+
of [typhoeus][]. The highlights for rest-firebase are:
|
|
44
|
+
|
|
45
|
+
* Concurrent/asynchronous requests
|
|
46
|
+
* Streaming requests
|
|
47
|
+
* Generate Firebase JWT for you (auto-refresh is WIP)
|
|
48
|
+
|
|
49
|
+
[rest-firebase]: https://github.com/CodementorIO/rest-firebase
|
|
50
|
+
[rest-core]: https://github.com/godfat/rest-core
|
|
51
|
+
[typhoeus]: https://github.com/typhoeus/typhoeus
|
|
52
|
+
|
|
53
|
+
### Concurrent/asynchronous requests
|
|
54
|
+
|
|
55
|
+
At times we want to notify two users at the same time, instead of preparing
|
|
56
|
+
two requests and wait for two requests to be done, we could simply do this:
|
|
57
|
+
(not a working example, just try to demonstrate, see [README.md][] for
|
|
58
|
+
working example)
|
|
59
|
+
|
|
60
|
+
``` ruby
|
|
61
|
+
f = RestFirebase.new
|
|
62
|
+
f.put("users/#{a.id}", :message => 'Hi')
|
|
63
|
+
f.put("users/#{b.id}", :message => 'Oh')
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
All requests are non-blocking, and it would only block when we try to look at
|
|
67
|
+
the response. Therefore the above requests would be processed concurrently and
|
|
68
|
+
asynchronously. To learn more about this, check [Concurrent HTTP Requests][].
|
|
69
|
+
|
|
70
|
+
Also, consequently, if you're not waiting for the requests to be done
|
|
71
|
+
somewhere, you might want to wait `at_exit` to make sure all
|
|
72
|
+
requests are properly done like this:
|
|
73
|
+
|
|
74
|
+
``` ruby
|
|
75
|
+
at_exit do
|
|
76
|
+
RestFirebase.shutdown
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Which would also shutdown the [thread pool][] if you're using it.
|
|
81
|
+
|
|
82
|
+
[README.md]: https://github.com/CodementorIO/rest-firebase/blob/master/README.md
|
|
83
|
+
[Concurrent HTTP Requests]: https://github.com/CodementorIO/rest-firebase/blob/master/README.md#concurrent-http-requests
|
|
84
|
+
[thread pool]: https://github.com/godfat/rest-core#thread-pool--connection-pool
|
|
85
|
+
|
|
86
|
+
### Streaming requests
|
|
87
|
+
|
|
88
|
+
To receive the online presence events, we have a specialized daemon to listen
|
|
89
|
+
on the presence node from Firebase. Something like below:
|
|
90
|
+
|
|
91
|
+
``` ruby
|
|
92
|
+
es = RestFirebase.new.event_source('presence')
|
|
93
|
+
es.onerror do |error|
|
|
94
|
+
Codementor.handle_error(error) unless error.kind_of?(EOFError)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
es.onreconnect do
|
|
98
|
+
firebase.auth = nil # refresh auth
|
|
99
|
+
!!@start # don't reconnect if we're closing
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
es.onmessage do |event, data|
|
|
103
|
+
next unless event == 'put'
|
|
104
|
+
next unless username = data['path'][%r{^/(\w+)/web$}, 1]
|
|
105
|
+
onpresence(username, data['data'])
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
es.start
|
|
109
|
+
sleep(1) while @start
|
|
110
|
+
|
|
111
|
+
es.close
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`onpresence` is the one doing our business logic.
|
|
115
|
+
|
|
116
|
+
### Generate Firebase JWT for you (auto-refresh is WIP)
|
|
117
|
+
|
|
118
|
+
We could use Firebase JWT instead of our secret in order to make authorized
|
|
119
|
+
requests. This would be much secure than simply use the secret, which would
|
|
120
|
+
never expire unless we explicitly ask for. Checkout
|
|
121
|
+
[Authenticating Your Server][] for more detail. [rest-firebase][] could
|
|
122
|
+
generate one for you automatically by passing your secret to it like this:
|
|
123
|
+
|
|
124
|
+
``` ruby
|
|
125
|
+
f = RestFirebase.new :secret => 'secret',
|
|
126
|
+
:d => {:auth_data => 'something'}
|
|
127
|
+
f.get('presence') # => attach JWT for auth in the request automatically
|
|
128
|
+
f.auth # => the JWT
|
|
129
|
+
f.auth = nil # => remove old JWT
|
|
130
|
+
f.auth # => generate a fresh new JWT
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Read the above document for what `:d` means here. Note that this JWT
|
|
134
|
+
would expire after 24 hours. Every time you initialize a new `RestFirebase`
|
|
135
|
+
it would generate a fresh new JWT, but if you want to keep using the same
|
|
136
|
+
instance, you would probably need to refresh the JWT by yourselves, just like
|
|
137
|
+
what we did when we tried to reconnect it in the streaming example.
|
|
138
|
+
|
|
139
|
+
[Authenticating Your Server]: https://www.firebase.com/docs/security/custom-login.html#authenticating-your-server
|
|
140
|
+
|
|
141
|
+
## Summary
|
|
142
|
+
|
|
143
|
+
In order to take the full advantage of using Firebase with Ruby, we introduce
|
|
144
|
+
you [rest-firebase][], which highlights:
|
|
145
|
+
|
|
146
|
+
* Concurrent/asynchronous requests
|
|
147
|
+
* Streaming requests
|
|
148
|
+
* Generate Firebase JWT for you (auto-refresh is WIP)
|
|
149
|
+
|
|
150
|
+
Please feel free to try it and use it. It's released under Apache License 2.0.
|
data/lib/rest-firebase.rb
CHANGED
|
@@ -7,7 +7,8 @@ RestFirebase = RC::Builder.client(:d, :secret, :auth) do
|
|
|
7
7
|
use RC::Timeout , 10
|
|
8
8
|
|
|
9
9
|
use RC::DefaultSite , 'https://SampleChat.firebaseIO-demo.com/'
|
|
10
|
-
use RC::DefaultHeaders, {'Accept' => 'application/json'
|
|
10
|
+
use RC::DefaultHeaders, {'Accept' => 'application/json',
|
|
11
|
+
'Content-Type' => 'application/json'}
|
|
11
12
|
use RC::DefaultQuery , nil
|
|
12
13
|
|
|
13
14
|
use RC::FollowRedirect, 1
|
|
@@ -74,9 +75,14 @@ module RestFirebase::Client
|
|
|
74
75
|
end
|
|
75
76
|
|
|
76
77
|
def request env, app=app
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
path = "#{env[REQUEST_PATH]}.json"
|
|
79
|
+
payload = if env[REQUEST_PAYLOAD]
|
|
80
|
+
{REQUEST_PAYLOAD => Json.encode(env[REQUEST_PAYLOAD])}
|
|
81
|
+
else
|
|
82
|
+
{}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
super(env.merge(REQUEST_PATH => path).merge(payload), app)
|
|
80
86
|
end
|
|
81
87
|
|
|
82
88
|
def generate_auth opts={}
|
data/rest-firebase.gemspec
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# stub: rest-firebase 0.9.
|
|
2
|
+
# stub: rest-firebase 0.9.1 ruby lib
|
|
3
3
|
|
|
4
4
|
Gem::Specification.new do |s|
|
|
5
5
|
s.name = "rest-firebase"
|
|
6
|
-
s.version = "0.9.
|
|
6
|
+
s.version = "0.9.1"
|
|
7
7
|
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
9
9
|
s.require_paths = ["lib"]
|
|
10
10
|
s.authors = [
|
|
11
11
|
"Codementor",
|
|
12
12
|
"Lin Jen-Shin (godfat)"]
|
|
13
|
-
s.date = "2014-
|
|
13
|
+
s.date = "2014-06-28"
|
|
14
14
|
s.description = "Ruby Firebase REST API client built on top of [rest-core][].\n\n[rest-core]: https://github.com/godfat/rest-core"
|
|
15
15
|
s.email = ["help@codementor.io"]
|
|
16
16
|
s.files = [
|
|
@@ -23,6 +23,7 @@ Gem::Specification.new do |s|
|
|
|
23
23
|
"README.md",
|
|
24
24
|
"Rakefile",
|
|
25
25
|
"TODO.md",
|
|
26
|
+
"doc/intro.md",
|
|
26
27
|
"lib/rest-firebase.rb",
|
|
27
28
|
"rest-firebase.gemspec",
|
|
28
29
|
"task/README.md",
|
data/test/test_api.rb
CHANGED
|
@@ -14,6 +14,9 @@ describe RestFirebase do
|
|
|
14
14
|
|
|
15
15
|
path = 'https://a.json?auth=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9%0A.eyJ2IjowLCJpYXQiOjAsImQiOm51bGx9%0A.C9JtzZhiCrsClNdAQcE7Irngr2BZJCH4x1p-IHxfrAo%3D%0A'
|
|
16
16
|
|
|
17
|
+
json = '{"status":"ok"}'
|
|
18
|
+
rbon = {'status' => 'ok'}
|
|
19
|
+
|
|
17
20
|
def firebase
|
|
18
21
|
RestFirebase.new(:secret => 'nnf')
|
|
19
22
|
end
|
|
@@ -24,12 +27,15 @@ describe RestFirebase do
|
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
should 'put {"status":"ok"}' do
|
|
27
|
-
json = '{"status":"ok"}'
|
|
28
|
-
rbon = {'status' => 'ok'}
|
|
29
30
|
stub_request(:put, path).with(:body => json).to_return(:body => json)
|
|
30
31
|
firebase.put('https://a', rbon).should.eq rbon
|
|
31
32
|
end
|
|
32
33
|
|
|
34
|
+
should 'have no payload for delete' do
|
|
35
|
+
stub_request(:delete, path).with(:body => nil).to_return(:body => json)
|
|
36
|
+
firebase.delete('https://a').should.eq rbon
|
|
37
|
+
end
|
|
38
|
+
|
|
33
39
|
should 'parse event source' do
|
|
34
40
|
stub_request(:get, path).to_return(:body => <<-SSE)
|
|
35
41
|
event: put
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rest-firebase
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Codementor
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2014-
|
|
12
|
+
date: 2014-06-28 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: rest-core
|
|
@@ -44,6 +44,7 @@ files:
|
|
|
44
44
|
- README.md
|
|
45
45
|
- Rakefile
|
|
46
46
|
- TODO.md
|
|
47
|
+
- doc/intro.md
|
|
47
48
|
- lib/rest-firebase.rb
|
|
48
49
|
- rest-firebase.gemspec
|
|
49
50
|
- task/README.md
|