Displaying ads from TextLinkAds in a rails application

Posted on Wed May 03 02:14:00 UTC 2006

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) %>

Posted in Rails  |  Tags ,  |  23 comments

Comments

  1. Patrick Gavin Patrick Gavin said // May 03, 2006 at 06:40 AM

    Pascal, great work! I will be contacting you about using this code on our site for other Ruby users, thanks again!

  2. Pascal Pascal said // May 03, 2006 at 12:48 PM

    Thank you, Patrick.

  3. bruno bruno said // May 04, 2006 at 07:27 AM

    You rock. This is exactly what I was looking for (and couldn't find) this morning. Then I stumbled upon it in my feed reader. Way to go!

  4. Justin Justin said // Jun 27, 2006 at 02:03 AM

    Pascal, I don't have much knowledge of rails, but how does the view code know to loop through the array and print out all the links?.. It looks like you're using link['URL'][0], link['Text'][0], etc... Wouldn't this simply print out the first link? Is some kind of a loop necessary, or does rails know to display all elements of the array in this fashion?

  5. Pascal Pascal said // Jun 27, 2006 at 02:42 AM

    Justin, that's a good point. the code above is missing the iteration, replace:

  6. <%= link['BeforeText'][0] -%> <%= link['Text'][0] -%> <%= link['AfterText'][0] -%>
  7. <% for link in @links["Link"] -%>
  8. <%= link['BeforeText'][0] -%> <%= link['Text'][0] -%> <%= link['AfterText'][0] -%>
  9. <% end -%> Or use the code from the typo plugin, which was correct. I must have missed something while formatting the article. I'll fix it in the post itself.

  10. Pascal Pascal said // Jun 27, 2006 at 02:49 AM

    the code in the typo plugin can be downloaded here: http://blog.nanorails.com/articles/2006/06/10/textlinkads-typo-sidebar-plugin

  11. Justin Justin said // Jun 27, 2006 at 06:58 AM

    Pascal, great, thanks for your quick response!

  12. Pascal Pascal said // Jun 27, 2006 at 12:55 PM

    Sure! Sorry for the missing bit.

  13. Ryan Jones Ryan Jones said // Jul 06, 2006 at 12:46 PM

    I just can't get this to work. Every time I include it in one of my controllers, I get "Application Error (Rails)" errors when trying to access any pages that calls any method in that controller. When I go to the production log to see what is going on, all it tells me is: "wrong number of arguments (0 for 1))" Any ideas?

  14. Pascal Pascal said // Jul 06, 2006 at 03:17 PM

    Ryan, can you tell me a bit a bit more about your setup? Maybe share some of the code. How about stack traces? Is there any besides the "wrong number of arguments..." error What version of rails are you using?

  15. Ryan Jones Ryan Jones said // Jul 07, 2006 at 01:40 AM

    Hi Pascal, thanks for the response. Our server is running version 1.0 of Rails. I did some primitive debugging, and it appears this bit of the text-link-ads code is causing the problem: url = "http://www.text-link-ads.com/xml.php?inventory_key=####TLA#KEY####&referer="+CGI::escape(@request.env['REQUEST_URI']) agent="&user_agent="+CGI::escape(@request.env['HTTP_USER_AGENT']) I can post a stack trace, but don't want to mess up your comments here.

  16. Ryan Jones Ryan Jones said // Jul 07, 2006 at 01:41 AM

    Ummm, I am an idiot - Pascal, can you please edit out my user key in the code above?

  17. Pascal Pascal said // Jul 07, 2006 at 02:48 AM

    Ryan, I've removed the key. Include the stack trace, if it messes up the comments, I'll fix it. Try having 4 spaces on the left, this might trigger markdown to use the code styling.

  18. Ryan Jones Ryan Jones said // Jul 07, 2006 at 06:05 AM

    Thanks Pascal. Here is the stack trace: C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/base.rb:902:in `request' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/base.rb:902:in `log_processing' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/base.rb:380:in `process_without_filters' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/filters.rb:377:in `process_without_session_management_support' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/session_management.rb:117:in `process' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/components.rb:71:in `process_with_components' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/components.rb:137:in `component_response' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/components.rb:108:in `render_component_as_string' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/components.rb:107:in `component_logging' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/components.rb:107:in `render_component_as_string' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/components.rb:44:in `send' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/components.rb:44:in `render_component' #{RAILS_ROOT}/app/views/layouts/application.rhtml:85:in `_run_rhtml_layouts_application' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_view/base.rb:314:in `send' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_view/base.rb:314:in `compile_and_render_template' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_view/base.rb:290:in `render_template' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_view/base.rb:249:in `render_file' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/layout.rb:249:in `render_without_benchmark' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/benchmarking.rb:53:in `render' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/benchmarking.rb:53:in `measure' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/benchmarking.rb:53:in `render' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/base.rb:911:in `perform_action_without_filters' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/filters.rb:368:in `perform_action_without_benchmark' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/benchmarking.rb:69:in `perform_action_without_rescue' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/benchmarking.rb:69:in `measure' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/benchmarking.rb:69:in `perform_action_without_rescue' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/rescue.rb:82:in `perform_action' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/base.rb:381:in `send' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/base.rb:381:in `process_without_filters' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/filters.rb:377:in `process_without_session_management_support' C:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.12.1/lib/action_controller/session_management.rb:117:in `process' C:/ruby/lib/ruby/gems/1.8/gems/rails-1.1.2/lib/dispatcher.rb:38:in `dispatch' C:/ruby/lib/ruby/gems/1.8/gems/rails-1.1.2/lib/fcgi_handler.rb:150:in `process_request' C:/ruby/lib/ruby/gems/1.8/gems/rails-1.1.2/lib/fcgi_handler.rb:54:in `process!' C:/ruby/lib/ruby/gems/1.8/gems/rails-1.1.2/lib/fcgi_handler.rb:53:in `each_cgi' C:/ruby/lib/ruby/site_ruby/1.8/fcgi.rb:595:in `each' C:/ruby/lib/ruby/site_ruby/1.8/fcgi.rb:595:in `each_cgi' C:/ruby/lib/ruby/gems/1.8/gems/rails-1.1.2/lib/fcgi_handler.rb:53:in `process!' C:/ruby/lib/ruby/gems/1.8/gems/rails-1.1.2/lib/fcgi_handler.rb:23:in `process!' F:/Inetpub/jnw_rails/public/dispatch.fcgi:24

  19. Pascal Pascal said // Jul 07, 2006 at 08:04 AM

    Ryan, just a hunch, but try renaming the request(url) method I have into something else.

  20. Ryan Jones Ryan Jones said // Jul 09, 2006 at 12:36 PM

    Thanks, Pascal, that seems to have mostly solved the problem. I am still getting an error on just on the "content" method. I get error related to this line: response.lifetime = 6.hour The error says: NoMethodError (undefined method `lifetime=' for #<actioncontroller::cgiresponse:0x39b7890>):

  21. Pascal Pascal said // Jul 09, 2006 at 01:23 PM

    for response.lifetime to work, you need to be using the expiring\_action\_cache plugin. If you are not using it, you don't need it. I just created a plugin to replace the old code. Should be much easier to deal with. to get started: script/plugin install http://nanorails.com/plugins/textlinkads The readme has some examples on how to use it.

  22. Ryan Jones Ryan Jones said // Jul 09, 2006 at 02:39 PM

    Excellent. I will give the new plugin a shot and let you know how it goes. Thanks for your help!

  23. Ryan Jones Ryan Jones said // Jul 09, 2006 at 03:20 PM

    I tried this out, but got the following error: ActionView::TemplateError (undefined local variable or method `links_TLA'

  24. Pascal Pascal said // Jul 09, 2006 at 03:42 PM

    You are welcome. Just to clarify on the response.lifetime. First the reason why I did not mention it. Very simple in fact. My main focus was on typo, and that plugin is included. But as you found out, is not in the default rails. Second, the reason why you need it. That has to do with the way rails caching works. Once a page built by a controller is rendered, and your environment is "production", rails will cache the rendered html so next time, the page will be rendered a lot faster. That cache is invalidated if the model changes. In the case of the links for TextLinkAds, they may change on the TextLinkAds server, but since the page is cached, it would still display the old links (or none if you previously had none). That's where expiring\_action\_cache enters the pictures. It lets you set an expiry on rails cache (in my example, I use 6 hours). So after that time, the cache will become invalid, forcing a rerendering with the latest links. If you are not using typo, you should be able to install that plugin using: script/plugin install svn://typosphere.org/typo/trunk/vendor/plugins/expiring_action_cache

  25. Pascal Pascal said // Jul 09, 2006 at 03:51 PM

    Ryan, I tried is with Rails 1.1.2 in a rthml generated from a scaffold and it works for me. Can you include the full stack? Can you tell me a bit more about your setup?

  26. Ryan Jones Ryan Jones said // Jul 10, 2006 at 08:32 AM

    I went ahead and used the original code, and just removed the line: response.lifetime = 6.hour Now everything seems to be working fine. Though there is one small thing. At present I of course have no real ads showing. But instead of showing the default link I put in the view: <% if @links != nil %> <% if (@links != nil)&&(@links["Link"] != nil) %> <% for link in @links["Link"] -%> <%= link['BeforeText'][0] -%> < a href="<%= link['URL'][0] -%>"><%= link['Text'][0] -%> <%= link['AfterText'][0] -%> <% end -%> <% end -%> <% else %> Advertise Here! <% end -%> I get a link that says "Test Link Ad" that links back to my site. This is my controller code at present: def tla_display url = "http://www.text-link-ads.com/xml.php?inventory_key=TLA_KEY&referer="+CGI::escape(@request.env['REQUEST_URI']) agent = "&user_agent="+CGI::escape(@request.env['HTTP_USER_AGENT']) url_time, url_data = fragment_key(url) #is it time to update the cache? time = read_fragment(url_time) if (time == nil) || (time.to_time < Time.now) @links = getLinks(url+agent) rescue nil render :layout => false #if we can get the latest, then update the cache if @links != nil expire_fragment(url_time) expire_fragment(url_data) write_fragment(url_time, Time.now+6.hour) write_fragment(url_data, @links) else #otherwise try again in 1 hour write_fragment(url_time, Time.now+1.hour) @links = read_fragment(url_data) end else #use the cache @links = read_fragment(url_data) render :layout => false end end

  27. Pascal Pascal said // Jul 10, 2006 at 09:47 AM

    Ryan, if you write the code like this, you don't need @links = read_fragment(url_data) after the "#otherwise try again in 1 hour" line However, if getLinks fails, render may not have anything to render even though you may have it in the cache. I would move render :layout => false below the block if @links != nil ... else .... end I 'm still interested in getting the stack trace in the plugin if you still have it. Someone else might hit the same problem!

(leave url/email »)

Comment Markup Help