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 +1 -1
- data/Gemfile.lock +1 -1
- data/README.rdoc +9 -6
- data/VERSION +1 -1
- data/lib/rack/schmobile/filters/is_mobile_param.rb +15 -0
- data/lib/rack/schmobile/filters/mobile_user_agent.rb +13 -0
- data/lib/rack/schmobile/filters.rb +2 -0
- data/lib/rack/schmobile/middleware.rb +61 -0
- data/lib/rack/schmobile/request_extension.rb +10 -0
- data/lib/rack/schmobile/user_agents.rb +32 -0
- data/lib/rack/schmobile.rb +6 -105
- data/schmobile.gemspec +18 -9
- data/test/helper.rb +59 -0
- data/test/test_middleware.rb +69 -0
- data/test/test_rack_request.rb +51 -0
- data/test/test_user_agents.rb +23 -0
- metadata +39 -29
- data/test/test_schmobile.rb +0 -208
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
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
|
-
|
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
|
-
|
18
|
-
require session access.
|
19
|
+
== Forcing mobile mode
|
19
20
|
|
20
|
-
|
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.
|
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
|
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,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,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
|
data/lib/rack/schmobile.rb
CHANGED
@@ -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
|
-
|
6
|
-
|
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
|
+
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-
|
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/
|
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.
|
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/
|
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>, ["
|
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>, ["
|
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>, ["
|
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:
|
5
|
-
prerelease:
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
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-
|
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
|
-
|
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
|
-
|
37
|
-
|
34
|
+
name: rack
|
35
|
+
version_requirements: *id001
|
38
36
|
prerelease: false
|
37
|
+
- !ruby/object:Gem::Dependency
|
39
38
|
type: :development
|
40
|
-
|
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
|
-
|
53
|
-
|
50
|
+
name: mocha
|
51
|
+
version_requirements: *id002
|
54
52
|
prerelease: false
|
53
|
+
- !ruby/object:Gem::Dependency
|
55
54
|
type: :development
|
56
|
-
|
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
|
-
|
67
|
-
|
64
|
+
name: shoulda
|
65
|
+
version_requirements: *id003
|
68
66
|
prerelease: false
|
67
|
+
- !ruby/object:Gem::Dependency
|
69
68
|
type: :development
|
70
|
-
|
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
|
-
|
83
|
-
|
80
|
+
name: bundler
|
81
|
+
version_requirements: *id004
|
84
82
|
prerelease: false
|
83
|
+
- !ruby/object:Gem::Dependency
|
85
84
|
type: :development
|
86
|
-
|
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
|
-
|
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/
|
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.
|
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/
|
165
|
+
- test/test_middleware.rb
|
166
|
+
- test/test_rack_request.rb
|
167
|
+
- test/test_user_agents.rb
|
data/test/test_schmobile.rb
DELETED
@@ -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
|