├── spec ├── spec_helper.rb └── mobilized_styles_spec.rb ├── lib ├── mobile-fu │ ├── version.rb │ └── helper.rb └── mobile-fu.rb ├── .gitignore ├── Gemfile ├── Rakefile ├── MIT-LICENSE ├── mobile-fu.gemspec ├── CHANGELOG └── README.md /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'mobile-fu' 2 | -------------------------------------------------------------------------------- /lib/mobile-fu/version.rb: -------------------------------------------------------------------------------- 1 | module MobileFu 2 | VERSION = "1.1.1" 3 | end 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | .bundle 3 | Gemfile.lock 4 | pkg/* 5 | vendor/ruby 6 | *.DS_Store -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | # Specify your gem's dependencies in mobile-fu.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'bundler' 3 | Bundler::GemHelper.install_tasks 4 | 5 | require 'rspec/core/rake_task' 6 | RSpec::Core::RakeTask.new('spec') 7 | task :default => :spec 8 | 9 | # desc 'Generate documentation for the mobile_fu plugin.' 10 | # Rake::RDocTask.new(:rdoc) do |rdoc| 11 | # rdoc.rdoc_dir = 'rdoc' 12 | # rdoc.title = 'MobileFu' 13 | # rdoc.options << '--line-numbers' << '--inline-source' 14 | # rdoc.rdoc_files.include('README') 15 | # rdoc.rdoc_files.include('lib/**/*.rb') 16 | # end 17 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008 Brendan G. Lim, Intridea, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /spec/mobilized_styles_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe MobileFu::MobilizedStyles do 4 | 5 | before(:each) do 6 | @view = mock :ActionView 7 | @request = mock :ActionRequest 8 | @view.extend MobileFu::MobilizedStyles 9 | @view.stub!(:request).and_return @request 10 | @request.stub! :user_agent 11 | end 12 | 13 | def ua(str) 14 | @request.stub!(:user_agent).and_return str 15 | end 16 | 17 | it "will include a mobilized css file if it recognizes a string in the user agent" do 18 | ua "iphone" 19 | File.should_receive(:exist?).with("stylesheets/style_iphone.css").and_return true 20 | @view.should_receive(:stylesheet_link_tag).with "style", "style_iphone" 21 | @view.stylesheet_link_tag_with_mobilization "style" 22 | end 23 | 24 | it "includes mobiziled css files whether or not the original call to stylesheet_link_tag used a file extension" do 25 | ua "blackberry" 26 | File.should_receive(:exist?).with("stylesheets/style_blackberry.css").and_return true 27 | @view.should_receive(:stylesheet_link_tag).with "style.css", "style_blackberry" 28 | @view.stylesheet_link_tag_with_mobilization "style.css" 29 | end 30 | end -------------------------------------------------------------------------------- /mobile-fu.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "mobile-fu/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "mobile-fu" 7 | s.version = MobileFu::VERSION 8 | s.platform = Gem::Platform::RUBY 9 | s.authors = ["Brendan Lim", "Ben Langfeld"] 10 | s.email = ["brendangl@gmail.com, ben@langfeld.me"] 11 | s.homepage = "https://github.com/benlangfeld/mobile-fu" 12 | s.summary = %q{Automatically detect mobile requests from mobile devices in your Rails application.} 13 | s.description = %q{Want to automatically detect mobile devices that access your Rails application? Mobile Fu allows you to do just that. People can access your site from a Palm, Blackberry, iPhone, iPad, Nokia, etc. and it will automatically adjust the format of the request from :html to :mobile.} 14 | 15 | s.rubyforge_project = "mobile-fu" 16 | 17 | s.files = ["CHANGELOG", "MIT-LICENSE", "README.md", "lib/mobile-fu.rb", "lib/mobile-fu/helper.rb", "lib/mobile-fu/version.rb" ] 18 | s.test_files = [ 'spec/mobilized_styles_spec.rb', 'spec/spec_helper.rb' ] 19 | s.executables = [ ] 20 | s.require_path = 'lib' 21 | 22 | s.add_dependency 'rails' 23 | s.add_dependency 'rack-mobile-detect' 24 | s.add_development_dependency 'rspec' 25 | s.add_development_dependency 'rdoc' 26 | end 27 | -------------------------------------------------------------------------------- /lib/mobile-fu/helper.rb: -------------------------------------------------------------------------------- 1 | module MobileFu 2 | module Helper 3 | def js_enabled_mobile_device? 4 | is_device?('iphone') || is_device?('ipod') || is_device?('ipad') || is_device?('mobileexplorer') || is_device?('android') 5 | end 6 | 7 | def stylesheet_link_tag_with_mobilization(*sources) 8 | mobilized_sources = Array.new 9 | 10 | # Figure out where stylesheets live, which differs depending if the asset 11 | # pipeline is used or not. 12 | stylesheets_dir = config.stylesheets_dir # Rails.root/public/stylesheets 13 | 14 | # Look for mobilized stylesheets in the app/assets path if asset pipeline 15 | # is enabled, because public/stylesheets will be missing in development 16 | # mode without precompiling assets first, and may also contain multiple 17 | # stylesheets with similar names because of checksumming during 18 | # precompilation. 19 | if Rails.application.config.respond_to?(:assets) # don't break pre-rails3.1 20 | if Rails.application.config.assets.enabled 21 | stylesheets_dir = File.join(Rails.root, 'app/assets/stylesheets/') 22 | end 23 | end 24 | 25 | device_names = respond_to?(:is_mobile_device?) && is_mobile_device? ? ['mobile', mobile_device.downcase] : [] 26 | 27 | sources.each do |source| 28 | mobilized_sources << source 29 | 30 | device_names.compact.each do |device_name| 31 | # support ERB and/or SCSS extensions (e.g., mobile.css.erb, mobile.css.scss.erb) 32 | possible_source = source.to_s.sub(/\.css.*$/, '') + "_#{device_name}" 33 | 34 | mobilized_files = Dir.glob(File.join(stylesheets_dir, "#{possible_source}.css*")).map { |f| f.sub(stylesheets_dir, '') } 35 | mobilized_sources += mobilized_files.map { |f| f.sub(/\.css.*/, '') } 36 | end 37 | end 38 | 39 | stylesheet_link_tag_without_mobilization *mobilized_sources 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | commit a46e0d5ff0a95407bbd8f3437a372ced53d90c9b 2 | Author: Brendan G. Lim 3 | Date: Sat Dec 20 20:59:49 2008 -0500 4 | 5 | Added a helper method to determine if a device is JavaScript enabled and one to output the XHTML Mobile 1.0 doctype 6 | 7 | commit f4b29b2e2c68a79b8904dddb7a91efd4341540ee 8 | Author: Brendan G. Lim 9 | Date: Sun Sep 21 21:39:10 2008 -0400 10 | 11 | Fixes issue with 'in_mobile_view' instead of 'is_mobile_view' helper method 12 | 13 | commit 29e8e8b407dd3dc8d5f3248183223d11c6a78843 14 | Author: Brendan G. Lim 15 | Date: Wed Sep 10 12:51:12 2008 -0400 16 | 17 | Updating CHANGELOG 18 | 19 | commit 0aac5bd7f0d2f71e6af7b144a04a18dad9f5aca5 20 | Author: Brendan G. Lim 21 | Date: Wed Sep 10 12:50:50 2008 -0400 22 | 23 | Styling overrides for iPods 24 | 25 | commit f30c97c794ba74e737856aa9226328469c8395b6 26 | Author: Brendan G. Lim 27 | Date: Mon Sep 8 00:07:27 2008 -0400 28 | 29 | Adding in some comments to the latest changes 30 | 31 | commit 78dae861e46d383356d7e4d6dfdaefbf60072e27 32 | Author: Brendan G. Lim 33 | Date: Mon Sep 8 00:06:16 2008 -0400 34 | 35 | Updating README 36 | 37 | commit 1a66ccd2142e387f6d3138acb688a58df4687fcd 38 | Author: Brendan G. Lim 39 | Date: Mon Sep 8 00:05:48 2008 -0400 40 | 41 | Adding in a helper method to check for a specific mobile device and adding in 'ipod' to the list of mobile user agents 42 | 43 | commit cbb26fff3287e16a757ee2aed2dcc8e98cbfb369 44 | Author: Brendan G. Lim 45 | Date: Fri Aug 22 22:16:19 2008 -0400 46 | 47 | Updating the CHANGELOG 48 | 49 | commit ee49945b593b1c55bf546da1d595a0941d071f94 50 | Author: Brendan G. Lim 51 | Date: Fri Aug 22 22:16:00 2008 -0400 52 | 53 | Updating README 54 | 55 | commit 02c12a8563466f7cef6aba8e53915dc2efec908c 56 | Author: Brendan G. Lim 57 | Date: Mon Aug 18 16:08:51 2008 -0400 58 | 59 | Adding in support for Android 60 | 61 | commit 2dffa5958905c0419288d03a0650127a0734b41a 62 | Author: Brendan G. Lim 63 | Date: Sat Jul 26 14:08:56 2008 -0400 64 | 65 | Adding in other phone user agents, including some Japanese and French manufacturers 66 | 67 | commit e8f9f1a9c2e668d639690f4f1beb4f701f742cfa 68 | Author: Brendan G. Lim 69 | Date: Fri Jul 18 11:15:52 2008 -0400 70 | 71 | Adding in CHANGELOG 72 | 73 | commit a3122b40c0c722b2942e9cb5341614d0e0d834a5 74 | Author: Brendan G. Lim 75 | Date: Fri Jul 18 11:14:35 2008 -0400 76 | 77 | Changes to README 78 | 79 | commit 64a24e36b6a770757963eb2d961262fb8a14ec1b 80 | Author: Brendan G. Lim 81 | Date: Fri Jul 18 11:10:40 2008 -0400 82 | 83 | Quick fix 84 | 85 | commit 116fa5b7c1168471cf56cb9961b98f0b73fc3308 86 | Author: Brendan G. Lim 87 | Date: Fri Jul 18 10:04:41 2008 -0400 88 | 89 | Fixed issue with mobile view session variable getting set incorrectly, when forcing mobile format 90 | 91 | commit d172665c9d0458801fd672076e269a3c0ee9d80d 92 | Author: Brendan G. Lim 93 | Date: Fri Jul 18 01:04:19 2008 -0400 94 | 95 | Adding in more MIT license goodness 96 | 97 | commit a6c947d684d2d9abdbe354dc1ea757f23cd5a633 98 | Author: Brendan G. Lim 99 | Date: Fri Jul 18 01:00:55 2008 -0400 100 | 101 | Change to the README 102 | 103 | commit dad4f4db1f3cb816283069a91c01982c953ff8a5 104 | Author: Brendan G. Lim 105 | Date: Fri Jul 18 01:00:16 2008 -0400 106 | 107 | Change to the README 108 | 109 | commit c5ec9de6ef19e48a66917d8f6f3a59771295765b 110 | Author: Brendan G. Lim 111 | Date: Fri Jul 18 00:59:36 2008 -0400 112 | 113 | Releasing Mobile Fu to the world 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Mobile Fu 2 | ========= 3 | 4 | Want to automatically detect mobile devices that access your Rails application? 5 | Mobile Fu allows you to do just that. People can access your site from a Palm, 6 | Blackberry, iPhone, iPad, Nokia, etc. and it will automatically adjust the format 7 | of the request from :html to :mobile or :tablet. 8 | 9 | Installation 10 | ------------ 11 | 12 | Simply add `gem 'mobile-fu'` to your Gemfile and run bundle install. 13 | 14 | Usage 15 | ----- 16 | 17 | Add this this one line to the controller. 18 | 19 | ```ruby 20 | class ApplicationController < ActionController::Base 21 | has_mobile_fu 22 | end 23 | ``` 24 | 25 | Once this is in place, any request that comes from a mobile device will be be 26 | set as :mobile format. It is up to you to determine how you want to handle 27 | these requests. It is also up to you to create the .mobile.erb versions of 28 | your views that are to be requested. 29 | 30 | If you do not want to set the format to :mobile or :tablet and only use the 31 | helper functions, pass false as an argument. 32 | 33 | ```ruby 34 | class ApplicationController < ActionController::Base 35 | has_mobile_fu false 36 | end 37 | ``` 38 | 39 | Mobile Fu automatically adds a new `:mobile` and `:tablet` to `text/html` mime type 40 | alias for Rails apps. If you already have a custom `:mobile` alias registered in 41 | `config/initializers/mime_types.rb`, you can remove that. 42 | 43 | I recommend that you setup a before_filter that will redirect to a specific page 44 | depending on whether or not it is a mobile request. How can you check this? 45 | 46 | ```ruby 47 | is_mobile_device? # => Returns true or false depending on the device or 48 | 49 | is_tablet_device? # => Returns true if the device is a tablet 50 | ``` 51 | 52 | You can also determine which format is currently set in by calling the following: 53 | 54 | ```ruby 55 | in_mobile_view? # => Returns true or false depending on current req. format or 56 | 57 | in_tablet_view? # => Returns true if the current req. format is for tablet view 58 | ``` 59 | 60 | Also, if you want the ability to allow a user to switch between 'mobile' and 61 | 'standard' format (:html), you can just adjust the mobile_view session variable 62 | in a custom controller action. 63 | 64 | ```ruby 65 | session[:mobile_view] # => Set to true if request format is :mobile and false 66 | if set to :html 67 | 68 | session[:tablet_view] # => Set to true if request format is :tablet and false 69 | if set to :html 70 | ``` 71 | 72 | So, different devices need different styling. Don't worry, we've got this 73 | baked in to Mobile Fu. 74 | 75 | If you are including a css or sass file via `stylesheet_link_tag`, all you have 76 | to do is add _device to the name of one of your files to override your styling 77 | for a certain device. The stylesheet that is loaded is dependant on which device 78 | is making the request. 79 | 80 | e.g., Accessing a page from a Blackberry. 81 | 82 | ```ruby 83 | stylesheet_link_tag 'mobile.css' 84 | ``` 85 | 86 | This loads mobile.css, and mobile_blackberry.css if the file exists. 87 | 88 | Supported stylesheet override device extensions at the moment are: 89 | 90 | * blackberry 91 | * iphone (iphone,ipod) 92 | * ipad 93 | * android 94 | * mobileexplorer 95 | * nokia 96 | * palm 97 | 98 | The stylesheet awesomeness was derived from [Michael Bleigh's browserized styles](http://www.intridea.com/2007/12/9/announcing-browserized-styles) 99 | 100 | Inspiration for Mobile Fu came from [Noel Rappin's rails_iui](http://blogs.pathf.com/agileajax/2008/05/rails-developme.html) 101 | 102 | Hopefully this should help you create some awesome mobile applications. 103 | 104 | Testing Mobile Interface 105 | ------------------------ 106 | 107 | If you want to force the mobile interface for testing, you can either use a 108 | mobile device emulator, or you can call `force_mobile_format` in a before filter. 109 | 110 | ```ruby 111 | class ApplicationController < ActionController::Base 112 | has_mobile_fu 113 | before_filter :force_mobile_format 114 | end 115 | ``` 116 | 117 | You can also force the tablet view by calling `force_tablet_format` instead 118 | 119 | ```ruby 120 | class ApplicationController < ActionController::Base 121 | has_mobile_fu 122 | before_filter :force_tablet_format 123 | end 124 | ``` 125 | 126 | Copyright (c) 2008 Brendan G. Lim, Intridea, Inc., released under the MIT license 127 | -------------------------------------------------------------------------------- /lib/mobile-fu.rb: -------------------------------------------------------------------------------- 1 | require 'rails' 2 | require 'rack/mobile-detect' 3 | 4 | module MobileFu 5 | autoload :Helper, 'mobile-fu/helper' 6 | 7 | class Railtie < Rails::Railtie 8 | initializer "mobile-fu.configure" do |app| 9 | app.config.middleware.use Rack::MobileDetect 10 | end 11 | 12 | if Rails::VERSION::MAJOR >= 3 13 | initializer "mobile-fu.action_controller" do |app| 14 | ActiveSupport.on_load :action_controller do 15 | include ActionController::MobileFu 16 | end 17 | end 18 | 19 | initializer "mobile-fu.action_view" do |app| 20 | ActiveSupport.on_load :action_view do 21 | include MobileFu::Helper 22 | alias_method_chain :stylesheet_link_tag, :mobilization 23 | end 24 | end 25 | end 26 | 27 | Mime::Type.register_alias "text/html", :mobile 28 | Mime::Type.register_alias "text/html", :tablet 29 | end 30 | end 31 | 32 | module ActionController 33 | module MobileFu 34 | # These are various strings that can be found in tablet devices. Please feel free 35 | # to add on to this list. 36 | TABLET_USER_AGENTS = /ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle|honeycomb|nexus 7/.freeze 37 | 38 | def self.included(base) 39 | base.extend ClassMethods 40 | end 41 | 42 | module ClassMethods 43 | 44 | # Add this to one of your controllers to use MobileFu. 45 | # 46 | # class ApplicationController < ActionController::Base 47 | # has_mobile_fu 48 | # end 49 | # 50 | # If you don't want mobile_fu to set the request format automatically, 51 | # you can pass false here. 52 | # 53 | # class ApplicationController < ActionController::Base 54 | # has_mobile_fu false 55 | # end 56 | # 57 | def has_mobile_fu(set_request_format = true) 58 | include ActionController::MobileFu::InstanceMethods 59 | 60 | before_filter :set_request_format if set_request_format 61 | 62 | helper_method :is_mobile_device? 63 | helper_method :is_tablet_device? 64 | helper_method :in_mobile_view? 65 | helper_method :in_tablet_view? 66 | helper_method :is_device? 67 | helper_method :mobile_device 68 | end 69 | 70 | # Add this to your controllers to prevent the mobile format from being set for specific actions 71 | # class AwesomeController < ApplicationController 72 | # has_no_mobile_fu_for :index 73 | # 74 | # def index 75 | # # Mobile format will not be set, even if user is on a mobile device 76 | # end 77 | # 78 | # def show 79 | # # Mobile format will be set as normal here if user is on a mobile device 80 | # end 81 | # end 82 | def has_no_mobile_fu_for(*actions) 83 | @mobile_exempt_actions = actions 84 | end 85 | end 86 | 87 | module InstanceMethods 88 | def set_request_format(force_mobile = false) 89 | force_mobile ? force_mobile_format : set_mobile_format 90 | end 91 | alias :set_device_type :set_request_format 92 | 93 | # Forces the request format to be :mobile 94 | def force_mobile_format 95 | unless request.xhr? 96 | request.format = :mobile 97 | session[:mobile_view] = true if session[:mobile_view].nil? 98 | end 99 | end 100 | 101 | # Forces the request format to be :tablet 102 | def force_tablet_format 103 | unless request.xhr? 104 | request.format = :tablet 105 | session[:tablet_view] = true if session[:tablet_view].nil? 106 | end 107 | end 108 | 109 | # Determines the request format based on whether the device is mobile or if 110 | # the user has opted to use either the 'Standard' view or 'Mobile' view or 111 | # 'Tablet' view. 112 | 113 | def set_mobile_format 114 | if !mobile_exempt? && is_mobile_device? && !request.xhr? 115 | request.format = :mobile unless session[:mobile_view] == false 116 | session[:mobile_view] = true if session[:mobile_view].nil? 117 | elsif !mobile_exempt? && is_tablet_device? && !request.xhr? 118 | request.format = :tablet unless session[:tablet_view] == false 119 | session[:tablet_view] = true if session[:tablet_view].nil? 120 | end 121 | end 122 | 123 | # Returns either true or false depending on whether or not the format of the 124 | # request is either :mobile or not. 125 | 126 | def in_mobile_view? 127 | return false unless request.format 128 | request.format.to_sym == :mobile 129 | end 130 | 131 | # Returns either true or false depending on whether or not the format of the 132 | # request is either :tablet or not. 133 | 134 | def in_tablet_view? 135 | return false unless request.format 136 | request.format.to_sym == :tablet 137 | end 138 | 139 | # Returns either true or false depending on whether or not the user agent of 140 | # the device making the request is matched to a device in our regex. 141 | 142 | def is_tablet_device? 143 | !!(request.user_agent.to_s.downcase =~ ActionController::MobileFu::TABLET_USER_AGENTS) 144 | end 145 | 146 | def is_mobile_device? 147 | !is_tablet_device? && !!mobile_device 148 | end 149 | 150 | def mobile_device 151 | request.headers['X_MOBILE_DEVICE'] 152 | end 153 | 154 | # Can check for a specific user agent 155 | # e.g., is_device?('iphone') or is_device?('mobileexplorer') 156 | 157 | def is_device?(type) 158 | request.user_agent.to_s.downcase.include? type.to_s.downcase 159 | end 160 | 161 | # Returns true if current action isn't supposed to use mobile format 162 | # See #has_no_mobile_fu_for 163 | 164 | def mobile_exempt? 165 | self.class.instance_variable_get("@mobile_exempt_actions").try(:include?, params[:action].to_sym) 166 | end 167 | end 168 | end 169 | end 170 | 171 | if Rails::VERSION::MAJOR < 3 172 | ActionController::Base.send :include, ActionController::MobileFu 173 | ActionView::Base.send :include, MobileFu::Helper 174 | ActionView::Base.send :alias_method_chain, :stylesheet_link_tag, :mobilization 175 | end 176 | --------------------------------------------------------------------------------