Fetching RSS Feeds With Ruby From Behind A Proxy

I was trying to fetch some RSS feeds with Ruby the other day. I just needed something quick (but not dirty, I don’t like dirty) to validate a couple of things I was trying out with Rails. It is not that difficult to fetch feeds, there is enough info around, but unfortunately I was behind a proxy at the time and this is where things went south quickly. It just seems that the Ruby ecosystem has this natural hatred for corporate proxies, it’s come to bite me time and time again. This is something that really needs to be rectified if we want Ruby to penetrate further into the corporate world, but that’s a story for another post.

Fetching Feeds Using Feedzirra

The first thing I tried to do was use Feedzirra, which I found out about from Railscasts. At first sight everything seemed ok, it will respect the proxy settings you have configured in your environment (i.e. the _httpproxy environment variable) as it uses a fork of Curb (libcurl bindings for Ruby). However if you haven’t configured the proxy within your environment, everything falls over since there doesn’t seem to be a way to supply your proxy settings to Feedzirra directly. I would be happy to be corrected here! I wanted to fetch feeds from within a Rails app, and Rails doesn’t inherit your environment variables – no good (I am sure there are ways to make Rails do so, but like I said – don’t want dirty).

What was more frustrating was the fact that diagnosing this problem was a pain. Normally to fetch a feed with Feedzirra you have to do the following:

ruby feed = Feedzirra::Feed.fetch_and_parse(feed_url)

You can then iterate over the entries:

ruby feed.entries.each do |entry| end

Without the proxy settings though, I was getting 0 (zero) as the return value of the initial call. Not very intuitive! Of course I suspected that the proxy was at fault, but still. After reading the documentation a little more closely I came across the following bit:

ruby feed = Feedzirra::Feed.fetch_and_parse(feed_url, :on_success => lambda {|feed| puts feed.title }, :on_failure => lambda {|url, response_code, response_header, response_body| puts response_body })

This lets you define custom behaviour for successes and failures, this made everything simpler, since the failure block you supply gets invoked when the feed is not fetched successfully and the _responsecode parameter of the block contains the correct response code (407 proxy authentication required). Still, diagnosing the problem doesn’t provide me with a cure. I am sure there must be a way to patch something to allow Feedzirra to take proxy settings directly. I had a quick look at the code, but it seemed like I would need to patch multiple things in order to get it to work since Feedzirra hangs off several other libraries. Instead I decided to take a lower level look at my feed fetching (and parsing).

Fetching Feeds Using Open-uri

Apparently, there is a way to work with RSS feeds that is built into Ruby. After a quick glance this looked pretty much as simple as Feedzirra so I decided to give it a go. Guess what problem I ran into? Doesn’t play nice with proxy – what a freaking surprise! The Ruby RSS library relies on open-uri to actually fetch the feed which should pick up the proxy settings from your environment as well as give you the ability to pass the proxy configuration directly, but even a simple stand-alone script (with the environment configured correctly) refused to work for me. After searching around for a bit I found this little tidbit.

Apparently open-uri has a bit of a bug/issue whereby the proxy username and password don’t get picked up. Long story short, if you want to get it to work you need to patch the OpenURI module slightly. Fortunately this is easy to do with Ruby, just open up the module and go for your life. Like so:

```ruby module OpenURI def OpenURI.open_http(buf, target, proxy, options) # :nodoc: if proxy raise “Non-HTTP proxy URI: #{proxy}” if proxy.class != URI::HTTP end

if target.userinfo && “1.9.0” <= RUBY_VERSION # don’t raise for 1.8 because compatibility. raise ArgumentError, “userinfo not supported. [RFC3986]” end

require ‘net/http’ klass = Net::HTTP if URI::HTTP === target # HTTP or HTTPS if proxy klass = Net::HTTP::Proxy(proxy.host, proxy.port, proxy.user, proxy.password) end target_host = target.host target_port = target.port request_uri = target.request_uri else # FTP over HTTP proxy target_host = proxy.host target_port = proxy.port request_uri = target.to_s end

http = klass.new(target_host, target_port) if target.class == URI::HTTPS require ‘net/https’ http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_PEER store = OpenSSL::X509::Store.new store.set_default_paths http.cert_store = store end

header = {} options.each {|k, v| header[k] = v if String === k }

resp = nil http.start { req = Net::HTTP::Get.new(request_uri, header) if options.include? :http_basic_authentication user, pass = options[:http_basic_authentication] req.basic_auth user, pass end http.request(req) {|response| resp = response if options[:content_length_proc] && Net::HTTPSuccess === resp if resp.key?(‘Content-Length’) options[:content_length_proc].call(resp[‘Content-Length’].to_i) else options[:content_length_proc].call(nil) end end resp.read_body {|str| buf << str if options[:progress_proc] && Net::HTTPSuccess === resp options[:progress_proc].call(buf.size) end } } } io = buf.io io.rewind io.status = [resp.code, resp.message] resp.each {|name,value| buf.io.meta_add_field name, value } case resp when Net::HTTPSuccess when Net::HTTPMovedPermanently, # 301 Net::HTTPFound, # 302 Net::HTTPSeeOther, # 303 Net::HTTPTemporaryRedirect # 307 throw :open_uri_redirect, URI.parse(resp[‘location’]) else raise OpenURI::HTTPError.new(io.status.join(’ ‘), io) end end end```

All of that is just a big copy/paste from the actual open-uri.rb source file, all you really need to worry about is one line. Completely off-topic, does anyone else feel like that method could use a bit of a refactor :). Back on topic, in the original open-uri, the line is:

ruby klass = Net::HTTP::Proxy(proxy.host, proxy.port)

we changed it to:

ruby klass = Net::HTTP::Proxy(proxy.host, proxy.port, proxy.user, proxy.password)

After doing this the standalone script started fetching feeds without any trouble. All you need to do is the following (as per the link above):

ruby source = "feed_url" content = "" open(source) { |s| content = s.read } rss = RSS::Parser.parse(content, false)

Now we just want the whole thing to work in Rails.

Getting It To Work In Rails

As I mentioned above, we don’t want Rails to inherit our environment variables, so we need to be able to supply the proxy configuration directly. This is fairly easy to do when you’re fetching the contents of the feed url:

ruby open(source, :proxy => "proxy_url") { |s| content = s.read } rss = RSS::Parser.parse(content, false)

The actual proxy url will have to be in the correct format. Assuming you’re on Linux, something along the lines of:

http://username:password@proxy-host:proxy-port

The only thing left to take care of now, is to find a place for our open-uri monkey-patch and also get Rails to recognise the fact that we actually patched a module in the first place. This is precisely what the:

RAILS_ROOT/config/initializers

directory is for. You can put the open-uri monkey-patch here and Rails will auto-magically load it. Alternatively you can put the patch file under:

RAILS_ROOT/lib

But you will still need to create a file under the initializers directory and then require the monkey-patch file from there. Incidentally this file would also be a good place to put any of the other extraneous requires if you don’t want to have them floating around your Rails controllers or models.

This whole ‘fetching feeds from behind a proxy’ thing ended up being a lot more complicated than it had to be, or at least that’s how it felt. On the positive side, at least I learned a lot, so there is always a silver lining.

For more tips and opinions on software development, process and people subscribe to skorks.com today.

Image by Stewart Ho

How To Become A Spammer (as a programmer) Regardless Of People Following You On Twitter

spam I saw a post by Max Klein today, How to become rich even if nobody is following you on twitter, it made me sad. Go have a read – I’ll wait…

Do you know why I am sad? Because, there is a name for what he is advocating, it’s called spam! I’ve been interested in marketing (the regular kind and the online kind) for almost 2 years now and nothing Max said is a revelation. The shadier parts of the online marketing world have been doing it for years, it’s got many names, micro-niche blogging, micro-niche sites, made-for-adsense sites etc. It’s not new, but it has hung around for years, because it works. You heard me, it works, which doesn’t make it any nobler.

I know he is not just advocating building made-for-adsense sites, it’s not the method, it’s the attitude that goes with it, that is the real issue. Lets not care about providing any kind of value, lets not worry about any sort of professionalism, all we want to do is scam some suckers out of $1 a day and believe you me there are plenty of suckers on the web. Let’s turn our craft into a factory that produces cheap, low-value crap, but can churn it out in hundreds and thousands. And maybe after a little while we start calling ourselves a ‘guru’, no ‘junior vice guru’; oh hell, let’s not mince words – ‘marketing consultant’. We’ll branch out into SEO and ‘brand management’. We’re no longer in the code business, we’re in the making $1 a day business.

There are hundreds of these ‘marketers’ lurking around the internet. Releasing “product” after useless “product” specifically designed to sucker poor shlubs into parting with their hard-earned cash. It’s a self-perpetuating meta-industry which makes money, from selling people products, on how to make money. It’s sickening, but also in a strange way seductive, maybe those poor suckers deserve to be taken for a ride, cause that would make you feel like less of a sucker yourself.

Thousands of people follow these spammers on twitter and everywhere else, they want to emulate these guys, I’d like to think that we programmers are better than that as a community. Not just each individual developer, but all of us together. Let’s face it, we’re smarter than your average sucker, more aware, capable of making a difference. It doesn’t matter where we live, we have the tools, drive and ability to succeed without having to resort to producing rubbish (but doing it quick) and spammy tactics.

And if you do happen to live in a country that is not considered part of the first world, think what this kind of attitude will do not just to your reputation, but your nation’s. Many countries in Eastern Europe and Asia are already considered havens for spam, hackers and porn. Whether rightly or not, do you want to perpetuate those beliefs and make life that much harder for the developers that follow you?

Surely if you can create even a marginally worthwhile product that can make you $1 a day, then a little bit of extra effort can turn that $1 into $2. A couple more features can turn that $2 into $4; some sensible promotion can turn that $4 into $40. If anyone (even one person a month) is willing to part with $29.95 to buy an app, then there surely are many others who will consider it (more than 12 people a year), I refuse to believe otherwise. All it needs is a little bit more work, maybe better documentation, maybe finding more people to spread the word. Abandoning it in a half-done state and moving on to build half of another crappy app is certainly not the answer.

I’ve got no problem with people trying to make some cash. But, as a developer you surely have enough skill and brains that you can do so and still maintain at least a semblance of respect for yourself, your craft and the people who use your products.

I really do hope that if you go down the path of creating a $1 a day empire of crap, nobody follows you on twitter, the world has enough make-money-online ‘experts’ we don’t need any more.

Image by chotda

Barbie Sat In My Lap – The Simplest Way To Change The Status Quo

change No, not the doll – it was a girl I met while out with my mates :). Apparently girls with that name actually do exist, but more about that later. How often have you been in a situation where you feel powerless to change the accepted state of affairs? Stupid question – it happens all the time especially if you’re plain-old ordinary Joe developer (as opposed to shiny and go-getting Joseph manager). The way I see it, this feeling of ‘drowning in the status quo’ actually has two phases.

  1. You see something wrong (that is out of your immediate control) and you’re eager to change it, so – bright eyed and idealistic – you seek out your immediate management, or business representatives, or whoever you think has some authority, only to be met with some of these classics:
    • “The business would never go for it”
    • “We’re too invested in this technology, process, tool etc.”
    • “Some battles aren’t worth fighting”
    • “The customer has specified the tech stack (lets not rock the boat)”
    • etc.
  2. At this point you become dejected, ambivalent or frustrated. You were willing to do what was right and necessary, even to take on the responsibility and hard work in order for everyone to experience the benefits down the track. Instead you got rebuffed at every turn, how stupid and pointless, oh well, back to doing whatever it is you do all day (that is if you can still get some satisfaction out of your work in other ways), or maybe time to dust off the old resume.

Often, having been in a place long enough you skip 1 and go straight to 2, you know there is no point in trying, nothing will ever come of it – this is the most insidious situation of all. Why? Because things aren’t always as bleak as they seem (well ok, often they are, but not always). It doesn’t matter if you’re at 1 or 2, there is one thing you can do which is always worth trying, mainly because it requires so little effort. Ask!

The One Sure-fire Way To Make Change Happen

Have you ever heard that saying – “You miss 100% of the shots you don’t take”. Do you see where I am going with this? No matter what response you get (if you’re at 1) ask for an explanation:

  • Have we actually asked the business if they would go for it? Do you mind if I set up a meeting to have a chat?
  • How are we invested in this technology, process, tool?
  • Do you mind if I have a crack at fighting this battle anyway?
  • Have we actually asked the customer if they know any better?

Ask every time and don’t accept a brush-off. More often then not, you won’t get a good explanation because there isn’t one, and that is a chink in the corporate armour – you can work with that. Better yet, sometimes your question will engender a response such as:

  • That’s a good point, why DON’T we have a chat with the business?
  • Good question, why the hell ARE we using this technology, tool, process – it’s crap?

When that happens – congratulations, you’ve changed the status quo, your team will thank you for it. And it only cost you a few words – more than worth it.

But what if you’re at 2, you’re already jaded and can’t be bothered making an effort. Well, snap the hell out of it! At least once in a while pretend like you still want to make things happen. Ask for the change that you want! Don’t expect someone else to do it. If you ever want something on a silver platter you have to go find the stupid piece of silverware and load the damn thing yourself. And how do you find a silver platter – you got it – ask someone where it is.

Sure, sometimes you’ll annoy people and be seen as the ‘bad guy’, but if you make things happen, nobody will care. I say beg, borrow or steal a little bit of chutzpah and ask the questions that need to be asked.

So how do you think I got Barbie to sit in my lap? I asked!

If It Still Doesn’t Work – Incentivize

But it wasn’t that easy. Barbie needed more of an incentive. This can happen, but it is not the end of the world. You did the right thing and asked, but got rebuffed, but (but to the power of 2) you could tell their heart wasn’t in it. All they need is a little push in the right direction, a reason to help you get what you’re after – an incentive.

Do you know what the best and easiest incentive is? Offer to do all the work yourself. Consider this:

  • Could you please set up a meeting with the customer to have a chat about the situation?

versus this:

  • If it’s ok with you I’ll set up a meeting and have a chat with the customer (blah, blah)?

In the first case you’re giving extra work to someone who is potentially your boss, in the second you’re actually taking some of their burden on yourself. You’re not really, since you created the extra work in the first place, but that’s what it will seem like to them. They get to do nothing, and you can try to create the change you want to happen, it’s a win/win.

The lesson here is this, find a small incentive that will potentially be of benefit to you also. Don’t try to go for the grandiose:

It will save us 1 million dollars!!!

drevil

It never works, small and easy benefits is the way to go:

  • This new CI tool is way easier to configure, don’t worry I’ll set it all up, it will be awesome.

Less project risk for them, playing with a new tool for you, better CI for the team – everybody wins. Of course this won’t work with a control-freak, so cater your incentive to the person you’re dealing with, it’s basic people skills.

If incentivizing creatively doesn’t work, you’re in the 20-part of the 8020 rule of making change happen – bummer. At this point, feel free to crack open your copy of “Fearless Change” and go for your life, this is going to be tough. Alternatively, you have my permission to become jaded and lethargic and go back to doing whatever it is you do all day. Sometime the energy expenditure really isn’t worth the pay-off.

Oh yeah, back to Barbie. So what incentive did she need? She wanted me to blog about it :). Sure, why not, I was gonna blog about something anyway, it let me practice my writing, it let me flex my creativity (how can I make it relevant to a software blog), it got me a girl in my lap; how many birds am I killing with one stone here :)? See what I mean about win/win incentives.

Let us wrap up on a geeky note. The thing you need to remember about companies, especially large corporates is that they are like spaceships. You can’t turn the Millennium Falcon instantly (assuming real physics and such), not to mention an Imperial Star Destroyer, but apply a bit of the right kind of pressure and it might start listing lazily to the left – keep up the pressure and before you know it you will have changed course entirely. Do you see where I am going with this metaphor? Of course you do, I hope you draw the right conclusions.

Oh and Macka – I told you I could tie that situation to software :).

For more tips and opinions on software development, process and people subscribe to skorks.com today.

Images by David Reece and LZ Creations