rack-html5 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ coverage
3
+ rdoc
4
+ doc
5
+ pkg
data/README.rdoc ADDED
@@ -0,0 +1,59 @@
1
+ = rack-html5
2
+
3
+ This is work in progress.
4
+
5
+ Rack::Html5 detects the browser and its version by parsing the user agent. It then matches the features available for the current request.
6
+
7
+ == Usage
8
+
9
+ gem install rack-html5
10
+ require 'rack-html5'
11
+ use Rack::Html5
12
+ app = lambda { |env| [200, { 'Content-Type' => 'text/html' }, '<html><body><p></p></body></html>'] }
13
+ run app
14
+
15
+ Rack::Html5 then sets the following HTTP headers:
16
+
17
+ * *X-Detected-Browser:* Browser that has been detected, eg.: Firefox
18
+ * *X-Detected-Version:* Version of the browser that has been detected, eg.: 3.6.6
19
+ * *X-Html5-Wysiwyg:* WYSIWYG editable elements
20
+ * *X-Html5-Classname:* getElementsByClassName
21
+ * *X-Html5-Elements:* Stylable HTML5 elements
22
+ * *X-Html5-Canvas:* Canvas (basic support)
23
+ * *X-Html5-Messaging:* Cross-document messaging
24
+ * *X-Html5-Audio:* Audio element
25
+ * *X-Html5-Video:* Video element
26
+ * *X-Html5-Textapi:* Text API for canvas
27
+ * *X-Html5-Draganddrop:* Drag and drop
28
+ * *X-Html5-Offline:* Offline web applications
29
+ * *X-Html5-Svg:* Inline SVG
30
+ * *X-Html5-Form:* Form features (Web Forms 2.0)
31
+
32
+ == Rails
33
+
34
+ I plan on providing some rails helpers in the future.
35
+
36
+ === Configuration for Rails 2
37
+
38
+ Add the following lines to the config block in your environment.rb file:
39
+ config.gem "rack-html5"
40
+ config.middleware.use "Rack::Html5"
41
+
42
+ === Configuration for Rails 3
43
+
44
+ Add the gem dependency to Gemfile:
45
+ gem 'rack-html5'
46
+
47
+ Install the gem with bundler:
48
+ sudo bundler install
49
+
50
+ Add the Rack middleware to config.ru:
51
+ use Rack::Html5
52
+
53
+ == Notices
54
+
55
+ * The matching is done based on the information provided on: http://caniuse.com/#eras=farpast,past,now&cats=HTML5&statuses=rec,pr,cr,wd,ietf&nodetails=1
56
+ * If the feature is not supported, the header will not be set.
57
+ * If a browser implements a feature only partially, the corresponding header will not be set.
58
+ * It is assumed that future versions will still implement a certain feature if it's implemented now.
59
+ * User agent matching is a dirty thing. Feel free to help improving the matching rules.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ # Load all tasks in rake directory
2
+ Dir[File.join(File.expand_path(File.dirname(__FILE__)), "rake", "**", "*.rb")].each { |file| require file }
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/lib/rack-html5.rb ADDED
@@ -0,0 +1 @@
1
+ require "rack/html5"
data/lib/rack/html5.rb ADDED
@@ -0,0 +1,177 @@
1
+ module Rack # :nodoc:
2
+
3
+ # Rack::Html5 sets custom headers for each HTML5 feature the browser supports.
4
+ # It does this by parsing the user agent and matching the features to the browser version based on information provided on: http://caniuse.com/#eras=farpast,past,now&cats=HTML5&statuses=rec,pr,cr,wd,ietf&nodetails=1
5
+ #
6
+ # The headers that are being set:
7
+ # * *X-Detected-Browser:* Browser that has been detected, eg.: Firefox
8
+ # * *X-Detected-Version:* Version of the browser that has been detected, eg.: 3.6.6
9
+ # * *X-Html5-Wysiwyg:* WYSIWYG editable elements
10
+ # * *X-Html5-Classname:* getElementsByClassName
11
+ # * *X-Html5-Elements:* Stylable HTML5 elements
12
+ # * *X-Html5-Canvas:* Canvas (basic support)
13
+ # * *X-Html5-Messaging:* Cross-document messaging
14
+ # * *X-Html5-Audio:* Audio element
15
+ # * *X-Html5-Video:* Video element
16
+ # * *X-Html5-Textapi:* Text API for canvas
17
+ # * *X-Html5-Draganddrop:* Drag and drop
18
+ # * *X-Html5-Offline:* Offline web applications
19
+ # * *X-Html5-Svg:* Inline SVG
20
+ # * *X-Html5-Form:* Form features (Web Forms 2.0)
21
+ #
22
+ # Please note:
23
+ # * If the feature is not supported, the header will not be set.
24
+ # * If a browser implements a feature only partially, the corresponding header will not be set.
25
+ class Html5
26
+ attr_accessor :version, :browser, :user_agent, :features, :headers
27
+
28
+ def initialize(app)
29
+ @app = app
30
+ end
31
+
32
+ def generate_headers
33
+ headers['x-detected-version'] = version
34
+ headers['x-detected-browser'] = browser.to_s
35
+ @features.each do |feature|
36
+ headers["x-html5-#{feature}"] = "true"
37
+ end
38
+ end
39
+
40
+ def call(env)
41
+ status, headers, response = @app.call(env)
42
+ @user_agent = env['HTTP_USER_AGENT']
43
+ @features = []
44
+ @headers = headers
45
+
46
+ detect_browser
47
+ detect_features
48
+ generate_headers
49
+
50
+ [status, @headers, response]
51
+ end
52
+
53
+ # Detects the browser and version based on the user agent
54
+ def detect_browser
55
+ user_agent_matcher.each do |browser, regexp|
56
+ match = @user_agent.match(regexp)
57
+ version = match.captures.first if match
58
+ if version
59
+ @browser = browser
60
+ @version = version
61
+ break
62
+ end
63
+ end
64
+ end
65
+
66
+ # Detects the HTML5 features the browser supports
67
+ def detect_features
68
+ return unless @browser && @version
69
+ feature_mapping[browser].each do |feature, version|
70
+ if version
71
+ raise "Versions are not comparable: #{version}, #{@version}" if comparable_version(version).length != comparable_version(@version).length
72
+ if comparable_version(@version) >= comparable_version(version)
73
+ @features << feature
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ # New features are only being shipped with major version updated.
80
+ # So we'll be on the safe side using the first two parts of the version number for comparisation.
81
+ # eg. version 10.5.6a will be cut down to 10.5, dots will be stripped, return value would be 105
82
+ def comparable_version(v)
83
+ v.split(".")[0..1].join
84
+ end
85
+
86
+ private
87
+
88
+ # Feature to browser version mapping according to the information provided on: http://caniuse.com/#eras=farpast,past,now&cats=HTML5&statuses=rec,pr,cr,wd,ietf&nodetails=1
89
+ def feature_mapping
90
+ {
91
+ :firefox => {
92
+ :wysiwyg => "3.5",
93
+ :classname => "3.0",
94
+ :elements => "3.0",
95
+ :canvas => "3.0",
96
+ :messaging => "3.0",
97
+ :audio => "3.5",
98
+ :video => "3.5",
99
+ :textapi => "3.5",
100
+ :draganddrop => "3.5",
101
+ :offline => "3.5",
102
+ :forms => nil,
103
+ :svg => nil
104
+ },
105
+ :ie => {
106
+ :wysiwyg => "6.0",
107
+ :classname => nil,
108
+ :elements => nil,
109
+ :canvas => nil,
110
+ :messaging => "8.0",
111
+ :audio => nil,
112
+ :video => nil,
113
+ :textapi => nil,
114
+ :draganddrop => nil,
115
+ :offline => nil,
116
+ :forms => nil,
117
+ :svg => nil
118
+ },
119
+ :opera => {
120
+ :wysiwyg => "10.1",
121
+ :classname => "10.1",
122
+ :elements => "10.1",
123
+ :canvas => "10.1",
124
+ :messaging => "10.1",
125
+ :audio => "10.5",
126
+ :video => "10.5",
127
+ :textapi => "10.5",
128
+ :draganddrop => nil,
129
+ :offline => "10.6",
130
+ :forms => "10.1",
131
+ :svg => nil
132
+ },
133
+ :chrome => {
134
+ :wysiwyg => "3.0",
135
+ :classname => "3.0",
136
+ :elements => "3.0",
137
+ :canvas => "3.0",
138
+ :messaging => "3.0",
139
+ :audio => "3.0",
140
+ :video => "3.0",
141
+ :textapi => "3.0",
142
+ :draganddrop => "3.0",
143
+ :offline => "4.0",
144
+ :forms => nil,
145
+ :svg => nil
146
+ },
147
+ :safari => {
148
+ :wysiwyg => "3.2",
149
+ :classname => "3.2",
150
+ :elements => "3.2",
151
+ :canvas => "3.2",
152
+ :messaging => "4.0",
153
+ :audio => "3.2",
154
+ :video => "3.2",
155
+ :textapi => "4.2",
156
+ :draganddrop => "4.0",
157
+ :offline => "4.0",
158
+ :forms => nil,
159
+ :svg => nil
160
+ }
161
+ }
162
+ end
163
+
164
+ # Browser to user agent matching
165
+ def user_agent_matcher
166
+ regexp_version = /((\d+\.?)+)/
167
+ {
168
+ :firefox => /Firefox\/#{regexp_version}/, # eg. Firefox/1.5.0.12
169
+ :ie => /MSIE #{regexp_version}/, # eg. compatible; MSIE 7.0;
170
+ :chrome => /Chrome\/#{regexp_version}/, # eg. Chrome/5.0.307.11 Safari/532.9
171
+ :safari => /AppleWebKit\/.*Version\/#{regexp_version}/, # eg. AppleWebKit/531.21.8 Version/4.0.4 Safari/531.21.10
172
+ :opera => /Opera\/#{regexp_version}/, # eg. Opera/9.80
173
+ }
174
+ end
175
+
176
+ end
177
+ end
@@ -0,0 +1,55 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rack-html5}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Christian Felder (masone)"]
12
+ s.date = %q{2010-07-14}
13
+ s.description = %q{Rack::Html5 sets custom headers for each HTML5 feature the browser supports.}
14
+ s.email = %q{ema@rh-productions.ch}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "README.rdoc",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "lib/rack-html5.rb",
24
+ "lib/rack/html5.rb",
25
+ "rack-html5.gemspec",
26
+ "rake/jeweler.rb",
27
+ "rake/rdoc.rb",
28
+ "rake/test.rb",
29
+ "test/rack/html5_test.rb",
30
+ "test/test_helper.rb"
31
+ ]
32
+ s.homepage = %q{http://github.com/masone/rack-html5}
33
+ s.rdoc_options = ["--charset=UTF-8"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.7}
36
+ s.summary = %q{Rack::Html5 sets custom headers for each HTML5 feature the browser supports.}
37
+ s.test_files = [
38
+ "test/rack/html5_test.rb",
39
+ "test/test_helper.rb"
40
+ ]
41
+
42
+ if s.respond_to? :specification_version then
43
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
48
+ else
49
+ s.add_dependency(%q<shoulda>, [">= 0"])
50
+ end
51
+ else
52
+ s.add_dependency(%q<shoulda>, [">= 0"])
53
+ end
54
+ end
55
+
data/rake/jeweler.rb ADDED
@@ -0,0 +1,15 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |s|
4
+ s.name = "rack-html5"
5
+ s.summary = "Rack::Html5 sets custom headers for each HTML5 feature the browser supports."
6
+ s.description = "Rack::Html5 sets custom headers for each HTML5 feature the browser supports."
7
+ s.email = "ema@rh-productions.ch"
8
+ s.homepage = "http://github.com/masone/rack-html5"
9
+ s.authors = ["Christian Felder (masone)"]
10
+ s.add_development_dependency "shoulda"
11
+ end
12
+ Jeweler::GemcutterTasks.new
13
+ rescue LoadError
14
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
15
+ end
data/rake/rdoc.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'hanna/rdoctask'
2
+
3
+ desc 'Generate RDoc documentation'
4
+ Rake::RDocTask.new(:rdoc) do |rdoc|
5
+ rdoc.rdoc_files.include('README.rdoc').
6
+ include('*.rb').
7
+ include('lib/**/*.rb').
8
+ exclude('test/*').
9
+ exclude('rake/*').
10
+ exclude('pkg/*')
11
+
12
+ rdoc.main = "README.rdoc" # page to start on
13
+ rdoc.title = "Rack::Html5"
14
+
15
+ rdoc.rdoc_dir = 'doc' # rdoc output folder
16
+ end
data/rake/test.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ desc 'Run test suite'
5
+ task :test do
6
+ Rake::TestTask.new do |t|
7
+ t.libs << "test"
8
+ t.test_files = FileList['test/**/*_test.rb']
9
+ t.verbose = true
10
+ end
11
+ end
@@ -0,0 +1,117 @@
1
+ require 'test_helper'
2
+ require 'rack/html5'
3
+
4
+ class Rack::Html5Test < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @app = lambda { |env| [200, {}, [""]] }
8
+ @rack = Rack::Html5.new(@app)
9
+ end
10
+
11
+ context "browser detection" do
12
+
13
+ should "detect firefox from Firefox/3.6.6" do
14
+ env = { 'HTTP_USER_AGENT' => "Mozilla/5.0 (X11; U; Darwin Power Macintosh; en-US; rv:1.8.0.12) Gecko/20070803 Firefox/3.6.6 Fink Community Edition" }
15
+ @rack.call(env)
16
+ assert_equal :firefox, @rack.browser
17
+ end
18
+
19
+ should "detect ie from MSIE 7.0" do
20
+ env = { 'HTTP_USER_AGENT' => "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GTB6.4; .NET CLR 1.1.4322; FDM; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)" }
21
+ @rack.call(env)
22
+ assert_equal :ie, @rack.browser
23
+ end
24
+
25
+ should "detect opera from Opera/9.80" do
26
+ env = { 'HTTP_USER_AGENT' => "Opera/9.62 (Windows NT 5.1; U; en) Presto/2.1.1" }
27
+ @rack.call(env)
28
+ assert_equal :opera, @rack.browser
29
+ end
30
+
31
+ should "detect safari from AppleWebKit/525.19" do
32
+ env = { 'HTTP_USER_AGENT' => "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Version/3.1.2 Safari/525.21" }
33
+ @rack.call(env)
34
+ assert_equal :safari, @rack.browser
35
+ end
36
+
37
+ should "detect chrome from Chrome/5.0.307.11" do
38
+ env = { 'HTTP_USER_AGENT' => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/532.9 (KHTML, like Gecko) Chrome/5.0.307.11 Safari/532.9" }
39
+ @rack.call(env)
40
+ assert_equal :chrome, @rack.browser
41
+ end
42
+
43
+ end
44
+
45
+ context "version detection" do
46
+
47
+ should "detect 3.6.6 from Firefox/3.6.6" do
48
+ env = { 'HTTP_USER_AGENT' => "Mozilla/5.0 (X11; U; Darwin Power Macintosh; en-US; rv:1.8.0.12) Gecko/20070803 Firefox/3.6.6 Fink Community Edition" }
49
+ @rack.call(env)
50
+ assert_equal "3.6.6", @rack.version
51
+ end
52
+
53
+ should "detect 7.0 from MSIE 7.0" do
54
+ env = { 'HTTP_USER_AGENT' => "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GTB6.4; .NET CLR 1.1.4322; FDM; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)" }
55
+ @rack.call(env)
56
+ assert_equal "7.0", @rack.version
57
+ end
58
+
59
+ should "detect 9.80 from Opera/9.80" do
60
+ env = { 'HTTP_USER_AGENT' => "Opera/9.80 (Windows NT 5.1; U; en) Presto/2.1.1" }
61
+ @rack.call(env)
62
+ assert_equal "9.80", @rack.version
63
+ end
64
+
65
+ should "detect 3.1.2 from AppleWebKit/525.19 Version/3.1.2" do
66
+ env = { 'HTTP_USER_AGENT' => "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Version/3.1.2 Safari/525.21" }
67
+ @rack.call(env)
68
+ assert_equal "3.1.2", @rack.version
69
+ end
70
+
71
+ should "detect 5.0.307.11 from Chrome/5.0.307.11" do
72
+ env = { 'HTTP_USER_AGENT' => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/532.9 (KHTML, like Gecko) Chrome/5.0.307.11 Safari/532.9" }
73
+ @rack.call(env)
74
+ assert_equal "5.0.307.11", @rack.version
75
+ end
76
+
77
+ end
78
+
79
+ context "features" do
80
+
81
+ should "detect features for Firefox 5.0" do
82
+ env = { 'HTTP_USER_AGENT' => "Mozilla/5.0 (X11; U; Darwin Power Macintosh; en-US; rv:1.8.0.12) Gecko/20070803 Firefox/5.0.10 Fink Community Edition" }
83
+ status, headers, response = @rack.call(env)
84
+ assert @rack.features.length > 0
85
+ end
86
+
87
+ # TODO: mock features and test them
88
+
89
+ end
90
+
91
+ context "headers" do
92
+
93
+ should "set the x-detected-version header to 5.0.307.11" do
94
+ env = { 'HTTP_USER_AGENT' => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/532.9 (KHTML, like Gecko) Chrome/5.0.307.11 Safari/532.9" }
95
+ status, headers, response = @rack.call(env)
96
+ assert_equal "5.0.307.11", headers['x-detected-version']
97
+ end
98
+
99
+ should "set the x-detected-browser header to " do
100
+ env = { 'HTTP_USER_AGENT' => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/532.9 (KHTML, like Gecko) Chrome/5.0.307.11 Safari/532.9" }
101
+ status, headers, response = @rack.call(env)
102
+ assert_equal "chrome", headers['x-detected-browser']
103
+ end
104
+
105
+ should "set the x-html5-elements header to true for Firefox 5.0" do
106
+ env = { 'HTTP_USER_AGENT' => "Mozilla/5.0 (X11; U; Darwin Power Macintosh; en-US; rv:1.8.0.12) Gecko/20070803 Firefox/5.0.10 Fink Community Edition" }
107
+ status, headers, response = @rack.call(env)
108
+ assert_equal "true", headers['x-html5-elements']
109
+ end
110
+
111
+ should "not include the x headers if user agent cannot be detected properly" do
112
+ env = { 'HTTP_USER_AGENT' => "Mozilla/5.0 Gecko/20070803 Notabrowser/9.9.99" }
113
+ status, headers, response = @rack.call(env)
114
+ assert_equal false, headers.include?('x-html5-wysiwyg')
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require 'shoulda'
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-html5
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 0
9
+ version: 1.0.0
10
+ platform: ruby
11
+ authors:
12
+ - Christian Felder (masone)
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-07-14 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: shoulda
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :development
32
+ version_requirements: *id001
33
+ description: Rack::Html5 sets custom headers for each HTML5 feature the browser supports.
34
+ email: ema@rh-productions.ch
35
+ executables: []
36
+
37
+ extensions: []
38
+
39
+ extra_rdoc_files:
40
+ - README.rdoc
41
+ files:
42
+ - .gitignore
43
+ - README.rdoc
44
+ - Rakefile
45
+ - VERSION
46
+ - lib/rack-html5.rb
47
+ - lib/rack/html5.rb
48
+ - rack-html5.gemspec
49
+ - rake/jeweler.rb
50
+ - rake/rdoc.rb
51
+ - rake/test.rb
52
+ - test/rack/html5_test.rb
53
+ - test/test_helper.rb
54
+ has_rdoc: true
55
+ homepage: http://github.com/masone/rack-html5
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options:
60
+ - --charset=UTF-8
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ requirements: []
80
+
81
+ rubyforge_project:
82
+ rubygems_version: 1.3.7
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: Rack::Html5 sets custom headers for each HTML5 feature the browser supports.
86
+ test_files:
87
+ - test/rack/html5_test.rb
88
+ - test/test_helper.rb