Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Initial commit.

  • Loading branch information...
commit d1670e724bcfaa8e9c14b7006d22325f1a75fa6f 0 parents
Mark Bates authored July 23, 2009
15  .gitignore
... ...
@@ -0,0 +1,15 @@
  1
+*.log
  2
+.DS_Store
  3
+doc
  4
+tmp
  5
+pkg
  6
+*.gem
  7
+*.pid
  8
+coverage
  9
+coverage.data
  10
+build/*
  11
+*.pbxuser
  12
+*.mode1v3
  13
+.svn
  14
+profile
  15
+spec/active_record/test.db
21  LICENSE
... ...
@@ -0,0 +1,21 @@
  1
+The MIT License
  2
+
  3
+Copyright (c) 2009 markbates
  4
+
  5
+Permission is hereby granted, free of charge, to any person obtaining a copy
  6
+of this software and associated documentation files (the "Software"), to deal
  7
+in the Software without restriction, including without limitation the rights
  8
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9
+copies of the Software, and to permit persons to whom the Software is
  10
+furnished to do so, subject to the following conditions:
  11
+
  12
+The above copyright notice and this permission notice shall be included in
  13
+all copies or substantial portions of the Software.
  14
+
  15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21
+THE SOFTWARE.
3  README
... ...
@@ -0,0 +1,3 @@
  1
+README
  2
+========================================================================
  3
+apn_on_rails was developed by: markbates
14  Rakefile
... ...
@@ -0,0 +1,14 @@
  1
+require 'rubygems'
  2
+require 'gemstub'
  3
+
  4
+Gemstub.test_framework = :rspec
  5
+
  6
+Gemstub.gem_spec do |s|
  7
+  # s.version = "0.0.1"
  8
+  # s.rubyforge_project = "apn_on_rails"
  9
+  # s.add_dependency('')
  10
+end
  11
+
  12
+Gemstub.rdoc do |rd|
  13
+  rd.title = "apn_on_rails"
  14
+end
3  lib/apn_on_rails.rb
... ...
@@ -0,0 +1,3 @@
  1
+Dir.glob(File.join(File.dirname(__FILE__), 'apn_on_rails', '**/*.rb')).each do |f|
  2
+  require File.expand_path(f)
  3
+end
18  lib/apn_on_rails/apn_on_rails.rb
... ...
@@ -0,0 +1,18 @@
  1
+require 'socket'
  2
+require 'openssl'
  3
+require 'configatron'
  4
+
  5
+module APN
  6
+end
  7
+
  8
+Dir.glob(File.join(File.dirname(__FILE__), 'app', 'models', 'apn', '*.rb')).sort.each do |f|
  9
+  require f
  10
+end
  11
+
  12
+%w{ models controllers helpers }.each do |dir| 
  13
+  path = File.join(File.dirname(__FILE__), 'app', dir)
  14
+  $LOAD_PATH << path 
  15
+  # puts "Adding #{path}"
  16
+  ActiveSupport::Dependencies.load_paths << path 
  17
+  ActiveSupport::Dependencies.load_once_paths.delete(path) 
  18
+end
29  lib/apn_on_rails/app/models/apn/device.rb
... ...
@@ -0,0 +1,29 @@
  1
+class APN::Device < ActiveRecord::Base
  2
+  set_table_name 'apn_devices'
  3
+  
  4
+  has_many :notifications, :class_name => 'APN::Notification'
  5
+  
  6
+  validates_uniqueness_of :token
  7
+  
  8
+  def token=(token)
  9
+    res = token.scan(/\<(.+)\>/).first
  10
+    unless res.nil? || res.empty?
  11
+      # self.token = res.first
  12
+      token = res.first
  13
+    end
  14
+    write_attribute('token', token)
  15
+  end
  16
+  
  17
+  before_validation :clean_token
  18
+  
  19
+  private
  20
+  def clean_token
  21
+    if self.token && self.token_changed?
  22
+      res = self.token.scan(/\<(.+)\>/).first
  23
+      unless res.nil? || res.empty?
  24
+        self.token = res.first
  25
+      end
  26
+    end
  27
+  end
  28
+  
  29
+end
6  lib/apn_on_rails/app/models/apn/notification.rb
... ...
@@ -0,0 +1,6 @@
  1
+class APN::Notification < ActiveRecord::Base
  2
+  set_table_name 'apn_notifications'
  3
+  
  4
+  belongs_to :device, :class_name => 'APN::Device'
  5
+  
  6
+end
94  lib/apn_on_rails/app/models/apple_push_notification.rb
... ...
@@ -0,0 +1,94 @@
  1
+# # 
  2
+# # Fabien Penso <fabien.penso@conovae.com>
  3
+# # April 6th, 2009.
  4
+# # 
  5
+# 
  6
+# require 'socket'
  7
+# require 'openssl'
  8
+# 
  9
+# class ApplePushNotification < ActiveRecord::Base
  10
+# 
  11
+#   HOST = "gateway.sandbox.push.apple.com"
  12
+#   PATH = '/'
  13
+#   PORT = 2195
  14
+#   path = File.join(RAILS_ROOT, 'config', 'apple_push_notification_development.pem')
  15
+#   CERT = File.read(path) if File.exists?(path)
  16
+#   PASSPHRASE = "foobar"
  17
+#   CACERT = File.expand_path(File.dirname(__FILE__) + "certs/ca.gateway.sandbox.push.apple.com.crt")
  18
+#   USERAGENT = 'Mozilla/5.0 (apple_push_notification Ruby on Rails 0.1)'
  19
+# 
  20
+#   # attr_accessor :paylod, :sound, :badge, :alert, :appdata
  21
+#   attr_accessible :device_token
  22
+# 
  23
+#   # validates_uniqueness_of :device_token
  24
+# 
  25
+#   # def send_notification
  26
+#   # 
  27
+#   #   ctx = OpenSSL::SSL::SSLContext.new
  28
+#   #   ctx.key = OpenSSL::PKey::RSA.new(CERT, PASSPHRASE)
  29
+#   #   ctx.cert = OpenSSL::X509::Certificate.new(CERT)
  30
+#   # 
  31
+#   #   s = TCPSocket.new(HOST, PORT)
  32
+#   #   ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
  33
+#   #   ssl.sync = true
  34
+#   #   ssl.connect
  35
+#   # 
  36
+#   #   ssl.write(self.apn_message_for_sending)
  37
+#   # 
  38
+#   #   ssl.close
  39
+#   #   s.close
  40
+#   # 
  41
+#   # rescue SocketError => error
  42
+#   #   raise "Error while sending notifications: #{error}"
  43
+#   # end
  44
+# 
  45
+#   def self.send_notifications(notifications)
  46
+#     ctx = OpenSSL::SSL::SSLContext.new
  47
+#     ctx.key = OpenSSL::PKey::RSA.new(CERT, PASSPHRASE)
  48
+#     ctx.cert = OpenSSL::X509::Certificate.new(CERT)
  49
+# 
  50
+#     s = TCPSocket.new(HOST, PORT)
  51
+#     ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
  52
+#     ssl.sync = true
  53
+#     ssl.connect
  54
+# 
  55
+#     for notif in notifications do
  56
+#       ssl.write(notif.apn_message_for_sending)
  57
+#       notif.sent_at = Time.now
  58
+#       notif.save
  59
+#     end
  60
+# 
  61
+#     ssl.close
  62
+#     s.close
  63
+#   rescue SocketError => error
  64
+#     raise "Error while sending notifications: #{error}"
  65
+#   end
  66
+# 
  67
+#   def to_apple_json
  68
+#     logger.debug "Sending #{self.apple_array.to_json}"
  69
+#     self.apple_array.to_json
  70
+#   end
  71
+# 
  72
+#   def apn_message_for_sending
  73
+#     json = self.to_apple_json
  74
+#     message = "\0\0 #{self.device_token_hexa}\0#{json.length.chr}#{json}"
  75
+#     raise "The maximum size allowed for a notification payload is 256 bytes." if message.size.to_i > 256
  76
+#     message
  77
+#   end
  78
+# 
  79
+#   def device_token_hexa
  80
+#     [self.device_token.delete(' ')].pack('H*')
  81
+#   end
  82
+# 
  83
+#   def apple_array
  84
+#     result = {}
  85
+#     result['aps'] = {}
  86
+#     result['aps']['alert'] = alert if alert
  87
+#     result['aps']['badge'] = badge.to_i if badge
  88
+#     result['aps']['sound'] = sound if sound and sound.is_a? String
  89
+#     result['aps']['sound'] = "1.aiff" if sound and sound.is_a?(TrueClass)
  90
+#     result.merge appdata if appdata
  91
+# 
  92
+#     result
  93
+#   end
  94
+# end
13  lib/apn_on_rails/db/migrate/20090723132058_create_apn_devices.rb
... ...
@@ -0,0 +1,13 @@
  1
+class CreateApnDevices < ActiveRecord::Migration
  2
+  def self.up
  3
+    create_table :apn_devices do |t|
  4
+      t.text :token, :size => 71, :null => false
  5
+
  6
+      t.timestamps
  7
+    end
  8
+  end
  9
+
  10
+  def self.down
  11
+    drop_table :apn_devices
  12
+  end
  13
+end
32  lib/apn_on_rails/db/migrate/20090723132059_create_apn_notifications.rb
... ...
@@ -0,0 +1,32 @@
  1
+class CreateApnNotifications < ActiveRecord::Migration
  2
+  
  3
+  def self.up
  4
+
  5
+    create_table :apn_notifications do |t|
  6
+      t.integer :device_id, :null => false
  7
+      t.integer :errors_nb, :default => 0 # used for storing errors from apple feedbacks
  8
+      t.string :device_language, :size => 5 # if you don't want to send localized strings
  9
+      t.text :payload
  10
+      t.string :sound
  11
+      t.integer :badge
  12
+      t.text :app_data
  13
+      t.datetime :sent_at
  14
+      t.timestamps
  15
+    end
  16
+    
  17
+    add_index :apn_notifications, :device_id
  18
+  end
  19
+
  20
+  def self.down
  21
+    drop_table :apn_notifications
  22
+  end
  23
+  
  24
+end
  25
+
  26
+# add_column :apple_push_notifications, :payload, :text
  27
+# add_column :apple_push_notifications, :sound, :string
  28
+# add_column :apple_push_notifications, :badge, :integer
  29
+# add_column :apple_push_notifications, :alert, :string
  30
+# add_column :apple_push_notifications, :appdata, :text
  31
+# add_column :apple_push_notifications, :sent_at, :datetime
  32
+# add_column :apple_push_notifications, :device_id, :integer
16  spec/active_record/setup_ar.rb
... ...
@@ -0,0 +1,16 @@
  1
+require 'rubygems'
  2
+require 'activerecord'
  3
+
  4
+logger = Logger.new(STDOUT)
  5
+logger.level = Logger::INFO
  6
+ActiveRecord::Base.logger = logger
  7
+
  8
+db_file = File.join(File.dirname(__FILE__), 'test.db')
  9
+FileUtils.rm(db_file) if File.exists?(db_file)
  10
+
  11
+ActiveRecord::Base.establish_connection({
  12
+  :adapter => 'sqlite3',
  13
+  :dbfile => db_file
  14
+})
  15
+
  16
+ActiveRecord::Migrator.up(File.join(File.dirname(__FILE__), '..', '..', 'lib', 'apn_on_rails', 'db', 'migrate'))
25  spec/apn_on_rails/app/models/apn/device_spec.rb
... ...
@@ -0,0 +1,25 @@
  1
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper.rb')
  2
+
  3
+describe APN::Device do
  4
+  
  5
+  describe 'token' do
  6
+    
  7
+    it 'should be unique' do
  8
+      device = DeviceFactory.new(:token => APN::Device.first.token)
  9
+      device.should_not be_valid
  10
+      device.errors['token'].should include('has already been taken')
  11
+      
  12
+      device = DeviceFactory.new(:token => device.token.succ)
  13
+      device.should be_valid
  14
+    end
  15
+    
  16
+    it 'should get cleansed if it contains brackets' do
  17
+      token = DeviceFactory.random_token
  18
+      device = DeviceFactory.new(:token => "<#{token}>")
  19
+      device.token.should == token
  20
+      device.token.should_not == "<#{token}>"
  21
+    end
  22
+    
  23
+  end
  24
+  
  25
+end
7  spec/apn_on_rails/app/models/apn/notification_spec.rb
... ...
@@ -0,0 +1,7 @@
  1
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper.rb')
  2
+
  3
+describe APN::Notification do
  4
+  
  5
+  it 'should do something'
  6
+  
  7
+end
10  spec/extensions/string.rb
... ...
@@ -0,0 +1,10 @@
  1
+class String
  2
+  
  3
+  def self.randomize(length = 10)
  4
+    chars = ("A".."H").to_a + ("J".."N").to_a + ("P".."T").to_a + ("W".."Z").to_a + ("3".."9").to_a
  5
+    newpass = ""
  6
+    1.upto(length) { |i| newpass << chars[rand(chars.size-1)] }
  7
+    return newpass.upcase
  8
+  end
  9
+  
  10
+end
28  spec/factories/device_factory.rb
... ...
@@ -0,0 +1,28 @@
  1
+module DeviceFactory
  2
+  
  3
+  class << self
  4
+    
  5
+    def new(options = {})
  6
+      options = {:token => DeviceFactory.random_token}.merge(options)
  7
+      return APN::Device.new(options)
  8
+    end
  9
+    
  10
+    def create(options = {})
  11
+      device = DeviceFactory.new(options)
  12
+      device.save
  13
+      return device
  14
+    end
  15
+    
  16
+    def random_token
  17
+      tok = []
  18
+      8.times do
  19
+        tok << String.randomize(8)
  20
+      end
  21
+      tok.join(' ').downcase
  22
+    end
  23
+    
  24
+  end
  25
+  
  26
+end
  27
+
  28
+DeviceFactory.create
0  spec/factories/notification_factory.rb
No changes.
2  spec/spec.opts
... ...
@@ -0,0 +1,2 @@
  1
+--format specdoc
  2
+--colour
34  spec/spec_helper.rb
... ...
@@ -0,0 +1,34 @@
  1
+require 'rubygems'
  2
+require 'spec'
  3
+
  4
+Dir.glob(File.join(File.dirname(__FILE__), 'extensions', '*.rb')).sort.each do |f|
  5
+  require f
  6
+end
  7
+
  8
+require File.join(File.dirname(__FILE__), 'active_record', 'setup_ar.rb')
  9
+
  10
+require File.join(File.dirname(__FILE__), '..', 'lib', 'apn_on_rails')
  11
+
  12
+Dir.glob(File.join(File.dirname(__FILE__), 'factories', '*.rb')).sort.each do |f|
  13
+  require f
  14
+end
  15
+
  16
+Spec::Runner.configure do |config|
  17
+  
  18
+  config.before(:all) do
  19
+    
  20
+  end
  21
+  
  22
+  config.after(:all) do
  23
+    
  24
+  end
  25
+  
  26
+  config.before(:each) do
  27
+
  28
+  end
  29
+  
  30
+  config.after(:each) do
  31
+    
  32
+  end
  33
+  
  34
+end

0 notes on commit d1670e7

Please sign in to comment.
Something went wrong with that request. Please try again.