pyapns2 1.0.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 (3) hide show
  1. data/lib/pyapns2/version.rb +3 -0
  2. data/lib/pyapns2.rb +134 -0
  3. metadata +147 -0
@@ -0,0 +1,3 @@
1
+ class Pyapns2
2
+ VERSION = "1.0.0"
3
+ end
data/lib/pyapns2.rb ADDED
@@ -0,0 +1,134 @@
1
+ require 'net/http'
2
+ require 'xml/libxml/xmlrpc'
3
+
4
+ class Pyapns2
5
+
6
+ require 'pyapns2/version'
7
+
8
+ class Error < StandardError; end
9
+
10
+ attr_reader :host, :port
11
+
12
+ class ProvisionedClient
13
+
14
+ attr_reader :client, :app_id
15
+
16
+ def initialize(client, app_id)
17
+ @client = client
18
+ @app_id = app_id
19
+ end
20
+
21
+ # See Pyapns2::Client#notify, with the exception this version prefills in the app_id.
22
+ def notify(token, notification = nil)
23
+ client.notify app_id, token, notification
24
+ end
25
+
26
+ # See Pyapns2::Client#feedback, with the exception this version prefills in the app_id.
27
+ def feedback
28
+ client.feedback app_id
29
+ end
30
+
31
+ def inspect
32
+ "#<#{self.class.name} server=#{host}:#{port}, app_id=#{app_id}>"
33
+ end
34
+
35
+ end
36
+
37
+ # Returns a pre-provisioned client that also automatically prepends
38
+ # the app_id automatically to all api calls, making giving a simpler interface.
39
+ def self.provision(options = {})
40
+ host, port = options.delete(:host), options.delete(:port)
41
+ host ||= "localhost"
42
+ port ||= 7077
43
+ client = new(host, port)
44
+ client.provision(options)
45
+ ProvisionedClient.new client, options[:app_id]
46
+ end
47
+
48
+ def initialize(host = 'localhost', port = 7077)
49
+ raise ArgumentError, "host must be a string" unless host.is_a?(String)
50
+ raise ArgumentError, "port must be a number" unless port.is_a?(Numeric)
51
+ @host = host
52
+ @port = port
53
+ @http = Net::HTTP.new host, port
54
+ @xmlrpc = XML::XMLRPC::Client.new @http, "/"
55
+ end
56
+
57
+ def inspect
58
+ "#<#{self.class.name} server=#{host}:#{port}>"
59
+ end
60
+
61
+ # Given a hash of options, calls provision on the pyapns server. This
62
+ # expects the following options and will raise an ArgumentError if they're
63
+ # not given:
64
+ #
65
+ # :app_id - A String name for your application
66
+ # :timeout - A number (e.g. 15) for how long to time out after when connecting to the apn server
67
+ # :env / :environment - One of production or sandbox. The type of server to connect to.
68
+ # :cert - Either a path to the certificate file or the certificate contents as a string.
69
+ def provision(options)
70
+ options[:environment] = options.delete(:env) if options.has_key?(:env)
71
+ app_id = options[:app_id]
72
+ timeout = options[:timeout]
73
+ cert = options[:cert]
74
+ env = options[:environment]
75
+ raise ArgumentError, ":app_id must be a string" unless app_id.is_a?(String) && !app_id.strip.empty?
76
+ raise ArgumentError, ":cert must be a string" unless cert.is_a?(String) && !cert.strip.empty?
77
+ raise ArgumentError, ":environment (or :env) must be one of sandbox or production" unless %w(production sandbox).include?(env)
78
+ raise ArgumentError, ":timeout must be a valid integer" unless timeout.is_a?(Numeric) && timeout >= 0
79
+ @xmlrpc.call 'provision', app_id, cert, env, timeout
80
+ true
81
+ rescue LibXML::XML::XMLRPC::RemoteCallError => e
82
+ raise Error.new e.message
83
+ end
84
+
85
+ # The main notification endpoint. Takes the app_id as the first argument, and then one
86
+ # of three sets of notification data:
87
+ #
88
+ # 1. A single token (as a string) and notification (as a dictionary)
89
+ # 2. A hash of token to notifications.
90
+ # 3. An array of tokens mapping to an array of notifications.
91
+ #
92
+ # Under the hook, it will automatically convert it to the most appropriate form before continuing.
93
+ # Will raise ArgumentError if you attempt to pass in bad information.
94
+ def notify(app_id, token, notification = nil)
95
+ if token.is_a?(Hash)
96
+ token, notification = extra_notification_info_from_hash token
97
+ end
98
+ raise ArgumentError, "Please ensure you provide an app_id" unless app_id
99
+ raise ArgumentError, "Please ensure you provide a single notification or an array of notifications" unless typed_item_of(notification, Hash)
100
+ raise ArgumentError, "Please ensure you provide device tokens or a string of tokens" unless typed_item_of(token, String)
101
+ types = [notification.is_a?(Array), token.is_a?(Array)]
102
+ if types.any? && !types.all?
103
+ raise ArgumentError, "The notifications and the strings must both be arrays if one is."
104
+ end
105
+ @xmlrpc.call 'notify', app_id, token, notification
106
+ true
107
+ rescue LibXML::XML::XMLRPC::RemoteCallError => e
108
+ raise Error.new e.message
109
+ end
110
+
111
+ # Takes an app id and returns the list of feedback from pyapns.
112
+ def feedback(app_id)
113
+ raise ArgumentError, "app_id must be provided" unless app_id
114
+ @xmlrpc.call('feedback', app_id).params
115
+ rescue LibXML::XML::XMLRPC::RemoteCallError => e
116
+ raise Error.new e.message
117
+ end
118
+
119
+ private
120
+
121
+ def extra_notification_info_from_hash(hash)
122
+ tokens, notifications = [], []
123
+ hash.each_pair do |k,v|
124
+ tokens << k
125
+ notifications << v
126
+ end
127
+ return tokens, notifications
128
+ end
129
+
130
+ def typed_item_of(value, klass)
131
+ value.is_a?(klass) || (value.is_a?(Array) && value.all? { |v| v.is_a?(klass) })
132
+ end
133
+
134
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pyapns2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Darcy Laycock
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: libxml-xmlrpc
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.4'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.4'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rr
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '1.0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '1.0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: webmock
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: vcr
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: ! 'Pyapns2 provides an alterantive, simpler client for the pyapns push
111
+ notification server, using libxml-xmlrpc to handle all of the xmlrpc.
112
+
113
+ It also is tested against Ruby 1.9'
114
+ email:
115
+ - darcy@filtersquad.com
116
+ executables: []
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - lib/pyapns2/version.rb
121
+ - lib/pyapns2.rb
122
+ homepage: http://github.com/filtersquad
123
+ licenses: []
124
+ post_install_message:
125
+ rdoc_options: []
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ none: false
136
+ requirements:
137
+ - - ! '>='
138
+ - !ruby/object:Gem::Version
139
+ version: 1.3.6
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 1.8.24
143
+ signing_key:
144
+ specification_version: 3
145
+ summary: An alternative ruby client for the pyapns push notification server with an
146
+ emphasis on Ruby 1.9 support.
147
+ test_files: []