ress 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -16,7 +16,7 @@ for which devices should be redirected to which version.
16
16
  ### HTML Annotations
17
17
 
18
18
  When you register alternate mobile versions of your website, Ress adds annotations
19
- to the `<head>` of your document that describes where these pages are located and
19
+ to the `<head>` of your document that describe where these pages are located and
20
20
  which devices should be redirected to them.
21
21
 
22
22
  For example, a typical alternate version for a mobile site might include a tag
@@ -34,22 +34,18 @@ version:
34
34
  ```
35
35
 
36
36
  These annotations conform to SEO best practices for mobile optimized websites
37
- as documented by Google
38
- [here](https://developers.google.com/webmasters/smartphone-sites/details).
37
+ [as documented by Google](https://developers.google.com/webmasters/smartphone-sites/details).
39
38
 
40
39
  ### Feature Detection
41
40
 
42
- Device.js will read all of the version links in your markup, and
43
- redirect you to the appropriate URL that serves the correct version of
44
- your webapp.
45
-
46
- When a request comes into your site, ress runs a some javascript to determine
47
- if there is an alternate version available that matches the client. If there
48
- is, the user is redirected to the url for that version.
41
+ When a request comes into your site, the javascript included with ress will parse
42
+ all of the `[rel="alternate"]` links in your markup, and evalute their media queries
43
+ to determine if there is an alternate version available that matches the client.
44
+ If there is, the user is redirected to the url for that version.
49
45
 
50
46
  ### Server Side Components
51
47
 
52
- Ress allows you to customize how your Rails application responds to requests in
48
+ Ress allows you to customize how your Rails application responds to mobile requests in
53
49
  two ways:
54
50
 
55
51
  1. It adds controller and helper methods to detect which version of your site has
@@ -64,7 +60,7 @@ been requested. This is useful for small tweeks in html or behaviour, eg:
64
60
  ```
65
61
 
66
62
  2. It prepends a view path for each alternate version of your site, so that you can
67
- override which templates or partials are rendered for certain requests. For example if
63
+ override the templates or partials that are rendered for certain requests. For example if
68
64
  you want to render a different html form for creating users on the mobile version of your
69
65
  site you could create `app/mobile_views/users/_form.html.erb` and Ress would have Rails
70
66
  select that template over `app/views/users/_form.html.erb` when a request comes in to the
@@ -81,34 +77,38 @@ And then execute:
81
77
 
82
78
  $ bundle install
83
79
 
84
- Run the installation generator:
80
+ Run the generator:
85
81
 
86
82
  $ rails g ress:install
87
83
 
88
84
  ## Usage
89
85
 
90
- ### Version override
86
+ ### Adding Alternate Versions
91
87
 
92
- You can manually override the detector and load a particular version of
93
- the site by passing in the `device` GET parameter with the ID of the
94
- version you'd like to load. This will look up the `link` tag based on
95
- the specified ID and load that version. For example, if you are on
96
- desktop but want the tablet version, visiting
97
- `http://foo.com/?version=tablet` will redirect to the tablet version at
98
- `http://tablet.foo.com`.
88
+ Alternate versions of an application are registered using the `#add_alternate` method in the
89
+ `ress.rb` initializer that is generated by the `ress:install` generator. The configurabe options
90
+ available are all documented in the [comments of that file](https://github.com/matthewrobertson/ress/blob/master/lib/generators/ress/templates/ress.rb).
99
91
 
100
- Relatedly, you can prevent redirection completely, by specifying the
101
- `force=1` GET parameter. For example, if you are on desktop and know the
102
- URL of the tablet site, you can load `http://tablet.foo.com/?force=1`.
92
+ ### Version override
103
93
 
104
- ```html
105
- <!-- Include a way to manually switch between device types -->
94
+ You can manually override the version detector javascript and allow mobile
95
+ clients to visit the canonical version of the app by passing in a the GET
96
+ url parameter `force_canonical=1`. This sets a session cookie in a `before_filter`
97
+ that stops the version detection scipt from redirecting users, so it only has to be
98
+ done once per session. Ress includes a helper / controller method `force_canonical` that returns
99
+ a link back to the canonical version of the current page with this query param appended.
100
+ For, example you may include something like this in your `<footer>` to let mobile users
101
+ access the canonical site.
102
+
103
+ ```erb
104
+ <!-- Let mobile devices access the canonical site -->
106
105
  <footer>
107
- <ul>
108
- <li><a href="?device=desktop">Desktop</a></li>
109
- <li><a href="?device=tablet">Tablet</a></li>
110
- <li><a href="?device=phone">Phone</a></li>
111
- </ul>
106
+ <% unless canonical_request? %>
107
+ <div>
108
+ You are currently viewing the mobile version of this site.
109
+ <%= link_to 'View the desktop version', force_canonical_url %>
110
+ </div>
111
+ <% end %>
112
112
  </footer>
113
113
  ```
114
114
 
@@ -130,7 +130,7 @@ redirection to point users to the right version of your webapp. Client-side
130
130
  redirection can have a performance overhead (though I haven't measured it).
131
131
  If you find this is true, you can keep your DOM the same, still using the
132
132
  SEO-friendly `<link rel="alternate">` tags, but simply remove the
133
- ress.js script and do your own server-side UA-based pushing.
133
+ ress.js script and do your own server-side UA-based redirection.
134
134
 
135
135
  ## Browser support
136
136
 
@@ -149,3 +149,19 @@ in a pull request.
149
149
  3. Commit your changes (`git commit -am 'Add some feature'`)
150
150
  4. Push to the branch (`git push origin my-new-feature`)
151
151
  5. Create new Pull Request
152
+
153
+ ## Resources
154
+
155
+ Ress is the compilation of a few different ideas packaged up for Ruby on Rails. You
156
+ may want to look at the following articles for more info:
157
+
158
+ - [devicejs](https://github.com/borismus/device.js) - a javascript library for client
159
+ side feature direction and the main inspiration for this gem. Boris has also provided
160
+ [a good write up about it](http://www.html5rocks.com/en/mobile/cross-device/) on
161
+ http://www.html5rocks.com/
162
+ - [Building Smartphone-Optimized Websites](https://developers.google.com/webmasters/smartphone-sites/details) - Google's
163
+ recommendations and best practices for building Mobile Optimized web apps
164
+ - [RESS: Responsive Design + Server Side Components](http://www.lukew.com/ff/entry.asp?1392) - an
165
+ article by Luke Wroblewski about combining Responsive Design and Server Side Components (the
166
+ inspiration for the name RESS).
167
+
@@ -1,6 +1,7 @@
1
1
  require "action_view"
2
2
 
3
3
  require "ress/version"
4
+ require "ress/subdomain"
4
5
  require "ress/alternate_version"
5
6
  require "ress/canonical_version"
6
7
  require "ress/category_collection"
@@ -2,35 +2,31 @@ module Ress
2
2
 
3
3
  class CanonicalVersion
4
4
 
5
- attr_reader :subdomain
6
-
7
5
  def initialize(options = {})
8
- @subdomain = options.fetch(:subdomain, false)
6
+ @subdomain = Ress::Subdomain.create(options.fetch(:subdomain, nil))
9
7
  end
10
8
 
11
- def matches?(subdomain)
12
- if self.subdomain
13
- self.subdomain == subdomain
14
- else
15
- subdomain.empty?
16
- end
9
+ def matches?(req_subdomain)
10
+ subdomain.matches?(req_subdomain)
17
11
  end
18
12
 
19
13
  # Create a tag of this format:
20
14
  # `<link rel="canonical" href="http://www.example.com/page-1" >`
21
- def link_tag(protocol, fullpath, subdomain, view)
22
- view.tag :link, :rel => 'canonical', :href => url(protocol, fullpath, subdomain)
15
+ def link_tag(protocol, fullpath, req_subdomain, view)
16
+
17
+ view.tag :link, :rel => 'canonical', :href => url(protocol, fullpath, req_subdomain)
23
18
  end
24
19
 
25
- def url(protocol, fullpath, subdomain)
26
- fullpath = fullpath[(subdomain.length + 1)..-1] unless subdomain.empty?
27
- if self.subdomain
28
- "#{protocol}#{self.subdomain}.#{fullpath}"
29
- else
30
- "#{protocol}#{fullpath}"
31
- end
20
+ def url(protocol, fullpath, req_subdomain)
21
+ subdomain.url(protocol, fullpath, req_subdomain)
32
22
  end
33
23
 
24
+ private
25
+
26
+ def subdomain
27
+ @subdomain
28
+ end
29
+
34
30
  end
35
31
 
36
32
  end
@@ -0,0 +1,78 @@
1
+ module Ress
2
+
3
+ class Subdomain
4
+
5
+ # this class is a factory, don't instantiate it directly
6
+ private_class_method :new
7
+
8
+ def self.create(subdomain)
9
+ case subdomain
10
+ when String
11
+ StringSubdomain.new(subdomain)
12
+ when Regexp
13
+ RegexpSubdomain.new(subdomain)
14
+ else
15
+ NilSubdomain.new
16
+ end
17
+ end
18
+
19
+ class NilSubdomain
20
+ def matches?(subdomain)
21
+ subdomain.empty?
22
+ end
23
+
24
+ def url(protocol, fullpath, subdomain)
25
+ fullpath = fullpath[(subdomain.length + 1)..-1] unless subdomain.empty?
26
+ "#{protocol}#{fullpath}"
27
+ end
28
+ end
29
+
30
+ class StringSubdomain
31
+
32
+ attr_reader :subdomain
33
+
34
+ def initialize(subdomain)
35
+ @subdomain = subdomain
36
+ end
37
+
38
+ def matches?(subdomain)
39
+ self.subdomain == subdomain
40
+ end
41
+
42
+ def url(protocol, fullpath, subdomain)
43
+ fullpath = fullpath[(subdomain.length + 1)..-1] unless subdomain.empty?
44
+ "#{protocol}#{self.subdomain}.#{fullpath}"
45
+ end
46
+
47
+ end
48
+
49
+ # The main inspiration for this class is to match xip.io
50
+ # subdomains for many devs working in development mode with
51
+ # the same configuration.
52
+ class RegexpSubdomain
53
+
54
+ attr_reader :subdomain
55
+
56
+ def initialize(subdomain)
57
+ @subdomain = subdomain
58
+ end
59
+
60
+ def matches?(subdomain)
61
+ self.subdomain =~ subdomain
62
+ end
63
+
64
+ def url(protocol, fullpath, subdomain)
65
+ fullpath = fullpath[(subdomain.length + 1)..-1]
66
+ begin
67
+ if matches?(subdomain)
68
+ return "#{protocol}#{subdomain}.#{fullpath}"
69
+ end
70
+ subdomain = subdomain.split('.')[1..-1].join('.')
71
+ end while(!subdomain.empty?)
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+
78
+ end
@@ -1,3 +1,3 @@
1
1
  module Ress
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.7"
3
3
  end
@@ -1,21 +1,7 @@
1
- require_relative '../../lib/ress/canonical_version'
1
+ require_relative '../../lib/ress'
2
2
 
3
3
  describe Ress::CanonicalVersion do
4
4
 
5
- describe '#subdomain' do
6
-
7
- it 'defaults to false' do
8
- category = Ress::CanonicalVersion.new
9
- category.subdomain.should == false
10
- end
11
-
12
- it 'can be overridden by an optional parameter' do
13
- category = Ress::CanonicalVersion.new(:subdomain => 'foo')
14
- category.subdomain.should == 'foo'
15
- end
16
-
17
- end
18
-
19
5
  describe '#matches?' do
20
6
 
21
7
  context 'with no canonical subdomain' do
@@ -27,12 +27,12 @@ describe Ress::CategoryCollection do
27
27
  describe 'canonical_version' do
28
28
 
29
29
  it 'defaults to a canonical_version with no subdomain' do
30
- collection.canonical_version.subdomain.should be_false
30
+ collection.canonical_version.matches?('').should be_true
31
31
  end
32
32
 
33
33
  it 'can be altered through set_canonical' do
34
34
  collection.set_canonical :subdomain => 'foo'
35
- collection.canonical_version.subdomain.should == 'foo'
35
+ collection.canonical_version.matches?('foo').should be_true
36
36
  end
37
37
 
38
38
  end
@@ -0,0 +1,112 @@
1
+ require_relative '../../lib/ress/subdomain'
2
+
3
+ describe Ress::Subdomain do
4
+
5
+ describe '.create' do
6
+
7
+ it 'returns a NilSubdomain when the subdomain is nil' do
8
+ Ress::Subdomain.create(nil).should be_a(Ress::Subdomain::NilSubdomain)
9
+ end
10
+
11
+ it 'returns a StringSubdomain when the subdomain is a String' do
12
+ Ress::Subdomain.create('foo').should be_a(Ress::Subdomain::StringSubdomain)
13
+ end
14
+
15
+ it 'returns a RegexpSubdomain when the subdomain is a Regex' do
16
+ Ress::Subdomain.create(/foo/).should be_a(Ress::Subdomain::RegexpSubdomain)
17
+ end
18
+
19
+ end
20
+
21
+ describe Ress::Subdomain::NilSubdomain do
22
+
23
+ let(:subdomain) { Ress::Subdomain::NilSubdomain.new }
24
+
25
+ describe '#matches?' do
26
+ it 'matches the empty string' do
27
+ subdomain.matches?('').should be_true
28
+ subdomain.matches?('s').should be_false
29
+ end
30
+ end
31
+
32
+ describe '#url' do
33
+ it 'strips urls from the fullpath if there is one' do
34
+ subdomain.url('http://', 'foo.bar.com/some/stuff', 'foo').should ==
35
+ 'http://bar.com/some/stuff'
36
+ end
37
+
38
+ it 'concatenates the protocol and fullpath together if there is no subdomain' do
39
+ subdomain.url('http://', 'bar.com/some/stuff', '').should ==
40
+ 'http://bar.com/some/stuff'
41
+ end
42
+ end
43
+ end
44
+
45
+ describe Ress::Subdomain::StringSubdomain do
46
+
47
+ let(:subdomain) { Ress::Subdomain::StringSubdomain.new('foo') }
48
+
49
+ describe '#matches?' do
50
+
51
+ it 'returns true if the url is exact match of that passed to the ctor' do
52
+ subdomain.matches?('foo').should be_true
53
+ end
54
+
55
+ it 'returns false if the url is not an exact match of that passed to the ctor' do
56
+ subdomain.matches?('boo').should be_false
57
+ end
58
+ end
59
+
60
+ describe '#url' do
61
+
62
+ it 'strips off extra subdomains from the fullpath' do
63
+ subdomain.url('http://', 'm.foo.bar.com/some/stuff', 'm.foo').should ==
64
+ 'http://foo.bar.com/some/stuff'
65
+ end
66
+
67
+ it 'handles cannonical fullpaths' do
68
+ subdomain.url('http://', 'foo.bar.com/some/stuff', 'foo').should ==
69
+ 'http://foo.bar.com/some/stuff'
70
+ end
71
+
72
+ end
73
+ end
74
+
75
+ describe Ress::Subdomain::RegexpSubdomain do
76
+ describe '#matches?' do
77
+ let(:subdomain) { Ress::Subdomain::RegexpSubdomain.new(/[fb]oo/) }
78
+
79
+ it 'returns true if the subdomain matches regex that passed to the ctor' do
80
+ subdomain.matches?('foo').should be_true
81
+ subdomain.matches?('boo').should be_true
82
+ end
83
+
84
+ it 'returns false if the subdomain doesnt matches regex that is passed to the ctor' do
85
+ subdomain.matches?('baz').should be_false
86
+ end
87
+
88
+ it 'can handle the xip.io format' do
89
+ subdomain = Ress::Subdomain::RegexpSubdomain.new(/^([0-9]+\.){3}([0-9]+)/)
90
+ subdomain.matches?('172.30.251.3').should be_true
91
+
92
+ subdomain.matches?('mobile.172.30.251.3').should be_false
93
+ end
94
+ end
95
+
96
+ describe '#url' do
97
+
98
+ let(:subdomain) { Ress::Subdomain::RegexpSubdomain.new(/^[fb]oo/) }
99
+
100
+ it 'echos back the url for cannonical requests' do
101
+ subdomain.url('http://', 'foo.bar.com/some/stuff', 'foo').should ==
102
+ 'http://foo.bar.com/some/stuff'
103
+ end
104
+
105
+ it 'strips off prepended subdomains that dont matche the regex' do
106
+ subdomain.url('http://', 'm.a.foo.bar.com/some/stuff', 'm.a.foo').should ==
107
+ 'http://foo.bar.com/some/stuff'
108
+ end
109
+
110
+ end
111
+ end
112
+ end
@@ -18,7 +18,7 @@ describe Ress do
18
18
 
19
19
  it 'can be altered through Ress.configure' do
20
20
  Ress.configure { |r| r.set_canonical :subdomain => 'foo' }
21
- Ress.canonical_version.subdomain.should == 'foo'
21
+ Ress.canonical_version.matches?('foo').should be_true
22
22
  end
23
23
 
24
24
  end
metadata CHANGED
@@ -2,14 +2,14 @@
2
2
  name: ress
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.6
5
+ version: 0.0.7
6
6
  platform: ruby
7
7
  authors:
8
8
  - Matthew Robertson
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-29 00:00:00.000000000 Z
12
+ date: 2013-02-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  version_requirements: !ruby/object:Gem::Requirement
@@ -65,6 +65,7 @@ files:
65
65
  - lib/ress/category_collection.rb
66
66
  - lib/ress/controller_additions.rb
67
67
  - lib/ress/engine.rb
68
+ - lib/ress/subdomain.rb
68
69
  - lib/ress/version.rb
69
70
  - lib/ress/view_helpers.rb
70
71
  - ress.gemspec
@@ -72,6 +73,7 @@ files:
72
73
  - spec/ress/canonical_version_spec.rb
73
74
  - spec/ress/category_collection_spec.rb
74
75
  - spec/ress/controller_additions_spec.rb
76
+ - spec/ress/subdomain_spec.rb
75
77
  - spec/ress/view_helpers_spec.rb
76
78
  - spec/ress_spec.rb
77
79
  - vendor/assets/javascripts/ress.js
@@ -105,5 +107,6 @@ test_files:
105
107
  - spec/ress/canonical_version_spec.rb
106
108
  - spec/ress/category_collection_spec.rb
107
109
  - spec/ress/controller_additions_spec.rb
110
+ - spec/ress/subdomain_spec.rb
108
111
  - spec/ress/view_helpers_spec.rb
109
112
  - spec/ress_spec.rb