lib/pubnub.rb in pubnub-3.3.0.7 vs lib/pubnub.rb in pubnub-3.4

- old
+ new

@@ -1,405 +1,61 @@ ## www.pubnub.com - PubNub realtime push service in the cloud. ## http://www.pubnub.com/blog/ruby-push-api - Ruby Push API Blog ## PubNub Real Time Push APIs and Notifications Framework -## Copyright (c) 2012 PubNub +## Copyright (c) 2013 PubNub ## http://www.pubnub.com/ ## ----------------------------------- -## PubNub 3.3 Real-time Push Cloud API +## PubNub 3.4 Real-time Push Cloud API ## ----------------------------------- +require 'uuid' +require 'json' require 'base64' require 'open-uri' require 'uri' - -require 'pubnub_crypto' -require 'pubnub_request' - +require 'openssl' require 'eventmachine' require 'em-http-request' -require 'yajl' -require 'json' -require 'uuid' -require 'active_support/core_ext/hash/indifferent_access' -require 'active_support/core_ext/string/inflections' -require 'active_support/core_ext/object/try' -require 'active_support/core_ext/object/blank' +require 'version.rb' -class Pubnub +require 'pubnub/client.rb' +require 'pubnub/request.rb' +require 'pubnub/configuration.rb' +require 'pubnub/error.rb' +require 'pubnub/crypto.rb' - SUCCESS_RESPONSE = 200 - MSG_TOO_LARGE_RESPONSE = 400 - - TIMEOUT_BAD_RESPONSE_CODE = 1 - TIMEOUT_BAD_JSON_RESPONSE = 0.5 - TIMEOUT_GENERAL_ERROR = 1 - TIMEOUT_SUBSCRIBE = 310 - TIMEOUT_NON_SUBSCRIBE = 5 - - PUBNUB_LOGGER = Logger.new("#{Dir.tmpdir}/pubnubError.log", 10, 10000000) - PUBNUB_LOGGER.level = Logger::DEBUG - - class PresenceError < RuntimeError; +class Object + def blank? + respond_to?(:empty?) ? empty? : !self end - class PublishError < RuntimeError; - end - class SubscribeError < RuntimeError; - end - class InitError < RuntimeError; - end - attr_accessor :publish_key, :subscribe_key, :secret_key, :cipher_key, :ssl, :channel, :origin, :session_uuid - - ORIGIN_HOST = 'pubsub.pubnub.com' - #ORIGIN_HOST = 'test.pubnub.com' - - def initialize(*args) - - if args.size == 5 # passing in named parameters - - @publish_key = args[0].to_s - @subscribe_key = args[1].to_s - @secret_key = args[2].to_s - @cipher_key = args[3].to_s - @ssl = args[4] - - elsif args.size == 1 && args[0].class == Hash # passing in an options hash - - options_hash = HashWithIndifferentAccess.new(args[0]) - @publish_key = options_hash[:publish_key].blank? ? nil : options_hash[:publish_key].to_s - @subscribe_key = options_hash[:subscribe_key].blank? ? nil : options_hash[:subscribe_key].to_s - @secret_key = options_hash[:secret_key].blank? ? nil : options_hash[:secret_key].to_s - @cipher_key = options_hash[:cipher_key].blank? ? nil : options_hash[:cipher_key].to_s - @ssl = options_hash[:ssl].blank? ? false : true - - else - raise(InitError, "Initialize with either a hash of options, or exactly 5 named parameters.") - end - - @session_uuid = uuid - verify_init + def present? + !blank? end +end - def verify_init - # publish_key and cipher_key are both optional. - raise(InitError, "subscribe_key is a mandatory parameter.") if @subscribe_key.blank? - end - - - def publish(options) - options = HashWithIndifferentAccess.new(options) - publish_request = PubnubRequest.new(:operation => :publish) - - #TODO: refactor into initializer code on request instantiation - - publish_request.ssl = @ssl - publish_request.set_origin(options) - publish_request.set_channel(options) - publish_request.set_callback(options) - publish_request.set_cipher_key(options, self.cipher_key) - publish_request.set_message(options, self.cipher_key) - publish_request.set_publish_key(options, self.publish_key) - publish_request.set_subscribe_key(options, self.subscribe_key) - publish_request.set_secret_key(options, self.secret_key) - - - publish_request.format_url! - - check_for_em publish_request - end - - def subscribe(options) - options = HashWithIndifferentAccess.new(options) - - operation = options[:operation].nil? ? :subscribe : :presence - - subscribe_request = PubnubRequest.new(:operation => operation, :session_uuid => @session_uuid) - - #TODO: refactor into initializer code on request instantiation - - subscribe_request.ssl = @ssl - subscribe_request.set_origin(options) - subscribe_request.set_channel(options) - subscribe_request.set_callback(options) - subscribe_request.set_cipher_key(options, self.cipher_key) unless subscribe_request.operation == "presence" - - subscribe_request.set_subscribe_key(options, self.subscribe_key) - - format_url_options = options[:override_timetoken].present? ? options[:override_timetoken] : nil - subscribe_request.format_url!(format_url_options) - - check_for_em subscribe_request - - end - - def presence(options) - usage_error = "presence() requires :channel and :callback options." - if options.class != Hash - raise(ArgumentError, usage_error) - end - - options = HashWithIndifferentAccess.new(options) unless (options == nil) - - unless options[:channel] && options[:callback] - raise(ArgumentError, usage_error) - end - - subscribe(options.merge(:operation => "presence")) - - end - - - def here_now(options = nil) - usage_error = "here_now() requires :channel and :callback options." - if options.class != Hash - raise(ArgumentError, usage_error) - end - - options = HashWithIndifferentAccess.new(options) unless (options == nil) - - unless options[:channel] && options[:callback] - raise(ArgumentError, usage_error) - end - - here_now_request = PubnubRequest.new(:operation => :here_now) - - here_now_request.ssl = @ssl - here_now_request.set_channel(options) - here_now_request.set_callback(options) - - here_now_request.set_subscribe_key(options, self.subscribe_key) - - here_now_request.format_url! - check_for_em here_now_request - - end - - def detailed_history(options = nil) - usage_error = "detailed_history() requires :channel, :callback, and :count options." - if options.class != Hash - raise(ArgumentError, usage_error) - end - - options = HashWithIndifferentAccess.new(options) unless (options == nil) - - unless options[:count] && options[:channel] && options[:callback] - raise(ArgumentError, usage_error) - end - - - detailed_history_request = PubnubRequest.new(:operation => :detailed_history) - - #TODO: refactor into initializer code on request instantiation - - # /detailed_history/SUBSCRIBE_KEY/CHANNEL/JSONP_CALLBACK/LIMIT - - detailed_history_request.ssl = @ssl - detailed_history_request.set_channel(options) - detailed_history_request.set_callback(options) - detailed_history_request.set_cipher_key(options, self.cipher_key) - - detailed_history_request.set_subscribe_key(options, self.subscribe_key) - - detailed_history_request.history_count = options[:count] - detailed_history_request.history_start = options[:start] - detailed_history_request.history_end = options[:end] - detailed_history_request.history_reverse = options[:reverse] - - detailed_history_request.format_url! - check_for_em detailed_history_request - - end - - def history(options = nil) - usage_error = "history() requires :channel, :callback, and :limit options." - if options.class != Hash - raise(ArgumentError, usage_error) - end - - options = HashWithIndifferentAccess.new(options) unless (options == nil) - - unless options[:limit] && options[:channel] && options[:callback] - raise(ArgumentError, usage_error) - end - - - history_request = PubnubRequest.new(:operation => :history) - - #TODO: refactor into initializer code on request instantiation - - # /history/SUBSCRIBE_KEY/CHANNEL/JSONP_CALLBACK/LIMIT - - history_request.ssl = @ssl - history_request.set_channel(options) - history_request.set_callback(options) - history_request.set_cipher_key(options, self.cipher_key) - - history_request.set_subscribe_key(options, self.subscribe_key) - history_request.history_limit = options[:limit] - - history_request.format_url! - check_for_em history_request - - end - - def time(options) - options = HashWithIndifferentAccess.new(options) - raise(PubNubRuntimeError, "You must supply a callback.") if options['callback'].blank? - - time_request = PubnubRequest.new(:operation => :time) - time_request.set_callback(options) - - time_request.format_url! - check_for_em time_request - end - - def my_callback(x, quiet = false) - if quiet !=false - puts("mycallback says: #{x.to_s}") +class Proc + def try(*a, &b) + if a.empty? && block_given? + yield self else - "" + __send__(*a, &b) end - end +end - def uuid - UUID.new.generate - end +module Pubnub + extend Configuration + include Error - def check_for_em request - if EM.reactor_running? - _request(request, true) - else - EM.run do - _request(request) - end - end - end + class << self + def new(options = {}) + #raise(Pubnub::Error::InitError, 'Initialize with either a hash of options, or exactly 5 named parameters.') unless args.size == 5 or (args.size == 1 and args[0].class == Hash) - private - - def _request(request, is_reactor_running = false) - request.format_url! - Thread.new{ - begin - - operation_timeout = %w(subscribe presence).include?(request.operation) ? TIMEOUT_SUBSCRIBE : TIMEOUT_NON_SUBSCRIBE - conn = EM::HttpRequest.new(request.url, :inactivity_timeout => operation_timeout) #client times out in 310s unless the server returns or timeout first - req = conn.get() - - req.errback{ - logAndRetryGeneralError(is_reactor_running, req, request) - } - - req.callback { - - if %w(subscribe presence).include?(request.operation) - if (checkForBadJSON(req) == true && request.operation == "subscribe") - logAndRetryBadJSON(is_reactor_running, req, request) - else - processGoodResponse(is_reactor_running, req, request) - end - else - if req.response_header.http_status.to_i != SUCCESS_RESPONSE - - begin - server_response = Yajl.load(req.response) - request.callback.call(server_response) - rescue => e - request.callback.call([0, "Bad server response: #{req.response_header.http_status.to_i}"]) - ensure - EM.stop unless is_reactor_running - end - - else - processGoodResponse(is_reactor_running, req, request) - end - end - - } - - rescue EventMachine::ConnectionError, RuntimeError => e # RuntimeError for catching "EventMachine not initialized" - error_message = "Network Error: #{e.message}" - puts(error_message) - return [0, error_message] - end - } - end - - def checkForBadJSON(req) - jsonError = false - begin - JSON.parse(req.response) - rescue => e - jsonError = true + Pubnub::Client.new(options) end - jsonError end +end - def processGoodResponse(is_reactor_running, req, request) - - if (req.response_header.http_status.to_i != SUCCESS_RESPONSE) - - unless (req.response_header.http_status.to_i == MSG_TOO_LARGE_RESPONSE) - logAndRetryBadResponseCode(is_reactor_running, req, request) - end - - else - - request.package_response!(req.response) - cycle = request.callback.call(request.response) - - if %w(subscribe presence).include?(request.operation) && (cycle != false || request.first_request?) - _request(request, is_reactor_running) - else - EM.stop unless is_reactor_running - end - end - end - - def logAndRetryGeneralError(is_reactor_running, req, request) - errMsg = "#{Time.now}: Network connectivity issue while attempting to reach #{request.url}" - logError(errMsg, request.url) - retryRequest(is_reactor_running, req, request, TIMEOUT_GENERAL_ERROR) - end - - def logAndRetryBadJSON(is_reactor_running, req, request) - errMsg = "#{Time.now}: Retrying from bad JSON: #{req.response.to_s}" - logError(errMsg, request.url) - retryRequest(is_reactor_running, req, request, TIMEOUT_BAD_JSON_RESPONSE) - end - - def logAndRetryBadResponseCode(is_reactor_running, req, request) - errMsg = "#{Time.now}: Retrying from bad server response code: (#{req.response_header.http_status.to_i}) #{req.response.to_s}" - logError(errMsg, request.url) - retryRequest(is_reactor_running, req, request, TIMEOUT_BAD_RESPONSE_CODE) - end - - def logError(errMsg, url) - PUBNUB_LOGGER.debug("url: #{url}") - PUBNUB_LOGGER.debug("#{errMsg}") - PUBNUB_LOGGER.debug("") - end - - def retryRequest(is_reactor_running, req, request, delay) - - if %w(subscribe presence).include?(request.operation) - EM::Timer.new(delay) do - _request(request, is_reactor_running) - end - else - error_msg = [0, "Request to #{request.url} failed."] - - request.set_error(true) - request.callback.call(error_msg) - - PUBNUB_LOGGER.debug(error_msg) - - EM.stop unless is_reactor_running - end - - end - -end \ No newline at end of file