Geocoder, how to test locally when ip is 127.0.0.1? - ruby-on-rails

I can't get geocoder to work correct as my local ip address is 127.0.0.1 so it can't located where I am correctly.
The request.location.ip shows "127.0.0.1"
How can I use a different ip address (my internet connection ip) so it will bring break more relevant data?

A nice clean way to do it is using MiddleWare. Add this class to your lib directory:
# lib/spoof_ip.rb
class SpoofIp
def initialize(app, ip)
#app = app
#ip = ip
end
def call(env)
env['HTTP_X_FORWARDED_FOR'] = nil
env['REMOTE_ADDR'] = env['action_dispatch.remote_ip'] = #ip
#status, #headers, #response = #app.call(env)
[#status, #headers, #response]
end
end
Then find an IP address you want to use for your development environment and add this to your development.rb file:
config.middleware.use('SpoofIp', '64.71.24.19')

For this I usually use params[:ip] or something in development. That allows me to test other ip addresses for functionality and pretend I'm anywhere in the world.
For example
class ApplicationController < ActionController::Base
def request_ip
if Rails.env.development? && params[:ip]
params[:ip]
else
request.remote_ip
end
end
end

I implemented this slightly different, and this works well for my case.
In application_controller.rb i have a lookup method which calls the Geocoder IP lookup directly passing in the results of request.remote_ip.
def lookup_ip_location
if Rails.env.development?
Geocoder.search(request.remote_ip).first
else
request.location
end
end
Then in config/environments/development.rb i monkey-patched the remote_ip call:
class ActionDispatch::Request
def remote_ip
"71.212.123.5" # ipd home (Denver,CO or Renton,WA)
# "208.87.35.103" # websiteuk.com -- Nassau, Bahamas
# "50.78.167.161" # HOL Seattle, WA
end
end
I just hard code some addresses, but you could do whatever you'd like here.

I had the same question. Here is how I implemented with geocoder.
#gemfile
gem 'httparty', :require => 'httparty', :group => :development
#application_controller
def request_ip
if Rails.env.development?
response = HTTParty.get('http://api.hostip.info/get_html.php')
ip = response.split("\n")
ip.last.gsub /IP:\s+/, ''
else
request.remote_ip
end
end
#controller
ip = request_ip
response = Geocoder.search(ip)
( code part with hostip.info from geo_magic gem, and based on the other answer to this question. )
now you can do something like response.first.state

This is an updated answer for geocoder 1.2.9 to provide a hardcoded IP for development and test environments. Just place this at the bottom of your config/initilizers/geocoder.rb:
if %w(development test).include? Rails.env
module Geocoder
module Request
def geocoder_spoofable_ip_with_localhost_override
ip_candidate = geocoder_spoofable_ip_without_localhost_override
if ip_candidate == '127.0.0.1'
'1.2.3.4'
else
ip_candidate
end
end
alias_method_chain :geocoder_spoofable_ip, :localhost_override
end
end
end

you may also do this
request.safe_location

Related

Failed to open TCP connection to :80 HTTParty gem

So in my course on Coursera I'm required to build this rather simple aplication to obtain and display an array from an external api. I'm using the ruby on rails framework. (I'm using windows 10)
Controller -
class CoursesController < ApplicationController
def index
#search_term = params[:looking_for] || 'jhu'
#courses = Coursera.for(#search_term)
end
end
Model
class Coursera
include HTTParty
default_options.update(verify: false) # Turn off SSL verification
base_uri 'https://api.coursera.org/api/courses.v1'
default_params fields: "photoUrl,description",q: "search"
format :json
def self.for term
get("",query: {query: term}) ["elements"]
end
end
The view is irrelevant as this works fine.
But in my other app , I get this error -
Errno::ECONNREFUSED: Failed to open TCP connection to :80 (Connection refused - connect(2) for nil port 80)
This is the other app which I'm having the problem with -
Controller -
class RecipesController < ApplicationController
def index
#search_term = params[:search] || 'chocolate'
#recipes = Recipe.for #search_term
end
end
Model -
class Recipe
include HTTParty
default_options.update(verify: false) # Turn off SSL verification
key_value = ENV['FOOD2FORK_KEY']
hostport = ENV['FOOD2FORK_SERVER_AND_PORT'] || 'food2fork.com'
base_uri = "https://doesntmatter.com/api" #website mentioned here
#doesn't matter , I get the error nonetheless
default_params key: ENV['FOOD2FORK_KEY'] ,q: "search"
format :json
def self.for term
get("/search",query: {query: term}) ["recipes"]
end
end
I've tried disabling all possible firewalls , unblocked all ports with TCP in windows firewall , but I still get the same error. Any ideas how to fix this ? Because I don't think its a problem in my code yet...
You have an excess symbol in your code:
class Recipe
# some code here
base_uri = "https://doesntmatter.com/api"
^ # the equal symbol is redundant.
# remove it and all will works as expected
# ....
end

How to test an unrouted controller action or exception_app in Rails

We have a custom exception app that has been raising (fail safe) exceptions (the application equivalent of having an exception in a rescue block).
I think I've fixed it, but am finding it hard to test. It's an unrouted controller, so I can't use controller tests (require routing).
i.e. I have Rails.configuration.exceptions_app = ExceptionController.action(:show), not Rails.configuration.exceptions_app = self.routes.
Basically what I think I need to do is
Generate a test request request = ActionDispatch::TestRequest.new
include Rack::Test or maybe mimic behavior in ActiveSupport::IntegrationTest
Set #app = ExceptionsController.action(:show)
Fake an exception request.env.merge! 'action_dispatch.exception' => ActionController::RoutingError.new(:foo)
Test response = #app.call(request.env)
Assert no exception is raised and correct response body and status
Problems:
The env needs
a warden / devise session with current_user request.env['warden'] = spy(Warden) and request.session = ActionDispatch::Integration::Session.new(#app)
to manipulate request formats so that I can check that a request without an accept defaults to json request.any?(:json)? constraints: { default: :json } ? `request.accept = "application/javascript"
work work with the respond_with responder
set action_dispatch.show_exceptions, consider all requests local, etc request.env["action_dispatch.show_detailed_exceptions"] = true
Also, I considered building a ActionDispatch::ShowException.new(app, ExceptionController.new) or a small rack app
But our gem has no tests and I haven't been able to apply anything that I've read in exception handling gems (most work at the rescue_action_in_public level or mix in to ShowException) or in the Rails source code
This is a Rails 4.2 app tested via Rspec and Capybara.
Thoughts, links, halp?
Example code and tests
RSpec.describe 'ExceptionController' do
class ExceptionController < ActionController::Base
use ActionDispatch::ShowExceptions, Rails.configuration.exceptions_app
use ActionDispatch::DebugExceptions
#Response
respond_to :html, :json
#Layout
layout :layout_status
#Dependencies
before_action :status, :app_name, :log_exception
def show
respond_with details, status: #status, location: nil
end
def show_detailed_exceptions?
request.local?
end
protected
####################
# Dependencies #
####################
#Info
def status
#exception = env['action_dispatch.exception']
#status = ActionDispatch::ExceptionWrapper.new(env, #exception).status_code
#response = ActionDispatch::ExceptionWrapper.rescue_responses[#exception.class.name]
end
#Format
def details
#details ||= {}.tap do |h|
I18n.with_options scope: [:exception, :show, #response], exception_name: #exception.class.name, exception_message: #exception.message do |i18n|
h[:name] = i18n.t "#{#exception.class.name.underscore}.title", default: i18n.t(:title, default: #exception.class.name)
h[:message] = i18n.t "#{#exception.class.name.underscore}.description", default: i18n.t(:description, default: #exception.message)
end
end
end
helper_method :details
####################
# Layout #
####################
private
def log_exception
if #status.to_s == '500'
request.env[:exception_details] = details
request.env[:exception_details][:location] = ActionDispatch::ExceptionWrapper.new(env, #exception).application_trace[0]
end
end
#Layout
def layout_status
#status.to_s != '404' ? 'error' : 'application'
end
#App
def app_name
#app_name = Rails.application.class.parent_name
end
end
include Rack::Test::Methods
include ActionDispatch::Integration::Runner
include ActionController::TemplateAssertions
include ActionDispatch::Routing::UrlFor
let(:exception) { ActionController::RoutingError.new(:foo) }
let(:request) { ActionDispatch::TestRequest.new }
def app
# Rails.application.config.exceptions_app
#app ||= ExceptionController.action(:show)
end
it 'logs unknown format errors' do
request.env['action_dispatch.show_exceptions'] = true
request.env['consider_all_requests_local'] = true
request.env['warden'] = spy(Warden)
request.session = ActionDispatch::Integration::Session.new(app)
exception = ActionController::RoutingError.new(:foo)
request.env.merge! 'action_dispatch.exception' => exception
post '/whatever'
expect(response.body).to eq("dunno?")
end
end
refs:
https://github.com/richpeck/exception_handler
https://github.com/plataformatec/responders/blob/8f03848a2f50d4685c15a31254a1f600af947bd7/test/action_controller/respond_with_test.rb#L265-L275
https://github.com/rails/rails/blob/1d43458c148f9532a81b92ee3a247da4f1c0b7ad/actionpack/test/dispatch/show_exceptions_test.rb#L92-L99
https://github.com/rails/rails/blob/3e36db4406beea32772b1db1e9a16cc1e8aea14c/railties/test/application/middleware/exceptions_test.rb#L86-L91
https://github.com/rails/rails/blob/34fa6658dd1b779b21e586f01ee64c6f59ca1537/actionpack/lib/action_dispatch/testing/integration.rb#L647-L674
https://github.com/rails/rails/blob/ef8d09d932e36b0614905ea5bc3fb6af318b6ce2/actionview/test/abstract_unit.rb#L146-L184
https://github.com/plataformatec/responders/blob/8f03848a2f50d4685c15a31254a1f600af947bd7/lib/action_controller/respond_with.rb#L196-L207
http://blog.plataformatec.com.br/2012/01/my-five-favorite-hidden-features-in-rails-3-2/
https://github.com/bugsnag/bugsnag-ruby/blob/master/lib/bugsnag/middleware/warden_user.rb
https://github.com/bugsnag/bugsnag-ruby/blob/master/lib/bugsnag/rails/action_controller_rescue.rb#L3-L33
https://github.com/bugsnag/bugsnag-ruby/blob/master/lib/bugsnag/rails/active_record_rescue.rb
https://github.com/rollbar/rollbar-gem/blob/master/spec/rollbar_spec.rb
http://andre.arko.net/2011/12/10/make-rails-3-stop-trying-to-serve-html/
https://github.com/rails/rails/blob/9503e65b9718e4f01860cf017c1cdcdccdfffde7/actionpack/lib/action_dispatch/middleware/show_exceptions.rb#L46-L49
Update 2015-08-27
It has been suggested that this question may be a duplicate of Testing error pages in Rails with Rspec + Capybara, however, that question addresses testing exception responses when the exceptions_app is set to routes.
As I wrote above, I'm using a Controller as the exceptions_app, so though I could use capybara to visit non-existing pages, I'd like to test Controller's action directly, rather than include the rest of the show exceptions stack. This is important because my problem is when the exceptions app is called with an unhandled content type, which I cannot easily test via capybara.
More generally, what I need to test is when the Exceptions app raises and exception, that I have fixed it.
I'm open to seeing some example code, though.

Restrict access to app by ip address OR via password (rails app)

I have the whitelabel working as follows in my application controller:
before_filter :protect
def protect
#ips = ['127.0.0.1', '203.123.10.1'] #And so on ...]
if not #ips.include? request.remote_ip
# Check for your subnet stuff here, for example
# if not request.remote_ip.include?('127.0,0')
render :text => "You are unauthorized"
return
end
end
I'd like to add the option that IF your IP is not whitelisted, you can enter a password to see the page.
There is no user model on this application (we only use it to display company metrics at the office (one page view) and want to be able to access the site at home/on mobile without having to constantly update ip's)
Thanks for any help
You can use the basic authentication, like below for your case:
def protect
#ips = ['127.0.0.1', '203.123.10.1'] #And so on ...]
if not #ips.include? request.remote_ip
if user = authenticate_with_http_basic { |u, p| u=='username' and p=='secret' }
#current_user = user
else
request_http_basic_authentication
end
end
end

Rails manually redirecting from naked domain

So currently I am manually directing from a naked domain due to restrictions with my hosting provider (Heroku). Everything works just fine. The problem is that if a users visits mydomain.com/route, a redirect will be issued back to www.mydomain.com without the /route. How would I go about re-appending the route, but still redirecting to www. ?
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :ensure_domain
APP_DOMAIN = 'www.domain.com'
def index
end
def ensure_domain
if Rails.env.production?
if request.env['HTTP_HOST'] != APP_DOMAIN
redirect_to "http://#{APP_DOMAIN}", :status => 301
end
end
end
end
EDIT
I removed my code above from my ApplicationController, and opted for using the refraction gem as suggested by hurikhan77, which solved my problem.
Here is refraction_rules.rb I used.
Refraction.configure do |req|
if req.host == "domain.com"
req.permanent! :host => "www.domain.com"
end
end
I suggest using the refraction gem for this: http://rubygems.org/gems/refraction
Ideally, you would set up rules like that in your web server configuration. Requests would become faster, because they would not even reach the rails stack. There would be no need to add any code to your app either.
However, if you are running in some restricted environment, like heroku, I'd advise adding a rack middleware. (Just for guidelines, can't guarantee if this particular code is bug free)
class Redirector
SUBDOMAIN = 'www'
def initialize(app)
#app = app
end
def call(env)
#env = env
if redirect?
redirect
else
#app.call(env)
end
end
private
def redirect?
# do some regex to figure out if you want to redirect
end
def redirect
headers = {
"location" => redirect_url
}
[302, headers, ["You are being redirected..."]] # 302 for temp, 301 for permanent
end
def redirect_url
scheme = #env["rack.url_scheme"]
if #env['SERVER_PORT'] == '80'
port = ''
else
port = ":#{#env['SERVER_PORT']}"
end
path = #env["PATH_INFO"]
query_string = ""
if !#env["QUERY_STRING"].empty?
query_string = "?" + #env["QUERY_STRING"]
end
host = "://#{SUBDOMAIN}." + domain # this is where we add the subdomain
"#{scheme}#{host}#{path}#{query_string}"
end
def domain
# extract domain from request or get it from an environment variable etc.
end
end
You can also test the whole thing in isolation
describe Redirector do
include Rack::Test::Methods
def default_app
lambda { |env|
headers = {'Content-Type' => "text/html"}
headers['Set-Cookie'] = "id=1; path=/\ntoken=abc; path=/; secure; HttpOnly"
[200, headers, ["default body"]]
}
end
def app()
#app ||= Rack::Lint.new(Redirector.new(default_app))
end
it "redirects unsupported subdomains" do
get "http://example.com/zomg?a=1"
last_response.status.should eq 301
last_response.header['location'].should eq "http://www.example.com/zomg?a=1"
end
# and so on
end
Then you can add it to production (or any preferred environments) only
# production.rb
# ...
config.middleware.insert_after 'ActionDispatch::Static', 'Redirector'
If you want to test it in development, add the same line to development.rb and add a record to your hosts file (usually /etc/hosts) to treat yoursubdomain.localhost as 127.0.0.1
Not sure if this is the best solution but you could regex the request.referrer and pull out anything after .com and append it to the APP_DOMAIN
Or I guess you could just take out everything before the first . in request.env['HTTP_HOST'] add replace with http://www. assuming you don't plan on using subdomains.

How do I mock an IP address in cucumber/capybara?

I'm using Cucumber and Capybara and I'd like a way to simulate the request IP address, like this:
Given the request ip address is "10.1.2.3"
I solved it by passing the IP address in an environment variable:
When /^the request ip address is "([^\"]*)"$/ do |ip_address|
ENV['RAILS_TEST_IP_ADDRESS'] = ip_address
end
application_controller.rb:
before_filter :mock_ip_address
def mock_ip_address
if Rails.env == 'cucumber' || Rails.env == 'test'
test_ip = ENV['RAILS_TEST_IP_ADDRESS']
unless test_ip.nil? or test_ip.empty?
request.instance_eval <<-EOS
def remote_ip
"#{test_ip}"
end
EOS
end
end
end
My mix of Leventix's and Ramon's solutions:
spec/support/remote_ip_monkey_patch.rb
module ActionDispatch
class Request
def remote_ip_with_mocking
test_ip = ENV['RAILS_TEST_IP_ADDRESS']
unless test_ip.nil? or test_ip.empty?
return test_ip
else
return remote_ip_without_mocking
end
end
alias_method_chain :remote_ip, :mocking
end
end

Resources