How I'm Using Your IP Address Right Now

A simple Ruby on Rails approach to IP address tracking

02/05/2018

By Aaron Rohrbacher

Share:

Yes, your IP address is being tracked by this silly little blog! While there are many available options for analytics, I simply wanted to store the city from which someone (you) is viewing my individual posts. Once I got to thinkin', it dawned on me that I could also use your IP address to somewhat "authenticate" the editing of comments. In this case, I'm matching the IP address of the computer that posted a comment to that of the computer trying to edit the comment. This, in my pea-brain, alleviates the need for you to log in, and limits the amount of long-term editing, without e-mailing me first! We'll see how it works.

While Rails provides a "magic" way of obtaining a user's IP address, what can you actually do with it? In this case, I'm using the freegeoip.net API.

First, knowing I needed to make an API call, I installed one of my favorite gems: RestClient, by adding it to my Gemfile:

# gemfile

gem 'rest-client', '~> 1.8'

And, of course, running $ bundle install from the root directory of my application.

Next, it was time to decide where and how to make this API call. I decided to create a service, as I see an API call as such. I created the directory app/services, and created the file ip_service.rb in the new services directory.

Knowing that Rails will provide me the IP address of the computer viewing my post using request.remote_ip (more on that in a moment), I researched how to get the city name from the freegeoip API. Using RestClient, I wrote my service as such (note that this API returns a pretty simple JSON result, and I used some JSON.parse action to return only one part of it):

# app/services/ip_service.rb
class IpService
  def detect_city(ip_address)
    result = RestClient.get("https://freegeoip.net/json/#{ip_address}")
    JSON.parse(result)['city']
  end

Great! Now I have access to the city of a post viewer. Now to make use of it!

I created a 'view' model, joining my 'post' and 'comment' models.

# app/models/view.rb
class View < ApplicationRecord
  belongs_to :post
end

# app/models/post.rb
class Post < ApplicationRecord
  has_many :comments
  has_many :views
end

# app/models/comment.rb
class Comment < ApplicationRecord
  belongs_to :post
end


I generated a views table for my database, among the post and comment tables.

# db migrations:

# views:
class CreateViews < ActiveRecord::Migration[5.1]
  def change
    create_table :views do |t|
      t.integer :post_id
      t.string :city
      t.string :ip_address

      t.timestamps
    end
  end
end

# posts:
class CreatePosts < ActiveRecord::Migration[5.1]
  def change
    create_table :posts do |t|
      t.string :title
      t.string :description
      t.text :content
      t.timestamps
    end
  end
end

# comments:
class CreateComments < ActiveRecord::Migration[5.1]
  def change
    create_table :comments do |t|
      t.string :name, default: 'Anonymous User'
      t.text :content
      t.integer :post_id
      t.string :ip_address
      t.timestamps
    end
  end
end

As you can see, I'm relating a view to a post, and including a city name, and (just for fun), the actual IP address of the viewer (once again, YOU! Thanks!)

Note: I'm also including :ip_address in comments; more on that in a bit!

Finally! In the post controller, in the 'show' method, I'm actually grabbing your IP address and storing it in the database (current_user is me, and me alone, as it stands-- this prevents the API call if I'm tooling around on my own site, like the narcissist I am):

# controllers/posts_controller.rb
def show
  @post = Post.find(params[:id])
  @comments = @post.comments.all

  @comment = @post.comments.new

  if !current_user
    ip_address = IpService.new
    @post.views.create(city: ip_address.detect_city(request.remote_ip), ip_address: request.remote_ip)
  end
end

Great! I can now take your IP address, as well as the city you're in via the (freegeoip.net) API, when you view my post.

I went a step further:

I wanted to prevent a need for you to "log in" to my site in order to leave a comment. We'll see how it goes, but in the following example, a user trying to edit a comment on this blog can only do so if he/she is on the same IP address from which he/she made the comment.

To do so, I did some "extra" things in my comment controller in update:

# controllers/comments_controller.rb
def update
  @post = Post.find(params[:post_id])
  @comment = Comment.find(params[:id])
  if @comment.ip_address == request.remote_ip
    @comment.update(post_params)
    flash[:alert]="Comment posted - edit again if needed!"
    redirect_to edit_post_comment_path(@post, @comment)
  else
    flash[:alert]="Your IP address does not match original comment's IP address. E-mail me if you need your comment updated!"
    redirect_to root_path
  end
end

And, in the view, I added a "hidden_field" to actually submit the originator's ip address:
Note: in my case, I'm adding comments from the post's "show" method. Adjust as necessary!

# views/posts/show
<%= form_for [@post, @comment] do |f| %>
  <div class = "form-group">
    <%= f.label 'Your name (optional):' %>
    <%= f.text_field :name, class: "form-control" %>
    <%= f.label 'Your comment:' %>

    <%= f.text_area :content, class: "form-control" %>
    <%= f.hidden_field :ip_address, value: request.remote_ip %>
    <%= f.submit 'Preview and edit', class: "btn btn-success" %>
  </div>
<%= render 'shared/comment_suggestions' %>
<% end %>

Alas! I'm doing two things by using Ruby on Rails's 'request.remote_ip', allowing me to access the cities in which my posts are being viewed, and allowing you to make comments and edit them, but only if it's really you. Thank you for reading my first Junior Rubyist Blog Post!

The source code for this application is available on my GitHub Repository; feel free to poke around!


Comments:

Michael said:


Cheers -- we'll see if it works!

Edit: it works (unless someone else manages to change this!)


Aaron Rohrbacher said:


Michael, thank you for your comment! I'm happy to report that I was unable to edit your comment!


Matt Ozrelic said:


Cool! Fun experiment. However, most ISP’sdon’t provide clients with static IP’s, usually they are dynamic. You could potentially use HTML5 local storage as a fall back or a check!


Aaron Rohrbacher said:


Thanks for reading, Matt! In this case, the user's dynamic IP address is just what I'm after; if a user is trying to edit a comment after the amount of time it takes for their IP address to change, I want them to e-mail me! On my end, Rails' request.remote_ip is tracing the trusted IP addresses back to the originator, which does account for proxies, and works well with the API I'm using for location. One of the many reasons I've come to love working in Ruby on Rails. But yes, should this blog one day end up with thousands of users (hah!), I would definitely need to rely on login and session data! Thanks again for reading and commenting- I look forward to chatting with you soon!


navsdevidz said:


uFg7P9 odgphkvyguhy, [url=http://tammleedonxh.com/]tammleedonxh[/url], [link=http://jnatzvzqcfnb.com/]jnatzvzqcfnb[/link], http://lbsvixlsfwtk.com/


Add a comment:

Markdown is allowed. Examples:

You type:

Here's a regular old comment!

Returns:

Here's a regular old comment!


You type:

Here's a sentence with `a little bit of code` in it.

Returns:

Here's a sentence with a little bit of code in it.


You type:

here is some text
This line must be blank! Press Enter!
``` JavaScript
thing = function() {
  return "stuff"
}
```

Returns:

here is some text

thing = function() {
  return "stuff"
}

In the above example, I've specified a language. If you don't, the default is Ruby (as it's clearly the best).