Update: as ‘ste’ points out in a comment Haml ships with an executable that does this for you. So hopefully googlers don’t bother reading through my lousy regexps and just use that tool ;)
If I were to write a post about how much Haml and Sass have changed the way I develop for the web I’d spend the first few paragraphs gushing about how Haml lets you write your DOM directly and takes care of the hideous (X)HTML in the middle. Then I’d write an elaborate sonnet about how SASS makes CSS almost usable for developers who like to keep things DRY.
But we’ll save that Yegge-length post for another day. For now, here’s a chance to almostly completely convert your CSS files into SASS with just a few regexps (using $’s rather than \’s because TextMate likes it that way):
1
2
3
4
5
6
7
8
9
10
11
12
|
# trim opening braces
find: /\s?{\s*$/
replace:
# and closing braces
find: /^\s*}/
replace:
# and rearrange your punctuation
find: /^([ ]*)([\w-]+):\s*([\w\d #%,\(\)/\.-]+);\s*$/
replace: $1:$2 $3
|
Then rearrange and simplify to your heart’s content. Haml+Sass makes developers happy.
Tags:haml·rails·regexp·sass·textmate
After failing miserably to scrape the craigslist blog and convert it into a feed it feels good to have won a round. I like reading the YCombinator news page but I don’t much like that there’s no way to see a preview of the articles in my feed reader. Just a headline, nothing else.
So I fixed that: http://github.com/JackDanger/hacker_news
Download and run that little Ruby script and you’ll have a working webserver spitting out an RSS feed for YCombinator’s news page and (assuming you have links) installed on your system you’ll even get the full article displaying in the feed.
I’ve got it running on a server at ‘news’ dot ‘9suits’ dot com. There’s no caching yet so go easy on it. Caching is working. Have at it: http://news.9suits.com
Tags:caching·github·hacker·hpricot·news·rss·ruby·scrape·ycombinator
My company, adPickles, needs to scale big. According to our estimates the site will have to serve pages into the series of tubes faster than the trucks will be able to carry them. Yeah. Something like 3-17 trucks per tube*. That fast.
So we’ve been keeping an eye on various types of caching. One of my favorites is simple method memoization of the kind we’ve all seen before:
1
2
3
4
|
def last_user
@last_user ||= User.something_Im_too_lazy_to_make_up
end
|
Which is great. As long as you don’t need that value to be updated for the life of the object it’s in that’s a solid way to prevent the value from being calculated twice.
But it can get pretty ugly when you have a multi-line method:
1
2
3
4
5
6
7
|
def current_advertising_balance
return @current_advertising_balance if @current_advertising_balance
amount_owed = Invoice.procces.something(:complicated) + OtherThing
amount_paid = Payment.procces.something(:complicated) + OtherThing
@current_advertising_balance ||= amount_owed - amount_paid
end
|
It gets the job done just fine but it’s a hack. You’re using @current_advertising_balance in three (3!) places in the same method.
I just came across this easier way. I don’t know where I found this but I haven’t been using it until lately and I’m really starting to like it. Check this out:
Update: Thanks to Mourad for pointing out the missing ’||=’
1
2
3
4
5
6
7
8
|
def current_advertising_balance
@current_advertising_balance ||= begin
amount_owed = Invoice.procces.something(:complicated) + OtherThing
amount_paid = Payment.procces.something(:complicated) + OtherThing
amount_owed - amount_paid
end
end
|
Whoah. I know. But begin…end is always used to catch errors, right? Well, apparently it’s also a great way to encapsulate any block of code. It has virtually no overhead, doesn’t pollute the namespace, and is easy to read. And you get your error handling for free:
1
2
3
4
5
6
7
8
9
10
11
12
|
def current_advertising_balance
@current_advertising_balance ||= begin
amount_owed = Invoice.procces.something(:complicated) + OtherThing
amount_paid = Payment.procces.something(:complicated) + OtherThing
amount_owed - amount_paid
rescue
0.0
ensure
Advertiser.mark_that_we_calculated_balance
end
end
|
- 1 truck per tube is equivalent to 1K requests per second
Tags:adpickles·begin·caching·end·memoize·rails·ruby·speedup
I heard from Jason Calacanis this morning that Craigslist has a new blog. The downside? No feed. A little searching turned up that Josh Catone (a great guy I met through RailsForum) pieced together a feed that just has the title and date of the blog entries for those of us who’re feed-reader dependent.
But I don’t want to have to visit the site, I just want it to appear in Google Reader like TechCrunch and RobotWalrus do. So I tried to piece together a ruby script that would scrape the blog and turn it into a feed. Conclusion? Total failure.
Here’s the code that
should work:
require 'rubygems'
require 'hpricot'
require 'activesupport'
require 'rss/maker'
require 'net/http'
blog = Hpricot.parse(Net::HTTP.get(URI.parse('http://blog.craigslist.org')))
main_table_cell = (blog / 'td').find {|td| td.attributes['width'] == '625' }
feed = RSS::Maker.make('1.0') do |rss|
rss.channel.about = "Craigslist Blog"
rss.channel.title = "Craigslist Blog"
rss.channel.description = "Craigslist Blog"
rss.channel.link = "http://blog.craigslist.org/"
(main_table_cell / 'a').select {|a| '' == a.inner_text }.each do |anchor|
intro = anchor.next_sibling
header = (intro / 'h2').first
date = Date.parse(header.next.inner_text.scan(/Posted (.*) by/).flatten.first)
author_link = header.next_sibling
comments_link = author_link.next_sibling
permalink = comments_link.next_sibling
contents = []
paragraph = intro
while paragraph = paragraph.next_sibling do
contents << paragraph.inner_html
end
contents << "<a href='#{comments_link.attributes['href']}'>Comments</a>"
item = rss.items.new_item
item.author = author_link.inner_text
item.title = header.inner_text
item.link = permalink.attributes['href']
item.date = date
item.description = '<p>' + contents.join('</p><p>') + '</p>'
end
end
puts feed
But the Craigslist blog has the least valid html I’ve seen since the 90’s. This script barfs out pretty quickly because of wildly inconsistent placement of <p>, <a>, and even <hr> tags. It would have to be a much bigger script to try to outsmart the CHTML (Crappy Hypertext Markup Language) on the craigslist blog.
So here’s my petition to Craigslist: let me read! Please! Even something so simple as wrapping each post in a <div class='post'> would fix everything.
Tags:blog·chtml·craigslist·feed·hpricot·invalid·josh catone·parse·rss·ruby·script·valid·xhtml
Ruby provides a handy shortcut for simple procs by letting you use a symbol:
[1, 2, 3].map {|number| number.odd? }
# => [true, false, true]
[1, 2, 3].map(&:odd?)
# => [true, false, true]
Despite a performance hit that occurs from converting the symbol to a proc it’s really useful and I use it in lots of production code when I only have to iterate through a loop a few times.
Today I was iterating through a few properties of an object and unconsciously used the inverse of the Symbol-to-Proc trick:
[ :name,
:type,
:amount_in_cents,
:created_at,
lambda {|record| record.class.superclass }
].map {|attribute| @item.send(attribute) }
Rather than specifying a symbol to perform a proc operation I felt like specifying a proc itself and just passing that into send. Surely that would get executed, right? Err… no.
I know passing an eval-able string (e.g. @item.send(‘class.superclass’) ) doesn’t work, so I figured I’d throw that lambda in there to calculate the name of @item’s superclass. No dice. But there’s a pretty simple workaround:
module ProcToSend
def self.included(target)
target.class_eval do
def send_with_proc_to_symbol(*args)
if args.first.is_a?(Proc)
args.shift.call(self, *args)
else
send_without_proc_to_symbol *args
end
end
alias_method_chain :send, :proc_to_symbol
end
end
end
Module.send :include, ProcToSend
Object.send :include, ProcToSend
Now this works:
Item.send Proc.new {|x| x.name }
# => 'Item'
But, like all hacks, I need to go refactor away the need for this.
Tags:proc·ruby·send
First Seth tossed out the idea and then Robby picked it up. There are things we don’t understand but could if we tried.
The more we learn the more there is yet to know – and here’s a list of things (in Ruby/Rails) that still baffle me:
- Mutexes
- RubyInline
- Memcache
- RSpec
- JSSpec
- Scaling on EC2
- How the Rubinius VM works
- Merb
- EventMachine
- When to use inline C
How about you?
Tags:rails·robby russel·ruby·seth godin
Slave to fashion that I am I just couldn’t help but drink the git Kool-aide. And because I’m so insecure about falling behind on the latest trend I thought I’d go ahead and try to patch Rails with some git plugin support.
Have you ever done this?:
$ script/plugin install http://svn.techno-weenie.net/my_code_is_prettier_than_your_girlfriend
Or (getting tricky) this?:
$ script/plugin install svn://svn.techno-weenie.net/seriously_this_stuff_is_gorgeous
Well, pretty soon you’re going to find yourself pasting this into a terminal somewhere:
$ script/plugin install git://github.com/technoweenie/sexy_code_fu
And you’re going to get a big fat error. Unless you comment on this ticket and help get it committed.
Why Git and legacy SVN apps work so well together
It can be pretty easy to clobber something installed via svn. If you’ve got a plugin and you overwrite the vendor/plugins/some_code/.svn directory your svn client is going to be totally befuddled. If you mangle the svn:externals property on that directory you’ll have an equally difficult time. So it’s natural you’d expect Git to hose your app. Not so.
If you’ve got an app that’s tied to subversion you can insert a Git plugin as easily as this:
$ git clone git://some_git_address vendor/plugins/my_git_plugin
What that does is like a ‘checkout’ of the Git plugin with full repository history into your app. You can now add all these files into subversion (svn add vendor/plugins/my_git_plugin) and forget that it’s even a Git app stored in there. And, when you want to update the plugin, it’s as simple as:
$ cd vendor/plugins/my_git_plugin
$ git pull
Why does that work? Because when you cloned it you got a .git directory in the plugin that manages all the Git information for you and remembers where the code needs to be updated from.
Now, there’s a downside to this. When you clone via git it creates a
FULL copy of all the history of that plugin in your app. This can be huge. But it’s not. Because we’re going to opt to keep this directory to a minimum with the “—depth 1” option when we clone (thanks to
evolving_jerk for the tip).
$ git clone --depth 1 git://some_git_address vendor/plugins/my_git_plugin
Now that .git directory is going to be tiny and manageable.
You’ve got nothing to lose! Check out all the lovely projects on Gitorious and GitHub for some fun ideas for plugins.
Tags:git·github·gitorious·plugins·rails·subversion·svn·trac
There’s something I really like about Ryan Davis: he publishes code. Lots of women and men write code but he actually makes it available to the world. Publishing changes the way you look at code you write. The temptation to cut corners and ignore cleanliness disappears when you know somebody else is going to rifle through it.
Publishing code is like planning to throw a party in your bedroom: it’s the fastest way to get rid of dirty laundry.
Note: I work for an incredible company called adPickles that has encouraged its developers to release code for public benefit. We hope our pioneering pays off for the Ruby community.
So how do you publish code? Specifically, how do you publish Rails code? Well, if you are asking yourself this question and you like bulleted lists THIS IS YOUR LUCKY DAY.
- Let someone else write it
Look around for existing plugins and gems. You don’t want to spend a weekend re-writing the gchart gem when you could have learned hang gliding or at least gone outside.
- Write the code
Whether it’s rhtml (or haml) view code, funky helper methods, controller filters, model stuff – whatever. Just write it and get it working inside your app. It doesn’t have to be perfect but don’t let it get too ugly. If it’s poorly written your app is going to suck and you’ll spend all your time fixing bugs rather than moving on to the next step.
- Extract the pieces that make sense together
Hopefully you started with putting the relevant code in their own methods. This is the time to grab all those methods together and put them into a module. Maybe even a module with different submodules that get inserted into different classes. Put this extracted code under a lib/ directory somewhere and require it with some low-level application file (in Rails I recommend config/initializers/application.rb)
- Try to abandon your code for a simpler alternative
Seriously, if this code isn’t absolutely necessary then now is a great time to delete it and make your app that much simpler. Writing code is good, deleting code is better.
- Make a plugin!
This part is very fun. Start with: script/generate plugin my_awesome_code and move your code into the vendor/plugins/my_awesome_code/lib/ directory. Now that this code lives outside your app you’re going to need to give it test cases of it’s own. This can be hard or it can be dead simple.
- Convert it to a gem
I just realized last week how easy this is. Install hoe and type ‘sow my_awesome_code’. Follow the instructions that get printed out and then just copy your plugin code to the new gem folder. Your code can function as both a gem and a plugin at the same time. A gem with an init.rb file is a plugin.
- Tell the world
Make sure your code is in svn or git somewhere. Post on your blog. Send out an email. Mention it on Twitter. Whatever. As long as you’ve got test coverage and the tests pass there’s no reason to be ashamed. Even if your code just spits out obscure Russian idioms it’s worth being very proud of.
This is the part where everybody sends you tons of money, people of your preferred gender battle for your attention, and your community thanks you. But the real payoff? The real payoff is that your app just got smaller. And that’s worth all the effort.
Tags:gem·hoe·plugin·publish·rails·ruby·rubyforge·testing plugins
Last September Ryan Bates, Kelli Shaver and I took 3rd place in the Rails Rumble with Admiteer.
Now that we’ve raked in our massive cash prizes, bought some yachts and sports cars and all three of us retired to a tropical island we’re open sourcing our app under the MIT license.
There’s been a number of folks interested in building off of what we started. It’s a great beginning for any event-oriented application and it’s extremely easy to modify. What we’re posting is precisely what we submitted to the contest. No improvements, no corrections.
Enjoy: Admiteer via SVN
Tags:admiteer·open source·rails·rails rumble
November 10, 2007 · 1 comment
Ted’s tackling one of our signup forms and finding himself in the middle of a surprisingly complicated part of our app: adPickles – Nested ActiveRecord Relationships
Tags:activerecord·adpickles·ted