schmobile 0.0.8 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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