houston 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
@@ -0,0 +1,18 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ houston (0.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ rake (0.9.2.2)
10
+ rspec (0.6.4)
11
+
12
+ PLATFORMS
13
+ ruby
14
+
15
+ DEPENDENCIES
16
+ houston!
17
+ rake (~> 0.9.2)
18
+ rspec (~> 0.6.1)
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2012 Mattt Thompson (http://mattt.me/)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,68 @@
1
+ # Houston
2
+ **Apple Push Notifications. No Dirigible Required**
3
+
4
+ > Houston, We Have Liftoff!
5
+
6
+ Push Notifications don't have to be difficult. And now they aren't.
7
+
8
+ Houston is a simple gem for sending Apple Push Notifications. Pass your credentials, construct your message, and send it.
9
+
10
+ In a production application, you will probably want to schedule or queue notifications into a background job. Whether you're using [queue_classic](https://github.com/ryandotsmith/queue_classic), [resque](https://github.com/defunkt/resque), or rolling you own infrastructure, integrating Houston couldn't be simpler.
11
+
12
+ Another caveat is that Houston doesn't manage device tokens for you. Since infrastructures can vary dramatically for these kinds of things, being agnostic and not forcing any conventions here is more a feature than a bug. That said, a simple web service adapter, similar to [Rack::CoreData](https://github.com/mattt/rack-core-data) may be in the cards.
13
+
14
+ ## Usage
15
+
16
+ ```ruby
17
+ # Environment variables are automatically read, or can be overridden by any specified options
18
+ APN = Houston::Client.new
19
+ APN.certificate = File.read("/path/to/apple_push_notification.pem")
20
+
21
+ # An example of the token sent back when a device registers for notifications
22
+ token = "<42e33061 9695b86b 59e5452b 061774c9 726eb8ec 22982c4b 558e34e2 784adee0>"
23
+
24
+ # Create a notification that alerts a message to the user, plays a sound, and sets the badge on the app
25
+ notification = Houston::Notification.new(device: token)
26
+ notification.alert = "Hello, World!"
27
+
28
+ # Notifications can also change the badge count, have a custom sound, or pass along arbitrary data.
29
+ notification.badge = 57
30
+ notification.sound = "sosumi.aiff"
31
+ notification.custom_data = {foo: "bar"}
32
+
33
+ # And... sent! That's all it takes.
34
+ APN.push(notification)
35
+ ```
36
+
37
+ ## Converting Your Certificate
38
+
39
+ > These instructions come from the [APN on Rails](https://github.com/PRX/apn_on_rails) project, which is another great option for sending push notifications.
40
+
41
+ Once you have the certificate from Apple for your application, export your key
42
+ and the apple certificate as p12 files. Here is a quick walkthrough on how to do this:
43
+
44
+ 1. Click the disclosure arrow next to your certificate in Keychain Access and select the certificate and the key.
45
+ 2. Right click and choose `Export 2 items…`.
46
+ 3. Choose the p12 format from the drop down and name it `cert.p12`.
47
+
48
+ Now covert the p12 file to a pem file:
49
+
50
+ ```
51
+ $ openssl pkcs12 -in cert.p12 -out apple_push_notification_production.pem -nodes -clcerts
52
+ ```
53
+
54
+ If you are using a development certificate, then change the name to apple_push_notification_development.pem instead.
55
+
56
+ Store the contents of the certificate files on the app model for the app you want to send notifications to.
57
+
58
+ ## Contact
59
+
60
+ Mattt Thompson
61
+
62
+ - http://github.com/mattt
63
+ - http://twitter.com/mattt
64
+ - m@mattt.me
65
+
66
+ ## License
67
+
68
+ Houston is available under the MIT license. See the LICENSE file for more info.
@@ -0,0 +1,11 @@
1
+ require "bundler"
2
+ Bundler.setup
3
+
4
+ gemspec = eval(File.read("houston.gemspec"))
5
+
6
+ task :build => "#{gemspec.full_name}.gem"
7
+
8
+ file "#{gemspec.full_name}.gem" => gemspec.files + ["houston.gemspec"] do
9
+ system "gem build houston.gemspec"
10
+ system "gem install houston-#{Houston::Verson}.gem"
11
+ end
@@ -0,0 +1,136 @@
1
+ Bag Attributes
2
+ friendlyName: Apple Development IOS Push Services: com.heroku.heroku
3
+ localKeyID: 60 1B C7 72 74 46 A1 D4 9D 74 2D DE D6 E1 48 3C 0C 4E 35 56
4
+ subject=/UID=com.heroku.heroku/CN=Apple Development IOS Push Services: com.heroku.heroku/C=US
5
+ issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
6
+ -----BEGIN CERTIFICATE-----
7
+ MIIFcTCCBFmgAwIBAgIIR6byv0WLtiowDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV
8
+ BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
9
+ ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
10
+ aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
11
+ HhcNMTIwOTA3MTY1NjMwWhcNMTMwOTA3MTY1NjMwWjBxMSEwHwYKCZImiZPyLGQB
12
+ AQwRY29tLmhlcm9rdS5oZXJva3UxPzA9BgNVBAMMNkFwcGxlIERldmVsb3BtZW50
13
+ IElPUyBQdXNoIFNlcnZpY2VzOiBjb20uaGVyb2t1Lmhlcm9rdTELMAkGA1UEBhMC
14
+ VVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7YxpLzpwjbY7RaSyB
15
+ oPOqqDxUJxkF9MUHOu6vBkVeaAzL1OEiwf43E+EieWDRnxX8tsBkJVlmtG+dteer
16
+ 8nIfOKL4eoth9aleLwCIOyfgus6DTQI30MsR+HwyKF2ZD9ububKdRdoBXrLzVCUE
17
+ FHgd1a7qZZeQlExfrffV6cQItFd6V+1I40YvMPzktz42L4bvomjscRlpehxZh5aU
18
+ Le9Y2GLcwOslNqswRt1yRezDlcTqpopMg4JTzyUkTQ0YhxO0y7WBoSd4vNuCeSQp
19
+ 9opTA7Jrsbnj3Ii93PnBgHa5QdJUSs8BNHuQnUJmISCws/Ka575b7/Ig5qbLE2AN
20
+ Vg/HAgMBAAGjggHlMIIB4TAdBgNVHQ4EFgQUYBvHcnRGodSddC3e1uFIPAxONVYw
21
+ CQYDVR0TBAIwADAfBgNVHSMEGDAWgBSIJxcJqbYYYIvs67r2R1nFUlSjtzCCAQ8G
22
+ A1UdIASCAQYwggECMIH/BgkqhkiG92NkBQEwgfEwgcMGCCsGAQUFBwICMIG2DIGz
23
+ UmVsaWFuY2Ugb24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1l
24
+ cyBhY2NlcHRhbmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVy
25
+ bXMgYW5kIGNvbmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5k
26
+ IGNlcnRpZmljYXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wKQYIKwYBBQUHAgEW
27
+ HWh0dHA6Ly93d3cuYXBwbGUuY29tL2FwcGxlY2EvME0GA1UdHwRGMEQwQqBAoD6G
28
+ PGh0dHA6Ly9kZXZlbG9wZXIuYXBwbGUuY29tL2NlcnRpZmljYXRpb25hdXRob3Jp
29
+ dHkvd3dkcmNhLmNybDALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwIw
30
+ EAYKKoZIhvdjZAYDAQQCBQAwDQYJKoZIhvcNAQEFBQADggEBAEbviaLfJahlvzGr
31
+ GzTyHf2HXDK+nqcszwbKBQm0Yf6lnITmHFcbK0QCVfrkXiEQUqg/3hCf3HcX2rQQ
32
+ /Wa8mnyHv42Lb1p1L7pJ1iWr5dB06pw7EeZxAt14dmhUhrspUq3pbxmVxc1wxBda
33
+ G5EcS16oPoqos2xU/hnCd5Te8zH2ks3wFxlzOWvSFQPFloU7HSHgtHGoAch3pfyb
34
+ f3OoO94/AQIUk2Vkv15856RrPuJSrz+krQm3zUBNhn6gMiNwR1drtKY7UvRBgia1
35
+ fbBwY4J0pXRl/FIEcIH46KCP5cp9gagMo7jFPnNOArPxPc1hi1GsmUG97hRH1KA7
36
+ QmGB4kM=
37
+ -----END CERTIFICATE-----
38
+ Bag Attributes
39
+ friendlyName: Apple Development IOS Push Services: com.heroku.heroku
40
+ localKeyID: 60 1B C7 72 74 46 A1 D4 9D 74 2D DE D6 E1 48 3C 0C 4E 35 56
41
+ subject=/UID=com.heroku.heroku/CN=Apple Development IOS Push Services: com.heroku.heroku/C=US
42
+ issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
43
+ -----BEGIN CERTIFICATE-----
44
+ MIIFcTCCBFmgAwIBAgIIR6byv0WLtiowDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV
45
+ BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
46
+ ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
47
+ aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
48
+ HhcNMTIwOTA3MTY1NjMwWhcNMTMwOTA3MTY1NjMwWjBxMSEwHwYKCZImiZPyLGQB
49
+ AQwRY29tLmhlcm9rdS5oZXJva3UxPzA9BgNVBAMMNkFwcGxlIERldmVsb3BtZW50
50
+ IElPUyBQdXNoIFNlcnZpY2VzOiBjb20uaGVyb2t1Lmhlcm9rdTELMAkGA1UEBhMC
51
+ VVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7YxpLzpwjbY7RaSyB
52
+ oPOqqDxUJxkF9MUHOu6vBkVeaAzL1OEiwf43E+EieWDRnxX8tsBkJVlmtG+dteer
53
+ 8nIfOKL4eoth9aleLwCIOyfgus6DTQI30MsR+HwyKF2ZD9ububKdRdoBXrLzVCUE
54
+ FHgd1a7qZZeQlExfrffV6cQItFd6V+1I40YvMPzktz42L4bvomjscRlpehxZh5aU
55
+ Le9Y2GLcwOslNqswRt1yRezDlcTqpopMg4JTzyUkTQ0YhxO0y7WBoSd4vNuCeSQp
56
+ 9opTA7Jrsbnj3Ii93PnBgHa5QdJUSs8BNHuQnUJmISCws/Ka575b7/Ig5qbLE2AN
57
+ Vg/HAgMBAAGjggHlMIIB4TAdBgNVHQ4EFgQUYBvHcnRGodSddC3e1uFIPAxONVYw
58
+ CQYDVR0TBAIwADAfBgNVHSMEGDAWgBSIJxcJqbYYYIvs67r2R1nFUlSjtzCCAQ8G
59
+ A1UdIASCAQYwggECMIH/BgkqhkiG92NkBQEwgfEwgcMGCCsGAQUFBwICMIG2DIGz
60
+ UmVsaWFuY2Ugb24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1l
61
+ cyBhY2NlcHRhbmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVy
62
+ bXMgYW5kIGNvbmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5k
63
+ IGNlcnRpZmljYXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wKQYIKwYBBQUHAgEW
64
+ HWh0dHA6Ly93d3cuYXBwbGUuY29tL2FwcGxlY2EvME0GA1UdHwRGMEQwQqBAoD6G
65
+ PGh0dHA6Ly9kZXZlbG9wZXIuYXBwbGUuY29tL2NlcnRpZmljYXRpb25hdXRob3Jp
66
+ dHkvd3dkcmNhLmNybDALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwIw
67
+ EAYKKoZIhvdjZAYDAQQCBQAwDQYJKoZIhvcNAQEFBQADggEBAEbviaLfJahlvzGr
68
+ GzTyHf2HXDK+nqcszwbKBQm0Yf6lnITmHFcbK0QCVfrkXiEQUqg/3hCf3HcX2rQQ
69
+ /Wa8mnyHv42Lb1p1L7pJ1iWr5dB06pw7EeZxAt14dmhUhrspUq3pbxmVxc1wxBda
70
+ G5EcS16oPoqos2xU/hnCd5Te8zH2ks3wFxlzOWvSFQPFloU7HSHgtHGoAch3pfyb
71
+ f3OoO94/AQIUk2Vkv15856RrPuJSrz+krQm3zUBNhn6gMiNwR1drtKY7UvRBgia1
72
+ fbBwY4J0pXRl/FIEcIH46KCP5cp9gagMo7jFPnNOArPxPc1hi1GsmUG97hRH1KA7
73
+ QmGB4kM=
74
+ -----END CERTIFICATE-----
75
+ Bag Attributes
76
+ friendlyName: Mattt Thompson
77
+ localKeyID: 60 1B C7 72 74 46 A1 D4 9D 74 2D DE D6 E1 48 3C 0C 4E 35 56
78
+ Key Attributes: <No Attributes>
79
+ -----BEGIN RSA PRIVATE KEY-----
80
+ MIIEogIBAAKCAQEAu2MaS86cI22O0WksgaDzqqg8VCcZBfTFBzrurwZFXmgMy9Th
81
+ IsH+NxPhInlg0Z8V/LbAZCVZZrRvnbXnq/JyHzii+HqLYfWpXi8AiDsn4LrOg00C
82
+ N9DLEfh8MihdmQ/bm7mynUXaAV6y81QlBBR4HdWu6mWXkJRMX6331enECLRXelft
83
+ SONGLzD85Lc+Ni+G76Jo7HEZaXocWYeWlC3vWNhi3MDrJTarMEbdckXsw5XE6qaK
84
+ TIOCU88lJE0NGIcTtMu1gaEneLzbgnkkKfaKUwOya7G549yIvdz5wYB2uUHSVErP
85
+ ATR7kJ1CZiEgsLPymue+W+/yIOamyxNgDVYPxwIDAQABAoIBAG0ODxbBRrODplOP
86
+ bxx+DHFsebfMDU8fFa/Qh2qLLsHzskbKPMBawFVTl3nVxBgbCtPJlAx0hCQ2rKLP
87
+ Y+X5Mpui/Y242/eF3iz3E8s1Ck9KyLc3kSvkstphj/4s6dQTm450s1O+fFLBx0m/
88
+ 1c8bDpDmdGbr+xpkhFJGz+7JnpIlSqyg3f3MSVa85WdyWwxY2nVOkZqyisUO/Ec2
89
+ 4UjRe1qx6g6SqKGSlWRDDYb74akCua8DtPVNpmRVHr/hOjo3nTOjEcozxt4zsu+D
90
+ S6HxaDPcfGbffgPNZZag/jK/a2RYvOhixC0vgi/ApiYgrOUW9Zy5Z5wxA/XvWkto
91
+ mM96HlECgYEA78W62cbbKB6//O5VrhAR436pA4z7JMJa99C/MuB5Ntq2uKS09djC
92
+ KMfAQ8uS98naAfF6r6WTKjZrlCKtFrxGAWZ1ky//ODgTV0BKUFXjwJz+mwyl09VX
93
+ Ltb46TZkBrZPkMg7Tg+zK9iY4XO2W0UGyxgrnHequlnSyOaY02vkUbkCgYEAyBHA
94
+ YjAA1YV9QvjcAyjxMXwmPulllQYY4oqMBNnU0OVoAfewFy3QTVaKoPeI7d5JJo9h
95
+ wEtUisNg39hCY/hT8wpE1aPPSWMoYMceNS0/d+QE1aAc2pBcThhvOyjDfXGUC6to
96
+ Uah5gndNPMjWejwtTq8FJ/Ec4oQoUQYiEjRULX8CgYBokN3k/bEz9P0bbEFXcNCs
97
+ Wt+Qmdoo9M3BR3sqz/Rs9MM9ES+OC+LY8AwsRMqqX5BAyrQLh/T/Ms6Fwv2jbaZG
98
+ FA3wi70NIn9kzLfcAykZJh5vNfvXvzGPTtcstbi3qKhYZspeR8CGvaYmpFUBxr+o
99
+ sLjMOu/s5yFi1hYOFf5zmQKBgCOZLP8LhYp4hxI8j4EsbsYcTf3Rr5Z1kEX22vUe
100
+ AUTuHLSE6tsk9rRcGqyLfty8OY3X40/A15kdLIaHx0Bcdit/f0H4NLDdKNkXdOOH
101
+ WoytkMKmC5xEJXhr1pbTKP2QEQSGijhFXuF88sxG/Aqh3HnsoGE8AKS5WScZjURp
102
+ 9TRnAoGAUfUJA15pVeT/wLbamPu1g0E8F/EdpI5GKJJHiHfCz++A1Spj7hMP2mSc
103
+ y+izt5A5PUsrD3hlYDd9z6+2O3rIVApcRytIegbhaXPSnYQMAiF+BN/Kd/D8tOa7
104
+ FrF/ldCMG+Kj1CnwLZkqLPPZmJYKa7HGuhYFfmQG1k9jIUdNsMw=
105
+ -----END RSA PRIVATE KEY-----
106
+ Bag Attributes
107
+ friendlyName: Mattt Thompson
108
+ localKeyID: 60 1B C7 72 74 46 A1 D4 9D 74 2D DE D6 E1 48 3C 0C 4E 35 56
109
+ Key Attributes: <No Attributes>
110
+ -----BEGIN RSA PRIVATE KEY-----
111
+ MIIEogIBAAKCAQEAu2MaS86cI22O0WksgaDzqqg8VCcZBfTFBzrurwZFXmgMy9Th
112
+ IsH+NxPhInlg0Z8V/LbAZCVZZrRvnbXnq/JyHzii+HqLYfWpXi8AiDsn4LrOg00C
113
+ N9DLEfh8MihdmQ/bm7mynUXaAV6y81QlBBR4HdWu6mWXkJRMX6331enECLRXelft
114
+ SONGLzD85Lc+Ni+G76Jo7HEZaXocWYeWlC3vWNhi3MDrJTarMEbdckXsw5XE6qaK
115
+ TIOCU88lJE0NGIcTtMu1gaEneLzbgnkkKfaKUwOya7G549yIvdz5wYB2uUHSVErP
116
+ ATR7kJ1CZiEgsLPymue+W+/yIOamyxNgDVYPxwIDAQABAoIBAG0ODxbBRrODplOP
117
+ bxx+DHFsebfMDU8fFa/Qh2qLLsHzskbKPMBawFVTl3nVxBgbCtPJlAx0hCQ2rKLP
118
+ Y+X5Mpui/Y242/eF3iz3E8s1Ck9KyLc3kSvkstphj/4s6dQTm450s1O+fFLBx0m/
119
+ 1c8bDpDmdGbr+xpkhFJGz+7JnpIlSqyg3f3MSVa85WdyWwxY2nVOkZqyisUO/Ec2
120
+ 4UjRe1qx6g6SqKGSlWRDDYb74akCua8DtPVNpmRVHr/hOjo3nTOjEcozxt4zsu+D
121
+ S6HxaDPcfGbffgPNZZag/jK/a2RYvOhixC0vgi/ApiYgrOUW9Zy5Z5wxA/XvWkto
122
+ mM96HlECgYEA78W62cbbKB6//O5VrhAR436pA4z7JMJa99C/MuB5Ntq2uKS09djC
123
+ KMfAQ8uS98naAfF6r6WTKjZrlCKtFrxGAWZ1ky//ODgTV0BKUFXjwJz+mwyl09VX
124
+ Ltb46TZkBrZPkMg7Tg+zK9iY4XO2W0UGyxgrnHequlnSyOaY02vkUbkCgYEAyBHA
125
+ YjAA1YV9QvjcAyjxMXwmPulllQYY4oqMBNnU0OVoAfewFy3QTVaKoPeI7d5JJo9h
126
+ wEtUisNg39hCY/hT8wpE1aPPSWMoYMceNS0/d+QE1aAc2pBcThhvOyjDfXGUC6to
127
+ Uah5gndNPMjWejwtTq8FJ/Ec4oQoUQYiEjRULX8CgYBokN3k/bEz9P0bbEFXcNCs
128
+ Wt+Qmdoo9M3BR3sqz/Rs9MM9ES+OC+LY8AwsRMqqX5BAyrQLh/T/Ms6Fwv2jbaZG
129
+ FA3wi70NIn9kzLfcAykZJh5vNfvXvzGPTtcstbi3qKhYZspeR8CGvaYmpFUBxr+o
130
+ sLjMOu/s5yFi1hYOFf5zmQKBgCOZLP8LhYp4hxI8j4EsbsYcTf3Rr5Z1kEX22vUe
131
+ AUTuHLSE6tsk9rRcGqyLfty8OY3X40/A15kdLIaHx0Bcdit/f0H4NLDdKNkXdOOH
132
+ WoytkMKmC5xEJXhr1pbTKP2QEQSGijhFXuF88sxG/Aqh3HnsoGE8AKS5WScZjURp
133
+ 9TRnAoGAUfUJA15pVeT/wLbamPu1g0E8F/EdpI5GKJJHiHfCz++A1Spj7hMP2mSc
134
+ y+izt5A5PUsrD3hlYDd9z6+2O3rIVApcRytIegbhaXPSnYQMAiF+BN/Kd/D8tOa7
135
+ FrF/ldCMG+Kj1CnwLZkqLPPZmJYKa7HGuhYFfmQG1k9jIUdNsMw=
136
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "houston"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "houston"
7
+ s.authors = ["Mattt Thompson"]
8
+ s.email = "m@mattt.me"
9
+ s.homepage = "http://github.com/mattt/houston"
10
+ s.version = Houston::Version
11
+ s.platform = Gem::Platform::RUBY
12
+ s.summary = "iOS Push Notifications"
13
+ s.description = ""
14
+
15
+ s.add_development_dependency "rspec", "~> 0.6.1"
16
+ s.add_development_dependency "rake", "~> 0.9.2"
17
+
18
+ s.files = Dir["./**/*"].reject { |file| file =~ /\.\/(bin|log|pkg|script|spec|test|vendor)/ }
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+ end
@@ -0,0 +1,147 @@
1
+ require 'uri'
2
+ require 'socket'
3
+ require 'openssl'
4
+ require 'json'
5
+
6
+ module Houston
7
+ Version = "0.0.1"
8
+
9
+ APPLE_PRODUCTION_GATEWAY_URI = "apn://gateway.push.apple.com:2195"
10
+ APPLE_PRODUCTION_FEEDBACK_URI = "apn://feedback.push.apple.com:2196"
11
+
12
+ APPLE_DEVELOPMENT_GATEWAY_URI = "apn://gateway.sandbox.push.apple.com:2195"
13
+ APPLE_DEVELOPMENT_FEEDBACK_URI = "apn://feedback.push.apple.com:2196"
14
+
15
+ class Client
16
+ attr_accessor :gateway_uri, :feedback_uri, :certificate, :passphrase
17
+
18
+ def initialize
19
+ @gateway_uri = ENV['APN_GATEWAY_URI']
20
+ @feedback_uri = ENV['APN_FEEDBACK_URI']
21
+ @certificate = ENV['APN_CERTIFICATE']
22
+ @passphrase = ENV['APN_CERTIFICATE_PASSPHRASE']
23
+ end
24
+
25
+ def self.development
26
+ client = self.new
27
+ client.gateway_uri = APPLE_DEVELOPMENT_GATEWAY_URI
28
+ client.feedback_uri = APPLE_DEVELOPMENT_FEEDBACK_URI
29
+ client
30
+ end
31
+
32
+ def self.production
33
+ client = self.new
34
+ client.gateway_uri = APPLE_PRODUCTION_GATEWAY_URI
35
+ client.feedback_uri = APPLE_PRODUCTION_FEEDBACK_URI
36
+ client
37
+ end
38
+
39
+ def push(*notifications)
40
+ Connection.open(connection_options_for_endpoint(:gateway)) do |connection, socket|
41
+ notifications.each do |notification|
42
+ next if notification.sent?
43
+
44
+ connection.write(notification.message)
45
+ notification.mark_as_sent!
46
+ end
47
+ end
48
+ end
49
+
50
+ def devices
51
+ devices = []
52
+
53
+ Connection.open(connection_options_for_endpoint(:feedback)) do |connection, socket|
54
+ while line = connection.read(38)
55
+ feedback = line.unpack('N1n1H140')
56
+ token = feedback[2].scan(/.{0,8}/).join(' ').strip
57
+ devices << token if token
58
+ end
59
+ end
60
+
61
+ devices
62
+ end
63
+
64
+ private
65
+
66
+ def connection_options_for_endpoint(endpoint = :gateway)
67
+ uri = case endpoint
68
+ when :gateway then URI(@gateway_uri)
69
+ when :feedback then URI(@feedback_uri)
70
+ else
71
+ raise ArgumentError
72
+ end
73
+
74
+ {
75
+ certificate: @certificate,
76
+ passphrase: @passphrase,
77
+ host: uri.host,
78
+ port: uri.port
79
+ }
80
+ end
81
+ end
82
+
83
+ class Notification
84
+ attr_accessor :device, :alert, :badge, :sound, :custom_data
85
+ attr_reader :sent_at
86
+
87
+ def initialize(options = {})
88
+ @device = options.delete(:device)
89
+ @alert = options.delete(:alert)
90
+ @badge = options.delete(:badge)
91
+ @sound = options.delete(:sound)
92
+
93
+ @custom_data = options
94
+ end
95
+
96
+ def payload
97
+ json = {}.merge(@custom_data || {})
98
+ json['aps'] = {}
99
+ json['aps']['alert'] = @alert
100
+ json['aps']['badge'] = @badge.to_i rescue 0
101
+ json['aps']['sound'] = @sound
102
+
103
+ json
104
+ end
105
+
106
+ def message
107
+ json = payload.to_json
108
+
109
+ "\0\0 #{[@device.gsub(/[<\s>]/, '')].pack('H*')}\0#{json.length.chr}#{json}"
110
+ end
111
+
112
+ def mark_as_sent!
113
+ @sent_at = Time.now
114
+ end
115
+
116
+ def sent?
117
+ !!@sent_at
118
+ end
119
+ end
120
+
121
+ class Connection
122
+ class << self
123
+ def open(options = {})
124
+ return unless block_given?
125
+
126
+ [:certificate, :passphrase, :host, :port].each do |option|
127
+ raise ArgumentError, "Missing connection parameter: #{option}" unless option
128
+ end
129
+
130
+ socket = TCPSocket.new(options[:host], options[:port])
131
+
132
+ context = OpenSSL::SSL::SSLContext.new
133
+ context.key = OpenSSL::PKey::RSA.new(options[:certificate], options[:passphrase])
134
+ context.cert = OpenSSL::X509::Certificate.new(options[:certificate])
135
+
136
+ ssl = OpenSSL::SSL::SSLSocket.new(socket, context)
137
+ ssl.sync = true
138
+ ssl.connect
139
+
140
+ yield ssl, socket
141
+
142
+ ssl.close
143
+ socket.close
144
+ end
145
+ end
146
+ end
147
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: houston
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mattt Thompson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-07 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70324475112900 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.6.1
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70324475112900
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70324475112400 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 0.9.2
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70324475112400
36
+ description: ''
37
+ email: m@mattt.me
38
+ executables: []
39
+ extensions: []
40
+ extra_rdoc_files: []
41
+ files:
42
+ - ./apple_push_notification_production.pem
43
+ - ./Gemfile
44
+ - ./Gemfile.lock
45
+ - ./houston.gemspec
46
+ - ./lib/houston.rb
47
+ - ./LICENSE
48
+ - ./Rakefile
49
+ - ./README.md
50
+ homepage: http://github.com/mattt/houston
51
+ licenses: []
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ segments:
63
+ - 0
64
+ hash: 1752936197030512533
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ segments:
72
+ - 0
73
+ hash: 1752936197030512533
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 1.8.15
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: iOS Push Notifications
80
+ test_files: []