Fixing The Web With Stylish

Tháng Năm 20, 2009

Source: http://blog.new-bamboo.co.uk/2009/2/27/fixing-the-web-with-stylish
Design becomes beautiful when there’s nothing left to take away. Google understands this; too many other web applications don’t.

Many apps grow new features simply to differentiate themselves from the competition, or to increase their target market by supporting multiple conflicting workflows. But every single thing I don’t need detracts from the usability of the things I do. In the past I’ve had to abandon apps that do more than I need, but not any more.

Now I can fix them.

Stylish is a Firefox plugin that lets you write your own css per-site, overriding the existing css. This means you can set display:none on everything unnecessary.

It’s a one-click install. You’ll also need Firebug to assist in identifying elements you want to block.

Once it’s installed, open firebug and point to the offending feature. Figure out a way to identify it with a CSS selector. In some cases this’ll be easy – it might have a classname like AnnoyingFeature. In others you’ll have to use CSS3 selectors. For example, you can do:

label[id*="requested_by_id"] {display:none}
view raw This Gist brought to you by GitHub.

This matches every Label element with an ‘id’ attribute containing ‘requested_by_id’. This is helpful if the site’s using ids like ‘story_34524_requested_by_id’.

But what about if you had this?

<div>
  <span class='annoying'>annoyance 1</span>
  <span>annoyance 2</span>
</div>
view raw This Gist brought to you by GitHub.

It’d be nice to block the parent div, thereby taking out ‘annoyance 2’, but you can’t; there’s no “parent selector” even in CSS3. Greasemonkey could fix this; Stylish can’t.

While you’re there, you can also fix other usability issues like textboxes being too small – it’s as simple as ‘textarea {height:300px}’.

Stylish has a live preview, and it’s only one click to turn off custom styles, so you can afford to be both aggressive and experimental.

You can share custom styles. I’d like to suggest that styles solely designed to remove distractions, rather than ‘re-skin’ sites, include ‘Simplify’ in the name to make them easy to find. Over the next few days I’ll be redesigning some of the web tools I use frequently to make them more of a joy to work with.

Source: http://ruby-toolbox.com/

Toolbox_red-256x256

Know your options!

Ruby developers can choose from a variety of tools to get their job done.

The Ruby Toolbox gives you an overview of these tools, sorted in categories and rated by the amount of watchers and forks in the corresponding source code repository on GitHub so you can find out easily what options you have and which are the most common ones in the Ruby community.

Want to know the best tools of choices? Check out http://ruby-toolbox.com/

Pacecar & automatic named scopes

Tháng Năm 18, 2009

Source: http://github.com/thoughtbot/pacecar/tree/master

Pacecar

Pacecar adds named_scope methods and other common functionality to ActiveRecord classes via database column introspection.

Pacecar automatically includes the Pacecar::Helpers module into all ActiveRecord::Base classes.

To get all Pacecar functionality, you need to “include Pacecar” in your class.

To get some subset (for example, only the state functionality), you can do something like “include Pacecar::State” to get only the module(s) you want.

Usage

Assuming a database schema…

  class CreateSchema < ActiveRecord::Migration
    def self.up
      create_table :users, :force => true do |t|
        t.boolean :admin, :default => false, :null => false
        t.datetime :approved_at
        t.datetime :rejected_at
        t.string :first_name
        t.string :last_name
        t.text :description
        t.timestamps
      end
      create_table :posts, :force => true do |t|
        t.string :owner_type
        t.integer :owner_id
        t.string :publication_state
        t.string :post_type
        t.timestamps
      end
      create_table :comments, :force => true do |t|
        t.integer :user_id
        t.text :description
        t.timestamps
      end
    end
  end

And some basic model declarations…

  class User < ActiveRecord::Base
    include Pacecar
    has_many :posts, :as => :owner
    has_many :comments
    has_many :articles
    has_ranking :comments
    has_recent_records :comments
    has_recent_records :articles, :comments
  end

  class Post < ActiveRecord::Base
    include Pacecar
    PUBLICATION_STATES = %w(Draft Submitted Rejected Accepted)
    TYPES = %w(Free Open Private Anonymous PostModern)
    belongs_to :owner, :polymorphic => true
    has_state :publication_state
    has_state :post_type, :with => TYPES
    has_polymorph :owner
  end

  class Comment < ActiveRecord::Base
    include Pacecar
    belongs_to :user
  end

  class Article < ActiveRecord::Base
    belongs_to :user
  end

All columns

Records where approved_at is not null, or where it is null…

  User.approved_at_present
  User.approved_at_missing

Records where first_name is not null, or where it is null…

  User.first_name_present
  User.first_name_missing

Records ordered by first_name (default to ‘asc’, can specify to override)…

  User.by_first_name
  User.by_first_name(:asc)
  User.by_first_name(:desc)

Records where an attribute matches a search term (column LIKE “%term%”)…

  User.first_name_matches('John')

Records where an attribute starts or ends with a search term…

  User.first_name_starts_with('A')
  User.first_name_ends_with('a')

Records where any non-state text or string column matches term…

  User.search_for('test')

Records where any of a list of columns match the term…

  User.search_for 'test', :on => [:first_name, :last_name]

Records where all of a list of columns match the term…

  User.search_for 'test', :on => [:first_name, :last_name], :require => :all

Boolean columns

Records that are all admins or non-admins…

  User.admin
  User.not_admin

The “balance” (count of true minus false for column in question)…

  User.admin_balance

Datetime columns

Records approved before or after certain times…

  User.approved_at_before(5.days.ago)
  User.approved_at_after(4.weeks.ago)

Records with approved_at in the past or future…

  User.approved_at_in_past
  User.approved_at_in_future

Records with approved_at inside or outside of two times…

  User.approved_at_inside(10.days.ago, 1.day.ago)
  User.approved_at_outside(2.days.ago, 1.day.ago)

Records with certain year, month or day…

  User.approved_at_in_year(2000)
  User.approved_at_in_month(01)
  User.approved_at_in_day(01)

Records with a duration (time delta between two columns) of, over or under a certain number of days…

  User.with_duration_of(14, :approved_at, :rejected_at)
  User.with_duration_over(14, :approved_at, :rejected_at)
  User.with_duration_under(14, :approved_at, :rejected_at)

Polymorphic relationships

Records which have an owner_type of User…

  Post.for_owner_type(User)

Associations

Records with the most and least associated records…

  User.maximum_comments
  User.minimum_comments

Records with associated records since a certain time…

  User.recent_comments_since(2.days.ago)
  User.recent_comments_and_posts_since(3.days.ago)
  User.recent_comments_or_posts_since(4.days.ago)

State columns

Records which are in a particular state, or not in a state…

  Post.publication_state_draft
  Post.post_type_not_open

Query methods on instances to check state…

  Post.first.publication_state_draft?
  Post.last.post_type_not_open?

Limits

First x records…

  User.limited(10)

Named scopes

Because these are all named_scope, you can combine them.

To get all users that have a first_name set, who are admins and approved more than 2 weeks ago, ordered by their first name…

  User.first_name_present.admin.approved_at_before(2.weeks.ago).by_first_name

To get the top 10 commenters…

  User.maximim_comments.limited(10)

Rails & named_scope

Tháng Năm 18, 2009

Source: http://mattpayne.ca/blog/post/named-scope

Lately everyone has been going mad over a relatively new addition to rails: named_scope. Up until now, I hadn’t had the chance to try it out. I always just assumed that it was more syntactic fluff that seems to accumulate. Wow, was I wrong! It’s an unbelievably cool and useful idea. Here’s a really simple example. Say I have 2 models: Country and Region. They are as you would expect. A Region belongs to a Country and a Country has many Regions, etc. I use them pretty much for dropdown lists and things like that. Here’s some code:

  1. class Region < ActiveRecord::Base
  2. belongs_to :country
  3. end
  4. class Country < ActiveRecord::Base
  5. has_many :regions
  6. end

Prior to named_scope, if you wanted to get all Regions in alphabetical order (as an example), you’d have to do something like:

  1. class Region < ActiveRecord::Base
  2. #other code
  3. def self.ordered
  4. find(:all, :order => “name ASC”)
  5. end
  6. end

Which was ok, but this is nicer:

  1. class Region < ActiveRecord::Base
  2. named_scope :ordered, :order => “name ASC”
  3. #other code
  4. end

This creates a method on the Region class called ordered that can simply be called.

OK, so that’s cool and all, but really not that big a deal, right? Well, no – until you consider that you can chain calls to named_scope, thus adding more and more constraints. For example:

  1. class Region < ActiveRecord::Base
  2. belongs_to :country
  3. named_scope :ordered, :order => “name ASC”
  4. named_scope :by_country, lambda { |c| { :conditions => [“country_id = ?”, c.id] } }
  5. named_scope :containing, lambda { |s| { :conditions => [“name like ?”, “%#{s}%”] } }
  6. end
  7. class Runner
  8. c = Country.find_by_name(“Canada”)
  9. regions = Region.by_country(c).containing(“A”).ordered
  10. puts “Provinces in Canada containing the letter A in ascending order: #{regions.inspect}”
  11. end

This generates SQL that looks like this:

SELECT * FROM `regions` WHERE ((name like ‘%A%’) AND (country_id = 1)) ORDER BY name ASC

Another reason that I like name_scope is that because models such as these are frequently eused in dropdowns, etc, you can use named scope to return a very lightweight version of the class, while excluding attributes that are unnecessary for a simple dropdown. For example:

  1. class Region < ActiveRecord::Base
  2. belongs_to :country
  3. named_scope :ordered, :order => “name ASC”
  4. named_scope :by_country, lambda { |c| { :conditions => [“country_id = ?”, c.id] } }
  5. named_scope :containing, lambda { |s| { :conditions => [“name like ?”, “%#{s}%”] } }
  6. named_scope :simple, :select => “id, name”
  7. end

This allows me to only bring back the attributes that I need. And again, it could be chained with other named scopes

I think that this is a very powerful concept that allows developers to write more DRY and readable code.

Source: http://www.rubyinside.com/19-rails-tricks-most-rails-coders-dont-know-131.html

When looking at my own Rails code and that of the community as a whole, I often see places where certain Rails techniques could have been used, but weren’t. As much for my own memory as yours, I thought I’d list down some Rails tricks and tips that can make your application or code more efficient:

Benchmark logic in your controller actions – It’s really easy. Use the benchmark class method available on all your models like this:

User.benchmark("adding and deleting 1000 users") do
	1000.times do
		User.create(:name => 'something')
		x = User.find_by_name('something')
		x.destroy
	end
end

Of course, your code would be a lot better ;-) The regular SQL logs are not shown when within the benchmark sections. Only the benchmark results are shown.

Nested-Set

acts_as_nested_set
– Almost everyone is familiar with acts_as_tree, but acts_as_nested_set snuck into Rails quietly. It’s much like acts_as_tree, but with the added benefit that you can select all of the children (and their own descendants) of a node with a single query. A list of the instance methods is available.

Easier collections with to_proc – Sick of writing things like post.collect { |p| p.title } or post.select { |p| p.activated? }.collect{ |p| p.title} ? A little Ruby hackery that allows you to convert symbols into proc references makes it easy. You can write post.collect(&:title) or post.select(&:activated?).collect(&:title) instead! Learn a lot more about this.

Convert arrays to sentences in views – If you were collecting a bunch of names to be shown in a view, you might end up with an array like [‘Peter’, ‘Fred’, ‘Chris’], and joining these with commas and inserting ‘and’ before the final one is a common pain. Not so, if you use the array method to_sentence as provided in Rails. names.to_sentence would return Peter, Fred, and Chris.

Send files back to the user – Usually, static files can be retrieved by using the direct URL and circumventing your Rails application. In some situations, however, it can be useful to hide the true location of files, particularly if you’re sending something of value (e-books, for example). It may be essential to only send files to logged in users too. send_file makes it possible. It sends files in 4096 byte chunks, so even large files can be sent without slowing the system down.

Iterating through page elements with RJS – Changing page elements with RJS is easy, but what if you don’t know exactly which elements you want to change, and would instead prefer to address them with CSS queries? You can with RJS’s select method. For example: page.select(‘#items li’).each { |item| item.hide } . Powerful stuff!

Check for existence – When doing a Model.find(id), an exception can be returned if the item with an id of ‘id’ doesn’t exist. If you want to avoid this, use Model.exists?(id) first to get a true or false for whether that item exists or not.

Number helpers for common number tasks – All of these number helpers aren’t commonly used but provide great shortcuts: number_to_currency(1234567.948) # => $1,234,567.95 or human_size(1234567890) # => 1.1GB or number_with_delimiter(999999999) # => 999,999,999. There are others.

Testing different route configurations easilywith_routing is a test helper that allows you to temporarily override the default ‘routes’ in routes.rb for test purposes. Demonstration:

with_routing do |set|
  set.draw { set.connect ':controller/:id/:action' }
  assert_equal(
     ['/content/10/show', {}],
     set.generate(:controller => 'content', :id => 10, :action => 'show')
  )
end

You can learn a little more here.

Get lots of info about requests – Checking request.post? and request.xhr? are popular ways to look for POST and AJAX requests, but some of the other request methods are lesser used. For example: request.subdomains can return an array of subdomains that you could use as part of your authentication scheme, request.request_uri returns the full local request URL, request.host returns the full hostname, request.method returns the HTTP method as a lowercase symbol, and request.ssl? returns true if it’s an HTTPS / SSL request.

Improving session performance even more than with ActiveRecord – By default, Rails stores sessions on the local file system. Many users change this to using ActiveRecordStore to store sessions in the database. An even faster alternative is to use Memcached to store sessions, but that takes a lot to set up (and isn’t available unless you run your own servers, etc). But you can get faster than ActiveRecordStore by using Stefan Kaes’ SQLSessionStore. It circumvents the inefficiencies of ActiveRecordStore using his own direct SQL technique to store sessions.

Caching unchanging data at application startup – If you have data that doesn’t change between application restarts, cache it in a constant somewhere. For example, you might have a YAML or XML file in /config that stores application configuration data, and you could load it into a constant in environment.rb, making lookups quick and easy application-wide.

Check your views are rendering valid HTML / XHTML – It’s not for everyone, but if your output validates as correct HTML / XHTML, it’s a sign your views are going to render properly. Scott Raymond has developed a assert_valid_markup test helper that you can use from your functional tests.

Cleaner HTML output testing – Combine why’s Hpricot HTML parser and a special test extension, and you can have powerful tests like so: assert_equal “My title”, tag(‘title’) or assert element(‘body’).should_contain(‘something’). This might be ideal for developing tests to test user built templates. In any case, it’s nicer than assert_tag!

Run long-running tasks separately in the backgroundBackgrounDRb is a small framework, by Ezra Zygmuntowicz, that runs as a daemon in the background that can accept tasks your Rails application sends to it, and whose execution is totally separate to your Rails app. It’s extremely powerful, and useful for many tasks such as sending hundreds of e-mails, fetching URLs, and other things you don’t want to slow down the request times for your main app. One great demo is to develop a task that increments a variable by 1 and sleeps for 1 second. You can then make a Rails method that queries the variable, and see the distinct separation. Learn more.

Make ids in URLs more user friendly – Override the to_param method on your model and return something like “#{id}-#{title.gsub(/[^a-z0-9]+/i, ‘-‘)}” to get URLs like so: http://yoursite.com/posts/show/123-post-title-goes-here .. Much nicer for users, and you don’t need to change anything with Post.find(params[:id]) as the non numeric characters will be stripped automagically! Get a full explanation here.

Separate out slices of functionality into Engines – Everyone’s heard of Rails’ plugins, but pitifully few are using Rails Engines! Rails Engines are like plugins on steroids. They can contain their own models, controllers, and views, and integrate with any applications you run them under. This allows you to split out common fragments of functionality (login, user management, content management, etc.) into separate ‘engines’ to use in your different projects within minutes. No more writing dull login code! Rails Engines is a big deal, but it should be a far bigger deal.

Calculations – Do you want to get maximums, minimums, averages, or sums for data in your tables? ActiveRecord’s Calculations make these all possible. Person.average(‘age’), Person.maximum(:age, :group => ‘last_name’), and Order.sum(‘total’) all become a reality. Most can be customized pretty deeply with extra options, so go read about them if they’re not already part of your code.

XML or YAML output of your data – It’s not necessarily to create a Builder .rxml template for all XML output. ActiveRecord has a to_xml method that will output the object or result set in XML format. It works with simple objects, to complete tables (like User.find(:all).to_xml). Using includes works too, as with Post.find(:all, :include => [:comments]).to_xml. YAML is also supported, by using to_yaml instead.

Source: http://pablotron.org/software/wirble/

Wirble is a set of enhancements for Irb. Wirble enables several items mentioned on the RubyGarden “Irb Tips and Tricks” page, including tab-completion, history, and a built-in ri command, as well as colorized results and a couple other goodies. The idea, of course, is to fill Irb with useful features without turning your ~/.irbrc file into swiss cheese. Using Wirble is simple, too. Here’s what a standard Wirble-enabled ~/.irbrc looks like:

# load libraries
require 'rubygems'
require 'wirble'

# start wirble (with color)
Wirble.init
Wirble.colorize

Don’t like the defaults? Wirble is configurable. For documentation on enabling specific features or tweaking the color settings. take a look at the excessively-verbose Wirble README. Finally, here’s a screenshot of Wirble-enabled Irb:

Wirble Screenshot
Obligatory Wirble Screenshot

Latest:
Download Wirble 0.1.2 Tarball (Signature)
Download Wirble 0.1.2 Gem (Signature)
Read the Wirble 0.1.2 README
Read the ChangeLog

Source: http://www.pauldix.net/2009/05/breath-fire-over-http-in-ruby-with-typhoeus.html

Typhoeus is a mythical greek god with 100 fire breathing serpent heads. He’s also the father of the more well known Hydra. Like the fearsome beast, Typhoeus is a fearsome Ruby library that enables parallel HTTP requests while cleanly encapsulating handling logic. Specifically, it uses libcurl and libcurl-multi to run HTTP really fast. Further, it’s designed with the focus of creating client libraries that work with web services. These could be external services like Twitter or systems like CouchDB and SimpleDB or custom web services that you write yourself.

The libcurl interface is contained within the library. Rather than trying to get Curb to do what I wanted, I decided to start with a clean slate and write the c bindings myself. Other than the libcurl interface, it has a nice DSL for creating classes and client libraries for web services.

The inspiration for the library came from an interview with Amazon CTO Werner Vogels. In the interview he states that when a user visits the Amazon.com home page it calls out to up to 100 different services to construct the single page before returning it to the user. I like Amazon’s approach of a services architecture, specially AWS, and wondered if I could do the same thing in Ruby. Typhoeus is the result of that effort.

I set up a benchmark to test how the parallel performance works vs Ruby’s built in NET::HTTP. The setup was a local evented HTTP server that would take a request, sleep for 500 milliseconds and then issued a blank response. I set up the client to call this 20 times. Here are the results:

 net::http 0.030000 0.010000 0.040000 ( 10.054327)
 typhoeus 0.020000 0.070000 0.090000 ( 0.508817)

We can see from this that NET::HTTP performs as expected, taking 10 seconds to run 20 500ms requests. Typhoeus only takes 500ms (the time of the response that took the longest.)

Hopefully I’ve whetted your appetite. Before I get to the code examples and the API, I’d like to put in a plug for my employer kgb. They were nice enough to let me release this library as open source. We’re also hiring for good front end rails developers, designers, and anyone with experience in search, information retrieval, and machine learning. Please drop me a line if you dominate code and if you’re interested in joining an awesome team.

Finally, on the the codez. Here are some usage examples and notes from the readme (gist for easier reading).

# here's an example for twitter search
# Including Typhoeus adds http methods like get, put, post, and delete.
# What's more interesting though is the stuff to build up what I call
# remote_methods.
class Twitter
 include Typhoeus
 remote_defaults :on_success => lambda {|response| JSON.parse(response.body)},
 :on_failure => lambda {|response| puts "error code: #{response.code}"},
 :base_uri => "http://search.twitter.com"

 define_remote_method :search, :path => '/search.json'
 define_remote_method :trends, :path => '/trends/:time_frame.json'
end

tweets = Twitter.search(:params => {:q => "railsconf"})

# if you look at the path argument for the :trends method, it has :time_frame.
# this tells it to add in a parameter called :time_frame that gets interpolated
# and inserted.
trends = Twitter.trends(:time_frame => :current)

# and then the calls don't actually happen until the first time you
# call a method on one of the objects returned from the remote_method
puts tweets.keys # it's a hash from parsed JSON

# you can also do things like override any of the default parameters
Twitter.search(:params => {:q => "hi"}, :on_success => lambda {|response| puts response.body})

# on_success and on_failure lambdas take a response object. 
# It has four accesssors: code, body, headers, and time

# here's and example of memoization
twitter_searches = []
10.times do
 twitter_searches << Twitter.search(:params => {:q => "railsconf"})
end

# this next part will actually make the call. However, it only makes one
# http request and parses the response once. The rest are memoized.
twitter_searches.each {|s| puts s.keys}

# you can also have it cache responses and do gets automatically
# here we define a remote method that caches the responses for 60 seconds
klass = Class.new do
 include Typhoeus

 define_remote_method :foo, :base_uri => "http://localhost:3001", :cache_responses => 60
end

klass.cache = some_memcached_instance_or_whatever
response = klass.foo 
puts response.body # makes the request

second_response = klass.foo
puts response.body # pulls from the cache without making a request

# you can also pass timeouts on the define_remote_method or as a parameter
# Note that timeouts are in milliseconds.
Twitter.trends(:time_frame => :current, :timeout => 2000)

# you also get the normal get, put, post, and delete methods
class Remote
 include Typhoeus
end

Remote.get("http://www.pauldix.net")
Remote.put("http://", :body => "this is a request body")
Remote.post("http://localhost:3001/posts.xml", 
 {:params => {:post => {:author => "paul", :title => "a title", :body => "a body"}}})
Remote.delete("http://localhost:3001/posts/1")

Important Update:I should have mentioned that some bits of C were pulled from Todd Fisher’s update to curb for the multi interface. The easy code is completely different and some of the C stuff in Multi has changed. However, a good chunk of the multi code comes straight from there. Thanks Todd. Sorry for not mentioning it earlier.

Source: http://www.thoughtbot.com/projects/shoulda/

Making Tests Easy on the Fingers and Eyes

The Shoulda gem makes it easy to write elegant, understandable, and maintainable Ruby tests. Shoulda consists of test macros, assertions, and helpers added on to the Test::Unit framework. It’s fully compatible with your existing tests, and requires no retooling to use.

  • Context & Should blocks – context and should provide RSpec-like test blocks for Test::Unit suites. In addition, you get nested contexts and a much more readable syntax.
  • Macros – Generate many ActionController and ActiveRecord tests with helpful error messages. They get you started quickly, and can help you ensure that your application is conforming to best practice.
  • Assertions – Many common Rails testing idioms have been distilled into a set of useful assertions.

The Shoulda gem can be used for non-Rails projects


Installation


sudo gem install thoughtbot-shoulda --source=http://gems.github.com

Usage

Context Helpers

Stop killing your fingers with all of those underscores… Name your tests with plain sentences!


class UserTest < Test::Unit 
  context "A User instance" do
    setup do
      @user = User.find(:first)
    end

    should "return its full name" do
      assert_equal 'John Doe', @user.full_name
    end

    context "with a profile" do
      setup do
        @user.profile = Profile.find(:first)
      end

      should "return true when sent #has_profile?" do
        assert @user.has_profile?
      end
    end
  end
end

Produces the following test methods:

"test: A User instance should return its full name." 
"test: A User instance with a profile should return true when sent #has_profile?."

So readable!

ActiveRecord Tests

Quick macro tests for your ActiveRecord associations and validations:


class PostTest < Test::Unit::TestCase
  should_belong_to :user
  should_have_many :tags, :through => :taggings

  should_require_unique_attributes :title
  should_require_attributes :body, :message => /wtf/
  should_require_attributes :title
  should_only_allow_numeric_values_for :user_id
end

class UserTest < Test::Unit::TestCase
  should_have_many :posts

  should_not_allow_values_for :email, "blah", "b lah" 
  should_allow_values_for :email, "a@b.com", "asdf@asdf.com" 
  should_ensure_length_in_range :email, 1..100
  should_ensure_value_in_range :age, 1..100
  should_protect_attributes :password
end

Makes TDD so much easier.

Controller Tests

Macros to test the most common controller patterns…


context "on GET to :show for first record" do
  setup do
    get :show, :id => 1
  end

  should_assign_to :user
  should_respond_with :success
  should_render_template :show
  should_not_set_the_flash

  should "do something else really cool" do
    assert_equal 1, assigns(:user).id
  end
end

Helpful Assertions

More to come here, but have fun with what’s there.


assert_same_elements([:a, :b, :c], [:c, :a, :b])
assert_contains(['a', '1'], /\d/)
assert_contains(['a', '1'], 'a')

Source: http://railstips.org/2009/3/4/following-redirects-with-net-http

The web is full of redirects. It isn’t that hard to figure out how to follow them using Ruby, but it always helps to have examples when you are learning. Not too long ago I was hacking on some feed auto discovery code and made a little class that, given a url, will find the endpoint and return the response from that endpoint.

I figured in the old spirit of clogging, I would post it here until I have time to package the full feed auto discovery library and release it on Github.

require 'logger'
require 'net/http'

class RedirectFollower
  class TooManyRedirects < StandardError; end

  attr_accessor :url, :body, :redirect_limit, :response

  def initialize(url, limit=5)
    @url, @redirect_limit = url, limit
    logger.level = Logger::INFO
  end

  def logger
    @logger ||= Logger.new(STDOUT)
  end

  def resolve
    raise TooManyRedirects if redirect_limit < 0

    self.response = Net::HTTP.get_response(URI.parse(url))

    logger.info "redirect limit: #{redirect_limit}" 
    logger.info "response code: #{response.code}" 
    logger.debug "response body: #{response.body}" 

    if response.kind_of?(Net::HTTPRedirection)      
      self.url = redirect_url
      self.redirect_limit -= 1

      logger.info "redirect found, headed to #{url}" 
      resolve
    end

    self.body = response.body
    self
  end

  def redirect_url
    if response['location'].nil?
      response.body.match(/<a href=\"([^>]+)\">/i)[1]
    else
      response['location']
    end
  end
end

You can then follow redirects as easily as this:

google = RedirectFollower.new('http://google.com').resolve
puts google.body

Which when run will output something like this:

I, [2009-03-04T17:16:58.879672 #69272]  INFO -- : redirect limit: 5
I, [2009-03-04T17:16:58.880669 #69272]  INFO -- : redirect found, headed to http://www.google.com/
I, [2009-03-04T17:16:58.987963 #69272]  INFO -- : redirect limit: 4

Followed by the html from http://www.google.com which I did not include. The logger method comes in ridiculously handy when following redirects as sometimes it is kind of hard to figure out what is going on. You can also optionally pass in a limit for how many times you would like to redirect.

RedirectFollower.new('http://foobar.com', 3).resolve

This would set the number of redirects to follow to 3, instead of the default 5. You always want to put some kind of limit on the number of redirects to follow or you could end up in infinite redirection.

Nothing fancy but it gets the job done. Maybe if I get a chance I’ll post on how to test this by stubbing responses.

Help kill Internet Explorer 6

Tháng Tư 19, 2009

Source: http://almost.done21.com/2009/04/announcing-ie6-update-help-kill-internet-explorer-6/

Today Done21 is proud to announce IE6 Update, a great new tool to encourage your Internet Explorer 6 website visitors to update their browser. Check out http://ie6update.com, and if you end up using IE6 Update, tell us about it on Twitter using the hashtag #ie6update!

Now, some back story…

Internet Explorer 6 is the plague of the Internet. Sure there are other battles, like net neutrality or censorship, but I think it’s safe to say that IE6 has held back innovation more than anything else, because of its slow JavaScript engine, incorrect rendering of web pages, bad security, and more. At the time of this writing, Net Applications says 18% of Internet users still use Internet Explorer 6. Even worse, many sites have a much higher percentage of Internet Explorer 6 visitors.

There have been numerous efforts to put an end to Internet Explorer 6 and we’d like to thank all of them, particularly these:

While we applaud any campaign to put an end to IE6, most efforts we’ve seen are very developer centric. And although there have been many other great sites that politely ask users to update, I think developers have made the bold assumption that your average IE6 user is capable of distinguishing between their web browser and the Internet. It’s kind of a hard thing for us tech types to understand, but just take a look at Microsoft’s marketing for Internet Explorer 8:

IE8 Home Page
IE8’s Homepage

IE8 Upgrade your Internet
Upgrade your Internet

Better Web
IE8 makes your web better… what?

IE8 Banner Advertisement
Download a new Internet!

See what I mean? Microsoft markets its browser as the Internet itself. It’s no wonder its been so hard for developers to explain the situation to their site visitors. We, the web community, have politely asked web surfers to upgrade from IE6. We’ve tried. Everyone has tried. So now it’s time to trick them into upgrading. That’s right, trick IE6 users into moving on. Tricking users is a big taboo, but if we’re talking about tricking users into upgrading from IE6 to a newer browser, we feel that it’s like tricking them into receiving free money.

How can we do this? Well, you know that yellow bar that sometimes appears in Internet Explorer at the top of the browser window? It’s called the Information Bar. It usually displays information about security updates, missing plug-ins, and things of that nature. We decided that the best way to get behind enemy lines would be to fake the Information Bar and offer the user a browser update. Check it out:

IE6 Update

To use IE6 Update, all you have to do is go to this website, and then copy and paste the IE6 Update code into your site. It’s that easy.