geocoder
gem will not work with localhost. And if to use it in production it can only give approximate results.
More precise result can be received by sending ajax request from frontend to backend.
Example:

<!-- application.html.erb -->
<!DOCTYPE html>
<html>
<head>
<%= javascript_include_tag 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js' %>
<%= javascript_include_tag 'https://cdnjs.cloudflare.com/ajax/libs/noty/3.1.0/noty.js' %>
<%= stylesheet_link_tag 'https://cdnjs.cloudflare.com/ajax/libs/noty/3.1.0/noty.min.css' %>
<script>
// It's bad to write inline javascript. To be moved into separate .js file
$(function() {
Noty.overrideDefaults({ theme: 'relax', layout: 'topCenter' });
$('.getLocation').click(function(e) {
event.preventDefault();
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
$('.latInput').val(position.coords.latitude);
$('.longInput').val(position.coords.longitude);
new Noty({
text: "Got location",
type: "success"
}).show();
}, function(position) {
new Noty({
text: "Could not get location",
type: "error"
}).show();
});
} else {
new Noty({
text: "Geolocation is not supported by this browser.",
type: "error"
}).show();
}
});
$('.resetLocation').click(function(e) {
event.preventDefault();
$('.latInput').val('');
$('.longInput').val('');
});
$('.submitAjax').click(function(e) {
event.preventDefault();
var lat = $('.latInput').val();
var long = $('.longInput').val();
if (lat && long) {
$.ajax({
url: "/",
data: {
lat: lat,
long: long
}
}).done(function() {
new Noty({
text: "Data was successfully submitted to server via AJAX",
type: "success"
}).show();
}).fail(function() {
new Noty({
text: "Couldn't send data",
type: "error"
}).show();
});
} else {
new Noty({
text: "Click on 'Get Location' button first",
type: "error"
}).show();
}
});
$('.clearAndSubmit').click(function(e) {
$('.latInput').val('');
$('.longInput').val('');
});
});
</script>
</head>
<body>
<%
ip = request.remote_ip
ip = '8.8.8.8' if ip.in?(["::1", "127.0.0.1"])
%>
Geocoder.search(ip):
<br />
<textarea cols="50" rows="10"><%= Geocoder.search(ip) %></textarea>
<hr />
<% if params[:lat].present? && params[:long].present? %>
Form was submitted:
<br />
Lat: <%= params[:lat] %>
<br />
Long: <%= params[:lat] %>
<% else %>
Form was not submitted
<% end %>
<br />
<br />
<%= form_tag '/', method: :get do |f| %>
<button class="getLocation">Get Location</button>
<button class="resetLocation">Reset Location</button>
<br />
<br />
<%= label_tag :lat, 'Lat:' %>
<%= text_field_tag :lat, params[:lat], class: 'latInput' %>
<%= label_tag :long, 'Long:' %>
<%= text_field_tag :long, params[:long], class: 'longInput' %>
<br />
<br />
<button class="submitAjax">Submit location to server via ajax</button>
<br />
<br />
<%= submit_tag "Submit location to server via form submit" %>
<br />
<br />
<button class="clearAndSubmit">Clear form and submit</button>
<% end %>
</body>
</html>
# application_controller.rb
class ApplicationController < ActionController::Base
before_action do
# Implemented before_action for demonstration purposes. Should be moved into action.
if params[:lat].present? && params[:long].present?
Rails.logger.info(['Received lat&long from AJAX request', {lat: params[:lat], long: params[:long]}])
# current_user.lat = params[:lat]
# current_user.lat = params[:lng]
# current_user.save!
end
end
end
# gemfile.rb
#...
gem 'geocoder'