Displaying ads from TextLinkAds in a rails application

Once my application with TextLinkAds was approved, I looked for a sample code to make it work in typo, or more generally in Ruby on Rails. TextLinkAds did not provide any, and a quick search in google did not return anything.

So I took their php example and came up with these snippets.

In your controller:

require ‘net/http’
require ‘cgi’

def content
  url = “http://www.text-link-ads.com/xml.php?inventory_key=”+TEXTLINKADS KEY+”&referer=”+CGI::escape(@request.env[‘REQUEST_URI’])+”&user_agent=”+CGI::escape(@request.env[‘HTTP_USER_AGENT’])
  @links = request(url) rescue nil
end

def request(url)
  XmlSimple.xml_in(http_get(url))
end

def http_get(url)
  Net::HTTP.get_response(URI.parse(url)).body.to_s
end

And in your view (rhtml), you just need to add:

<% if @links != nil %>
    <% if (@links != nil)&&(@links["Link"] != nil) %>
        <% for link in @links["Link"] -%>
            <li><%= link['BeforeText'][0] -%> <
                a href="<%= link['URL'][0] -%>&#8221;><%= link['Text'][0] -%></a> <%= link['AfterText'][0] -%> </li>
        <% end -%>
    <% end -%>
<% else %>
    Advertise here!
<% end -%>

To make the above look right with markdown, I had to cut the < a > element in between. When pasting this, remove the carriage return. If someone has a better idea on how to prevent the < a > element from being interpreted as a link even in a code block, please let me know!

Based on the example, it is fine to cache the result, so I’ll be adding that later tonight. As soon as I get caching up and running, I’ll also post the typo sidebar plugin

Text Link Ads

5/4/06 update: I tweaked the code to account for the no links at all case. TextLinkAds starts with a test link that they later remove, and my code broke. I replaced:

<% if @links != nil %>

by

<% if (@links != nil)&&(@links["Link"] != nil) %>

Pascal’s svn and Capistrano Cheat Sheet

Here’s my recipe to get a source tree commited into subversion, then setup Capistrano and then deploy the application on a production server.
I’ve done this many times, and everytime I stumble of one or more steps to find find what comes next, what path to use, etc. So here’s a note to hopefully settle this once and for all.

Assumes that the subversion database is created on domain.net under /home/psq/svn/domainsvn. Assumes that the initial “domain” directory contains trunk, tags and branches and that you are deploying the trunk.

cd domain
svn import . svn+ssh://domain.net/home/psq/svn/domainsvn

mkdir domainsvn
cd domainsvn
svn co svn+ssh://domain.net/home/psq/svn/domainsvn/trunk .

cap -A .

svn add config/deploy.rb
svn add lib/tasks/capistrano.rake

edit deploy.rb
And initialize user, application, repository…

set :use_sudo, false
set :user, &#8216;psq&#8217;
set :application, &#8220;domain.net&#8221;
set :repository, &#8220;svn+ssh://domain.net/home/#{user}/svn/domainsvn/trunk&#8221;
set :deploy_to, &#8220;/home/#{user}/#{application}&#8221;

Replace defaulf roles

role :web, application
role :app, application
role :db,  application, :primary => true

Add at the end of deploy.rb. See A better alternative to killall for details.

desc &#8220;Restart the FCGI processes on the app server as a regular user.&#8221;
task :restart, :roles => :app do
  run &#8220;pkill -9 -u #{user} ruby&#8221;
end

Now you are ready for the first deployment.

rake remote:exec ACTION=setup

And finally, to deploy:

cap deploy [or]
cap -q deploy (for quiet output)

If you you use “migration”, as you should, to create the tables on the remote host, and every time you update the tables:

rake remote:migrate

This assumed that your app was fully configured to work in your hosting environment. And if you haven’t done that yet, now if the time to do it.

Tracks: Getting things done

Yesterday, I came across a cool rails project: Tracks and it got me very excited. I spent 30 minutes to install it, and started entering some data. And the best part, I already completed 2 actions!
Tracks is a web based application to help you in Getting Things DONE: The Art of Stress-Free Productivity, a very effective method by David Allen. David’s approach is based on a few simple tenets: chanelling all inputs to as few buckets as possible, figuring out what the next action is for each project, deciding what to do next based on energy level, time available and priority… David developed this method based on his years as a consultant and anyone can approach it and implement it effectively with a little discipline.
Using Tracks can help you Get Things DONE! I had been trying to use BaseCamp so far, but I like Tracks approach better. With basecamp, I can’t figure out easily what to do next. Tracks presents you all the actions to be done on all projects sorted by due date.
Don’t get me wrong, BaseCamp is a very nice tool and comparing the 2 is not really fair for either. I had been trying to use basecamp to do what Tracks is designed to do.

The project is still a bit rough and I didn’t see much installation instructions, and it could be I didn’t look hard enough. I got the source code from the project subversion, and after after a few attempts, here’s what I did on Kubuntu 5.10.

svn co &#8211;username=guest http://www.rousette.org.uk/svn/tracks-repos/tags/tracks-1.041 tracks-local/

password is “guest”

create a database (tracks)

Create environment.rb and database.yml from the templates provided:

cp environment.rb.tmpl environment.rb
cp database.yml.tmpl database.yml

Edit database.yml (set correct user, password if you used tracks). I did not need to modify environment.rb.

Create a log directory

mkdir log

Remove the updates from 2adduser_id.rb

#    execute &#8220;UPDATE &#8216;contexts&#8217; SET &#8216;user_id&#8217; = 1;&#8221;
#    execute &#8220;UPDATE &#8216;projects&#8217; SET &#8216;user_id&#8217; = 1;&#8221;
#    execute &#8220;UPDATE &#8216;todos&#8217; SET &#8216;user_id&#8217; = 1;&#8221;

run:

rake migrate

check the path to ruby in script/server
And finally run:

script/server

Then first, create a user: http://localhost:3000/signup.
Then you can start using it: http://localhost:3000

Selecting a rails hosting service

Is there is one thing you could agree with, it is that there are many hosting options for your Ruby on Rail application. And this is a good thing for you. This is good because this means that there are a lot of companies competing for your business.

But it can be easy to get lost in this multitude of offers. So you need to define a selection process before you begin. Write down the criteria that you find important. And as you do your research, that list may change, and that’s ok. This is only a starting point. If you change that list too much, this may be warning flag that you are getting influenced too much but what you are reading.

To help you get started, here’s the list I used when selecting a hosting service:

rails support: that’s the main reason you are looking for a hosting solution. So get the best you can get! This one almost trumps all the others. Ideally you want to find someone that has had quite a bit of experience with rails, and has had a chance to fine tune their systems over time. You also want to make sure they offer more than just plain CGI, something like FastCGI, or FCGI, or even offer LightTPD support. If you don’t have to set it up, any of these will do. In my experience, it is not so important that they provide the latest gems and rails versions as you are better off freezing your gems.

Subversion support: to use Capistrano, this is something which is going to be helpful. You may be able to get CVS working, but subversion is a much better solution overall.

Capistrano support: with ssh and svn, you should be good
Price: Price matters, but most offerings are in the same ballpark, making this secondary. Get something you are comfortable with.

Disk space: you need enough to get your application running. If you envision something with a lots of graphics, sounds or movies, then you will need a lot.

Bandwidth: same as for disk space. You can start small to keep the price down, and most hosting companies will let you upgrade to their next offering, which generally gives you more bandwith, and only charge you the price difference.

Number of domains: how many domains do you really need? 5 to 10 should be more than you need. The thing you really need is the ability to have real domains, without some sort of redirection or frame redirecting to a subdirectory to your main domain.

Number of subdomains: I would not settle for less than an unlimited domain.
Number of databases: you only generally need just 1 or 2 for your application, but why you be charged by the database? They already limit the disk space and the bandwidth. So you’ll get the more serious ones by selecting one that doesn’t limit you.

Backup: once your application reaches production state, you need backups (trust me on that one, you don’t want to get caught without a backup). Site5 has a very nice feature where you get to restore your onw backups whenever you want.

Statistics: you need at least decent statistics. although you may feel the need to use a specialized service, such as Google Analytics, SiteMeter, or more analytics options which will give you a lot more details that what most hosting services give you.

SSH access: this one is a must. To run Capistrano, you need this. Plus this generally comes with ftp access, you get to run rails by hand if necessary, etc.
Email: that’s a no brainer, and I would just look for a good spam solution to go with that.

Excellent uptime: 99.9… or so. You can only go with what the vendor claims. If they are willing to back it with some refund, then they really mean it.
Moneyback: all the serious hosting company will let you try it for at least a month. To weed out the less serious, insist on 60 days or more.

Once you’ve defined your list of criteria, do some research, use google, ask your friends, look for what other people are using.

Create a spreadsheet where you list the various plans for each hosting company with some notes on each criteria.

Very quickly, the top 2 or 3 will start to emerge. Then you can dig a bit deeper, look for what others are saying, look to see if there are a lot of people complaining. For the price, you may even want to give your top 2 or 3 a test drive and see for yourself.

Good luck!

AjaxScaffold 3.0.0 released

He quickly released 2 updates so the big kinks are now out of the way. Thank you Richard for your hard work.

There is one bit of bad news though, you can forget about migrating from previous versions, but given the improvements, I think this is only a small price to pay.

For downloads, tutorials, documentation and a live demo, see Ajax Scaffold Generator.

Typo trackback Spam

Taking a look at lib/spam_protection.rb, and scan_uri called when adding a trackback, scan_uri only checks against the RBL database.

So I’ve added the following to scan_uri:

# Pattern scanning
BlacklistPattern.find_all.each do |pattern|
  logger.info(&#8220;[SP] Scanning domain for #{pattern.class} #{pattern.pattern}&#8221;)

  if pattern.kind_of?(RegexPattern)
    throw :hit, &#8220;Regex #{pattern.pattern} matched on host&#8221; if domain.join(&#8216;.&#8217;).match(/#{pattern.pattern}/)
  else
    throw :hit, &#8220;String #{pattern.pattern} matched on host&#8221; if domain.join(&#8216;.&#8217;).match(/\b#{Regexp.quote(pattern.pattern)}\b/)
  end
end

Ultimately, this code should be factored out and called from scan_text and scan_uri.
So here’s the full version:

def scan_uri(host)
return scan_ip(host) if host =~ Format::IP_ADDRESS

host_parts = host.split(&#8216;.&#8217;).reverse
domain = Array.new

# Check for two level TLD
(SECOND_LEVEL.include?(host_parts[1]) ? 3:2).times do
  domain.unshift(host_parts.shift)
end

# Pattern scanning
BlacklistPattern.find_all.each do |pattern|
  logger.info(&#8220;[SP] Scanning domain for #{pattern.class} #{pattern.pattern}&#8221;)

  if pattern.kind_of?(RegexPattern)
    throw :hit, &#8220;Regex #{pattern.pattern} matched on host&#8221; if domain.join(&#8216;.&#8217;).match(/#{pattern.pattern}/)
  else
    throw :hit, &#8220;String #{pattern.pattern} matched on host&#8221; if domain.join(&#8216;.&#8217;).match(/\b#{Regexp.quote(pattern.pattern)}\b/)
  end
end
logger.info(&#8220;[SP] Scanning domain #{domain.join(&#8216;.&#8217;)}&#8221;)
query_rbls(HOST_RBLS, host, domain.join(&#8216;.&#8217;))
end

I’ll run this for a few days, and if it works, I will add a patch to the typo trac database.

So far, with the right pattern in the blacklist, it has been able to fend off one attack. So far, so good :)

[SP] Scanning for StringPattern HIDDEN
[SP] Scanning for StringPattern HIDDEN
[SP] Scanning IP 193.219.28.245
[SP] Scanning domain for StringPattern HIDDEN
[SP] Hit: String HIDDENmatched on host

I’ve replaced the pattern with HIDDEN just not to give this guy more publicity than he deserves.

rake freeze_gems fails because rails gem is not installed

that the code is not picking up the right version for rails.

A quick look at:

gem list

receals that I have:

rails (1.0.0)
    Web-application framework with template engine, control-flow layer, and ORM.

rails_analyzer_tools (1.1.0)
    Tools for analyzing the performance of web sites.

And 1.1.0 is exactly what it shows as my version of rails. Well, again, I beg to differ!

The quick and dirty fix:

edit /usr/local/lib/ruby/gems/1.8/gems/rails-1.0.0/lib/tasks/framework.rake

and hardcode the version:

rails = if version = ENV[&#8216;VERSION&#8217;]
        Gem.cache.search(&#8216;rails&#8217;, &#8220;= #{version}&#8221;).first
      else
        Gem.cache.search(&#8216;rails&#8217;).sort_by { |g| g.version }.last
      end
version ||= rails.version
version  = &#8216;1.0&#8217;  #HARDCODE VERSION

Now, freezing rails works as expected:

rake freeze_gems

An other solution may be what Michael Gorsuch did.

Freeze all your ruby gems on a shared host

Things started to go downlhill when I had to check back on a previous article I had written about (Allowing full access to a sub directory in Apache Server) and my server would just hang!
Well, not a problem, let’s just try to hit the refresh key, hmmm, nothing. Ok, for good measure, let’s hit the button, just to be sure. Hmmm, still nothing.
Ok, let’s check out the logs, started from the apache logs.

[Tue Mar 28 21:04:31 2006] [error] [client 66.249.66.226] FastCGI: incomplete headers
(0 bytes) received from server &#8220;/home/psq/blog.nanorails.com/current/public/dispatch.fcgi&#8221;

Ok, yep, something’s wrong. Next, let’s hit the production.log. Nothing useful.

Next, a trick I learned before (aren’t you glad it is not your first time troubleshooting FastCGI?). Let’s call public/dispatch.fcgi directly:

/usr/local/lib/site_ruby/1.8/rubygems.rb:194:in `report_activate_error&#8217;:
    RubyGem version error: activerecord(1.13.2 not = 1.14.0) (Gem::LoadError)
    from /usr/local/lib/site_ruby/1.8/rubygems.rb:136:in `activate&#8217;
    from /usr/local/lib/site_ruby/1.8/rubygems.rb:162:in `activate&#8217;
    from /usr/local/lib/site_ruby/1.8/rubygems.rb:161:in `each&#8217;
    from /usr/local/lib/site_ruby/1.8/rubygems.rb:161:in `activate&#8217;
    from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:23:in `require&#8217;
    from ./../config/boot.rb:14
    from ./../config/environment.rb:8:in `require&#8217;
    from ./../config/environment.rb:8
    from ./dispatch.fcgi:21:in `require&#8217;
    from ./dispatch.fcgi:21

Ah ha! Bingo. What does it all mean, though?

It has been hard to miss that Rails 1.1 is not officially out. Yeahh!!! I wonder whether Dreamhost (aff) has maybe, just maybe, decided to upgrade.

gem list|grep rails

Ah ha! again. Yes, rails is is now 1.0 and 1.1. So I futz a little bit to try to freeze the gems manually, to not avail :(. So I go home, and fortunately, thanks to Capistrano, I have a full copy on my home machine! After doing a little reading, I figure the solution has to be:

rake freeze_gems

So first things first, I get home, rush to my machine, run the magic command, then run what was going to solve all the recent woes:

cap deploy -q

and hit the refresh button…
Duh! Still doesn’t work. Major Bummer!

Next step is to get a copy of all my gems (for typo):

 cd vendor/rails
 gem unpack activerecord
 gem unpack activesupport
 gem unpack actionpack
 gem unpack actionmailer
 gem unpack actionwebservice
 mv activerecord-1.13.2/ rails/
 mv actionmailer-1.1.5/ actionmailer
 mv actionpack-1.11.2/ actionpack
 mv actionwebservice-1.0.0/ actionwebservice
 mv activerecord-1.13.2/ activerecord
 mv activesupport-1.2.5/ activesupport

Your versions may vary depending on your install.

Then, committed everything, and redeployed.

and Ta Da! everything’s now back to normal! Phew…

The moral of the story, it that wasn’t clear already is:

Do not rely on the gems installed on your shared host!
Make sure your freeze all your gems

And I can worry on my own time to upgrade to rails 1.1 in my production environmment.

Update about 2 hours later:
Assuming you can run rake (which I couldn’t because of some broken dependency), an other method to lock down to rails 1.0 is to use:
rake freeze_edgeREVISION=3303

this assumes that’s you are using rails directly from svn already.

Update about 18h later:
An other solution is to lock down the versions of the gems you use in environment.rb using what suggested on Ruby On Rails Wiki. Thank you, Miles Barr for the tip!