wurfl_cloud_client_light 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 (38) hide show
  1. data/.gitignore +5 -0
  2. data/FOSS_THIRD_PARTY_LICENSES.txt +23 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +40 -0
  5. data/LICENSE.txt +75 -0
  6. data/Rakefile +14 -0
  7. data/lib/wurfl_cloud/cache/cookie.rb +36 -0
  8. data/lib/wurfl_cloud/cache/local_memory.rb +36 -0
  9. data/lib/wurfl_cloud/cache/null.rb +34 -0
  10. data/lib/wurfl_cloud/client.rb +98 -0
  11. data/lib/wurfl_cloud/configuration.rb +81 -0
  12. data/lib/wurfl_cloud/device_capabilities.rb +71 -0
  13. data/lib/wurfl_cloud/environment.rb +41 -0
  14. data/lib/wurfl_cloud/errors.rb +16 -0
  15. data/lib/wurfl_cloud/helper.rb +14 -0
  16. data/lib/wurfl_cloud/rack/cache_manager.rb +47 -0
  17. data/lib/wurfl_cloud/rails.rb +27 -0
  18. data/lib/wurfl_cloud/version.rb +7 -0
  19. data/lib/wurfl_cloud.rb +42 -0
  20. data/spec/files/generic.json +1 -0
  21. data/spec/files/generic_filtered.json +1 -0
  22. data/spec/files/lumia.json +1 -0
  23. data/spec/files/lumia_filtered.json +1 -0
  24. data/spec/files/strange_values.json +1 -0
  25. data/spec/spec_helper.rb +20 -0
  26. data/spec/support/rack_helpers.rb +79 -0
  27. data/spec/support/string_extensions.rb +26 -0
  28. data/spec/wurfl_cloud/client_spec.rb +200 -0
  29. data/spec/wurfl_cloud/configuration_spec.rb +111 -0
  30. data/spec/wurfl_cloud/cookie_cache_spec.rb +44 -0
  31. data/spec/wurfl_cloud/device_capabilities_spec.rb +136 -0
  32. data/spec/wurfl_cloud/environment_spec.rb +86 -0
  33. data/spec/wurfl_cloud/helper_spec.rb +28 -0
  34. data/spec/wurfl_cloud/null_cache_spec.rb +30 -0
  35. data/spec/wurfl_cloud/rack_cache_manager_spec.rb +69 -0
  36. data/spec/wurfl_cloud/server_request_spec.rb +38 -0
  37. data/wurfl_cloud_client_light.gemspec +31 -0
  38. metadata +161 -0
@@ -0,0 +1,71 @@
1
+ #
2
+ # This software is the Copyright of ScientiaMobile, Inc.
3
+ # Please refer to the LICENSE.txt file distributed with the software for licensing information
4
+ #
5
+ require 'wurfl_cloud/errors'
6
+ require 'json'
7
+
8
+ module WurflCloud
9
+ class DeviceCapabilities
10
+ attr_accessor :mtime, :id, :user_agent
11
+
12
+ def initialize(ua = nil)
13
+ @capabilities = {}
14
+ @mtime = Time.at(0)
15
+ @id = nil
16
+ @user_agent = ua
17
+ end
18
+
19
+ def [](key)
20
+ @capabilities[key]
21
+ end
22
+
23
+ def []=(key, value)
24
+ @capabilities[key] = value
25
+ end
26
+
27
+ def merge(newvalues)
28
+ @capabilities.merge!(newvalues)
29
+ end
30
+
31
+ def empty?
32
+ @capabilities.empty?
33
+ end
34
+
35
+ def has_key?(key)
36
+ @capabilities.has_key?(key)
37
+ end
38
+
39
+ def to_hash
40
+ @capabilities.clone
41
+ end
42
+ class << self
43
+ def parse(json)
44
+ object = JSON.parse(json)
45
+ new.tap do |device_capabilities|
46
+ object["capabilities"].each do |key,value|
47
+ device_capabilities[key] = cast_value(value)
48
+ end
49
+
50
+ device_capabilities.id = object["id"]
51
+ device_capabilities["id"] = object["id"]
52
+ device_capabilities.mtime = Time.at(object["mtime"].to_i)
53
+ end
54
+ rescue
55
+ raise WurflCloud::Errors::MalformedResponseError.new
56
+ end
57
+
58
+ private
59
+
60
+ def cast_value(value)
61
+ case value.to_s
62
+ when "true" then true ;
63
+ when "false" then false ;
64
+ when /^[1-9][0-9]*$/ then value.to_s.to_i ;
65
+ when /^[1-9][0-9]*\.[0-9]+$/ then value.to_s.to_f ;
66
+ else value
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,41 @@
1
+ #
2
+ # This software is the Copyright of ScientiaMobile, Inc.
3
+ # Please refer to the LICENSE.txt file distributed with the software for licensing information
4
+ #
5
+ require 'wurfl_cloud/errors'
6
+
7
+ module WurflCloud
8
+ class Environment
9
+
10
+ USER_AGENT_FIELDS = [
11
+ 'HTTP_X_DEVICE_USER_AGENT',
12
+ 'HTTP_X_ORIGINAL_USER_AGENT',
13
+ 'HTTP_X_OPERAMINI_PHONE_UA',
14
+ 'HTTP_X_SKYFIRE_PHONE',
15
+ 'HTTP_X_BOLT_PHONE_UA',
16
+ 'HTTP_USER_AGENT'
17
+ ]
18
+
19
+ attr_reader :user_agent, :x_forwarded_for, :x_accept, :x_wap_profile
20
+
21
+ # Extracts the http headers that are relevant
22
+ # to the library. They are used by the client
23
+ # when communicating with the API server.
24
+ def initialize(http_env={})
25
+ USER_AGENT_FIELDS.each do |ua_field|
26
+ @user_agent ||= http_env[ua_field]
27
+ end
28
+
29
+ @x_accept = http_env["HTTP_ACCEPT"]
30
+
31
+ if !(@x_wap_profile = http_env["HTTP_X_WAP_PROFILE"])
32
+ @x_wap_profile = http_env["HTTP_PROFILE"]
33
+ end
34
+
35
+ if http_env["REMOTE_ADDR"]
36
+ @x_forwarded_for = http_env["REMOTE_ADDR"]
37
+ @x_forwarded_for = "#{x_forwarded_for}, #{http_env["HTTP_X_FORWARDED_FOR"]}" if http_env["HTTP_X_FORWARDED_FOR"]
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,16 @@
1
+ #
2
+ # This software is the Copyright of ScientiaMobile, Inc.
3
+ # Please refer to the LICENSE.txt file distributed with the software for licensing information
4
+ #
5
+ module WurflCloud
6
+ module Errors
7
+ class GenericError < StandardError
8
+ end
9
+ class MalformedResponseError < GenericError
10
+ end
11
+ class ConfigurationError < GenericError
12
+ end
13
+ class ConnectionError < GenericError
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ #
2
+ # This software is the Copyright of ScientiaMobile, Inc.
3
+ # Please refer to the LICENSE.txt file distributed with the software for licensing information
4
+ #
5
+ module WurflCloud
6
+ module Helper
7
+
8
+ def wurfl_detect_device(env)
9
+ wurfl_env = WurflCloud::Environment.new(env)
10
+ wurfl_cache = WurflCloud.configuration.cache(env)
11
+ @device ||= WurflCloud::Client.detect_device(wurfl_env, wurfl_cache)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,47 @@
1
+ #
2
+ # This software is the Copyright of ScientiaMobile, Inc.
3
+ # Please refer to the LICENSE.txt file distributed with the software for licensing information
4
+ #
5
+ module WurflCloud::Rack
6
+ class CacheManager
7
+ # to be refactored: make configurable
8
+ COOKIE_NAME = 'WurflCloud_Client'
9
+ EXPIRY = 86400
10
+
11
+ def initialize(app, options={})
12
+ @app = app
13
+ end
14
+ def call(env)
15
+ # extract cookie
16
+ request = Rack::Request.new(env)
17
+ env['wurfl.cookie.device_cache'] = extract_wurfl_cookie(request.cookies)
18
+
19
+ # execute upstream request
20
+ status, headers, body = @app.call(env)
21
+
22
+ # store cookie
23
+ response = Rack::Response.new body, status, headers
24
+ response.set_cookie(COOKIE_NAME, {:value => {'date_set'=>Time.now.to_i, 'capabilities'=>env['wurfl.cookie.device_cache']}.to_json, :path => "/", :expires => Time.now+EXPIRY})
25
+ response.finish
26
+ end
27
+
28
+ private
29
+
30
+ def extract_wurfl_cookie(cookies)
31
+ # read the cookie and try to parse it
32
+ raw_cookie = cookies[COOKIE_NAME]
33
+ parsed_cookie = JSON.parse(raw_cookie)
34
+
35
+ # if parsed then check the expiry
36
+ return nil if !parsed_cookie.has_key?('date_set') || (parsed_cookie['date_set'].to_i+EXPIRY < Time.now.to_i)
37
+
38
+ # check if the capabilities params are present
39
+ return nil if !parsed_cookie.has_key?('capabilities') || !parsed_cookie['capabilities'].is_a?(Hash) || parsed_cookie['capabilities'].empty?
40
+
41
+ parsed_cookie['capabilities']
42
+
43
+ rescue Exception=>e
44
+ nil
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,27 @@
1
+ #
2
+ # This software is the Copyright of ScientiaMobile, Inc.
3
+ # Please refer to the LICENSE.txt file distributed with the software for licensing information
4
+ #
5
+ require 'wurfl_cloud/helper'
6
+ require 'wurfl_cloud/rack/cache_manager'
7
+
8
+ module WurflCloud
9
+ class Engine < ::Rails::Engine
10
+
11
+ # # Initialize the cache manager rack middleware
12
+ # config.app_middleware.use WurflCloud::Rack::CacheManager
13
+
14
+ # adds the moethods to the controller and views
15
+ initializer "wurf_cloud.helpers" do
16
+ ActiveSupport.on_load(:action_controller) do
17
+ include WurflCloud::Helper
18
+ helper_method :wurfl_detect_device
19
+ end
20
+
21
+ ActiveSupport.on_load(:action_view) do
22
+ include WurflCloud::Helper
23
+ end
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ #
2
+ # This software is the Copyright of ScientiaMobile, Inc.
3
+ # Please refer to the LICENSE.txt file distributed with the software for licensing information
4
+ #
5
+ module WurflCloud
6
+ VERSION = "1.0.0"
7
+ end
@@ -0,0 +1,42 @@
1
+ #
2
+ # This software is the Copyright of ScientiaMobile, Inc.
3
+ # Please refer to the LICENSE.txt file distributed with the software for licensing information
4
+ #
5
+ require 'wurfl_cloud/configuration'
6
+ require 'wurfl_cloud/cache/null'
7
+ require 'wurfl_cloud/cache/cookie'
8
+ require 'wurfl_cloud/version'
9
+ require 'wurfl_cloud/client'
10
+ require 'wurfl_cloud/environment'
11
+ require 'wurfl_cloud/device_capabilities'
12
+ require 'wurfl_cloud/errors'
13
+
14
+ module WurflCloud
15
+
16
+ class << self
17
+
18
+ # A configuration object. See WurflCloud::Configuration.
19
+ attr_writer :configuration
20
+
21
+ # Call this method to modify defaults in your initializers.
22
+ #
23
+ # @example
24
+ # WurflCloud.configure do |config|
25
+ # config.api_key = '00000000:xxxxxxxxxxxxxxxxxxxxxxxxxxx'
26
+ # config.host = 'staging.wurflcloud.com'
27
+ # end
28
+ def configure(silent = false)
29
+ yield(configuration)
30
+ end
31
+
32
+ # The configuration object.
33
+ # @see WurflCloud.configure
34
+ def configuration
35
+ @configuration ||= WurflCloud::Configuration.new
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
42
+ require 'wurfl_cloud/rails' if defined?(Rails)
@@ -0,0 +1 @@
1
+ {"apiVersion":"WurflCloud 1.3.2","mtime":1330016154,"id":"generic","capabilities":{"id":"generic","user_agent":"","fall_back":"root","browser_id":"browser_root","mobile_browser":"","nokia_feature_pack":0,"device_os":"","nokia_series":0,"has_qwerty_keyboard":false,"pointing_method":"mouse","mobile_browser_version":"","is_tablet":false,"nokia_edition":0,"uaprof":"","can_skip_aligned_link_row":false,"device_claims_web_support":false,"ununiqueness_handler":"","model_name":"","device_os_version":"","uaprof2":"","is_wireless_device":false,"uaprof3":"","brand_name":"generic web browser","model_extra_info":"","marketing_name":"","can_assign_phone_number":true,"release_date":"2002_january","unique":true,"icons_on_menu_items_support":false,"opwv_wml_extensions_support":false,"built_in_back_button_support":false,"proportional_font":false,"insert_br_element_after_widget_recommended":false,"wizards_recommended":false,"wml_can_display_images_and_text_on_same_line":false,"softkey_support":false,"wml_make_phone_call_string":"wtai:\/\/wp\/mc;","deck_prefetch_support":false,"menu_with_select_element_recommended":false,"numbered_menus":false,"card_title_support":true,"image_as_link_support":false,"wrap_mode_support":false,"table_support":true,"access_key_support":false,"wml_displays_image_in_center":false,"elective_forms_recommended":true,"times_square_mode_support":false,"break_list_of_links_with_br_element_recommended":true,"menu_with_list_of_links_recommended":true,"imode_region":"none","chtml_can_display_images_and_text_on_same_line":false,"chtml_displays_image_in_center":false,"chtml_make_phone_call_string":"tel:","chtml_table_support":false,"chtml_display_accesskey":false,"emoji":false,"xhtml_preferred_charset":"utf8","xhtml_supports_css_cell_table_coloring":false,"xhtml_select_as_radiobutton":false,"xhtml_autoexpand_select":false,"xhtml_avoid_accesskeys":false,"accept_third_party_cookie":true,"xhtml_make_phone_call_string":"tel:","xhtml_allows_disabled_form_elements":false,"xhtml_supports_invisible_text":false,"xhtml_select_as_dropdown":false,"cookie_support":true,"xhtml_send_mms_string":"none","xhtml_table_support":false,"xhtml_display_accesskey":false,"xhtml_can_embed_video":"none","xhtml_supports_iframe":"none","xhtmlmp_preferred_mime_type":"application\/vnd.wap.xhtml+xml","xhtml_supports_monospace_font":false,"xhtml_supports_inline_input":false,"xhtml_supports_forms_in_table":false,"xhtml_document_title_support":true,"xhtml_support_wml2_namespace":false,"xhtml_readable_background_color1":"#FFFFFF","xhtml_format_as_attribute":false,"xhtml_supports_table_for_layout":false,"xhtml_readable_background_color2":"#FFFFFF","xhtml_select_as_popup":false,"xhtml_send_sms_string":"none","xhtml_format_as_css_property":false,"xhtml_file_upload":"not_supported","xhtml_honors_bgcolor":false,"opwv_xhtml_extensions_support":false,"xhtml_marquee_as_css_property":false,"xhtml_nowrap_mode":false,"ajax_preferred_geoloc_api":"none","ajax_xhr_type":"none","ajax_support_getelementbyid":false,"ajax_support_event_listener":false,"ajax_manipulate_dom":false,"ajax_support_javascript":false,"ajax_support_inner_html":false,"ajax_manipulate_css":false,"ajax_support_events":false,"html_web_3_2":false,"html_wi_imode_htmlx_1":false,"html_wi_imode_html_1":false,"html_wi_oma_xhtmlmp_1_0":true,"html_wi_imode_html_2":false,"html_wi_w3_xhtmlbasic":true,"html_wi_imode_compact_generic":false,"html_wi_imode_html_3":false,"wml_1_1":true,"html_wi_imode_html_4":false,"wml_1_2":false,"html_wi_imode_html_5":false,"wml_1_3":false,"preferred_markup":"html_wi_oma_xhtmlmp_1_0","xhtml_support_level":1,"voicexml":false,"html_wi_imode_htmlx_1_1":false,"multipart_support":false,"html_web_4_0":false,"time_to_live_support":false,"total_cache_disable_support":false,"physical_screen_height":27,"columns":11,"dual_orientation":false,"physical_screen_width":27,"rows":6,"max_image_width":90,"resolution_height":40,"resolution_width":90,"max_image_height":35,"greyscale":false,"jpg":false,"gif":true,"transparent_png_index":false,"epoc_bmp":false,"bmp":false,"wbmp":true,"gif_animated":false,"colors":256,"svgt_1_1_plus":false,"svgt_1_1":false,"transparent_png_alpha":false,"png":false,"tiff":false,"emptyok":false,"empty_option_value_support":true,"basic_authentication_support":true,"post_method_support":true,"nokia_voice_call":false,"wta_pdc":false,"wta_voice_call":false,"wta_misc":false,"wta_phonebook":false,"phone_id_provided":false,"https_support":true,"sdio":false,"wifi":false,"has_cellular_radio":true,"max_data_rate":9,"vpn":false,"max_length_of_username":0,"max_url_length_bookmark":0,"max_no_of_bookmarks":0,"max_deck_size":4000,"max_url_length_cached_page":0,"max_length_of_password":0,"max_no_of_connection_settings":0,"max_url_length_in_requests":128,"max_object_size":0,"max_url_length_homepage":0,"video":false,"picture_bmp":false,"picture":false,"wallpaper_df_size_limit":0,"picture_preferred_width":0,"wallpaper_oma_size_limit":0,"picture_greyscale":false,"inline_support":false,"ringtone_qcelp":false,"screensaver_oma_size_limit":0,"screensaver_wbmp":false,"picture_resize":"none","picture_preferred_height":0,"ringtone_rmf":false,"wallpaper_wbmp":false,"wallpaper_jpg":false,"screensaver_bmp":false,"screensaver_max_width":0,"picture_inline_size_limit":0,"picture_colors":2,"ringtone_midi_polyphonic":false,"ringtone_midi_monophonic":false,"screensaver_preferred_height":0,"ringtone_voices":1,"ringtone_3gpp":false,"oma_support":false,"ringtone_inline_size_limit":0,"wallpaper_preferred_width":0,"wallpaper_greyscale":false,"screensaver_preferred_width":0,"wallpaper_preferred_height":0,"picture_max_width":0,"picture_jpg":false,"ringtone_aac":false,"ringtone_oma_size_limit":0,"wallpaper_directdownload_size_limit":0,"screensaver_inline_size_limit":0,"ringtone_xmf":false,"picture_max_height":0,"screensaver_max_height":0,"ringtone_mp3":false,"wallpaper_png":false,"screensaver_jpg":false,"ringtone_directdownload_size_limit":0,"wallpaper_max_width":0,"wallpaper_max_height":0,"screensaver":false,"ringtone_wav":false,"wallpaper_gif":false,"screensaver_directdownload_size_limit":0,"picture_df_size_limit":0,"wallpaper_tiff":false,"screensaver_df_size_limit":0,"ringtone_awb":false,"ringtone":false,"wallpaper_inline_size_limit":0,"picture_directdownload_size_limit":0,"picture_png":false,"wallpaper_bmp":false,"picture_wbmp":false,"ringtone_df_size_limit":0,"picture_oma_size_limit":0,"picture_gif":false,"screensaver_png":false,"wallpaper_resize":"none","screensaver_greyscale":false,"ringtone_mmf":false,"ringtone_amr":false,"wallpaper":false,"ringtone_digiplug":false,"ringtone_spmidi":false,"ringtone_compactmidi":false,"ringtone_imelody":false,"screensaver_resize":"none","wallpaper_colors":2,"directdownload_support":false,"downloadfun_support":false,"screensaver_colors":2,"screensaver_gif":false,"oma_v_1_0_combined_delivery":false,"oma_v_1_0_separate_delivery":false,"oma_v_1_0_forwardlock":false,"streaming_vcodec_mpeg4_asp":-1,"streaming_video_size_limit":0,"streaming_mov":false,"streaming_wmv":"none","streaming_acodec_aac":"none","streaming_vcodec_h263_0":-1,"streaming_real_media":"none","streaming_3g2":false,"streaming_3gpp":false,"streaming_acodec_amr":"none","streaming_vcodec_h264_bp":-1,"streaming_vcodec_h263_3":-1,"streaming_preferred_protocol":"rtsp","streaming_vcodec_mpeg4_sp":-1,"streaming_flv":false,"streaming_video":false,"streaming_preferred_http_protocol":"none","streaming_mp4":false,"expiration_date":false,"utf8_support":false,"connectionless_cache_operation":false,"connectionless_service_load":false,"iso8859_support":false,"connectionoriented_confirmed_service_indication":false,"connectionless_service_indication":false,"ascii_support":false,"connectionoriented_confirmed_cache_operation":false,"connectionoriented_confirmed_service_load":false,"wap_push_support":false,"connectionoriented_unconfirmed_cache_operation":false,"connectionoriented_unconfirmed_service_load":false,"connectionoriented_unconfirmed_service_indication":false,"doja_1_5":false,"j2me_datefield_broken":false,"j2me_clear_key_code":0,"j2me_right_softkey_code":0,"j2me_heap_size":0,"j2me_canvas_width":0,"j2me_motorola_lwt":false,"doja_3_5":false,"j2me_wbmp":false,"j2me_rmf":false,"j2me_wma":false,"j2me_left_softkey_code":0,"j2me_jtwi":false,"j2me_jpg":false,"j2me_return_key_code":0,"j2me_real8":false,"j2me_max_record_store_size":0,"j2me_realmedia":false,"j2me_midp_1_0":false,"j2me_bmp3":false,"j2me_midi":false,"j2me_btapi":false,"j2me_locapi":false,"j2me_siemens_extension":false,"j2me_h263":false,"j2me_audio_capture_enabled":false,"j2me_midp_2_0":false,"j2me_datefield_no_accepts_null_date":false,"j2me_aac":false,"j2me_capture_image_formats":"none","j2me_select_key_code":0,"j2me_xmf":false,"j2me_photo_capture_enabled":false,"j2me_realaudio":false,"j2me_realvideo":false,"j2me_mp3":false,"j2me_png":false,"j2me_au":false,"j2me_screen_width":0,"j2me_mp4":false,"j2me_mmapi_1_0":false,"j2me_http":false,"j2me_imelody":false,"j2me_socket":false,"j2me_3dapi":false,"j2me_bits_per_pixel":0,"j2me_mmapi_1_1":false,"j2me_udp":false,"j2me_wav":false,"j2me_middle_softkey_code":0,"j2me_svgt":false,"j2me_gif":false,"j2me_siemens_color_game":false,"j2me_max_jar_size":0,"j2me_wmapi_1_0":false,"j2me_nokia_ui":false,"j2me_screen_height":0,"j2me_wmapi_1_1":false,"j2me_wmapi_2_0":false,"doja_1_0":false,"j2me_serial":false,"doja_2_0":false,"j2me_bmp":false,"j2me_amr":false,"j2me_gif89a":false,"j2me_cldc_1_0":false,"doja_2_1":false,"doja_3_0":false,"j2me_cldc_1_1":false,"doja_2_2":false,"doja_4_0":false,"j2me_3gpp":false,"j2me_video_capture_enabled":false,"j2me_canvas_height":0,"j2me_https":false,"j2me_mpeg4":false,"j2me_storage_size":0,"mms_3gpp":false,"mms_wbxml":false,"mms_symbian_install":false,"mms_png":false,"mms_max_size":0,"mms_rmf":false,"mms_nokia_operatorlogo":false,"mms_max_width":0,"mms_max_frame_rate":0,"mms_wml":false,"mms_evrc":false,"mms_spmidi":false,"mms_gif_static":false,"mms_max_height":0,"sender":false,"mms_video":false,"mms_vcard":false,"mms_nokia_3dscreensaver":false,"mms_qcelp":false,"mms_midi_polyphonic":false,"mms_wav":false,"mms_jpeg_progressive":false,"mms_jad":false,"mms_nokia_ringingtone":false,"built_in_recorder":false,"mms_midi_monophonic":false,"mms_3gpp2":false,"mms_wmlc":false,"mms_nokia_wallpaper":false,"mms_bmp":false,"mms_vcalendar":false,"mms_jar":false,"mms_ota_bitmap":false,"mms_mp3":false,"mms_mmf":false,"mms_amr":false,"mms_wbmp":false,"built_in_camera":false,"receiver":false,"mms_mp4":false,"mms_xmf":false,"mms_jpeg_baseline":false,"mms_midi_polyphonic_voices":0,"mms_gif_animated":false,"ems":false,"text_imelody":false,"nokiaring":false,"siemens_logo_height":29,"ems_variablesizedpictures":false,"sckl_groupgraphic":false,"siemens_ota":false,"sagem_v1":false,"largeoperatorlogo":false,"sagem_v2":false,"ems_version":0,"ems_odi":false,"nokiavcal":false,"operatorlogo":false,"siemens_logo_width":101,"ems_imelody":false,"sckl_vcard":false,"siemens_screensaver_width":101,"sckl_operatorlogo":false,"panasonic":false,"ems_upi":false,"nokiavcard":false,"callericon":false,"sms_enabled":true,"gprtf":false,"siemens_screensaver_height":50,"sckl_ringtone":false,"picturemessage":false,"sckl_vcalendar":false,"rmf":false,"qcelp":false,"awb":false,"smf":false,"wav":false,"nokia_ringtone":false,"aac":false,"digiplug":false,"sp_midi":false,"compactmidi":false,"voices":1,"mp3":false,"mld":false,"evrc":false,"amr":false,"xmf":false,"mmf":false,"imelody":false,"midi_monophonic":false,"au":false,"midi_polyphonic":false,"flash_lite_version":"","fl_wallpaper":false,"fl_browser":false,"fl_screensaver":false,"fl_standalone":false,"full_flash_support":false,"fl_sub_lcd":false,"css_gradient":"none","css_border_image":"none","css_rounded_corners":"none","css_spriting":false,"css_supports_width_as_percentage":true,"is_transcoder":false,"transcoder_ua_header":"user-agent","rss_support":false,"pdf_support":false,"playback_oma_size_limit":0,"playback_acodec_aac":"none","playback_vcodec_h263_3":-1,"playback_vcodec_mpeg4_asp":-1,"playback_mp4":false,"playback_3gpp":false,"playback_df_size_limit":0,"playback_acodec_amr":"none","playback_mov":false,"playback_wmv":"none","playback_acodec_qcelp":false,"progressive_download":false,"playback_directdownload_size_limit":0,"playback_real_media":"none","playback_3g2":false,"playback_vcodec_mpeg4_sp":-1,"playback_vcodec_h263_0":-1,"playback_inline_size_limit":0,"hinted_progressive_download":false,"playback_vcodec_h264_bp":-1,"streaming_video_acodec_amr":false,"streaming_video_vcodec_h263_0":false,"streaming_video_max_video_bit_rate":0,"video_acodec_amr":false,"video_vcodec_h263_3":false,"video_directdownload_size_limit":0,"video_max_width":0,"streaming_video_sqcif":false,"streaming_real_media_10":false,"has_pointing_device":false,"streaming_video_sqcif_max_width":0,"video_vcodec_mpeg4":false,"video_inline_size_limit":0,"streaming_video_vcodec_h263_3":false,"streaming_video_max_bit_rate":0,"video_vcodec_h264":"none","streaming_video_sqcif_max_height":0,"video_df_size_limit":0,"video_3gpp2":false,"video_real_media_8":false,"streaming_video_max_audio_bit_rate":0,"video_real_media_10":false,"video_real_media_9":false,"xhtml_supports_file_upload":false,"streaming_video_vcodec_mpeg4":false,"video_acodec_qcelp":false,"video_acodec_awb":false,"video_max_height":0,"video_mp4":false,"video_3gpp":false,"streaming_video_acodec_aac_ltp":false,"streaming_video_acodec_awb":false,"streaming_video_max_frame_rate":0,"streaming_real_media_8":false,"video_acodec_aac":false,"video_oma_size_limit":0,"streaming_video_acodec_aac":false,"streaming_video_min_video_bit_rate":0,"streaming_real_media_9":false,"video_vcodec_h263_0":false,"video_preferred_width":0,"video_sqcif":false,"video_qcif":false,"streaming_video_qcif":false,"video_preferred_height":0,"video_mov":false,"streaming_prefered_http_protocol":"none","streaming_video_vcodec_h264":"none","streaming_video_qcif_max_height":0,"streaming_video_qcif_max_width":0,"video_acodec_aac_ltp":false,"video_max_frame_rate":0,"video_wmv":false,"https_detectable":false,"canvas_support":"none","viewport_width":"","html_preferred_dtd":"html4","viewport_supported":false,"viewport_minimum_scale":"","viewport_initial_scale":"","mobileoptimized":false,"viewport_maximum_scale":"","viewport_userscalable":"","image_inlining":false,"handheldfriendly":false,"is_smarttv":false,"fm_tuner_support":false,"nfc_support":false,"ux_full_desktop":false,"ux_full_web_usability_index":-100,"num_queries":3,"match_type":"none","matcher":"none","match":false,"lookup_time":0.0031659603118896,"matcher_history":"CatchAllUserAgentMatcher(exact),CatchAllUserAgentMatcher(conclusive),CatchAllUserAgentMatcher(recovery),http_accept","actual_root_device":"","fall_back_tree":"generic"},"errors":{}}
@@ -0,0 +1 @@
1
+ {"apiVersion":"WurflCloud 1.3.2","mtime":1330016154,"id":"generic","capabilities":{"is_wireless_device":false,"browser_id":"browser_root","fall_back":"root","user_agent":"","resolution_width": 800,"device_os_version":10.5},"errors":{}}
@@ -0,0 +1 @@
1
+ {"apiVersion":"WurflCloud 1.3.2","mtime":1330016154,"id":"nokia_lumia_800_ver1","capabilities":{"id":"nokia_lumia_800_ver1","user_agent":"Mozilla\/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident\/5.0; IEMobile\/9.0; NOKIA; Lumia 800)","fall_back":"generic_ms_nokia_phone_os7_5","actual_device_root":1,"browser_id":"browser_winmo_iemobile9","mobile_browser":"IEMobile","nokia_feature_pack":0,"device_os":"Windows Phone OS","nokia_series":0,"has_qwerty_keyboard":true,"pointing_method":"touchscreen","mobile_browser_version":"9.0","is_tablet":false,"nokia_edition":0,"uaprof":"http:\/\/nds1.nds.nokia.com\/uaprof\/Nokia800r100.xml","can_skip_aligned_link_row":true,"device_claims_web_support":true,"ununiqueness_handler":"","model_name":"Lumia 800","device_os_version":7.5,"uaprof2":"","is_wireless_device":true,"uaprof3":"","brand_name":"Nokia","model_extra_info":"","marketing_name":"","can_assign_phone_number":true,"release_date":"2011_october","unique":true,"icons_on_menu_items_support":false,"opwv_wml_extensions_support":false,"built_in_back_button_support":false,"proportional_font":false,"insert_br_element_after_widget_recommended":false,"wizards_recommended":false,"wml_can_display_images_and_text_on_same_line":false,"softkey_support":false,"wml_make_phone_call_string":"wtai:\/\/wp\/mc;","deck_prefetch_support":false,"menu_with_select_element_recommended":false,"numbered_menus":false,"card_title_support":true,"image_as_link_support":false,"wrap_mode_support":false,"table_support":true,"access_key_support":false,"wml_displays_image_in_center":false,"elective_forms_recommended":true,"times_square_mode_support":false,"break_list_of_links_with_br_element_recommended":true,"menu_with_list_of_links_recommended":true,"imode_region":"none","chtml_can_display_images_and_text_on_same_line":false,"chtml_displays_image_in_center":false,"chtml_make_phone_call_string":"tel:","chtml_table_support":false,"chtml_display_accesskey":false,"emoji":false,"xhtml_preferred_charset":"utf8","xhtml_supports_css_cell_table_coloring":false,"xhtml_select_as_radiobutton":false,"xhtml_autoexpand_select":false,"xhtml_avoid_accesskeys":false,"accept_third_party_cookie":true,"xhtml_make_phone_call_string":"tel:","xhtml_allows_disabled_form_elements":false,"xhtml_supports_invisible_text":false,"xhtml_select_as_dropdown":false,"cookie_support":true,"xhtml_send_mms_string":"none","xhtml_table_support":true,"xhtml_display_accesskey":false,"xhtml_can_embed_video":"none","xhtml_supports_iframe":"full","xhtmlmp_preferred_mime_type":"application\/vnd.wap.xhtml+xml","xhtml_supports_monospace_font":false,"xhtml_supports_inline_input":false,"xhtml_supports_forms_in_table":false,"xhtml_document_title_support":true,"xhtml_support_wml2_namespace":false,"xhtml_readable_background_color1":"#FFFFFF","xhtml_format_as_attribute":false,"xhtml_supports_table_for_layout":false,"xhtml_readable_background_color2":"#FFFFFF","xhtml_select_as_popup":false,"xhtml_send_sms_string":"none","xhtml_format_as_css_property":false,"xhtml_file_upload":"not_supported","xhtml_honors_bgcolor":false,"opwv_xhtml_extensions_support":false,"xhtml_marquee_as_css_property":false,"xhtml_nowrap_mode":false,"ajax_preferred_geoloc_api":"none","ajax_xhr_type":"standard","ajax_support_getelementbyid":true,"ajax_support_event_listener":true,"ajax_manipulate_dom":true,"ajax_support_javascript":true,"ajax_support_inner_html":true,"ajax_manipulate_css":true,"ajax_support_events":true,"html_web_3_2":false,"html_wi_imode_htmlx_1":false,"html_wi_imode_html_1":false,"html_wi_oma_xhtmlmp_1_0":true,"html_wi_imode_html_2":false,"html_wi_w3_xhtmlbasic":true,"html_wi_imode_compact_generic":false,"html_wi_imode_html_3":false,"wml_1_1":false,"html_wi_imode_html_4":false,"wml_1_2":false,"html_wi_imode_html_5":false,"wml_1_3":false,"preferred_markup":"html_web_4_0","xhtml_support_level":4,"voicexml":false,"html_wi_imode_htmlx_1_1":false,"multipart_support":false,"html_web_4_0":true,"time_to_live_support":false,"total_cache_disable_support":false,"physical_screen_height":161,"columns":16,"dual_orientation":true,"physical_screen_width":97,"rows":12,"max_image_width":320,"resolution_height":800,"resolution_width":480,"max_image_height":480,"greyscale":false,"jpg":true,"gif":true,"transparent_png_index":false,"epoc_bmp":false,"bmp":false,"wbmp":false,"gif_animated":true,"colors":65536,"svgt_1_1_plus":false,"svgt_1_1":false,"transparent_png_alpha":false,"png":true,"tiff":false,"emptyok":false,"empty_option_value_support":true,"basic_authentication_support":true,"post_method_support":true,"nokia_voice_call":false,"wta_pdc":false,"wta_voice_call":false,"wta_misc":false,"wta_phonebook":false,"phone_id_provided":false,"https_support":true,"sdio":false,"wifi":true,"has_cellular_radio":true,"max_data_rate":384,"vpn":false,"max_length_of_username":0,"max_url_length_bookmark":0,"max_no_of_bookmarks":0,"max_deck_size":100000,"max_url_length_cached_page":0,"max_length_of_password":0,"max_no_of_connection_settings":0,"max_url_length_in_requests":512,"max_object_size":0,"max_url_length_homepage":0,"video":false,"picture_bmp":false,"picture":false,"wallpaper_df_size_limit":0,"picture_preferred_width":0,"wallpaper_oma_size_limit":0,"picture_greyscale":false,"inline_support":false,"ringtone_qcelp":false,"screensaver_oma_size_limit":0,"screensaver_wbmp":false,"picture_resize":"none","picture_preferred_height":0,"ringtone_rmf":false,"wallpaper_wbmp":false,"wallpaper_jpg":false,"screensaver_bmp":false,"screensaver_max_width":0,"picture_inline_size_limit":0,"picture_colors":2,"ringtone_midi_polyphonic":false,"ringtone_midi_monophonic":false,"screensaver_preferred_height":0,"ringtone_voices":1,"ringtone_3gpp":false,"oma_support":false,"ringtone_inline_size_limit":0,"wallpaper_preferred_width":0,"wallpaper_greyscale":false,"screensaver_preferred_width":0,"wallpaper_preferred_height":0,"picture_max_width":0,"picture_jpg":false,"ringtone_aac":false,"ringtone_oma_size_limit":0,"wallpaper_directdownload_size_limit":0,"screensaver_inline_size_limit":0,"ringtone_xmf":false,"picture_max_height":0,"screensaver_max_height":0,"ringtone_mp3":false,"wallpaper_png":false,"screensaver_jpg":false,"ringtone_directdownload_size_limit":0,"wallpaper_max_width":0,"wallpaper_max_height":0,"screensaver":false,"ringtone_wav":false,"wallpaper_gif":false,"screensaver_directdownload_size_limit":0,"picture_df_size_limit":0,"wallpaper_tiff":false,"screensaver_df_size_limit":0,"ringtone_awb":false,"ringtone":false,"wallpaper_inline_size_limit":0,"picture_directdownload_size_limit":0,"picture_png":false,"wallpaper_bmp":false,"picture_wbmp":false,"ringtone_df_size_limit":0,"picture_oma_size_limit":0,"picture_gif":false,"screensaver_png":false,"wallpaper_resize":"none","screensaver_greyscale":false,"ringtone_mmf":false,"ringtone_amr":false,"wallpaper":false,"ringtone_digiplug":false,"ringtone_spmidi":false,"ringtone_compactmidi":false,"ringtone_imelody":false,"screensaver_resize":"none","wallpaper_colors":2,"directdownload_support":false,"downloadfun_support":false,"screensaver_colors":2,"screensaver_gif":false,"oma_v_1_0_combined_delivery":false,"oma_v_1_0_separate_delivery":false,"oma_v_1_0_forwardlock":false,"streaming_vcodec_mpeg4_asp":-1,"streaming_video_size_limit":0,"streaming_mov":false,"streaming_wmv":9,"streaming_acodec_aac":"lc","streaming_vcodec_h263_0":10,"streaming_real_media":"none","streaming_3g2":true,"streaming_3gpp":true,"streaming_acodec_amr":"nb","streaming_vcodec_h264_bp":0,"streaming_vcodec_h263_3":-1,"streaming_preferred_protocol":"http","streaming_vcodec_mpeg4_sp":0,"streaming_flv":false,"streaming_video":true,"streaming_preferred_http_protocol":"microsoft_smooth_streaming","streaming_mp4":true,"expiration_date":false,"utf8_support":false,"connectionless_cache_operation":false,"connectionless_service_load":true,"iso8859_support":false,"connectionoriented_confirmed_service_indication":false,"connectionless_service_indication":true,"ascii_support":false,"connectionoriented_confirmed_cache_operation":false,"connectionoriented_confirmed_service_load":false,"wap_push_support":true,"connectionoriented_unconfirmed_cache_operation":false,"connectionoriented_unconfirmed_service_load":false,"connectionoriented_unconfirmed_service_indication":false,"doja_1_5":false,"j2me_datefield_broken":false,"j2me_clear_key_code":0,"j2me_right_softkey_code":0,"j2me_heap_size":0,"j2me_canvas_width":0,"j2me_motorola_lwt":false,"doja_3_5":false,"j2me_wbmp":false,"j2me_rmf":false,"j2me_wma":false,"j2me_left_softkey_code":0,"j2me_jtwi":false,"j2me_jpg":false,"j2me_return_key_code":0,"j2me_real8":false,"j2me_max_record_store_size":0,"j2me_realmedia":false,"j2me_midp_1_0":false,"j2me_bmp3":false,"j2me_midi":false,"j2me_btapi":false,"j2me_locapi":false,"j2me_siemens_extension":false,"j2me_h263":false,"j2me_audio_capture_enabled":false,"j2me_midp_2_0":false,"j2me_datefield_no_accepts_null_date":false,"j2me_aac":false,"j2me_capture_image_formats":"none","j2me_select_key_code":0,"j2me_xmf":false,"j2me_photo_capture_enabled":false,"j2me_realaudio":false,"j2me_realvideo":false,"j2me_mp3":false,"j2me_png":false,"j2me_au":false,"j2me_screen_width":0,"j2me_mp4":false,"j2me_mmapi_1_0":false,"j2me_http":false,"j2me_imelody":false,"j2me_socket":false,"j2me_3dapi":false,"j2me_bits_per_pixel":0,"j2me_mmapi_1_1":false,"j2me_udp":false,"j2me_wav":false,"j2me_middle_softkey_code":0,"j2me_svgt":false,"j2me_gif":false,"j2me_siemens_color_game":false,"j2me_max_jar_size":0,"j2me_wmapi_1_0":false,"j2me_nokia_ui":false,"j2me_screen_height":0,"j2me_wmapi_1_1":false,"j2me_wmapi_2_0":false,"doja_1_0":false,"j2me_serial":false,"doja_2_0":false,"j2me_bmp":false,"j2me_amr":false,"j2me_gif89a":false,"j2me_cldc_1_0":false,"doja_2_1":false,"doja_3_0":false,"j2me_cldc_1_1":false,"doja_2_2":false,"doja_4_0":false,"j2me_3gpp":false,"j2me_video_capture_enabled":false,"j2me_canvas_height":0,"j2me_https":false,"j2me_mpeg4":false,"j2me_storage_size":0,"mms_3gpp":true,"mms_wbxml":false,"mms_symbian_install":false,"mms_png":true,"mms_max_size":614400,"mms_rmf":false,"mms_nokia_operatorlogo":false,"mms_max_width":1600,"mms_max_frame_rate":0,"mms_wml":false,"mms_evrc":false,"mms_spmidi":false,"mms_gif_static":true,"mms_max_height":1600,"sender":true,"mms_video":true,"mms_vcard":false,"mms_nokia_3dscreensaver":false,"mms_qcelp":false,"mms_midi_polyphonic":false,"mms_wav":true,"mms_jpeg_progressive":false,"mms_jad":false,"mms_nokia_ringingtone":false,"built_in_recorder":false,"mms_midi_monophonic":false,"mms_3gpp2":true,"mms_wmlc":false,"mms_nokia_wallpaper":false,"mms_bmp":true,"mms_vcalendar":false,"mms_jar":false,"mms_ota_bitmap":false,"mms_mp3":true,"mms_mmf":false,"mms_amr":false,"mms_wbmp":false,"built_in_camera":false,"receiver":true,"mms_mp4":true,"mms_xmf":false,"mms_jpeg_baseline":true,"mms_midi_polyphonic_voices":0,"mms_gif_animated":true,"ems":false,"text_imelody":false,"nokiaring":false,"siemens_logo_height":29,"ems_variablesizedpictures":false,"sckl_groupgraphic":false,"siemens_ota":false,"sagem_v1":false,"largeoperatorlogo":false,"sagem_v2":false,"ems_version":0,"ems_odi":false,"nokiavcal":false,"operatorlogo":false,"siemens_logo_width":101,"ems_imelody":false,"sckl_vcard":false,"siemens_screensaver_width":101,"sckl_operatorlogo":false,"panasonic":false,"ems_upi":false,"nokiavcard":false,"callericon":false,"sms_enabled":true,"gprtf":false,"siemens_screensaver_height":50,"sckl_ringtone":false,"picturemessage":false,"sckl_vcalendar":false,"rmf":false,"qcelp":false,"awb":false,"smf":false,"wav":true,"nokia_ringtone":false,"aac":true,"digiplug":false,"sp_midi":false,"compactmidi":false,"voices":1,"mp3":true,"mld":false,"evrc":false,"amr":false,"xmf":false,"mmf":false,"imelody":false,"midi_monophonic":false,"au":false,"midi_polyphonic":false,"flash_lite_version":"","fl_wallpaper":false,"fl_browser":false,"fl_screensaver":false,"fl_standalone":false,"full_flash_support":false,"fl_sub_lcd":false,"css_gradient":"none","css_border_image":"none","css_rounded_corners":"none","css_spriting":true,"css_supports_width_as_percentage":true,"is_transcoder":false,"transcoder_ua_header":"user-agent","rss_support":true,"pdf_support":true,"playback_oma_size_limit":0,"playback_acodec_aac":"lc","playback_vcodec_h263_3":-1,"playback_vcodec_mpeg4_asp":-1,"playback_mp4":true,"playback_3gpp":true,"playback_df_size_limit":0,"playback_acodec_amr":"nb","playback_mov":false,"playback_wmv":9,"playback_acodec_qcelp":false,"progressive_download":true,"playback_directdownload_size_limit":0,"playback_real_media":"none","playback_3g2":true,"playback_vcodec_mpeg4_sp":0,"playback_vcodec_h263_0":10,"playback_inline_size_limit":0,"hinted_progressive_download":false,"playback_vcodec_h264_bp":0,"streaming_video_acodec_amr":false,"streaming_video_vcodec_h263_0":false,"streaming_video_max_video_bit_rate":0,"video_acodec_amr":false,"video_vcodec_h263_3":false,"video_directdownload_size_limit":0,"video_max_width":0,"streaming_video_sqcif":false,"streaming_real_media_10":false,"has_pointing_device":false,"streaming_video_sqcif_max_width":0,"video_vcodec_mpeg4":false,"video_inline_size_limit":0,"streaming_video_vcodec_h263_3":false,"streaming_video_max_bit_rate":0,"video_vcodec_h264":"none","streaming_video_sqcif_max_height":0,"video_df_size_limit":0,"video_3gpp2":false,"video_real_media_8":false,"streaming_video_max_audio_bit_rate":0,"video_real_media_10":false,"video_real_media_9":false,"xhtml_supports_file_upload":false,"streaming_video_vcodec_mpeg4":false,"video_acodec_qcelp":false,"video_acodec_awb":false,"video_max_height":0,"video_mp4":false,"video_3gpp":false,"streaming_video_acodec_aac_ltp":false,"streaming_video_acodec_awb":false,"streaming_video_max_frame_rate":0,"streaming_real_media_8":false,"video_acodec_aac":false,"video_oma_size_limit":0,"streaming_video_acodec_aac":false,"streaming_video_min_video_bit_rate":0,"streaming_real_media_9":false,"video_vcodec_h263_0":false,"video_preferred_width":0,"video_sqcif":false,"video_qcif":false,"streaming_video_qcif":false,"video_preferred_height":0,"video_mov":false,"streaming_prefered_http_protocol":"microsoft_smooth_streaming","streaming_video_vcodec_h264":"none","streaming_video_qcif_max_height":0,"streaming_video_qcif_max_width":0,"video_acodec_aac_ltp":false,"video_max_frame_rate":0,"video_wmv":false,"https_detectable":false,"canvas_support":"none","viewport_width":"device_width_token","html_preferred_dtd":"html4","viewport_supported":true,"viewport_minimum_scale":"","viewport_initial_scale":"","mobileoptimized":false,"viewport_maximum_scale":"","viewport_userscalable":"no","image_inlining":true,"handheldfriendly":false,"is_smarttv":false,"fm_tuner_support":false,"nfc_support":false,"ux_full_desktop":false,"ux_full_web_usability_index":-50,"num_queries":2,"match_type":"exact","matcher":"WindowsPhoneUserAgentMatcher","match":true,"lookup_time":0.0045168399810791,"matcher_history":"WindowsPhoneUserAgentMatcher(exact)","actual_root_device":"nokia_lumia_800_ver1","fall_back_tree":"nokia_lumia_800_ver1,generic_ms_nokia_phone_os7_5,generic_ms_phone_os7_5,generic_ms_phone_os7,generic_xhtml,generic_mobile,generic"},"errors":{}}
@@ -0,0 +1 @@
1
+ {"apiVersion":"WurflCloud 1.3.2","mtime":1330016154,"id":"nokia_lumia_800_ver1","capabilities":{"is_wireless_device":true,"browser_id":"browser_winmo_iemobile9","fall_back":"generic_ms_nokia_phone_os7_5","user_agent":"Mozilla\/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident\/5.0; IEMobile\/9.0; NOKIA; Lumia 800)"},"errors":{}}
@@ -0,0 +1 @@
1
+ {"apiVersion":"WurflCloud 1.3.2","mtime":1330016154,"id":"generic","capabilities":{"release_date": "1994_january","is_wireless_device":"true","is_tablet":"false","resolution_width": "800"},"errors":{}}
@@ -0,0 +1,20 @@
1
+ #
2
+ # This software is the Copyright of ScientiaMobile, Inc.
3
+ # Please refer to the LICENSE.txt file distributed with the software for licensing information
4
+ #
5
+ require 'simplecov'
6
+ SimpleCov.start do
7
+ add_filter "/spec/"
8
+ end
9
+
10
+
11
+ require 'wurfl_cloud'
12
+ require 'webmock/rspec'
13
+ require 'rack'
14
+
15
+ # Load support files
16
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
17
+
18
+ RSpec.configure do |config|
19
+ config.include(WurflCloud::Spec::RackHelpers)
20
+ end
@@ -0,0 +1,79 @@
1
+ # encoding: utf-8
2
+ # adapted from warden
3
+ #
4
+ # Copyright (c) 2009 Daniel Neighman
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #
25
+ require 'wurfl_cloud/rack/cache_manager'
26
+
27
+ module WurflCloud::Spec
28
+ module RackHelpers
29
+ FAILURE_APP = lambda{|e|[401, {"Content-Type" => "text/plain"}, ["You Fail!"]] }
30
+
31
+ def env_with_params(path = "/", params = {}, env = {})
32
+ method = params.delete(:method) || "GET"
33
+ env = { 'HTTP_VERSION' => '1.1', 'REQUEST_METHOD' => "#{method}" }.merge(env)
34
+ Rack::MockRequest.env_for("#{path}?#{Rack::Utils.build_query(params)}", env)
35
+ end
36
+
37
+ def setup_rack(app = nil, opts = {}, &block)
38
+ app ||= block if block_given?
39
+
40
+ opts[:failure_app] ||= failure_app
41
+
42
+ Rack::Builder.new do
43
+ use opts[:session] || WurflCloud::Spec::RackHelpers::Session
44
+ use WurflCloud::Rack::CacheManager, opts
45
+ run app
46
+ end
47
+ end
48
+
49
+ def valid_response
50
+ Rack::Response.new("OK").finish
51
+ end
52
+
53
+ def failure_app
54
+ WurflCloud::Spec::RackHelpers::FAILURE_APP
55
+ end
56
+
57
+ def success_app
58
+ lambda{|e| [200, {"Content-Type" => "text/plain"}, ["You Win"]]}
59
+ end
60
+
61
+ class Session
62
+ attr_accessor :app
63
+ def initialize(app,configs = {})
64
+ @app = app
65
+ end
66
+
67
+ def call(e)
68
+ e['rack.session'] ||= {}
69
+ @app.call(e)
70
+ end
71
+ end # session
72
+
73
+
74
+ def authenticated_uri
75
+ c = WurflCloud.configuration
76
+ "#{c.schema}://#{c.api_user}:#{c.api_password}@#{c.host}:#{c.port}#{c.path}"
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,26 @@
1
+ #
2
+ # This software is the Copyright of ScientiaMobile, Inc.
3
+ # Please refer to the LICENSE.txt file distributed with the software for licensing information
4
+ #
5
+ class String
6
+ class << self
7
+ def random(length=20)
8
+ self.generic_random(length, ("0".."z"))
9
+ end
10
+ def az_random(length=20)
11
+ self.generic_random(length, ("a".."z"))
12
+ end
13
+ def aznum_random(length=20)
14
+ self.generic_random(length, ("a".."z").to_a + ("0".."9").to_a)
15
+ end
16
+ def az_random_with_num(length=20, num_lenght=20)
17
+ self.generic_random(length, ("a".."z").to_a)+self.generic_random(length, ("0".."9").to_a)
18
+ end
19
+ def generic_random(length,char_range)
20
+ chars = char_range.to_a
21
+ Array.new.tap do |a|
22
+ 1.upto(length) { |i| a << chars[rand(chars.size-1)]}
23
+ end.join
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,200 @@
1
+ #
2
+ # This software is the Copyright of ScientiaMobile, Inc.
3
+ # Please refer to the LICENSE.txt file distributed with the software for licensing information
4
+ #
5
+ require 'spec_helper'
6
+ require 'wurfl_cloud/cache/local_memory'
7
+
8
+ describe WurflCloud::Client do
9
+ subject { WurflCloud::Client.new(WurflCloud::Environment.new, nil) }
10
+
11
+ it "should contain a device_capabilities object" do
12
+ subject.device_capabilities.should be_instance_of(WurflCloud::DeviceCapabilities)
13
+ end
14
+
15
+ it "should delegate the [] method to the device_capabilities object" do
16
+ capability_name = String.random
17
+ subject.device_capabilities.should_receive(:[]).with(capability_name)
18
+ subject[capability_name]
19
+ end
20
+
21
+ it "should have a read_from_cache method" do
22
+ subject.should respond_to(:read_from_cache)
23
+ end
24
+
25
+ context "the detect_device method" do
26
+ it "should be defined with 2 parameters" do
27
+ WurflCloud::Client.should respond_to(:detect_device).with(2).arguments
28
+ end
29
+
30
+ end
31
+
32
+ context "when using with the default null cache" do
33
+ before(:each) do
34
+ @http_env = {
35
+ "HTTP_USER_AGENT"=>String.random,
36
+ "HTTP_ACCEPT"=>String.random,
37
+ "HTTP_X_WAP_PROFILE"=>String.random,
38
+ "REMOTE_ADDR"=>String.random
39
+ }
40
+ @environment = WurflCloud::Environment.new(@http_env)
41
+ @cache = WurflCloud.configuration.cache(nil)
42
+
43
+ @device_data = File.new("#{File.dirname(__FILE__)}/../files/generic.json").read
44
+
45
+ stub_request(:any, authenticated_uri).to_return(:status=>200, :body => @device_data)
46
+
47
+ @wurfl_client = WurflCloud::Client.detect_device(@environment, @cache)
48
+
49
+ end
50
+
51
+ it "should validate the cache when loading from server" do
52
+ @cache.should_receive(:validate).with(1330016154)
53
+ @wurfl_client.load_from_server!
54
+ end
55
+
56
+ it "should return an instance of the client class" do
57
+ @wurfl_client.should be_instance_of(WurflCloud::Client)
58
+ end
59
+
60
+ it "should call the remote webservice" do
61
+ WebMock.should have_requested(:get, authenticated_uri)
62
+ end
63
+
64
+ it "should call the remote webservice each time detect_device is called" do
65
+ WurflCloud::Client.detect_device(@environment, @cache)
66
+ WebMock.should have_requested(:get, authenticated_uri).times(2)
67
+ end
68
+
69
+
70
+ it "should pass the correct headers" do
71
+ headers = {
72
+ 'User-Agent' => @http_env["HTTP_USER_AGENT"],
73
+ 'X-Cloud-Client' => "WurflCloudClient/Ruby_#{WurflCloud::VERSION}",
74
+ 'X-Forwarded-For' => @http_env["REMOTE_ADDR"],
75
+ 'X-Accept' => @http_env["HTTP_ACCEPT"],
76
+ 'X-Wap-Profile' => @http_env["HTTP_X_WAP_PROFILE"],
77
+ }
78
+ stub_request(:any, authenticated_uri).with(:headers=>headers).to_return(:status=>200, :body => @device_data)
79
+ WurflCloud::Client.detect_device(@environment, @cache)
80
+ end
81
+
82
+ { "is_wireless_device"=>false,
83
+ "browser_id"=>"browser_root",
84
+ "fall_back"=>"root",
85
+ "user_agent"=>"",
86
+ "resolution_width"=>90
87
+ }.each do |key, value|
88
+ it "should return the right values for the capability #{key}" do
89
+ @wurfl_client[key].should ==value
90
+ end
91
+ it "should not call the webservice to read a capability that's present in the answer" do
92
+ value = @wurfl_client[key]
93
+ WebMock.should have_requested(:get, authenticated_uri).times(1)
94
+ end
95
+ end
96
+
97
+ end
98
+
99
+ context "when using with a simple local memory cache" do
100
+
101
+ before(:each) do
102
+ @http_env = {
103
+ "HTTP_USER_AGENT"=>String.random,
104
+ "HTTP_ACCEPT"=>String.random,
105
+ "HTTP_X_WAP_PROFILE"=>String.random,
106
+ "REMOTE_ADDR"=>String.random
107
+ }
108
+ @environment = WurflCloud::Environment.new(@http_env)
109
+ @cache = WurflCloud::Cache::LocalMemory.new
110
+
111
+ @device_data = File.new("#{File.dirname(__FILE__)}/../files/generic.json").read
112
+
113
+ stub_request(:any, authenticated_uri).to_return(:status=>200, :body => @device_data)
114
+ @wurfl_client = WurflCloud::Client.detect_device(@environment, @cache)
115
+ end
116
+
117
+
118
+ it "should call the remote webservice once if the same agent is detected twice" do
119
+ WurflCloud::Client.detect_device(@environment, @cache)
120
+ WebMock.should have_requested(:get, authenticated_uri).times(1)
121
+ end
122
+
123
+ context "requesting a non existent capability" do
124
+ context "just after the capabilities have been read from the server" do
125
+ it "should not call again the webservice" do
126
+ value = @wurfl_client['my_non_existent_capability']
127
+ WebMock.should have_requested(:get, authenticated_uri).times(1)
128
+ end
129
+ it "should return nil" do
130
+ @wurfl_client['my_non_existent_capability'].should be_nil
131
+ end
132
+ end
133
+
134
+ context "detecting the device from the cache" do
135
+ before(:each) do
136
+ @wurfl_client = WurflCloud::Client.detect_device(@environment, @cache)
137
+ end
138
+
139
+ it "should call again the webservice" do
140
+ value = @wurfl_client['my_non_existent_capability']
141
+ WebMock.should have_requested(:get, authenticated_uri).times(2)
142
+ end
143
+
144
+ it "should return nil if it doesn't exist" do
145
+ @wurfl_client['my_non_existent_capability'].should be_nil
146
+ end
147
+
148
+ it "should return the right value if exists" do
149
+ stub_request(:any, authenticated_uri).to_return(:status=>200, :body => %{{"apiVersion":"WurflCloud 1.3.2","mtime":1330016154,"id":"generic","capabilities":{"my_non_existent_capability":"OK"},"errors":{}}})
150
+ @wurfl_client['my_non_existent_capability'].should =="OK"
151
+ end
152
+
153
+ it "should call the webservice only once for each access to the capability value" do
154
+ stub_request(:any, authenticated_uri).to_return(:status=>200, :body => %{{"apiVersion":"WurflCloud 1.3.2","mtime":1330016154,"id":"generic","capabilities":{"my_non_existent_capability":"OK"},"errors":{}}})
155
+ value = @wurfl_client['my_non_existent_capability']
156
+ value = @wurfl_client['my_non_existent_capability']
157
+ WebMock.should have_requested(:get, authenticated_uri).times(2)
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ context "when the webservice has errors" do
164
+ before(:each) do
165
+ @http_env = {
166
+ "HTTP_USER_AGENT"=>String.random,
167
+ "HTTP_ACCEPT"=>String.random,
168
+ "HTTP_X_WAP_PROFILE"=>String.random,
169
+ "REMOTE_ADDR"=>String.random
170
+ }
171
+ @environment = WurflCloud::Environment.new(@http_env)
172
+ @cache = WurflCloud.configuration.cache(nil)
173
+
174
+ @device_data = File.new("#{File.dirname(__FILE__)}/../files/generic.json").read
175
+ end
176
+
177
+ it "should raise WurflCloud::Errors::ConnectionError if there are connection timeouts" do
178
+ stub_request(:any, authenticated_uri).to_timeout
179
+
180
+ expect { WurflCloud::Client.detect_device(@environment, @cache) }.to raise_error(WurflCloud::Errors::ConnectionError)
181
+ end
182
+ it "should raise WurflCloud::Errors::ConnectionError if there are connection errors" do
183
+ stub_request(:any, authenticated_uri).to_raise("some error")
184
+
185
+ expect { WurflCloud::Client.detect_device(@environment, @cache) }.to raise_error(WurflCloud::Errors::ConnectionError)
186
+ end
187
+ it "should raise WurflCloud::Errors::ConnectionError if there are server errors" do
188
+ stub_request(:any, authenticated_uri).to_return(:status=>500)
189
+
190
+ expect { WurflCloud::Client.detect_device(@environment, @cache) }.to raise_error(WurflCloud::Errors::ConnectionError)
191
+ end
192
+ it "should raise WurflCloud::Errors::MalformedResponseError if there are unparsable responses" do
193
+ stub_request(:any, authenticated_uri).to_return(:status=>200, :body => %{badjson})
194
+
195
+ expect { WurflCloud::Client.detect_device(@environment, @cache) }.to raise_error(WurflCloud::Errors::MalformedResponseError)
196
+ end
197
+
198
+ end
199
+
200
+ end