neverland 0.0.2 → 0.0.3

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.
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ script: bundle exec rspec spec
5
+ before_script:
6
+ - "export DISPLAY=:99.0"
7
+ - "sh -e /etc/init.d/xvfb start"
@@ -0,0 +1,39 @@
1
+ # Neverland
2
+
3
+ [![Build Status](https://secure.travis-ci.org/mhoran/neverland.png)](http://travis-ci.org/mhoran/neverland)
4
+
5
+ Neverland takes the pain out of testing HTML5 geolocation within your Rails
6
+ app. For more on HTML5 geolocation, check out [the
7
+ spec](http://dev.w3.org/geo/api/spec-source.html).
8
+
9
+ ## Getting Started
10
+
11
+ To install, add the following to your Gemfile:
12
+
13
+ ```ruby
14
+ gem 'neverland'
15
+ ```
16
+
17
+ In your test environment initializer, add the Neverland middleware:
18
+
19
+ ```ruby
20
+ Dummy::Application.configure do
21
+ # ...
22
+
23
+ config.middleware.use Neverland::Middleware
24
+ end
25
+ ```
26
+
27
+ By default, Neverland will mock the result of
28
+ navigator.geolocation.getCurrentLocation to latitude 42.31283, longitude
29
+ -71.114287. This can be overridden by sending the parameters
30
+ `neverland[:latitude]` and `neverland[:longitude]`. Error states can also be
31
+ triggered by setting `neverland[:error_code]`.
32
+
33
+ ## Caveats
34
+
35
+ * The implementation naievely overrides the browser's geolocation
36
+ implementation by inserting a JavaScript tag into the response).
37
+
38
+ * The middleware will treat the response body as HTML. If you're using XHTML,
39
+ this could cause issues.
@@ -1,6 +1,8 @@
1
1
  require "neverland/version"
2
2
  require 'neverland/middleware'
3
3
  require 'neverland/engine'
4
+ require 'neverland/parameter_extractor'
5
+ require 'neverland/script_injector'
4
6
 
5
7
  module Neverland
6
8
  end
@@ -7,34 +7,11 @@ module Neverland
7
7
  end
8
8
 
9
9
  def call(env)
10
- cookies = Rack::Request.new(env).cookies
11
- latitude = cookies['mock_latitude']
12
- longitude = cookies['mock_longitude']
10
+ params = ParameterExtractor.extract(env)
13
11
 
14
12
  status, headers, response = @app.call(env)
15
13
 
16
- case response
17
- when ActionDispatch::Response
18
- document = Nokogiri::HTML(response.body)
19
- head = document.at('head')
20
-
21
- script = Nokogiri::XML::Node.new('script', document)
22
- script['type'] = 'text/javascript'
23
- script['src'] = '/javascripts/neverland.js'
24
- head << script
25
-
26
- if latitude.present? && longitude.present?
27
- script = Nokogiri::XML::Node.new('script', document)
28
- script['type'] = 'text/javascript'
29
- script.inner_html = <<-SCRIPT
30
- Neverland.setLatitude(#{latitude})
31
- Neverland.setLongitude(#{longitude})
32
- SCRIPT
33
- head << script
34
- end
35
-
36
- response.body = document.to_s
37
- end
14
+ response = ScriptInjector.inject(response, params)
38
15
 
39
16
  [status, headers, response]
40
17
  end
@@ -0,0 +1,47 @@
1
+ module Neverland
2
+ class ParameterExtractor
3
+ def self.extract(env)
4
+ new(env).extract
5
+ end
6
+
7
+ def initialize(env)
8
+ @request = Rack::Request.new(env)
9
+ end
10
+
11
+ def extract
12
+ Parameters.new(latitude, longitude, error_code)
13
+ end
14
+
15
+ private
16
+
17
+ def params
18
+ @request.params
19
+ end
20
+
21
+ def neverland_params
22
+ params['neverland'] || {}
23
+ end
24
+
25
+ def latitude
26
+ neverland_params['latitude']
27
+ end
28
+
29
+ def longitude
30
+ neverland_params['longitude']
31
+ end
32
+
33
+ def error_code
34
+ neverland_params['error_code']
35
+ end
36
+ end
37
+
38
+ class Parameters
39
+ attr_reader :latitude, :longitude, :error_code
40
+
41
+ def initialize(latitude = nil, longitude = nil, error_code = nil)
42
+ @latitude = latitude
43
+ @longitude = longitude
44
+ @error_code = error_code
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,69 @@
1
+ module Neverland
2
+ class ScriptInjector
3
+ def self.inject(response, params)
4
+ new(response).inject(params)
5
+ end
6
+
7
+ def initialize(response)
8
+ case response
9
+ when ActionDispatch::Response
10
+ @injector = ActionDispatchScriptInjector.new(response)
11
+ else
12
+ @injector = NullScriptInjector.new(response)
13
+ end
14
+ end
15
+
16
+ def inject(params)
17
+ @injector.inject(params)
18
+ end
19
+ end
20
+
21
+ class ActionDispatchScriptInjector
22
+ def initialize(response)
23
+ @response = response
24
+ @document = Nokogiri::HTML(@response.body)
25
+ @head = @document.at('head')
26
+ end
27
+
28
+ def inject(params)
29
+ if @head
30
+ inject_script(:src => '/javascripts/neverland.js')
31
+
32
+ if params.error_code
33
+ inject_script(:body => <<SCRIPT)
34
+ Neverland.setError(#{params.error_code})
35
+ SCRIPT
36
+ elsif params.latitude && params.longitude
37
+ inject_script(:body => <<SCRIPT)
38
+ Neverland.setLatitude(#{params.latitude})
39
+ Neverland.setLongitude(#{params.longitude})
40
+ SCRIPT
41
+ end
42
+
43
+ @response.body = @document.to_s
44
+ end
45
+
46
+ @response
47
+ end
48
+
49
+ private
50
+
51
+ def inject_script(opts = {})
52
+ script = Nokogiri::XML::Node.new('script', @document)
53
+ script['type'] = 'text/javascript'
54
+ script['src'] = opts[:src] if opts[:src]
55
+ script.inner_html = opts[:body] if opts[:body]
56
+ @head << script
57
+ end
58
+ end
59
+
60
+ class NullScriptInjector
61
+ def initialize(response)
62
+ @response = response
63
+ end
64
+
65
+ def inject(_)
66
+ @response
67
+ end
68
+ end
69
+ end
@@ -1,3 +1,3 @@
1
1
  module Neverland
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -1,24 +1,50 @@
1
1
  // http://dev.w3.org/geo/api/spec-source.html
2
2
 
3
3
  navigator.geolocation.getCurrentPosition = function(success, error) {
4
- success({coords: Neverland.getCoords(), timestamp: Date.now()});
4
+ Neverland.getCoords(function(position) {
5
+ success(position)
6
+ }, function(positionError) {
7
+ error(positionError)
8
+ })
5
9
  }
6
10
 
7
- var Neverland = new function() {
8
- var _latitude = 42.31283;
9
- var _longitude = -71.114287;
11
+ var Neverland = (function() {
12
+ var _latitude = 42.31283,
13
+ _longitude = -71.114287,
14
+ _positionError
15
+
16
+ var PositionError = function(code) {
17
+ this.PERMISSION_DENIED = 1
18
+ this.POSITION_UNAVAILABLE = 2
19
+ this.TIMEOUT = 3
20
+ this.code = code
21
+ this.message = null
22
+ Object.freeze(this)
23
+ }
24
+
25
+ var Coordinates = function() {
26
+ this.latitude = _latitude,
27
+ this.longitude = _longitude,
28
+ this.altitude = null,
29
+ this.accuracy = 33,
30
+ this.altitudeAccuracy = null,
31
+ this.heading = null,
32
+ this.speed = null
33
+ Object.freeze(this)
34
+ }
35
+
36
+ var Position = function() {
37
+ this.coords = new Coordinates()
38
+ this.timestamp = Date.now()
39
+ Object.freeze(this)
40
+ }
10
41
 
11
42
  return {
12
- getCoords: function () {
13
- return {
14
- latitude: _latitude,
15
- longitude: _longitude,
16
- altitude: null,
17
- accuracy: 33,
18
- altitudeAccuracy: null,
19
- heading: null,
20
- speed: null
21
- };
43
+ getCoords: function (success, error) {
44
+ if (_positionError)
45
+ error(_positionError)
46
+ else
47
+ success(new Position())
22
48
  },
23
49
 
24
50
  setLatitude: function(latitude) {
@@ -27,6 +53,10 @@ var Neverland = new function() {
27
53
 
28
54
  setLongitude: function(longitude) {
29
55
  _longitude = longitude;
56
+ },
57
+
58
+ setError: function(code) {
59
+ _positionError = new PositionError(code);
30
60
  }
31
61
  }
32
- }
62
+ })()
@@ -0,0 +1,3 @@
1
+ class DecapitatedController < ApplicationController
2
+ layout 'decapitated'
3
+ end
@@ -0,0 +1,8 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <body>
4
+
5
+ <%= yield %>
6
+
7
+ </body>
8
+ </html>
@@ -1,9 +1,19 @@
1
- <div>Latitude: <span id="latitude"></span></div>
2
- <div>Longitude: <span id="longitude"><span></div>
1
+ <div id="success">
2
+ Latitude: <span id="latitude"></span><br>
3
+ Longitude: <span id="longitude"></span>
4
+ </div>
5
+
6
+ <div id="error"></div>
3
7
 
4
8
  <%= javascript_tag do %>
5
- navigator.geolocation.getCurrentPosition(function(success) {
6
- $('#latitude').text(success.coords.latitude);
7
- $('#longitude').text(success.coords.longitude);
9
+ navigator.geolocation.getCurrentPosition(function(position) {
10
+ $('#latitude').text(position.coords.latitude)
11
+ $('#longitude').text(position.coords.longitude)
12
+ }, function(positionError) {
13
+ switch(positionError.code) {
14
+ case positionError.PERMISSION_DENIED:
15
+ $('#error').text('Permission Denied')
16
+ $('#success').hide()
17
+ }
8
18
  });
9
19
  <% end %>
@@ -1,3 +1,4 @@
1
1
  Dummy::Application.routes.draw do
2
2
  root :to => 'test#index'
3
+ match '/decapitated' => 'decapitated#index'
3
4
  end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ module Neverland
4
+ describe ParameterExtractor do
5
+ describe '.extract' do
6
+ let(:latitude) { '42.31283' }
7
+ let(:longitude) { '-71.114287' }
8
+ let(:error_code) { '3' }
9
+ let(:params) { { :neverland => { :latitude => latitude, :longitude => longitude, :error_code => error_code } } }
10
+ let(:env) { Rack::MockRequest.env_for('/bananas', :params => params) }
11
+
12
+ it 'should return the provided latitude' do
13
+ ParameterExtractor.extract(env).latitude.should == latitude
14
+ end
15
+
16
+ it 'should return the provided longitude' do
17
+ ParameterExtractor.extract(env).longitude.should == longitude
18
+ end
19
+
20
+ it 'should return the provided error code' do
21
+ ParameterExtractor.extract(env).error_code.should == error_code
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ module Neverland
4
+ describe ScriptInjector do
5
+ describe '.inject' do
6
+ context 'with an ActionDispatch::Response' do
7
+ let(:parameters) { Parameters.new }
8
+ let(:response) { ActionDispatch::Response.new(200, {}, ['<html><head></head></html>']) }
9
+
10
+ it 'should insert the mock JavaScript into the head' do
11
+ injected_response = ScriptInjector.inject(response, parameters)
12
+ Nokogiri::HTML(injected_response.body).css('head script[src="/javascripts/neverland.js"]').should_not be_empty
13
+ end
14
+ end
15
+
16
+ context 'with a Rack::Response' do
17
+ let(:parameters) { Parameters.new }
18
+ let(:response) { Rack::Response.new(['<html><head></head></html>'], 200, {}) }
19
+
20
+ it 'should not insert the mock JavaScript into the head' do
21
+ injected_response = ScriptInjector.inject(response, parameters)
22
+ Nokogiri::HTML(injected_response.body.first).css('head script[src="/javascripts/neverland.js"]').should be_empty
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,6 +1,24 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'neverland' do
4
+ it "should not insert the mock JavaScript on an unsuccessful response" do
5
+ lambda { visit '/nonexistent' }.should raise_error(ActionController::RoutingError)
6
+
7
+ page.should_not have_selector('head script[src="/javascripts/neverland.js"]')
8
+ end
9
+
10
+ it "should not raise an error if the response has no head" do
11
+ lambda { visit '/decapitated' }.should_not raise_error
12
+
13
+ page.should_not have_selector('head script[src="/javascripts/neverland.js"]')
14
+ end
15
+
16
+ it 'should not insert the mock JavaScript if the response has no head' do
17
+ visit '/decapitated'
18
+
19
+ page.should_not have_selector('head script[src="/javascripts/neverland.js"]')
20
+ end
21
+
4
22
  it 'should insert the mock JavaScript on a successful response' do
5
23
  visit '/'
6
24
 
@@ -16,23 +34,17 @@ describe 'neverland' do
16
34
  end
17
35
 
18
36
  it 'should mock the provided coordinates on a successful response', :js => true do
19
- visit '/'
20
- cookie 'mock_latitude', '37.2630556'
21
- cookie 'mock_longitude', '-115.7930198'
22
-
23
- visit '/'
37
+ latitude = '37.2630556'
38
+ longitude = '-115.7930198'
39
+ visit root_path(:neverland => {:latitude => latitude, :longitude => longitude})
24
40
 
25
- find('#latitude').should have_content('37.2630556')
26
- find('#longitude').should have_content('-115.7930198')
41
+ find('#latitude').should have_content(latitude)
42
+ find('#longitude').should have_content(longitude)
27
43
  end
28
44
 
29
- it "should not mock on an error response" do
30
- lambda { visit '/nonexistent' }.should raise_error(ActionController::RoutingError)
45
+ it 'should display Permission Denied when the location request is denied', :js => true do
46
+ visit root_path(:neverland => {:error_code => 1})
31
47
 
32
- page.should_not have_selector('head script[src="/javascripts/neverland.js"]')
48
+ find('#error').should have_content('Permission Denied')
33
49
  end
34
50
  end
35
-
36
- def cookie(name, value)
37
- Capybara.current_session.driver.browser.manage.add_cookie(:name => name, :value => value)
38
- end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: neverland
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-24 00:00:00.000000000 Z
12
+ date: 2012-03-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
16
- requirement: &16437000 !ruby/object:Gem::Requirement
16
+ requirement: &9117980 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *16437000
24
+ version_requirements: *9117980
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec-rails
27
- requirement: &16436500 !ruby/object:Gem::Requirement
27
+ requirement: &9117340 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *16436500
35
+ version_requirements: *9117340
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: tzinfo
38
- requirement: &16435920 !ruby/object:Gem::Requirement
38
+ requirement: &9116640 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *16435920
46
+ version_requirements: *9116640
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: capybara
49
- requirement: &16435360 !ruby/object:Gem::Requirement
49
+ requirement: &9116000 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *16435360
57
+ version_requirements: *9116000
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: jquery-rails
60
- requirement: &16434720 !ruby/object:Gem::Requirement
60
+ requirement: &9115500 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,7 +65,7 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *16434720
68
+ version_requirements: *9115500
69
69
  description: Ever need to test geolocation in your Rails app? This gem makes it easy.
70
70
  email:
71
71
  - matthew.horan@livestream.com
@@ -75,12 +75,16 @@ extra_rdoc_files: []
75
75
  files:
76
76
  - .gitignore
77
77
  - .rspec
78
+ - .travis.yml
78
79
  - Gemfile
79
80
  - MIT-LICENSE
81
+ - README.md
80
82
  - Rakefile
81
83
  - lib/neverland.rb
82
84
  - lib/neverland/engine.rb
83
85
  - lib/neverland/middleware.rb
86
+ - lib/neverland/parameter_extractor.rb
87
+ - lib/neverland/script_injector.rb
84
88
  - lib/neverland/version.rb
85
89
  - neverland.gemspec
86
90
  - public/javascripts/neverland.js
@@ -89,9 +93,12 @@ files:
89
93
  - spec/dummy/app/assets/javascripts/application.js
90
94
  - spec/dummy/app/assets/stylesheets/application.css
91
95
  - spec/dummy/app/controllers/application_controller.rb
96
+ - spec/dummy/app/controllers/decapitated_controller.rb
92
97
  - spec/dummy/app/controllers/test_controller.rb
93
98
  - spec/dummy/app/helpers/application_helper.rb
99
+ - spec/dummy/app/views/decapitated/index.html.erb
94
100
  - spec/dummy/app/views/layouts/application.html.erb
101
+ - spec/dummy/app/views/layouts/decapitated.html.erb
95
102
  - spec/dummy/app/views/test/index.html.erb
96
103
  - spec/dummy/config.ru
97
104
  - spec/dummy/config/application.rb
@@ -106,6 +113,8 @@ files:
106
113
  - spec/dummy/public/500.html
107
114
  - spec/dummy/public/favicon.ico
108
115
  - spec/dummy/script/rails
116
+ - spec/neverland/parameter_extractor_spec.rb
117
+ - spec/neverland/script_injector_spec.rb
109
118
  - spec/requests/neverland_spec.rb
110
119
  - spec/spec_helper.rb
111
120
  homepage: ''