Posts Tagged ‘Ruby’

Testing Your Apps.

Saturday, August 23rd, 2008

I’ve been really putting off using Rspec. Unfortunately ( and like the vast majority or programmers out there) I have not been writing enough tests for my apps. When I did write them, they were using Rail’s built in Test::Unit library, which, many will say… sucks. Plus, honestly, they were half assed.

So, I’ve taken it upon myself to dive into Rspec. Most everyone agrees it is the way to go, and it gets you into BDD, which gives me another bussword I can throw on the resume (/sarcasm).

The part about writing tons and tons of tests to cover all your app that didn’t make sense was that you would end up writing so much more code. In reality, I think it boiled down to being lazy, which all programmers are. Since the Rails community prides itself on keeping things DRY and keeping the amount of code written to a minimum, why the hell would I want to write tons of tests for stuff I don’t think are going to break?

In the long run, for any programmer, it is really not about how much code you actually have to write, its more about how much time you send making the app work. If you’re like me, there have been many nights spent debugging some controller that totally broke and you’re stuck refreshing your browser, creating a user, deleting a user, and checking the log over and over until you solve it. I hate it and have probably wasted days doing this. If you had written tests for your app, starting with that first controller, and continued writing quality tests, used autotest, you would avoided this. You would be confident that you code was rock solid.

What about if you’re absolutely sure you’re code won’t break? Well, it will. Maybe not all of it, but you’re gonna have problems eventually. I don’t think that writing tests for trivia stuff (like validating that validates_presence_of is working) is really worth while, but other stuff is fair game.

The list point I’d like to make is that I think since really writing tests and using Rspec, my thinking has changed. I know think writing code, not just as how do I make this work, but also, how to I ensure this WILL work. You’ll find yourself trying look for ways that your code will break, looking for more flaws, and consequently, leaning from those flaws. I’ve found myself refactoring much more code and that end up making my code more flexible and cleaner.

I don’t want to pretend to be a captain awesome when it comes to writing tests yet, but I really think I’ve been coming along very well and am so glad I’ve been investing so much time in it. Give it a try ya’ll.

rDigg – Ruby on Rails Digg API plugin

Thursday, August 14th, 2008

I’ve sort of finished up rDigg today. It’s to the point I’m comfortable letting other people start to play with it anyways.

As you’d expect, rDigg is a Digg API wrapper in the form of a Ruby on Rails plugin. It still needs some work, but works pretty well.

For example:

#create new Rdigg object
digg = Rdigg.new

# find the 3 newest submissions from Kevin Rose
stories = digg.user.find_submissions("kevinrose", :count => 3)

# stories is now an array with a hash for each story
stories.first[:story] #=> the story's text
stories.first[:href] #=> the story's url
stories.first[:diggs] #=> number of diggs the story has

Grab it at: http://github.com/johnyerhot/rdigg/tree/master. I’ll have the Rdoc up at rdigg.yerhot.org later tonight You can peruse the documentation at rdigg.yerhot.org. I highly recommend you check out all methods that are available to you.

If you want to really dig in (sorry couldn’t resist) I’d go over the Digg API wiki to see what arguments you can pass.

I hope you enjoy the plugin!

Simple Meta Programming in Ruby

Sunday, August 10th, 2008

Metaprogramming.  What a lovely buzz word.  I guess I’ve heard it enough and knew what the short definition is. Metaprogramming is code that writes code.  I think it is one of those things I just never thought about, even though I had used concepts and even written some before without realizing it until recently.

Here is a short and simple example.

class Something

@my_hash = {"foo" => "1234", "bar" => "5678"}

def initialize
  @my_hash.each do |a, b|
       self.instance_eval do
             define_method(a.to_s) {b.to_s}
       end
  end
end
end

And now we can play with it.

a = Something.new
a.foo  # => 1234
a.bar # => 5678

Now, what happened here is pretty neat in my opinion. We took our hash, my_hash, and in our initialize method, created two instance methods from its values. define_method is what did all the magic. You need to pass it a proc or, like what I did, just give it a simple block (just the string value from my hash).

Pretty neat, eh?

Super Simple Ruby Web Scraper

Monday, May 19th, 2008

Alrighty folks. Quick walk though for scraping remote web data with Ruby. This is how I did it for my little web scraper I wrote on Saturday..

DISCLAIMER: Web Scraping is kind of a gray area.. don’t steal things that are copywritten and don’t be a jerk. Give credit where credit is due..

First thing is first. You’ll need to install the Mechanize Ruby Gem.

sudo gem install mechanize

Mechanize is pretty slick. It will iterate through a given url and let you access various html elements. Further, you can use Hpricot methods to further grab data.Lets get going..

require ‘rubygems’require ‘mechanize’require ‘uri’

url = “http://www.johnyerhot.com”

The way this is set up, you MUST have a complete url.

@mech = WWW::Mechanize.new

@page = @mech.get(url)

Now, lets say we want to get all the urls for embedded images from the webpage (http://www.johnyerhot.com)..

@imgs = @page.search(”img[@src]“).map {|src| src['src']}

You’ve now got an array (@imgs) with all the urls for embeded images! What we actually did was use Hpricot’s search method to look for and image tags and sucked out the src attribute of that tag. Mechanize does have its own methods for grabbing tags also, for example, you can grab all the link targets from every link in the web page.

#remember @page is just our mechanize instance
# w/ http://www.johnyerhot.com

@links = Array.new
@page.links.each do |link|
@links << link.href
end

Now lets weed out any links to non-images:

@links.each do |link| #yeah we’re only collecting jpgs

if (link.to_s.include? “.jpg”) || (link.to_s.include? “.JPG”) || (link.to_s.include? “.jpeg”) || (link.to_s.include? “.JPEG”)

@imgs << link
end
end

Finally, lets actually grab all those pictures and save them locally using Open URI…

@counter =0
@imgs.each do |image|
url = URI.parse(image)#parse the url and separate need info
Net::HTTP.start(url.host, url.port) { |http|
#appeand the image path with the web root.
image = http.get(image)#actually make the file to save
open(”#{url.host}_#{counter}.jpg”, “wb”) { |file|
file.write(image.body)
counter = counter + 1
}
end

And there you have it! Put it all together and you should have a functioning Ruby web scraper…Sort of. You still have to account for relative vs. absolute urls, are you gonna let in more than jpgs?, what if you need basic authentication for the url? There are still some missing pieces that need to be implemented to have this be ready for general use, but the core is there.
Further Reading
Mechanize Docs
Hpricot
Open URI
Ruby net/http Docs

Integrating Google Maps in Your Rails 2.0 App.

Saturday, January 5th, 2008

NOTE:  THIS IS AN OLD POST AND THE INSTRUCTIONS MAY BE OUTDATED. YMMV

In my first real post in a year, I thought I’d share some Ruby stuff I’ve recently done – Integrating your Rails 2.0 app with Google Maps.

So, here is what we are aiming to accomplish: You are going to provide an address, say 123 Foobar St, Anywhere, MN 55812. We are going to send it to a geocoder, get the longitude and latitude, send that to Google, and get back the map information.

whew. Actually, its not nearly as hard as it sounds.

First, install the google-geocode gem:

sudo gem install google-geocode

The install the ym4r_gm, which will talk to Google for us.

script/plugin install svn://rubyforge.org/var/svn/ym4r/Plugins/GM/trunk/ym4r_gm

Alright, finally, include the google-geocode gem in your controller.

require 'rubygems'
require 'google_geocode'

Now, you will need to get an API key from Google, and then put that key in the newely created config/gmaps_api_key.yml (as Drew Bushaw points out) or hard code it (Which I do in the example). There should be three keys already in there, and they will work when using your local machine, but will not with anything other than localhost. Alternativly, if you want to hardcode it in, place it where I have “your api key here” in the next section.

Add to your page header (prolly your layout and view) to include the required API javascripts, etc…
<%= GMap.header %>
<%= @map.to_html %>

Ok, now heres the fun. To get the geocode information, simply:

gg = GoogleGeocode.new "your api key here" #hard coded
#not hardcoded
#gg = GoogleGeocode.new YAML.load_file(RAILS_ROOT +‘/config/gmaps_api_key.yml’)[ENV['RAILS_ENV']]

loc = gg.locate @property.full_address
Now, I have @property and full_address is its full address (123 Foobar, Somecity, MN 55812 for example). You can also try it without some vitals, such as zip code.

Now we have to send Google Maps the correct information:

@map = GMap.new("map_div")
@map.control_init(:small => true) #add :large_map => true to get zoom controls
@map.center_zoom_init([loc.latitude, loc.longitude],14)
@map.overlay_init(GMarker.new([loc.latitude, loc.longitude],:title => @property.name, :info_bubble => loc.address))

Notice, “map_div”, which will come into play in the next step. Some useful tid bits here include the :title, which will be the title of the pin on the map, :info_bubble which will appear when you hover over the pin, and where I specified “14″. Here you specify the altitude the map will be at. Experiment for whatever your needs are.

Now, the final thing you need to do is put this to work in your view.

<%= @map.div(:width => 493, :height => 300) %>

As you can see, I’ve specified a :width and :height. Now, fire everything up and you should get something similar to…

screen_grab_google_maps_1_5_07

Now in my case I had people inputing address and if/when they enter one that is non-existent or incorrect, I had to catch it since a google-geocode will throw a big error if you give it bad information.

Enjoy!