ruby-mpns 1.1.2 → 1.2.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.
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: