Restoring Rails session data when cookies aren't available

C is for Cookie
If you've ever needed to implement user-friendly upload, you know intimately what a pain it is to get right. The web just isn't built for uploading files from a browser. I mean, it kinda works, but even then only with a dozen or so limitations. Even the major photo and video sites have tried various solutions to make this easier for users. So when I built Dibs.net, I decided rather quickly to abandon all hope of getting it working flawlessly with plain ol' Javascript and HTML, and instead looked into using a fairly nonintrusive Flash uploader component. (Without Flash installed, it just falls back to a simple HTML-based file-upload form.)

That's not to say it was perfectly simple to get working with Rails. Because Dibs.net accepts uploads only from logged-in users, I ran into two limitations that would not allow me to use this solution:

  • Flash doesn't send the cookies from the browser (at least it doesn't in Firefox; it might in IE)
  • Rails doesn't support non-cookie sessions

Because Flash doesn't send the session cookie, Rails thinks the request is coming from a new, logged-out user and creates a new session for it. Adding a cookies feature to Flash was well out of my hands since I don't work for Adobe, so I looked into a way to restore the session from a session key passed as a URL parameter. After some experimentation, I found a solution that works great.

Assumptions

I use a modified version of the acts_as_authenticated plugin. Upon authentication, the plugin sets the :user session key to the authenticated user's id. You'll need to adapt for your own configuration.

Example Rails Code

In RAILS_ROOT/app/controllers/show_my_ip_controller.rb:

class ImagesController < ApplicationController
	session :off, :only => :create
	prepend_before_filter :restore_session_user_from_param, :only => :create
	requires_login :except => :index

	def create
	  # Handle the file upload here
	end

	private
	def restore_session_user_from_param
	    data = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:database_manager].session_class.find_session( params[:_session_id] ).data
	    sess_obj = Marshal.load( Base64.decode64( data ) )
	    @current_user = User.find( sess_obj[:user] )
  	rescue
    	authorization_required
  	end
end

Then we include the session id as a parameter in the form's action URL in the view:

<form action="<%= images_path(:_session_id => session.session_id) %>" method="post" id="photoupload" enctype="multipart/form-data">

How it works

Under normal circumstances the acts_as_authenticated plugin sets the @current_user instance variable to the current logged-in user at the start of each request. Since we have no session data when a Flash app hits the controller, there's effectively no current_user. Our goal is to get current_user working, so we:

  • turn sessions off for the relevant action; otherwise Rails will create useless sessions any time Flash hits that action
  • prepend a before filter to set the @current_user instance variable
  • require login for most of the actions, including create

In the before_filter, we grab the session data from whatever session store we're using, decode and unmarshall it, and set the @current_user instance variable to the User we find with the id we get from the session hash.

Simple? Not really. But it works!

Further Reading

I couldn't find much documentation on any of this beyond stomping through the Rails code & Ruby's CGI Standard Library docs.

Update: A Word of Caution

I forgot to mention when I published this earlier that there's a reason parameterized sessions is discouraged: browsers will send the entire current link, including the session id, in referer headers to offsite hosts. This doesn't affect Dibs.net's Flash upload, but in other scenarios use the above with caution, or with SSL.

Feedback and Article Ideas

Want to see a topic explored here? Send Me a Message.

De-tangling attr_reader, attr_writer and attr_accessor from attr_protected & attr_accessible (Part 1)

Car crashWhat a mess. You have undoubtedly run across these methods sprinkled throughout the Ruby and Rails world. If you've been working with Rails for even a short time, you've probably read a little about security and attr_accessible. But do you really understand what each of them do and when to use them?
I'm not a fan of whoever made these methods so closely named, especially because they serve very different purposes: two are specific to Rails (or more accurately, ActiveRecord), while the other three are Ruby core methods. When I have a need for any of them, I still have to really think about which one I actually want to use. Often I still have to glance at the rdocs to be reassured my choice is the right one. So let's dive in and figure out what the heck these are supposed to do, and when to use them.

Forget Rails, Let's Talk Ruby Attributes

I've noticed a fair number of folks try to write Rails apps without much knowledge of Ruby. That's a noble thing to try, but to be a truly effective Rails developer, you'll need to be intimately knowledgeable about the Ruby language.

For this reason, and to get our feet wet, we'll ignore the Rails methods for a moment and focus on the Ruby, since that's the source of three important attr_* methods: attr_accessor, attr_reader, and attr_writer. Put these three methods together in a bucket inside your head, as they are closely related.

Say we have a simple little Ruby class (remember we're ignoring Rails at the moment, so this is just a basic Ruby Object class, not an ActiveRecord model).

In address.rb:

class Address

   def initialize(line1, line2, city, state, zip)
     @line1 = line1
     @line2 = line2
     @city = city
     @state = state
     @zip = zip
   end

   def to_s
     "#{@line1}\n#{@line2}\n#{@city}, #{@state} #{@zip}"
   end

end

Our class doesn't do much yet, but we can create an Address instance and print it as a string using the to_s method:

irb(main):001:0> require 'address'
=> true
irb(main):002:0> address = Address.new("Centropy", "PO Box 1236", "Santa Clara", "CA", "95052")
=> #<Address:0x85908 @line2="PO Box 1236" @zip="95052" @line1="Centropy" @state="CA" @city="Santa Clara">
irb(main):003:0> puts address.to_s
Centropy
PO Box 1236
Santa Clara, CA 95052

What if we want to be able to get each of those attributes separately from the full address, say to print out only the zip code? Trying to print the zip attribute with our current code won't work:

irb(main):004:0> puts address.zip
NoMethodError: undefined method `zip' for #<Address:0x85908>
        from (irb):4
        from :0

In order to call address.zip, we need an attribute getter to make the @zip instance variable visible outside the instance. If you're from the world of Java you're probably intimately familiar with these so-called "getter" methods. So, you'd go and write your "getter" method for @zip:

class Address

   def initialize(line1, line2, city, state, zip)
     @line1 = line1
     @line2 = line2
     @city = city
     @state = state
     @zip = zip
   end

   def zip
     @zip
   end

   def to_s
     "#{@line1}\n#{@line2}\n#{@city}, #{@state} #{@zip}"
   end

end

Okay, only four more methods to go and you can head over to the water cooler for a well-deserved break! (You get paid by the line, after all!) It feels kinda silly, though, since all these methods follow an identical pattern. Since this is such a common pattern, Ruby gives us a little shortcut: anytime you would add an attribute getter as above, you can and should use the handy attr_reader method instead, which effectively creates the attribute getter methods for us behind the scenes, without us having to explicitly define each method in detail. Our new class looks like this:

class Address

   def initialize(line1, line2, city, state, zip)
     @line1 = line1
     @line2 = line2
     @city = city
     @state = state
     @zip = zip
   end

   # Note we can give attr_reader multiple attributes,
   # and they are specified as :symbols
   attr_reader :line1, :line2, :city, :state, :zip

   def to_s
     "#{@line1}\n#{@line2}\n#{@city}, #{@state} #{@zip}"
   end

end

Now we can grab all of our attributes apart from the full address:

irb(main):001:0> require 'address'
=> true
irb(main):002:0> address = Address.new("Centropy", "PO Box 1236", "Santa Clara", "CA", "95052")
=> #<Address:0x854bc @line2="PO Box 1236" @zip="95052" @line1="Centropy" @state="CA" @city="Santa Clara">
irb(main):003:0> puts address.zip
95052

But what about setting the zip code?

irb(main):004:0> address.zip='95050'
NoMethodError: undefined method `zip=' for #<Address:0x854bc>
        from (irb):4
        from :0

Nope, doesn't work. Maybe that's what you want: if an attribute should not be settable after the object is instantiated, you wouldn't want the ability to set the value of an attribute like this. (If you ever hear the term "immutable", that is what we're talking about here: Address instances are currently immutable because they can not be "mutated," or changed, after instantiation.) But if you do want to set your attribute values, you'll need to create a way for code outside the instance to change the value of that somewhat elusive @zip instance variable. Again, if you are coming from Java programming, you'd attempt to write a "setter" method here:

class Address

   def initialize(line1, line2, city, state, zip)
     @line1 = line1
     @line2 = line2
     @city = city
     @state = state
     @zip = zip
   end

   # Note we can give attr_reader multiple attributes,
   # and they are specified as :symbols
   attr_reader :line1, :line2, :city, :state, :zip

   # Allow setting the instance attribute to a new value
   def zip=(new_zip)
     @zip = new_zip
   end

   def to_s
     "#{@line1}\n#{@line2}\n#{@city}, #{@state} #{@zip}"
   end

end

But, if you're starting to hear harp music right now, you might be thinking that Ruby might give us an equally simple, declarative way to make attribute setter methods as it did for getters. Lo and behold, attr_writer comes to the rescue:

class Address

   def initialize(line1, line2, city, state, zip)
     @line1 = line1
     @line2 = line2
     @city = city
     @state = state
     @zip = zip
   end

   # Note we can give attr_reader multiple attributes,
   # and they are specified as :symbols
   attr_reader :line1, :line2, :city, :state, :zip

   # Those attributes are writable, too
   attr_writer :line1, :line2, :city, :state, :zip

   def to_s
     "#{@line1}\n#{@line2}\n#{@city}, #{@state} #{@zip}"
   end

end

And now we can set or get any attribute value:

irb(main):001:0> require 'address'
=> true
irb(main):002:0> address = Address.new("Centropy", "PO Box 1236", "Santa Clara", "CA", "95052")
=> #<Address:0x8519c @line2="PO Box 1236" @zip="95052" @line1="Centropy" @state="CA" @city="Santa Clara">
irb(main):003:0> address.zip='95050'
=> "95050"
irb(main):004:0> puts address.zip
95050

Now, you might think we're in good shape: we've consolidated what would have been ten different method definitions into a two lines of declarative, readable code. Not so fast! There's a bit of redundancy in those two lines, isn't there? Specifying all your fields in two places is clearly less maintainable than if we could tell Ruby "we want getters and setters for this list of attributes." It might not be a big deal for our little Address class, but when you have hundreds or thousands of classes that you haven't looked at in a year, every extraneous line matters.

Again, this is such a common scenario in programming that Ruby offers the third attribute method, attr_accessor, which does exactly what two separate calls to attr_reader and attr_writer would do.

So, let's get rid of that repetition now:

class Address

   def initialize(line1, line2, city, state, zip)
     @line1 = line1
     @line2 = line2
     @city = city
     @state = state
     @zip = zip
   end

   # attr_accessor can take multiple attributes like the others,
   # and they are specified as :symbols
   attr_accessor :line1, :line2, :city, :state, :zip

   def to_s
     "#{@line1}\n#{@line2}\n#{@city}, #{@state} #{@zip}"
   end

end

And this works exactly like the previous example:

irb(main):001:0> require 'address'
=> true
irb(main):002:0> address = Address.new("Centropy", "PO Box 1236", "Santa Clara", "CA", "95052")
=> #<Address:0x8519c @line2="PO Box 1236" @zip="95052" @line1="Centropy" @state="CA" @city="Santa Clara">
irb(main):003:0> address.zip='95050'
=> "95050"
irb(main):004:0> puts address.zip
95050

Hopefully this clarifies what these three methods are for. Next time, we'll explore the similarly-named-but-entirely-different-purposed attr_protected and attr_accessible methods brought to us by Rails' ActiveRecord models.

Further Reading

The attr_accessor, attr_reader, and attr_writer methods are fairly well documented in the Ruby core rdocs.

Feedback and Article Ideas

Want to see a topic explored here? Send Me a Message.

How to obtain the IP address of the current user

Some house address

Web applications can receive requests directly, via a CGI process, through proxy servers, relayed from front-end web servers, and so on. This can complicate how you might find out where the request originated if you, for example, wanted to limit an online poll to one vote per IP address. Luckily, Rails consolidates most of the ways to get this info into a single convenience method on the request object for us.

The Convenience Method: #remote_ip

Without the request.remote_ip method, you'd have to look for specific headers that are used to carry this data in the HTTP request beyond the server where the actual client's connection was terminated.

Rails' request.remote_ip method is pretty smart: it looks for and parses the headers HTTP_CLIENT_IP, HTTP_X_FORWARDED_FOR and REMOTE_ADDR and parse the value which are commonly used for this purpose.

Example Rails Code

In RAILS_ROOT/app/controllers/show_my_ip_controller.rb:

class ShowMyIpController > ApplicationController

  def index
    @client_ip = request.remote_ip
  end

end

In RAILS_ROOT/app/views/show_my_ip/index.html.erb:

Your IP address is <%= @client_ip >

Further Reading

The request.remote_ip method is documented in the Rails Framework rdocs.

Feedback and Article Ideas

Want to see a topic explored here? Send Me a Message.

Microsoft adCenter vs Google Adwords (Round 1)

At the risk of being called a keyword Nazi and having to endure accusations that I care just a little too much about this stuff: Microsoft just managed surpass my experience with Yahoo.

As I mentioned, I've been advertising my online community classifieds site, Dibs.net, on Google for almost a month. Last night I decided to try out Microsoft's advertising network for my community classifieds site. I copied some of my better performing ads from my Classifieds Ad Group on Google into my Classifieds Ad Group on Microsoft.

Here's representative ad from the bunch:
Ad on Microsoft

I was in business fairly quickly -- or so I thought. Microsoft has a similar "editorial guidelines" process to Yahoo's. But at Microsoft, the process must go something like this:

  1. Take break from huffing paint fumes and watching Scooby-Doo
  2. Flip coin
  3. Heads, you get to advertise with your keyword; tails, you don't

Microsoft rejected what I consider to the the most important keywords from my advertising, including "classifieds" and "community classifieds."

I've given them a chance to respond before I start calling them names here ;)

(PS - Google's success in advertising appears to be not without reason.)

Update: Microsoft took only a few hours to respond, and did so positively by reinstating the classifieds-related keywords. Good for them (and me). They blamed the problem on their automated editorial process, which raises the question: why would anyone automate such a thing? I think we can all agree computers are great at some things, but applying subjective and vaguely defined editorial guidelines isn't one of them. Either way, I take back my paint fumes and Scooby-Doo comment above, since it appears they aren't nuts after all.

Yahoo Marketing vs Google Adwords (Round 1)

Yahoo Ad
I've been advertising Dibs.net on both the Google and Yahoo ad networks for three weeks. I'll have more to say later on, but I did want to share some notes about Yahoo's "editorial process" regarding what search terms they let you advertise against. This process is allegedly for quality purposes, so that more relevant advertising is displayed to searchers.

Google has taken a more free-market approach to their editorializing of search terms you can advertise against. I'm sure they have a quality-control staff to keep ads clean, but they tend to let you spend your money however you want. I'm no Google fan, but I think Google's approach is far superior, mainly because the market actually does self-correct mispositioned advertising: as people find their advertising for certain keywords isn't working, they pare back the selection of keywords to be more laser-focused. I did this very thing; it works.

Yahoo, on the other hand, voluntarily protects certain company names (though who knows which ones) and is awfully serious about the relevancy of keywords you choose. But it's all so inconsistent and confusing that you never know what they will allow. For example, they apparently allow some misspellings of brand names, but not others. And today I got notice they decided a term wasn't relevant enough to Dibs.net -- which surprised me, since I often describe Dibs.net using the exact term they rejected.

So, I just fired off an email to Yahoo support to see if they can make this situation clearer than the Mississippi mud they've given me till now.

Can you give me more detail as to why you believe the keyword "flea markets" isn't applicable to my company, which is itself an online flea market (dibs.net)?

Removing the keyword from my campaign strikes me as an overly aggressive editorial decision, especially after conducting this search on Yahoo and noting NOT A SINGLE AD relates even remotely to flea markets. In fact, mine is the MOST RELEVANT of any company there. Of the most absurd listings you apparently allow, BizRate, Shopping.com, PalmersUniforms.com and Restaurant.com are advertised on the "flea markets" keyword.

Yes, Restaurant.com advertises under "flea markets" but dibs.net cannot? Come on, Yahoo! I'm trying to like your ad service. I really am. But this sort of random, haphazard enforcement of "editorial rules" leaves me plain confounded.

I have paused my advertising until I receive a response explaining how Yahoo can possibly hope to apply such picky rules consistently.

Thanks,
Kevin Hunt

We'll see what they have to say about Restaurant.com's flea market. Mmmmm, makes you hungry just thinking about it, doesn't it?

Update (8/23): Yahoo responded this morning. They explained that because Dibs.net's content is not about flea markets, the keyword is not allowed. Ok, fair enough, if those are the actual rules -- but they're not. That would be stupid. I responded and asked if Amazon would be allowed to advertise with the keywork "book stores," since they have no information about actual book stores. Also, Yahoo's response did not address how a site about restaurants can use the keyword they are disallowing for me. I'm still confused.

Come on, Yahoo! I really want to like you. Granted, this isn't such a big deal in the grand scheme of things; but if I'm to commit an ad budget to you, you'd better clarify your rules and apply them consistently.