schmobile 0.0.8 → 0.1.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/Gemfile CHANGED
@@ -6,7 +6,7 @@ source "http://rubygems.org"
6
6
  # Add dependencies to develop your gem here.
7
7
  # Include everything needed to run rake, tests, features, etc.
8
8
  group :development do
9
- gem "rack", "1.1.0"
9
+ gem "rack", "~> 1.1.0"
10
10
  gem "mocha", "0.9.9"
11
11
  gem "shoulda", ">= 0"
12
12
  gem "bundler", "~> 1.0.0"
data/Gemfile.lock CHANGED
@@ -19,5 +19,5 @@ DEPENDENCIES
19
19
  bundler (~> 1.0.0)
20
20
  jeweler (~> 1.5.2)
21
21
  mocha (= 0.9.9)
22
- rack (= 1.1.0)
22
+ rack (~> 1.1.0)
23
23
  shoulda
data/README.rdoc CHANGED
@@ -12,12 +12,15 @@ It supports string interpolation for dynamic destinations:
12
12
 
13
13
  use Rack::Schmobile, :redirect_to => "/mobile/#!/{{path}}"
14
14
 
15
- == Forcing mobile mode
15
+ Finally the middleware provides a request level method to determine if the client is a mobile device
16
+
17
+ Rack::Request#is_mobile?
16
18
 
17
- You can force toggle mobile mode by sending +schmobile_mode=enabled+ and +schmobile_mode=disabled+ - note that these
18
- require session access.
19
+ == Forcing mobile mode
19
20
 
20
- Finally the middleware provides Rack::Request#is_mobile?
21
+ You can force toggle mobile mode by sending +is_mobile=true+ and +is_mobile=false+ as
22
+ parameters in the request URL - note that these require session access. This entirely
23
+ overrides the user agent detection.
21
24
 
22
25
  == Rolling out
23
26
 
@@ -27,8 +30,8 @@ Finally the middleware provides Rack::Request#is_mobile?
27
30
 
28
31
  You can add/remove user agents like so:
29
32
 
30
- Rack::Schmobile.add_user_agent_pattern("wibble")
31
- Rack::Schmobile.remote_user_agent_pattern("ipad")
33
+ Rack::Schmobile::UserAgents.add_user_agent_pattern("wibble")
34
+ Rack::Schmobile::UserAgents.remove_user_agent_pattern("ipad")
32
35
 
33
36
  == Thanks
34
37
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.8
1
+ 0.1.0
@@ -0,0 +1,15 @@
1
+ module Rack
2
+ module Schmobile
3
+ module Filters
4
+ module IsMobileParam
5
+ def self.call(request)
6
+ if request.params.key?(Rack::Schmobile::IS_MOBILE)
7
+ request.session[Rack::Schmobile::IS_MOBILE] = (request.params[Rack::Schmobile::IS_MOBILE] == "true")
8
+ end
9
+
10
+ request.session[Rack::Schmobile::IS_MOBILE]
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ require 'rack/schmobile/user_agents'
2
+
3
+ module Rack
4
+ module Schmobile
5
+ module Filters
6
+ module MobileUserAgent
7
+ def self.call(request)
8
+ Rack::Schmobile::UserAgents.is_mobile_agent?(request.user_agent)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,2 @@
1
+ require 'rack/schmobile/filters/is_mobile_param'
2
+ require 'rack/schmobile/filters/mobile_user_agent'
@@ -0,0 +1,61 @@
1
+ module Rack
2
+ module Schmobile
3
+ class Middleware
4
+
5
+ def initialize(app, options = {})
6
+ @app = app
7
+ @options = options
8
+ end
9
+
10
+ def call(env)
11
+ request = Rack::Request.new(env)
12
+
13
+ if request.is_mobile? && redirect?(request)
14
+ [ 301, { "Location" => redirect_location(request) }, [] ]
15
+ else
16
+ @app.call(env)
17
+ end
18
+ end
19
+
20
+ # Returns true if this middleware has been configured with a redirect_to and the requested path
21
+ # is not already below the configured redirect_to
22
+ def redirect?(request)
23
+ redirecting = true
24
+
25
+ if @options.key?(:redirect_if)
26
+ redirecting = @options[:redirect_if].call(request)
27
+ end
28
+
29
+ if @options.key?(:redirect_to)
30
+ redirecting &&= request.path !~ /^#{@options[:redirect_to]}/
31
+ else
32
+ redirecting = false
33
+ end
34
+
35
+ redirecting ? redirect_location(request) : nil
36
+ end
37
+
38
+ def redirect_location(request)
39
+ "#{@options[:redirect_to]}#{redirect_with(request)}"
40
+ end
41
+
42
+ def redirect_with(request)
43
+ build_path(@options[:redirect_with].to_s, request)
44
+ end
45
+
46
+ private
47
+
48
+ def build_path(destination, request)
49
+ final_destination = destination.dup
50
+ destination.scan(/\{\{\w+\}\}/) do |call|
51
+ func = call.scan(/\w+/).to_s
52
+ if request.respond_to?(func)
53
+ final_destination.sub!(/\{\{#{func}\}\}/, request.send(func))
54
+ end
55
+ end
56
+ final_destination
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,10 @@
1
+ Rack::Request.class_eval do
2
+ def is_mobile?
3
+ Rack::Schmobile::FILTERS.each do |filter|
4
+ result = filter.call(self)
5
+ return result unless result.nil?
6
+ end
7
+
8
+ false
9
+ end
10
+ end
@@ -0,0 +1,32 @@
1
+ module Rack
2
+ module Schmobile
3
+ class UserAgents
4
+
5
+ MOBILE_USER_AGENTS = %w(
6
+ alcatel amoi android astel audiovox blackberry cdm ce chtml danger docomo ericsson htc_touch
7
+ iphone ipod j2me kddi midp minimo mmp mobi mobile mobileexplorer mot- motorola netfront nokia
8
+ novarra palm pdxgw phone plucker pocket portable portalmmm sagem samsung sgh sie- softbank
9
+ sprint symbian telit ucweb up.b upg1 vodafone webos windows x240 x320 xiino
10
+ )
11
+
12
+ def self.remove_user_agent_pattern(pattern)
13
+ MOBILE_USER_AGENTS.delete(pattern)
14
+ @mobile_agent_matcher = nil
15
+ end
16
+
17
+ def self.add_user_agent_pattern(pattern)
18
+ MOBILE_USER_AGENTS.push(*pattern)
19
+ @mobile_agent_matcher = nil
20
+ end
21
+
22
+ def self.is_mobile_agent?(user_agent)
23
+ !(user_agent.to_s.downcase =~ mobile_agent_matcher).nil?
24
+ end
25
+
26
+ def self.mobile_agent_matcher
27
+ @mobile_agent_matcher ||= Regexp.new(MOBILE_USER_AGENTS.uniq.compact.map { |v| Regexp.escape(v) }.join("|"))
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -1,110 +1,11 @@
1
1
  require 'rack'
2
+ require 'rack/schmobile/middleware'
3
+ require 'rack/schmobile/request_extension'
4
+ require 'rack/schmobile/filters'
2
5
 
3
6
  module Rack
4
-
5
- # Rack::Schmobile is a minimalist mobile UA detection middleware
6
- class Schmobile
7
- MOBILE_USER_AGENTS = %w(
8
- palm blackberry nokia phone midp mobi symbian chtml ericsson minimo audiovox motorola samsung telit upg1 windows ce
9
- ucweb astel plucker x320 x240 j2me sgh portable sprint docomo kddi softbank android mmp pdxgw netfront xiino vodafone
10
- portalmmm sagem mot- sie- ipod up.b webos amoi novarra cdm alcatel pocket ipad iphone mobileexplorer mobile
11
- )
12
-
13
- SCHMOBILE_MODE = 'schmobile_mode'
14
-
15
- def self.remove_user_agent_pattern(pattern)
16
- MOBILE_USER_AGENTS.delete(pattern)
17
- @mobile_agent_matcher = nil
18
- end
19
-
20
- def self.add_user_agent_pattern(pattern)
21
- MOBILE_USER_AGENTS.push(*pattern)
22
- @mobile_agent_matcher = nil
23
- end
24
-
25
- def self.is_mobile_request?(request)
26
- request.user_agent.to_s.downcase =~ mobile_agent_matcher
27
- end
28
-
29
- def self.mobile_agent_matcher
30
- @mobile_agent_matcher ||= Regexp.new(MOBILE_USER_AGENTS.uniq.compact.map { |v| Regexp.escape(v) }.join("|"))
31
- end
32
-
33
- def initialize(app, options = {})
34
- @app = app
35
- @options = options
36
- end
37
-
38
- def call(env)
39
- request = Rack::Request.new(env)
40
-
41
- if is_mobile_session?(env, request) && redirect?(request)
42
- return [ 301, { "Location" => redirect_location(request) }, [] ]
43
- end
44
-
45
- @app.call(env)
46
- end
47
-
48
- # Checks if this session has been forced into mobile mode and returns that if that is the case. Otherwise
49
- # checks the user agent via request.is_mobile?
50
- def is_mobile_session?(env, request)
51
- session = env['rack.session'] ||= {}
52
-
53
- if request.params[SCHMOBILE_MODE]
54
- session[SCHMOBILE_MODE] = request.params[SCHMOBILE_MODE]
55
- end
56
-
57
- unless session[SCHMOBILE_MODE].nil?
58
- return session[SCHMOBILE_MODE] == 'enabled'
59
- end
60
-
61
- request.is_mobile?
62
- end
63
-
64
- # Returns true if this middleware has been configured with a redirect_to and the requested path is not already
65
- # below the configured redirect_to
66
- def redirect?(request)
67
- redirecting = true
68
-
69
- if @options.key?(:redirect_if)
70
- redirecting = @options[:redirect_if].call(request)
71
- end
72
-
73
- if @options.key?(:redirect_to)
74
- redirecting &&= request.path !~ /^#{@options[:redirect_to]}/
75
- else
76
- redirecting = false
77
- end
78
-
79
- redirecting
80
- end
81
-
82
- def redirect_location(request)
83
- "#{@options[:redirect_to]}#{redirect_with(request)}"
84
- end
85
-
86
- def redirect_with(request)
87
- build_path(@options[:redirect_with].to_s, request)
88
- end
89
-
90
- private
91
-
92
- def build_path(destination, request)
93
- final_destination = destination.dup
94
- destination.scan(/\{\{\w+\}\}/) do |call|
95
- func = call.scan(/\w+/).to_s
96
- if request.respond_to?(func)
97
- final_destination.sub!(/\{\{#{func}\}\}/, request.send(func))
98
- end
99
- end
100
- final_destination
101
- end
102
-
103
- end
104
- end
105
-
106
- Rack::Request.class_eval do
107
- def is_mobile?
108
- Rack::Schmobile.is_mobile_request?(self)
7
+ module Schmobile
8
+ FILTERS = [ Rack::Schmobile::Filters::IsMobileParam, Rack::Schmobile::Filters::MobileUserAgent ]
9
+ IS_MOBILE = "is_mobile"
109
10
  end
110
11
  end
data/schmobile.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{schmobile}
8
- s.version = "0.0.8"
8
+ s.version = "0.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Morten Primdahl"]
12
- s.date = %q{2011-02-17}
12
+ s.date = %q{2011-05-25}
13
13
  s.description = %q{Used to determine if a request is from a mobile client, and possibly redirect it if that's the case}
14
14
  s.email = %q{morten@zendesk.com}
15
15
  s.extra_rdoc_files = [
@@ -25,40 +25,49 @@ Gem::Specification.new do |s|
25
25
  "Rakefile",
26
26
  "VERSION",
27
27
  "lib/rack/schmobile.rb",
28
+ "lib/rack/schmobile/filters.rb",
29
+ "lib/rack/schmobile/filters/is_mobile_param.rb",
30
+ "lib/rack/schmobile/filters/mobile_user_agent.rb",
31
+ "lib/rack/schmobile/middleware.rb",
32
+ "lib/rack/schmobile/request_extension.rb",
33
+ "lib/rack/schmobile/user_agents.rb",
28
34
  "lib/schmobile.rb",
29
35
  "schmobile.gemspec",
30
36
  "test/helper.rb",
31
- "test/test_schmobile.rb"
37
+ "test/test_middleware.rb",
38
+ "test/test_rack_request.rb",
39
+ "test/test_user_agents.rb"
32
40
  ]
33
41
  s.homepage = %q{http://github.com/morten/schmobile}
34
42
  s.licenses = ["MIT"]
35
43
  s.require_paths = ["lib"]
36
- s.rubygems_version = %q{1.3.7}
44
+ s.rubygems_version = %q{1.6.2}
37
45
  s.summary = %q{A Rack middleware for detecting mobile user agents}
38
46
  s.test_files = [
39
47
  "test/helper.rb",
40
- "test/test_schmobile.rb"
48
+ "test/test_middleware.rb",
49
+ "test/test_rack_request.rb",
50
+ "test/test_user_agents.rb"
41
51
  ]
42
52
 
43
53
  if s.respond_to? :specification_version then
44
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
45
54
  s.specification_version = 3
46
55
 
47
56
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
48
- s.add_development_dependency(%q<rack>, ["= 1.1.0"])
57
+ s.add_development_dependency(%q<rack>, ["~> 1.1.0"])
49
58
  s.add_development_dependency(%q<mocha>, ["= 0.9.9"])
50
59
  s.add_development_dependency(%q<shoulda>, [">= 0"])
51
60
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
52
61
  s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
53
62
  else
54
- s.add_dependency(%q<rack>, ["= 1.1.0"])
63
+ s.add_dependency(%q<rack>, ["~> 1.1.0"])
55
64
  s.add_dependency(%q<mocha>, ["= 0.9.9"])
56
65
  s.add_dependency(%q<shoulda>, [">= 0"])
57
66
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
58
67
  s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
59
68
  end
60
69
  else
61
- s.add_dependency(%q<rack>, ["= 1.1.0"])
70
+ s.add_dependency(%q<rack>, ["~> 1.1.0"])
62
71
  s.add_dependency(%q<mocha>, ["= 0.9.9"])
63
72
  s.add_dependency(%q<shoulda>, [">= 0"])
64
73
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
data/test/helper.rb CHANGED
@@ -9,4 +9,63 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
9
9
  require 'rack/schmobile'
10
10
 
11
11
  class Test::Unit::TestCase
12
+ def ipod
13
+ 'Mozilla/5.0 (iPod; U; CPU iPhone OS 2_2 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5G77 Safari/525.20'
14
+ end
15
+
16
+ def iphone
17
+ 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7C144 Safari/528.16'
18
+ end
19
+
20
+ def android
21
+ 'Mozilla/5.0 (Linux; U; Android 2.0; ld-us; sdk Build/ECLAIR) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17'
22
+ end
23
+
24
+ def blackberry
25
+ 'BlackBerry9000/4.6.0.167 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/102'
26
+ end
27
+
28
+ def samsung
29
+ 'Mozilla/4.0 (compatible; MSIE 6.0; BREW 3.1.5; en )/800x480 Samsung SCH-U960'
30
+ end
31
+
32
+ def request(overwrite = {})
33
+ Rack::Request.new(environment(overwrite))
34
+ end
35
+
36
+ def environment(overwrite = {})
37
+ {
38
+ 'GATEWAY_INTERFACE'=> 'CGI/1.2',
39
+ 'HTTP_ACCEPT'=> 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
40
+ 'HTTP_ACCEPT_CHARSET'=> 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
41
+ 'HTTP_ACCEPT_ENCODING'=> 'gzip,deflate',
42
+ 'HTTP_ACCEPT_LANGUAGE'=> 'en-us,en;q=0.5',
43
+ 'HTTP_CONNECTION'=> 'keep-alive',
44
+ 'HTTP_HOST'=> 'localhost:4567',
45
+ 'HTTP_KEEP_ALIVE'=> 300,
46
+ 'HTTP_USER_AGENT'=> 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.3) Gecko/20090920 Firefox/3.5.3 (Swiftfox)',
47
+ 'HTTP_VERSION'=> 'HTTP/1.1',
48
+ 'PATH_INFO'=> '/',
49
+ 'QUERY_STRING'=> '',
50
+ 'REMOTE_ADDR'=> '127.0.0.1',
51
+ 'REQUEST_METHOD'=> 'GET',
52
+ 'REQUEST_PATH'=> '/',
53
+ 'REQUEST_URI'=> '/',
54
+ 'SCRIPT_NAME'=> '',
55
+ 'SERVER_NAME'=> 'localhost',
56
+ 'SERVER_PORT'=> '4567',
57
+ 'SERVER_PROTOCOL'=> 'HTTP/1.1',
58
+ 'SERVER_SOFTWARE'=> 'Mongrel 1.1.5',
59
+ 'rack.multiprocess'=> false,
60
+ 'rack.multithread'=> true,
61
+ 'rack.request.form_hash'=> '',
62
+ 'rack.request.form_vars'=> '',
63
+ 'rack.request.query_hash'=> '',
64
+ 'rack.request.query_string'=> '',
65
+ 'rack.run_once'=> false,
66
+ 'rack.url_scheme'=> 'http',
67
+ 'rack.version'=> '1: 0'
68
+ }.merge(overwrite)
69
+ end
70
+
12
71
  end
@@ -0,0 +1,69 @@
1
+ require 'helper'
2
+
3
+ class TestMiddleware < Test::Unit::TestCase
4
+
5
+ context "Rack::Schmobile::Middleware" do
6
+ setup do
7
+ @app = Class.new { def call(app); true; end }.new
8
+ @rack = Rack::Schmobile::Middleware.new(@app)
9
+ end
10
+
11
+ should "return an HTTP permanent redirect when given a redirect path and used by a mobile client" do
12
+ @rack = Rack::Schmobile::Middleware.new(@app, :redirect_to => "/hello")
13
+ Rack::Request.any_instance.expects(:is_mobile?).returns(true)
14
+
15
+ assert_equal [301, { "Location"=>"/hello" }, []], @rack.call(environment)
16
+ end
17
+
18
+ context "with no redirect_to" do
19
+ should "not redirect mobile traffic" do
20
+ Rack::Request.any_instance.expects(:params).returns({})
21
+ @app.expects(:call)
22
+ @rack.call(environment("HTTP_USER_AGENT" => iphone))
23
+ end
24
+
25
+ should "query the request object to determine if this is a mobile request" do
26
+ Rack::Request.any_instance.expects(:is_mobile?).returns(true)
27
+ @rack.call(environment("HTTP_USER_AGENT" => iphone))
28
+ end
29
+ end
30
+
31
+ context "with redirect_to" do
32
+ setup do
33
+ @rack = Rack::Schmobile::Middleware.new(@app, :redirect_to => "/wonderland")
34
+ end
35
+
36
+ context "#redirect?" do
37
+ should "return true when not on mobile path" do
38
+ assert @rack.redirect?(request("PATH_INFO" => "/somewhere"))
39
+ end
40
+
41
+ should "return true on base path" do
42
+ assert @rack.redirect?(request("PATH_INFO" => "/"))
43
+ end
44
+
45
+ should "return false when already on path" do
46
+ assert !@rack.redirect?(request("PATH_INFO" => "/wonderland"))
47
+ assert !@rack.redirect?(request("PATH_INFO" => "/wonderland/more/stuff"))
48
+ end
49
+
50
+ should "return false when :if resolves to false" do
51
+ @rack = Rack::Schmobile::Middleware.new(@app, :redirect_to => "/wonderland", :redirect_if => Proc.new { |request| false })
52
+ assert !@rack.redirect?(request("PATH_INFO" => "/somewhere"))
53
+ end
54
+ end
55
+
56
+ context "#redirect" do
57
+ should "interpolate the argument string" do
58
+ @rack = Rack::Schmobile::Middleware.new(@app, :redirect_to => "/wonderland", :redirect_with => "/{{path}}")
59
+ assert_equal "/wonderland/wiffle", @rack.redirect_location(request("PATH_INFO" => "wiffle"))
60
+ end
61
+
62
+ should "interpolate a multipart argument string" do
63
+ @rack = Rack::Schmobile::Middleware.new(@app, :redirect_to => "/wonderland/", :redirect_with => "{{path}}/lemurs/{{path}}")
64
+ assert_equal "/wonderland/wiffle/lemurs/wiffle", @rack.redirect_location(request("PATH_INFO" => "wiffle"))
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,51 @@
1
+ require 'helper'
2
+
3
+ class TestRackRequest < Test::Unit::TestCase
4
+
5
+ context "Rack::Request" do
6
+ context "#is_mobile?" do
7
+ context "without params" do
8
+ setup do
9
+ Rack::Request.any_instance.stubs(:params).returns({})
10
+ end
11
+
12
+ should "not detect a regular browser as mobile" do
13
+ assert !request.is_mobile?
14
+ assert !request("HTTP_USER_AGENT" => nil).is_mobile?
15
+ end
16
+
17
+ should "detect mobile units as mobile" do
18
+ [ :ipod, :iphone, :android, :blackberry, :samsung ].each do |phone|
19
+ agent = self.send(phone)
20
+ assert request("HTTP_USER_AGENT" => agent).is_mobile?
21
+ end
22
+ end
23
+
24
+ should "return true for a mobile browser" do
25
+ assert request("HTTP_USER_AGENT" => iphone).is_mobile?
26
+ end
27
+
28
+ should "return false when forced in the session" do
29
+ Rack::Request.any_instance.stubs(:session).returns({ Rack::Schmobile::IS_MOBILE => false })
30
+ assert !request("HTTP_USER_AGENT" => iphone).is_mobile?
31
+ end
32
+
33
+ should "return true when forced in the session" do
34
+ assert request("rack.session" => { Rack::Schmobile::IS_MOBILE => "true" }).is_mobile?
35
+ end
36
+ end
37
+
38
+ context "with params" do
39
+ should "return false when forced via a request parameter" do
40
+ Rack::Request.any_instance.stubs(:params).returns({ Rack::Schmobile::IS_MOBILE => "false" })
41
+ assert !request("HTTP_USER_AGENT" => iphone).is_mobile?
42
+ end
43
+
44
+ should "return true when forced via a request parameter" do
45
+ Rack::Request.any_instance.stubs(:params).returns({ Rack::Schmobile::IS_MOBILE => "true" })
46
+ assert request.is_mobile?
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,23 @@
1
+ require 'helper'
2
+
3
+ class TestUserAgents < Test::Unit::TestCase
4
+
5
+ context "Rack::Schmobile::UserAgents" do
6
+ context "#remove_user_agent_pattern" do
7
+ should "allow removal of a user agent" do
8
+ assert Rack::Schmobile::UserAgents.is_mobile_agent?("novarra")
9
+ Rack::Schmobile::UserAgents.remove_user_agent_pattern("novarra")
10
+ assert !Rack::Schmobile::UserAgents.is_mobile_agent?("novarra")
11
+ end
12
+ end
13
+
14
+ context "#add_user_agent_pattern" do
15
+ should "allow adding a user agent" do
16
+ assert !Rack::Schmobile::UserAgents.is_mobile_agent?("wibble")
17
+ Rack::Schmobile::UserAgents.add_user_agent_pattern("wibble")
18
+ assert Rack::Schmobile::UserAgents.is_mobile_agent?("wibble")
19
+ end
20
+ end
21
+ end
22
+
23
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schmobile
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
5
- prerelease: false
4
+ hash: 27
5
+ prerelease:
6
6
  segments:
7
7
  - 0
8
+ - 1
8
9
  - 0
9
- - 8
10
- version: 0.0.8
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Morten Primdahl
@@ -15,17 +15,15 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-02-17 00:00:00 -08:00
18
+ date: 2011-05-25 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- prerelease: false
23
22
  type: :development
24
- name: rack
25
- version_requirements: &id001 !ruby/object:Gem::Requirement
23
+ requirement: &id001 !ruby/object:Gem::Requirement
26
24
  none: false
27
25
  requirements:
28
- - - "="
26
+ - - ~>
29
27
  - !ruby/object:Gem::Version
30
28
  hash: 19
31
29
  segments:
@@ -33,12 +31,12 @@ dependencies:
33
31
  - 1
34
32
  - 0
35
33
  version: 1.1.0
36
- requirement: *id001
37
- - !ruby/object:Gem::Dependency
34
+ name: rack
35
+ version_requirements: *id001
38
36
  prerelease: false
37
+ - !ruby/object:Gem::Dependency
39
38
  type: :development
40
- name: mocha
41
- version_requirements: &id002 !ruby/object:Gem::Requirement
39
+ requirement: &id002 !ruby/object:Gem::Requirement
42
40
  none: false
43
41
  requirements:
44
42
  - - "="
@@ -49,12 +47,12 @@ dependencies:
49
47
  - 9
50
48
  - 9
51
49
  version: 0.9.9
52
- requirement: *id002
53
- - !ruby/object:Gem::Dependency
50
+ name: mocha
51
+ version_requirements: *id002
54
52
  prerelease: false
53
+ - !ruby/object:Gem::Dependency
55
54
  type: :development
56
- name: shoulda
57
- version_requirements: &id003 !ruby/object:Gem::Requirement
55
+ requirement: &id003 !ruby/object:Gem::Requirement
58
56
  none: false
59
57
  requirements:
60
58
  - - ">="
@@ -63,12 +61,12 @@ dependencies:
63
61
  segments:
64
62
  - 0
65
63
  version: "0"
66
- requirement: *id003
67
- - !ruby/object:Gem::Dependency
64
+ name: shoulda
65
+ version_requirements: *id003
68
66
  prerelease: false
67
+ - !ruby/object:Gem::Dependency
69
68
  type: :development
70
- name: bundler
71
- version_requirements: &id004 !ruby/object:Gem::Requirement
69
+ requirement: &id004 !ruby/object:Gem::Requirement
72
70
  none: false
73
71
  requirements:
74
72
  - - ~>
@@ -79,12 +77,12 @@ dependencies:
79
77
  - 0
80
78
  - 0
81
79
  version: 1.0.0
82
- requirement: *id004
83
- - !ruby/object:Gem::Dependency
80
+ name: bundler
81
+ version_requirements: *id004
84
82
  prerelease: false
83
+ - !ruby/object:Gem::Dependency
85
84
  type: :development
86
- name: jeweler
87
- version_requirements: &id005 !ruby/object:Gem::Requirement
85
+ requirement: &id005 !ruby/object:Gem::Requirement
88
86
  none: false
89
87
  requirements:
90
88
  - - ~>
@@ -95,7 +93,9 @@ dependencies:
95
93
  - 5
96
94
  - 2
97
95
  version: 1.5.2
98
- requirement: *id005
96
+ name: jeweler
97
+ version_requirements: *id005
98
+ prerelease: false
99
99
  description: Used to determine if a request is from a mobile client, and possibly redirect it if that's the case
100
100
  email: morten@zendesk.com
101
101
  executables: []
@@ -114,10 +114,18 @@ files:
114
114
  - Rakefile
115
115
  - VERSION
116
116
  - lib/rack/schmobile.rb
117
+ - lib/rack/schmobile/filters.rb
118
+ - lib/rack/schmobile/filters/is_mobile_param.rb
119
+ - lib/rack/schmobile/filters/mobile_user_agent.rb
120
+ - lib/rack/schmobile/middleware.rb
121
+ - lib/rack/schmobile/request_extension.rb
122
+ - lib/rack/schmobile/user_agents.rb
117
123
  - lib/schmobile.rb
118
124
  - schmobile.gemspec
119
125
  - test/helper.rb
120
- - test/test_schmobile.rb
126
+ - test/test_middleware.rb
127
+ - test/test_rack_request.rb
128
+ - test/test_user_agents.rb
121
129
  has_rdoc: true
122
130
  homepage: http://github.com/morten/schmobile
123
131
  licenses:
@@ -148,10 +156,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
156
  requirements: []
149
157
 
150
158
  rubyforge_project:
151
- rubygems_version: 1.3.7
159
+ rubygems_version: 1.6.2
152
160
  signing_key:
153
161
  specification_version: 3
154
162
  summary: A Rack middleware for detecting mobile user agents
155
163
  test_files:
156
164
  - test/helper.rb
157
- - test/test_schmobile.rb
165
+ - test/test_middleware.rb
166
+ - test/test_rack_request.rb
167
+ - test/test_user_agents.rb
@@ -1,208 +0,0 @@
1
- require 'helper'
2
-
3
- class TestSchmobile < Test::Unit::TestCase
4
- context "Rack::Request" do
5
- context "#is_mobile?" do
6
- should "not detect a regular browser as mobile" do
7
- assert !Rack::Request.new(environment).is_mobile?
8
- assert !Rack::Request.new("HTTP_USER_AGENT" => nil).is_mobile?
9
- end
10
-
11
- should "detect mobile units as mobile" do
12
- [ :ipod, :iphone, :android, :blackberry, :samsung ].each do |phone|
13
- agent = self.send(phone)
14
- assert Rack::Request.new(environment("HTTP_USER_AGENT" => agent)).is_mobile?
15
- end
16
- end
17
- end
18
- end
19
-
20
- context "Rack::Schmobile class" do
21
- context "#remove_user_agent_pattern" do
22
- should "allow removal of a user agent" do
23
- request = stub(:user_agent => "novarra")
24
- assert Rack::Schmobile.is_mobile_request?(request)
25
- Rack::Schmobile.remove_user_agent_pattern("novarra")
26
- assert !Rack::Schmobile.is_mobile_request?(request)
27
- end
28
- end
29
-
30
- context "#add_user_agent_pattern" do
31
- should "allow adding a user agent" do
32
- request = stub(:user_agent => "wibble")
33
- assert !Rack::Schmobile.is_mobile_request?(request)
34
- Rack::Schmobile.add_user_agent_pattern("wibble")
35
- assert Rack::Schmobile.is_mobile_request?(request)
36
- end
37
- end
38
- end
39
-
40
- context "Rack::Schmobile" do
41
- setup do
42
- @app = Class.new { def call(app); true; end }.new
43
- @rack = Rack::Schmobile.new(@app)
44
- end
45
-
46
- should "return an HTTP permanent redirect when given a redirect path and used by a mobile client" do
47
- @rack = Rack::Schmobile.new(@app, :redirect_to => "/hello")
48
- @rack.expects(:is_mobile_session?).returns(true)
49
-
50
- assert_equal [301, {"Location"=>"/hello"}, []], @rack.call(environment)
51
- end
52
-
53
- context "#is_mobile_session?" do
54
- should "return false for regular browsers" do
55
- Rack::Request.any_instance.expects(:params).returns({})
56
- assert !@rack.is_mobile_session?(environment, request)
57
- end
58
-
59
- should "return true for a mobile browser" do
60
- Rack::Request.any_instance.expects(:params).returns({})
61
- assert @rack.is_mobile_session?(environment, request("HTTP_USER_AGENT" => iphone))
62
- end
63
-
64
- should "return false when forced in the session" do
65
- Rack::Request.any_instance.stubs(:params).returns({})
66
- Rack::Request.any_instance.expects(:is_mobile?).never
67
-
68
- assert !@rack.is_mobile_session?(environment("HTTP_USER_AGENT" => iphone, "rack.session" => { Rack::Schmobile::SCHMOBILE_MODE => "disabled" }), request)
69
- end
70
-
71
- should "return true when forced in the session" do
72
- Rack::Request.any_instance.expects(:params).returns({})
73
- Rack::Request.any_instance.expects(:is_mobile?).never
74
-
75
- assert @rack.is_mobile_session?(environment("rack.session" => { Rack::Schmobile::SCHMOBILE_MODE => "enabled" }), request)
76
- end
77
-
78
- should "return false when forced via a request parameter" do
79
- Rack::Request.any_instance.stubs(:params).returns({ Rack::Schmobile::SCHMOBILE_MODE => "disabled" })
80
- Rack::Request.any_instance.expects(:is_mobile?).never
81
-
82
- assert !@rack.is_mobile_session?(environment, request("HTTP_USER_AGENT" => iphone))
83
- end
84
-
85
- should "return true when forced via a request parameter" do
86
- Rack::Request.any_instance.stubs(:params).returns({ Rack::Schmobile::SCHMOBILE_MODE => "enabled" })
87
- Rack::Request.any_instance.expects(:is_mobile?).never
88
-
89
- assert @rack.is_mobile_session?(environment, request)
90
- end
91
- end
92
-
93
- context "with no redirect_to" do
94
- should "not redirect mobile traffic" do
95
- Rack::Request.any_instance.expects(:params).returns({})
96
-
97
- @app.expects(:call)
98
- @rack.call(environment("HTTP_USER_AGENT" => iphone))
99
- end
100
-
101
- should "query the request object to determine if this is a mobile request" do
102
- Rack::Request.any_instance.expects(:params).returns({})
103
- Rack::Request.any_instance.expects(:is_mobile?).returns(true)
104
-
105
- @rack.call(environment("HTTP_USER_AGENT" => iphone))
106
- end
107
- end
108
-
109
- context "with redirect_to" do
110
- setup do
111
- @rack = Rack::Schmobile.new(@app, :redirect_to => "/wonderland")
112
- end
113
-
114
- context "#redirect?" do
115
- should "return true when not on mobile path" do
116
- assert @rack.redirect?(request("PATH_INFO" => "/somewhere"))
117
- end
118
-
119
- should "return true on base path" do
120
- assert @rack.redirect?(request("PATH_INFO" => "/"))
121
- end
122
-
123
- should "return false when already on path" do
124
- assert !@rack.redirect?(request("PATH_INFO" => "/wonderland"))
125
- assert !@rack.redirect?(request("PATH_INFO" => "/wonderland/more/stuff"))
126
- end
127
-
128
- should "return false when :if resolves to false" do
129
- @rack = Rack::Schmobile.new(@app, :redirect_to => "/wonderland", :redirect_if => Proc.new { |request| false })
130
- assert !@rack.redirect?(request("PATH_INFO" => "/somewhere"))
131
- end
132
- end
133
-
134
- context "#redirect" do
135
- should "interpolate the argument string" do
136
- @rack = Rack::Schmobile.new(@app, :redirect_to => "/wonderland", :redirect_with => "/{{path}}")
137
- assert_equal "/wonderland/wiffle", @rack.redirect_location(request("PATH_INFO" => "wiffle"))
138
- end
139
-
140
- should "interpolate a multipart argument string" do
141
- @rack = Rack::Schmobile.new(@app, :redirect_to => "/wonderland/", :redirect_with => "{{path}}/lemurs/{{path}}")
142
- assert_equal "/wonderland/wiffle/lemurs/wiffle", @rack.redirect_location(request("PATH_INFO" => "wiffle"))
143
- end
144
- end
145
- end
146
-
147
- end
148
-
149
- # User agents for testing
150
- def ipod
151
- 'Mozilla/5.0 (iPod; U; CPU iPhone OS 2_2 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5G77 Safari/525.20'
152
- end
153
-
154
- def iphone
155
- 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7C144 Safari/528.16'
156
- end
157
-
158
- def android
159
- 'Mozilla/5.0 (Linux; U; Android 2.0; ld-us; sdk Build/ECLAIR) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17'
160
- end
161
-
162
- def blackberry
163
- 'BlackBerry9000/4.6.0.167 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/102'
164
- end
165
-
166
- def samsung
167
- 'Mozilla/4.0 (compatible; MSIE 6.0; BREW 3.1.5; en )/800x480 Samsung SCH-U960'
168
- end
169
-
170
- def request(overwrite = {})
171
- Rack::Request.new(environment(overwrite))
172
- end
173
-
174
- def environment(overwrite = {})
175
- {
176
- 'GATEWAY_INTERFACE'=> 'CGI/1.2',
177
- 'HTTP_ACCEPT'=> 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
178
- 'HTTP_ACCEPT_CHARSET'=> 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
179
- 'HTTP_ACCEPT_ENCODING'=> 'gzip,deflate',
180
- 'HTTP_ACCEPT_LANGUAGE'=> 'en-us,en;q=0.5',
181
- 'HTTP_CONNECTION'=> 'keep-alive',
182
- 'HTTP_HOST'=> 'localhost:4567',
183
- 'HTTP_KEEP_ALIVE'=> 300,
184
- 'HTTP_USER_AGENT'=> 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.3) Gecko/20090920 Firefox/3.5.3 (Swiftfox)',
185
- 'HTTP_VERSION'=> 'HTTP/1.1',
186
- 'PATH_INFO'=> '/',
187
- 'QUERY_STRING'=> '',
188
- 'REMOTE_ADDR'=> '127.0.0.1',
189
- 'REQUEST_METHOD'=> 'GET',
190
- 'REQUEST_PATH'=> '/',
191
- 'REQUEST_URI'=> '/',
192
- 'SCRIPT_NAME'=> '',
193
- 'SERVER_NAME'=> 'localhost',
194
- 'SERVER_PORT'=> '4567',
195
- 'SERVER_PROTOCOL'=> 'HTTP/1.1',
196
- 'SERVER_SOFTWARE'=> 'Mongrel 1.1.5',
197
- 'rack.multiprocess'=> false,
198
- 'rack.multithread'=> true,
199
- 'rack.request.form_hash'=> '',
200
- 'rack.request.form_vars'=> '',
201
- 'rack.request.query_hash'=> '',
202
- 'rack.request.query_string'=> '',
203
- 'rack.run_once'=> false,
204
- 'rack.url_scheme'=> 'http',
205
- 'rack.version'=> '1: 0'
206
- }.merge(overwrite)
207
- end
208
- end