ruby-mpns 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## Release 1.2.0
4
+
5
+ * NEW: unit tests has been added
6
+ * NEW: the raw notification supports multi-level hash
7
+
3
8
  ## Release 1.1.2
4
9
 
5
10
  * FIX: Fix the tile notification xml generation
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gem 'htmlentities'
3
+ gem 'builder'
4
4
 
5
5
  group :development do
6
6
  gem "shoulda", ">= 0"
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
+ builder (3.0.4)
4
5
  git (1.2.5)
5
- htmlentities (4.3.1)
6
6
  jeweler (1.8.3)
7
7
  bundler (~> 1.0)
8
8
  git (>= 1.2.5)
@@ -27,8 +27,8 @@ PLATFORMS
27
27
  ruby
28
28
 
29
29
  DEPENDENCIES
30
+ builder
30
31
  bundler (>= 1.0.0)
31
- htmlentities
32
32
  jeweler (~> 1.8.3)
33
33
  rdoc (~> 3.12)
34
34
  shoulda
data/README.md CHANGED
@@ -14,7 +14,7 @@ Or in your gemfile:
14
14
 
15
15
  ## Usage
16
16
 
17
- To use it juste use the singleton `MicrosoftPushNotificationService` like the following:
17
+ To use it just use the singleton `MicrosoftPushNotificationService` like the following:
18
18
 
19
19
  uri = "http://my-uri.com/to/the-windows-phone-i-am-targetting"
20
20
 
@@ -73,10 +73,12 @@ For general information about Push Notification on Windows Phone check the [MSDN
73
73
 
74
74
  ### Missing features
75
75
 
76
- * Add unit tests
77
- * Add support for multi-level hash for raw notifications
78
76
  * Add an ActiveRecord extension (acts_as_microsoft_pushable)
79
77
 
78
+ ### Contributors
79
+
80
+ * [Dmitry Medvinsky](https://github.com/dmedvinsky)
81
+
80
82
  ## License
81
83
 
82
84
  Copyright (c) 2012 [Nicolas VERINAUD](http://www.nverinaud.com). Released under the MIT license.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.2
1
+ 1.2.0
data/lib/ruby-mpns.rb CHANGED
@@ -1,10 +1,10 @@
1
- require "htmlentities"
2
- require "net/http"
3
- require "uri"
1
+ require 'builder'
2
+ require 'net/http'
3
+ require 'uri'
4
4
 
5
5
  module MicrosoftPushNotificationService
6
6
 
7
- def self.extended(base)
7
+ def self.extended(base)
8
8
  unless base.respond_to?(:device_uri)
9
9
  base.class.class_eval do
10
10
  attr_accessor :device_uri
@@ -12,94 +12,51 @@ module MicrosoftPushNotificationService
12
12
  end
13
13
  end
14
14
 
15
- def self.send_notification uri, type, options = {}
16
- device = Object.new
17
- device.extend MicrosoftPushNotificationService
18
- device.device_uri = uri
19
- device.send_notification type, options
15
+ def self.send_notification(uri, type, options = {})
16
+ device = Object.new
17
+ device.extend MicrosoftPushNotificationService
18
+ device.device_uri = uri
19
+ device.send_notification type, options
20
20
  end
21
21
 
22
- def send_notification type, options = {}
23
- type = safe_type_to_sym type
24
- header = self.http_header_for_type type
25
-
26
- notification = nil
27
- notification_class = nil
28
-
29
- if type == :tile
30
- notification = tile_notification_with_options options
31
- notification_class = "1"
32
- elsif type == :toast
33
- notification = toast_notification_with_options options
34
- notification_class = "2"
35
- else
36
- notification = raw_notification_with_options options
37
- notification_class = "3"
38
- end
39
-
40
- # HTTP connection
22
+ def send_notification(type, options = {})
23
+ type = safe_type_to_sym(type)
24
+ notification, notification_class = build_notification(type, options)
41
25
  uri = URI.parse(self.device_uri)
42
26
 
43
- http = Net::HTTP.new(uri.host, uri.port)
44
27
  request = Net::HTTP::Post.new(uri.request_uri)
45
- request.content_type = "text/xml"
46
-
47
- if type.to_sym != :raw
48
- request["X-WindowsPhone-Target"] = type.to_s
49
- end
50
- request["X-NotificationClass"] = notification_class
28
+ request.content_type = 'text/xml'
29
+ request['X-WindowsPhone-Target'] = type.to_s if type.to_sym != :raw
30
+ request['X-NotificationClass'] = notification_class
51
31
  request.body = notification
52
32
  request.content_length = notification.length
53
-
54
- response = Net::HTTP.start(uri.host, uri.port) do |http|
55
- http.request(request)
56
- end
57
33
 
58
- return response
34
+ Net::HTTP.start(uri.host, uri.port) { |http| http.request request }
59
35
  end
60
36
 
61
37
 
62
38
  protected
63
39
 
64
- def safe_type_to_sym type
65
- sym = nil
66
-
67
- unless type.nil?
68
- sym = type.to_sym
69
- else
70
- sym = :raw
71
- end
72
-
73
- if sym != :tile && sym != :toast
74
- sym = :raw
75
- end
76
-
77
- return sym
40
+ def safe_type_to_sym(type)
41
+ sym = type.to_sym unless type.nil?
42
+ sym = :raw unless [:tile, :toast].include?(sym)
43
+ sym
78
44
  end
79
45
 
80
- def http_header_for_type type
81
-
82
- if type == :toast || type == :tile
83
- "X-WindowsPhone-Target:#{type.to_s}"
46
+ def notification_builder_for_type(type)
47
+ case type
48
+ when :tile
49
+ :tile_notification_with_options
50
+ when :toast
51
+ :toast_notification_with_options
52
+ else
53
+ :raw_notification_with_options
84
54
  end
85
55
  end
86
56
 
87
- # Toast options :
88
- # - title : string
89
- # - content : string
90
- # - params : hash
91
- def toast_notification_with_options options = {}
92
- coder = HTMLEntities.new
93
-
94
- notification = '<?xml version="1.0" encoding="utf-8"?>'
95
- notification << '<wp:Notification xmlns:wp="WPNotification">'
96
- notification << '<wp:Toast>'
97
- notification << '<wp:Text1>' << coder.encode(options[:title]) << '</wp:Text1>'
98
- notification << '<wp:Text2>' << coder.encode(options[:content]) << '</wp:Text2>'
99
- notification << '<wp:Param>' << coder.encode(format_params(options[:params])) << '</wp:Param>'
100
- notification << '</wp:Toast>'
101
- notification << '</wp:Notification>'
102
- return notification
57
+ def build_notification(type, options = {})
58
+ notification_builder = notification_builder_for_type(type)
59
+ send(notification_builder, options)
103
60
  end
104
61
 
105
62
  # Tile options :
@@ -110,54 +67,59 @@ protected
110
67
  # - back_background_image : string, path to local image embedded in the app or accessible via HTTP (.jpg or .png, 173x137px, max 80kb)
111
68
  # - back_content : string
112
69
  # - (optional) navigation_uri : string, the exact navigation URI for the tile to update, only needed if you wish to update a secondary tile
113
- def tile_notification_with_options options = {}
114
- coder = HTMLEntities.new
115
-
116
- navigation_uri = options[:navigation_uri]
117
- tile_id = navigation_uri.nil? ? "" : 'Id="' + coder.encode(navigation_uri) + '"'
118
-
119
- notification = '<?xml version="1.0" encoding="utf-8"?>'
120
- notification << '<wp:Notification xmlns:wp="WPNotification">'
121
- notification << '<wp:Tile' << tile_id << '>'
122
- notification << '<wp:BackgroundImage>' << coder.encode(options[:background_image]) << '</wp:BackgroundImage>'
123
- notification << '<wp:Count>' << coder.encode(options[:count]) << '</wp:Count>'
124
- notification << '<wp:Title>' << coder.encode(options[:title]) << '</wp:Title>'
125
- notification << '<wp:BackBackgroundImage>' << coder.encode(options[:back_background_image]) << '</wp:BackBackgroundImage>'
126
- notification << '<wp:BackTitle>' << coder.encode(options[:back_title]) << '</wp:BackTitle>'
127
- notification << '<wp:BackContent>' << coder.encode(options[:back_content]) << '</wp:BackContent>'
128
- notification << '</wp:Tile>'
129
- notification << '</wp:Notification>'
130
- return notification
70
+ def tile_notification_with_options(options = {})
71
+ uri = options[:navigation_uri]
72
+ xml = Builder::XmlMarkup.new
73
+ xml.instruct!
74
+ xml.tag!('wp:Notification', 'xmlns:wp' => 'WPNotification') do
75
+ xml.tag!('wp:Tile', uri.nil? ? {} : {'Id' => uri}) do
76
+ xml.tag!('wp:BackgroundImage') { xml.text!(options[:background_image] || '') }
77
+ xml.tag!('wp:Count') { xml.text!(options[:count].to_s || '') }
78
+ xml.tag!('wp:Title') { xml.text!(options[:title] || '') }
79
+ xml.tag!('wp:BackBackgroundImage') { xml.text!(options[:back_background_image] || '') }
80
+ xml.tag!('wp:BackTitle') { xml.text!(options[:back_title] || '') }
81
+ xml.tag!('wp:BackContent') { xml.text!(options[:back_content] || '') }
82
+ end
83
+ end
84
+ [xml.target!, '1']
85
+ end
86
+
87
+ # Toast options :
88
+ # - title : string
89
+ # - content : string
90
+ # - params : hash
91
+ def toast_notification_with_options(options = {})
92
+ xml = Builder::XmlMarkup.new
93
+ xml.instruct!
94
+ xml.tag!('wp:Notification', 'xmlns:wp' => 'WPNotification') do
95
+ xml.tag!('wp:Toast') do
96
+ xml.tag!('wp:Text1') { xml.text!(options[:title] || '') }
97
+ xml.tag!('wp:Text2') { xml.text!(options[:content] || '') }
98
+ xml.tag!('wp:Param') { xml.text!(format_params(options[:params])) }
99
+ end
100
+ end
101
+ [xml.target!, '2']
131
102
  end
132
103
 
133
104
  # Raw options :
134
105
  # - raw values send like: <key>value</key>
135
- def raw_notification_with_options options = {}
136
- coder = HTMLEntities.new
106
+ def raw_notification_with_options(options = {})
107
+ xml = Builder::XmlMarkup.new
108
+ xml.instruct!
109
+ xml.root { build_hash(xml, options) }
110
+ [xml.target!, '3']
111
+ end
137
112
 
138
- notification = '<?xml version="1.0" encoding="utf-8"?>'
139
- notification << '<root>'
140
- options.each do |key,value|
141
- notification << '<' << coder.encode(key.to_s) << '>' << coder.encode(value.to_s) << '</' << coder.encode(key.to_s) << '>'
113
+ def build_hash(xml, options)
114
+ options.each do |k, v|
115
+ xml.tag!(k.to_s) { v.is_a?(Hash) ? build_hash(xml, v) : xml.text!(v.to_s) }
142
116
  end
143
- notification << '</root>'
144
-
145
- puts notification
146
- return notification
147
117
  end
148
118
 
149
- def format_params params = {}
150
- p = "?"
151
- length = params.length
152
- count = 0
153
- params.each do |key,value|
154
- p << key.to_s << "=" << value.to_s
155
- count += 1
156
- if count < length
157
- p << "&"
158
- end
159
- end
160
- return p
119
+ def format_params(params = {})
120
+ return '' if params.nil?
121
+ query = params.collect { |k, v| k.to_s + '=' + v.to_s } * '&'
122
+ '?' + query
161
123
  end
162
124
 
163
125
  end
data/ruby-mpns.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "ruby-mpns"
8
- s.version = "1.1.2"
8
+ s.version = "1.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Nicolas VERINAUD"]
12
- s.date = "2012-09-29"
12
+ s.date = "2013-01-12"
13
13
  s.description = "This gem provides an easy way to send push notifications to Windows Phone devices using Microsoft Push Notification Service."
14
14
  s.email = "n.verinaud@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -43,14 +43,14 @@ Gem::Specification.new do |s|
43
43
  s.specification_version = 3
44
44
 
45
45
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
46
- s.add_runtime_dependency(%q<htmlentities>, [">= 0"])
46
+ s.add_runtime_dependency(%q<builder>, [">= 0"])
47
47
  s.add_development_dependency(%q<shoulda>, [">= 0"])
48
48
  s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
49
49
  s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
50
50
  s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
51
51
  s.add_development_dependency(%q<simplecov>, [">= 0"])
52
52
  else
53
- s.add_dependency(%q<htmlentities>, [">= 0"])
53
+ s.add_dependency(%q<builder>, [">= 0"])
54
54
  s.add_dependency(%q<shoulda>, [">= 0"])
55
55
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
56
56
  s.add_dependency(%q<bundler>, [">= 1.0.0"])
@@ -58,7 +58,7 @@ Gem::Specification.new do |s|
58
58
  s.add_dependency(%q<simplecov>, [">= 0"])
59
59
  end
60
60
  else
61
- s.add_dependency(%q<htmlentities>, [">= 0"])
61
+ s.add_dependency(%q<builder>, [">= 0"])
62
62
  s.add_dependency(%q<shoulda>, [">= 0"])
63
63
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
64
64
  s.add_dependency(%q<bundler>, [">= 1.0.0"])
@@ -1,7 +1,103 @@
1
1
  require 'helper'
2
2
 
3
3
  class TestRubyMpns < Test::Unit::TestCase
4
- should "probably rename this file and start testing for real" do
5
- flunk "hey buddy, you should probably rename this file and start testing for real"
4
+ should 'safely convert type to symbol' do
5
+ mpns = Object.new.extend MicrosoftPushNotificationService
6
+ assert_equal :raw, mpns.send(:safe_type_to_sym, nil)
7
+ assert_equal :raw, mpns.send(:safe_type_to_sym, 'beer')
8
+ assert_equal :raw, mpns.send(:safe_type_to_sym, 'raw')
9
+ assert_equal :raw, mpns.send(:safe_type_to_sym, :raw)
10
+ assert_equal :tile, mpns.send(:safe_type_to_sym, 'tile')
11
+ assert_equal :tile, mpns.send(:safe_type_to_sym, :tile)
12
+ assert_equal :toast, mpns.send(:safe_type_to_sym, 'toast')
13
+ assert_equal :toast, mpns.send(:safe_type_to_sym, :toast)
14
+ end
15
+
16
+ should 'get correct notification builder based on type' do
17
+ mpns = Object.new.extend MicrosoftPushNotificationService
18
+ assert_equal :tile_notification_with_options, mpns.send(:notification_builder_for_type, :tile)
19
+ assert_equal :toast_notification_with_options, mpns.send(:notification_builder_for_type, :toast)
20
+ assert_equal :raw_notification_with_options, mpns.send(:notification_builder_for_type, :raw)
21
+ assert_equal :raw_notification_with_options, mpns.send(:notification_builder_for_type, :beer)
22
+ end
23
+
24
+ should 'return correct notification_class' do
25
+ mpns = Object.new.extend MicrosoftPushNotificationService
26
+ _, cls = mpns.send(:build_notification, :tile)
27
+ assert_equal cls, '1'
28
+ _, cls = mpns.send(:build_notification, :toast)
29
+ assert_equal cls, '2'
30
+ _, cls = mpns.send(:build_notification, :raw)
31
+ assert_equal cls, '3'
32
+ end
33
+
34
+ should 'format params like a boss' do
35
+ mpns = Object.new.extend MicrosoftPushNotificationService
36
+ q = mpns.send(:format_params, { a: 1, b: 2 })
37
+ assert_equal q, '?a=1&b=2'
38
+ q = mpns.send(:format_params, { phone: '+0987 654321', email: 'luke@example.com' })
39
+ assert_equal q, '?phone=+0987 654321&email=luke@example.com'
40
+ end
41
+
42
+ should 'make tile XML' do
43
+ mpns = Object.new.extend MicrosoftPushNotificationService
44
+ xml, _ = mpns.send(:tile_notification_with_options,
45
+ { title: 'title', count: 1337,
46
+ background_image: 'bg', back_title: 'bktitle',
47
+ back_background_image: 'bkbg',
48
+ back_content: 'bkcontent',
49
+ navigation_uri: 'uri' })
50
+ assert_equal xml, '<?xml version="1.0" encoding="UTF-8"?>' +
51
+ '<wp:Notification xmlns:wp="WPNotification">' +
52
+ '<wp:Tile Id="uri">' +
53
+ '<wp:BackgroundImage>bg</wp:BackgroundImage>' +
54
+ '<wp:Count>1337</wp:Count>' +
55
+ '<wp:Title>title</wp:Title>' +
56
+ '<wp:BackBackgroundImage>bkbg</wp:BackBackgroundImage>' +
57
+ '<wp:BackTitle>bktitle</wp:BackTitle>' +
58
+ '<wp:BackContent>bkcontent</wp:BackContent>' +
59
+ '</wp:Tile>' +
60
+ '</wp:Notification>'
61
+ end
62
+
63
+ should 'make toast XML' do
64
+ mpns = Object.new.extend MicrosoftPushNotificationService
65
+ xml, _ = mpns.send(:toast_notification_with_options,
66
+ { title: 'title', content: 'content', params: {} })
67
+ assert_equal xml, '<?xml version="1.0" encoding="UTF-8"?>' +
68
+ '<wp:Notification xmlns:wp="WPNotification">' +
69
+ '<wp:Toast>' +
70
+ '<wp:Text1>title</wp:Text1>' +
71
+ '<wp:Text2>content</wp:Text2>' +
72
+ '<wp:Param>?</wp:Param>' +
73
+ '</wp:Toast>' +
74
+ '</wp:Notification>'
75
+ end
76
+
77
+ should 'make raw XML' do
78
+ mpns = Object.new.extend MicrosoftPushNotificationService
79
+ xml, _ = mpns.send(:raw_notification_with_options,
80
+ { key1: 'val1', key2: 'val2' })
81
+ assert_equal xml, '<?xml version="1.0" encoding="UTF-8"?>' +
82
+ '<root>' +
83
+ '<key1>val1</key1>' +
84
+ '<key2>val2</key2>' +
85
+ '</root>'
86
+ end
87
+
88
+ should 'make raw XML with nested tags' do
89
+ mpns = Object.new.extend MicrosoftPushNotificationService
90
+ xml, _ = mpns.send(:raw_notification_with_options,
91
+ { key1: 'val1', key2: { subkey1: 'subval1', subkey2: { subsubkey1: 'subsubval1' } } })
92
+ assert_equal xml, '<?xml version="1.0" encoding="UTF-8"?>' +
93
+ '<root>' +
94
+ '<key1>val1</key1>' +
95
+ '<key2>' +
96
+ '<subkey1>subval1</subkey1>' +
97
+ '<subkey2>' +
98
+ '<subsubkey1>subsubval1</subsubkey1>' +
99
+ '</subkey2>' +
100
+ '</key2>' +
101
+ '</root>'
6
102
  end
7
103
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-mpns
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,10 +9,10 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-29 00:00:00.000000000 Z
12
+ date: 2013-01-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: htmlentities
15
+ name: builder
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements: