ress 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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