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.
- data/.travis.yml +7 -0
- data/README.md +39 -0
- data/lib/neverland.rb +2 -0
- data/lib/neverland/middleware.rb +2 -25
- data/lib/neverland/parameter_extractor.rb +47 -0
- data/lib/neverland/script_injector.rb +69 -0
- data/lib/neverland/version.rb +1 -1
- data/public/javascripts/neverland.js +45 -15
- data/spec/dummy/app/controllers/decapitated_controller.rb +3 -0
- data/spec/dummy/app/views/decapitated/index.html.erb +1 -0
- data/spec/dummy/app/views/layouts/decapitated.html.erb +8 -0
- data/spec/dummy/app/views/test/index.html.erb +15 -5
- data/spec/dummy/config/routes.rb +1 -0
- data/spec/neverland/parameter_extractor_spec.rb +25 -0
- data/spec/neverland/script_injector_spec.rb +27 -0
- data/spec/requests/neverland_spec.rb +26 -14
- metadata +21 -12
data/.travis.yml
ADDED
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# Neverland
|
2
|
+
|
3
|
+
[](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.
|
data/lib/neverland.rb
CHANGED
data/lib/neverland/middleware.rb
CHANGED
@@ -7,34 +7,11 @@ module Neverland
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def call(env)
|
10
|
-
|
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
|
-
|
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
|
data/lib/neverland/version.rb
CHANGED
@@ -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
|
-
|
4
|
+
Neverland.getCoords(function(position) {
|
5
|
+
success(position)
|
6
|
+
}, function(positionError) {
|
7
|
+
error(positionError)
|
8
|
+
})
|
5
9
|
}
|
6
10
|
|
7
|
-
var Neverland =
|
8
|
-
var _latitude = 42.31283
|
9
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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 @@
|
|
1
|
+
|
@@ -1,9 +1,19 @@
|
|
1
|
-
<div
|
2
|
-
|
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(
|
6
|
-
$('#latitude').text(
|
7
|
-
$('#longitude').text(
|
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 %>
|
data/spec/dummy/config/routes.rb
CHANGED
@@ -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
|
-
|
20
|
-
|
21
|
-
|
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(
|
26
|
-
find('#longitude').should have_content(
|
41
|
+
find('#latitude').should have_content(latitude)
|
42
|
+
find('#longitude').should have_content(longitude)
|
27
43
|
end
|
28
44
|
|
29
|
-
it
|
30
|
-
|
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
|
-
|
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.
|
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-
|
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: &
|
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: *
|
24
|
+
version_requirements: *9117980
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec-rails
|
27
|
-
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: *
|
35
|
+
version_requirements: *9117340
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: tzinfo
|
38
|
-
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: *
|
46
|
+
version_requirements: *9116640
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: capybara
|
49
|
-
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: *
|
57
|
+
version_requirements: *9116000
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: jquery-rails
|
60
|
-
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: *
|
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: ''
|