├── install.rb ├── uninstall.rb ├── spec ├── spec.opts ├── spec_helper.rb └── mobilized_styles_spec.rb ├── init.rb ├── Rakefile ├── MIT-LICENSE ├── lib ├── mobile_fu_helper.rb ├── mobilized_styles.rb └── mobile_fu.rb ├── README.rdoc └── CHANGELOG /install.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /uninstall.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/spec.opts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + "/../lib/mobilized_styles" -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/lib/mobile_fu_helper.rb' 2 | require File.dirname(__FILE__) + '/lib/mobilized_styles' 3 | require File.dirname(__FILE__) + '/lib/mobile_fu' 4 | 5 | ActionView::Base.send(:include, MobileFuHelper) 6 | ActionView::Base.send(:include, MobilizedStyles) 7 | ActionView::Base.send(:alias_method_chain, :stylesheet_link_tag, :mobilization) -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rake/testtask' 3 | require 'rake/rdoctask' 4 | 5 | desc 'Default: run unit tests.' 6 | task :default => :test 7 | 8 | desc 'Test the sms_fu plugin.' 9 | Rake::TestTask.new(:test) do |t| 10 | t.libs << 'lib' 11 | t.pattern = 'test/**/*_test.rb' 12 | t.verbose = true 13 | end 14 | 15 | desc 'Generate documentation for the sms_fu plugin.' 16 | Rake::RDocTask.new(:rdoc) do |rdoc| 17 | rdoc.rdoc_dir = 'rdoc' 18 | rdoc.title = 'SmsFu' 19 | rdoc.options << '--line-numbers' << '--inline-source' 20 | rdoc.rdoc_files.include('README') 21 | rdoc.rdoc_files.include('lib/**/*.rb') 22 | end 23 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/mobile_fu_helper.rb: -------------------------------------------------------------------------------- 1 | module MobileFuHelper 2 | ACCEPTABLE_TYPES = [:mobile, :basic] 3 | 4 | def mobile_xhtml_doctype(type = :mobile, version = '1.0') 5 | raise Exception.new("MobileFu: XHTML DOCTYPE type must either be ':mobile' or ':basic'") unless ACCEPTABLE_TYPES.include?(type) 6 | raise Exception.new("MobileFu: XHTML DOCTYPE version must be in the format of '1.0' or '1.1', etc.") unless version.include?('.') 7 | 8 | doc_type = "\n" 9 | doc_type += "" 13 | when :basic 14 | "\"-//W3C//DTD XHTML Basic #{version}//EN\" \"http://www.w3.org/TR/xhtml-basic/xhtml-basic#{version.gsub('.','')}.dtd\">" 15 | end 16 | doc_type 17 | end 18 | 19 | def js_enabled_mobile_device? 20 | is_device?('iphone') || is_device?('ipod') || is_device?('ipad') || is_device?('mobileexplorer') || is_device?('android') 21 | end 22 | end 23 | 24 | ActionView::Base.send(:include, MobileFuHelper) 25 | -------------------------------------------------------------------------------- /spec/mobilized_styles_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require File.dirname(__FILE__) + '/spec_helper' 3 | 4 | module ActionView 5 | module Helpers 6 | module AssetTagHelper 7 | STYLESHEETS_DIR = "stylesheets" 8 | end 9 | end 10 | end 11 | 12 | describe MobilizedStyles do 13 | before(:each) do 14 | @view = mock(:ActionView) 15 | @request = mock(:ActionRequest) 16 | @view.extend(MobilizedStyles) 17 | @view.stub!(:request).and_return(@request) 18 | @request.stub!(:user_agent) 19 | end 20 | 21 | def ua(str) 22 | @request.stub!(:user_agent).and_return(str) 23 | end 24 | 25 | it "will include a mobilized css file if it recognizes a string in the user agent" do 26 | ua "iphone" 27 | File.should_receive(:exist?).with("stylesheets/style_iphone.css").and_return(true) 28 | @view.should_receive(:stylesheet_link_tag_without_mobilization).with("style", "style_iphone") 29 | @view.stylesheet_link_tag_with_mobilization("style") 30 | end 31 | 32 | it "includes mobiziled css files whether or not the original call to stylesheet_link_tag used a file extension" do 33 | ua "blackberry" 34 | File.should_receive(:exist?).with("stylesheets/style_blackberry.css").and_return(true) 35 | @view.should_receive(:stylesheet_link_tag_without_mobilization).with("style.css", "style_blackberry") 36 | @view.stylesheet_link_tag_with_mobilization("style.css") 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/mobilized_styles.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2008 Brendan G. Lim (brendan@intridea.com) 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 | 22 | module MobilizedStyles 23 | 24 | # This logic was taken from Michael Bleigh's browserized styles 25 | # with modification to work for mobile browsers. 26 | 27 | def user_agent_device_name 28 | @user_agent_device_name ||= begin 29 | 30 | ua = request.user_agent 31 | return nil if ua.nil? 32 | ua.downcase! 33 | 34 | if ua.index('mobileexplorer') || ua.index('windows ce') 35 | 'mobileexplorer' 36 | elsif ua.index('blackberry') 37 | 'blackberry' 38 | elsif ua.index('iphone') || ua.index('ipod') 39 | 'iphone' 40 | elsif ua.index('ipad') 41 | 'ipad' 42 | elsif ua.index('android') 43 | 'android' 44 | elsif ua.index('nokia') 45 | 'nokia' 46 | elsif ua.index('palm') 47 | 'palm' 48 | end 49 | end 50 | end 51 | 52 | def stylesheet_link_tag_with_mobilization(*sources) 53 | mobilized_sources = Array.new 54 | sources.each do |source| 55 | subbed_source = source.to_s.gsub(".css","") 56 | 57 | possible_sources = ["#{subbed_source.to_s}_#{user_agent_device_name}"] 58 | 59 | mobilized_sources << source 60 | 61 | for possible_source in possible_sources 62 | path = File.join(config.stylesheets_dir,"#{possible_source}.css") 63 | sass_path = File.join(config.stylesheets_dir,"sass","#{possible_source}.sass") 64 | mobilized_sources << possible_source if File.exist?(path) || File.exist?(sass_path) 65 | end 66 | end 67 | 68 | stylesheet_link_tag_without_mobilization(*mobilized_sources) 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /lib/mobile_fu.rb: -------------------------------------------------------------------------------- 1 | module ActionController 2 | module MobileFu 3 | # These are various strings that can be found in mobile devices. Please feel free 4 | # to add on to this list. 5 | MOBILE_USER_AGENTS = 'palm|blackberry|nokia|phone|midp|mobi|symbian|chtml|ericsson|minimo|' + 6 | 'audiovox|motorola|samsung|telit|upg1|windows ce|ucweb|astel|plucker|' + 7 | 'x320|x240|j2me|sgh|portable|sprint|docomo|kddi|softbank|android|mmp|' + 8 | 'pdxgw|netfront|xiino|vodafone|portalmmm|sagem|mot-|sie-|ipod|up\\.b|' + 9 | 'webos|amoi|novarra|cdm|alcatel|pocket|ipad|iphone|mobileexplorer|' + 10 | 'mobile|zune' 11 | 12 | def self.included(base) 13 | base.extend(ClassMethods) 14 | end 15 | 16 | module ClassMethods 17 | 18 | # Add this to one of your controllers to use MobileFu. 19 | # 20 | # class ApplicationController < ActionController::Base 21 | # has_mobile_fu 22 | # end 23 | # 24 | # You can also force mobile mode by passing in 'true' 25 | # 26 | # class ApplicationController < ActionController::Base 27 | # has_mobile_fu(true) 28 | # end 29 | 30 | def has_mobile_fu(test_mode = false) 31 | include ActionController::MobileFu::InstanceMethods 32 | 33 | if test_mode 34 | before_filter :force_mobile_format 35 | else 36 | before_filter :set_mobile_format 37 | end 38 | 39 | helper_method :is_mobile_device? 40 | helper_method :in_mobile_view? 41 | helper_method :is_device? 42 | end 43 | 44 | def is_mobile_device? 45 | @@is_mobile_device 46 | end 47 | 48 | def in_mobile_view? 49 | @@in_mobile_view 50 | end 51 | 52 | def is_device?(type) 53 | @@is_device 54 | end 55 | end 56 | 57 | module InstanceMethods 58 | 59 | # Forces the request format to be :mobile 60 | 61 | def force_mobile_format 62 | if !request.xhr? 63 | request.format = :mobile 64 | session[:mobile_view] = true if session[:mobile_view].nil? 65 | end 66 | end 67 | 68 | # Determines the request format based on whether the device is mobile or if 69 | # the user has opted to use either the 'Standard' view or 'Mobile' view. 70 | 71 | def set_mobile_format 72 | if is_mobile_device? && !request.xhr? 73 | request.format = session[:mobile_view] == false ? :html : :mobile 74 | session[:mobile_view] = true if session[:mobile_view].nil? 75 | end 76 | end 77 | 78 | # Returns either true or false depending on whether or not the format of the 79 | # request is either :mobile or not. 80 | 81 | def in_mobile_view? 82 | request.format.to_sym == :mobile 83 | end 84 | 85 | # Returns either true or false depending on whether or not the user agent of 86 | # the device making the request is matched to a device in our regex. 87 | 88 | def is_mobile_device? 89 | request.user_agent.to_s.downcase =~ Regexp.new(ActionController::MobileFu::MOBILE_USER_AGENTS) 90 | end 91 | 92 | # Can check for a specific user agent 93 | # e.g., is_device?('iphone') or is_device?('mobileexplorer') 94 | 95 | def is_device?(type) 96 | request.user_agent.to_s.downcase.include?(type.to_s.downcase) 97 | end 98 | end 99 | 100 | end 101 | 102 | end 103 | 104 | ActionController::Base.send(:include, ActionController::MobileFu) 105 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Mobile Fu 2 | 3 | Want to automatically detect mobile devices that access your Rails application? 4 | Mobile Fu allows you to do just that. People can access your site from a Palm, 5 | Blackberry, iPhone, iPad, Nokia, etc. and it will automatically adjust the format 6 | of the request from :html to :mobile. 7 | 8 | == Rails 3 Compatibility 9 | 10 | The master branch of this plugin is Rails 3 compatible. 11 | 12 | To use the Rails 3 compatible version of this plugin, please install it like so: 13 | 14 | rails plugin install git://github.com/brendanlim/mobile-fu.git 15 | 16 | == Rails 2 Compatibility 17 | 18 | You will need to install this plugin from the 2.x branch. 19 | 20 | To use the Rails 2 compatible version of this plugin, please install it like so: 21 | 22 | script/plugin install git://github.com/brendanlim/mobile-fu.git -r 2.x 23 | 24 | == Usage 25 | 26 | Add this this one line to the controller. 27 | 28 | class ApplicationController < ActionController::Base 29 | has_mobile_fu 30 | end 31 | 32 | Once this is in place, any request that comes from a mobile device will be be 33 | set as :mobile format. It is up to you to determine how you want to handle 34 | these requests. It is also up to you to create the .mobile.erb versions of 35 | your views that are to be requested. 36 | 37 | Then add the line below to config/initializers/mime_types.rb 38 | 39 | Mime::Type.register_alias "text/html", :mobile 40 | 41 | I recommend that you setup a before_filter that will redirect to a specific page 42 | depending on whether or not it is a mobile request. How can you check this? 43 | 44 | is_mobile_device? # => Returns true or false depending on the device 45 | 46 | You can also determine which format is currently set in by calling the following: 47 | 48 | in_mobile_view? # => Returns true or false depending on current req. format 49 | 50 | Also, if you want the ability to allow a user to switch between 'mobile' and 51 | 'standard' format (:html), you can just adjust the mobile_view session variable 52 | in a custom controller action. 53 | 54 | session[:mobile_view] # => Set to true if request format is :mobile and false 55 | if set to :html 56 | 57 | So, different devices need different styling. Don't worry, we've got this 58 | baked in to Mobile Fu. 59 | 60 | If you are including a css or sass file via stylesheet_link_tag, all you have 61 | to do is add _device to the name of one of your files to override your styling 62 | for a certain device. The stylesheet that is loaded is dependant on which device 63 | is making the request. 64 | 65 | e.g., Accessing a page from a Blackberry. 66 | 67 | ... stylesheet_link_tag 'mobile.css' ... 68 | 69 | This loads mobile.css, and mobile_blackberry.css if the file exists. 70 | 71 | Supported stylesheet override device extensions at the moment are: 72 | 73 | blackberry 74 | iphone (iphone,ipod) 75 | ipad 76 | android 77 | mobileexplorer 78 | nokia 79 | palm 80 | 81 | The stylesheet awesomeness was derived from Michael Bleigh's browserized styles: 82 | http://www.intridea.com/2007/12/9/announcing-browserized-styles 83 | 84 | Inspiration for Mobile Fu came from Noel Rappin's rails_iui: 85 | http://blogs.pathf.com/agileajax/2008/05/rails-developme.html 86 | 87 | Hopefully this should help you create some awesome mobile applications. 88 | 89 | == Testing Mobile Interface 90 | 91 | If you want to force the mobile interface for testing, you can either use a 92 | mobile device emulator, or you can pass 'true' to has_mobile_fu. 93 | 94 | class ApplicationController < ActionController::Base 95 | has_mobile_fu(true) 96 | end 97 | 98 | 99 | Copyright (c) 2008 Brendan G. Lim, Intridea, Inc., released under the MIT license 100 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------