Posts tagged with ruby
How to do rails tests when running with restful_authentication
Posted by Simon on May 26, 2009 at 01:18 AM
Categories: code, rails, ruby
The Restful Authentication plugin seems to be the standard right now, although I'm staring to wish I'd tried something else, maybe AuthLogic... because restful_authentication is kind of poorly documented. One serious error of ommission is how the hell do you update your tests so that you can run them on controllers that require a logged in user? Well, I have had the pain, and so you can have the quick answer, here it is.
Assuming you are using ActionController::TestCase ... first edit test_helper.rb:
# Add this helper function to test_helper.rb
# It will allow you to run any block under the aegis of
# a controller of your choosing. This is not something
# that is possibly by default
def run_with_controller( controller_class )
old_controller = @controller
@controller = controller_class.new
yield
@controller = old_controller
end
Now you can use that helper method in your test cases, in the setup function do this:
def setup
run_with_controller(SessionsController) do
post :create, { :login => "george", :password => "monkey" }
end
end
What you're doing here is simply making a POST to the Restful Authentication SessionsController to "create" a new session, pass in a login/password that exists in your fixtures. And that's it.
By the way, if you chose to install with RSpec because of the dire warnings that old fashioned tests will be out of date, but aren't currently using RSpec for you other tests, you should copy the rspec fixture data into test/ so that you get the right password hashes. Tricky...
Don't use porn in your slides at a tech conference
Posted by Simon on May 15, 2009 at 09:47 PM
Categories: tech, rails, theories, ruby
So Merb developer Matt Aimonetti made a presentation at GoGaRoCu heavily laden with soft-core pornography and some people got upset. In particular, a woman got upset—Sarah Allen. It didn't help that she was one of only six women at a 200 person conference. Holy shit!
Don't put sexual images of women in your slides. If you must do it, then put just as many sexual images of men in your slides. Be fair. Unless of course you're presenting to a club that only allows male members. Which in a way is what pisses off the women who are reacting to this incident—because it implies that Ruby/Rails/whatever is a men-wanted-only club.
If I had been there, I suspect I would have walked out. I've walked out of presentations, movies, plays, etc. for less. I have low patience no pride.
Why the lucky stiff posted a summary of women rubyist's reactions, and there's discussion aplenty there and elsewhere on the net. Hopefully the positive outcome will be a community that is more aware of issues that differ between men and women, and therefore has more women in the future.
The better way to code error-handling routines
Posted by Simon on April 17, 2009 at 12:50 PM
Categories: code, rails, ruby
There's an excellent guide to Rails 2 that I'm reading through right now, but I don't like this bit:
if @comment.save
redirect_to post_comment_url(@post, @comment)
else
render :action => "new"
end
It's much better like this:
render :action => "new" and return unless @comment.save
redirect_to post_comment_url(@post, @comment)
They do exactly the same thing, but mine is 2 lines instead of 5, and mine clearly eliminates the exceptional/error case first, and then leaves you to see the normal case. In fact, I virtually always code this way when I can, deal with the error cases first, and return, and then the rest of the function is an un-nested normal case handler.
Simplelog-X... is coming...
Posted by Simon on April 11, 2009 at 02:50 AM
Categories: meta, rails, ruby, simplelogx
So I just made some major updates to the software running this site, which for now I'm calling "Simplelog-X". And the source code for simplelog-x is now on github.
This will probably interest people who are running the original SimpleLog by Garrett Murray which he no longer supports (and incidentally it doesn't run on Rails 2.x). Simplelog-X also has quite a few other changes aside from working on (presently) Rails 2.2.2, all of the details are in the README.
I'm not going to annouce this loudly just yet because I still have a whole ton of my own site files in the public directory, and I need to move them to public/system I guess and get them out of the repo but still capistrano-friendly.
I was just making a lot of changes and it struck me how much more I know about Rails now than when I started this journey...
Wondeful joy! Mix Ruby and Objective-C with MacRuby
Posted by Simon on February 10, 2009 at 02:51 AM
Categories: code, mac, ruby
How great is this. You can use any language you want in your MacRuby projects. Want to add in some code you already wrote in Objective-C++? No problemo!
Check out the cool screenshot below which shows everything in action! (You might have to click on the full post view to get the picture).
Also, you might be wondering how to get full bi-directional bridge going on between Ruby and Objective-C/Cocoa. In otherwords, how do you call/access/include/import/require your own Objective-C classes from Ruby and your Ruby objects from Objective-C? It's actually done sort of automagically. If you have a Ruby class Foo and a ObjC class Bar...
(You seem to have to use NSClassFromString(@"FooController") to avoid getting a linker error. Apparently the bridge is not yet running at compile/link time. Also, you (seem to) have to type your bridged objects to id in ObjC, although maybe there's a way around both those limitations that I don't know about yet.)
# Foo.rb
class Foo
def foo
puts 'FOO'
bar = Bar.new
puts bar.bar
end
def baz
return "BAZ"
end
end
// Bar.h
#import <Cocoa/Cocoa.h>
@interface Bar : NSObject {
}
- (NSString*)bar;
@end
// Bar.m
#import "Bar.h"
@implementation Bar
- (NSString*)bar; {
id foo = [[NSClassFromString(@"FooController") alloc] init];
NSLog(@"%@", [foo baz]);
return @"BAR";
}
@end
And you will get this output when you call Foo.foo:
FOO
2009-02-10 02:46:28.017 Untitled[37257:10b] BAZ
BAR
Boing boing boing! Enjoy!!!!
iphone objective-c pain ... give me ruby, bastards!
Posted by Simon on January 11, 2009 at 02:37 AM
Categories: code, mobile, ruby
I'm writing an iPhone app in Cocoa Objective-C and really wishing that I could be writing it in Ruby instead (cocoa-ruby on iphone anyone?).
Take this one line:
if( ! [foo isEqualToString:bar] ) {Oh yeah, I can really tell what that does by glancing at it... not! But here's the equivalent ruby:
unless foo == bar
Shorter, more obvious, easier to write, easier to read and maintain. Why does Cocoa need to be so wordy? Even [foo equals:bar] would be better. But no, that would be too ... ambiguous. I don't know. isEqual: is defined in NSObject but who knows what highly dangerous or unexpected result it would have between two strings. Maybe it would only be true if they were the same pointers, or copy: results. No one in ObjC land seems to be thinking about making syntax easier for the programmers, do they? And why don't we have overloading???
For example, if we had method overloading, you could easily have different versions of equals: (or isEqual: if you must) that handle NSString*, NSWhatever*... of course this would require the compiler to be smarter, and the runtime to be smarter, and who uses Objective-C other than Apple ... no one, that's who. Hey, here's an idea—why not ditch ObjC wholesale for Ruby? It's not like Ruby is going to be, you know, slower. Well, of course it will be slower in the C sections.
At least now with "Fast Enumeration" in Objective-C 2.0 we can do something like this:
for( NSString * bird in [NSArray arrayWithObjects:@"owl", @"parrot", @"partridge", @"pigeon"] ) {Although again, it's really annoying that there's no convenience syntax for creating an array. How hard would it be? Not very hard, that's how hard. In Ruby it would be simply:
for bird in ["owl", "parrot", "partridge", "pigeon"]
At least we have convenience syntax for NSString * animal = @"Bandicoot" right? I suppose we should pray at the altar for that one and hope they never take it away...
Finally, I have to kiss good-bye to any kind of interesting use of first-order functions and closures. Technically Objective-C CAN actually do dynamic function calls at least, but it's so bloody wordy that I rarely use it unless forced to. I can't use the lovely map function in Objective-C which adds considerably to my line count.
But wait, Apple is adding closures? ... why don't they spend the effort on making Ruby Cocoa better instead? Or maybe give me garbage collection on the bloody iPhone.
Ruby on Rails Feed/RSS Aggregator (35 lines)
Posted by Simon on December 07, 2008 at 03:17 AM
Categories: code, rails, ruby
I wrote myself a feed aggregator for my front page. And... voila! I'm finally satisfied with it to post it.
Update: I've now published this as a complete standalone rails app on github/sbwoodside/portal. The important bits are app/controllers/portal_controller.rb and config/config.yml.
For me I run this as a standalone rails app, separately from my weblog. You could do that (and redirect requests to / or /index.html with Apache or nginx/etc. Or you could integrate it into your own app. Up to you.
Features:
- Will aggregate ANY feed, no matter how badly mangled by the creators, using FeedTools (I also tried feed_normalizer and simple rss but they're not as good)
- Deals with slowness of downloading feeds, RSS, etc., and REXML by caching
- Deals with need to recache using elegant http/cron periodic system
- Display the feeds in a facebook-like news feed format, sorted by dated.
- You can easily re-label the feeds, add and renew feeds (in the code)
- Only 35 lines of controller code!
The heart of it is the controller, obviously. The best thing? It's only one page of code! Ruby rocks!
require 'feed_tools'
class PortalController < ApplicationController
layout 'site'
# Instructions: 1. Change @@secret. 2. Add a cron job to regularly call /?recache=yes&secret=XXXXXXX
# This is a feed aggregator that uses FeedTools because it handles practically any feed.
# But FeedTools is super slow in every way so this aggregator stops using it as soon as possible.
# TODO add XML feed output
@@secret = "change_this" # change this to protect your site from DoS attack
# The array of feeds you want to aggregate. If you change this then manually delete the whole cache.
@@uris = ['http://simonwoodside.com:8080/posts/rss', 'http://simonwoodside.com/comments/rss',
'http://semacode.com/posts/rss',
'http://api.flickr.com/services/feeds/photos_public.gne?id=20938094@N00&lang=en-us&format=rss_200',
'http://api.flickr.com/services/feeds/activity.gne?user_id=20938094@N00']
# A map between the "official" feed titles in the XML, and the titles you want to show when rendered.
@@title_map = { "Simon Says" => "Simon Says:", "Simon Says: Comments" => "Simon Says comment:",
"Uploads from sbwoodside" => "Flickr picture:", "Semacode" => "Semacode blog post:",
'Comments on your photostream and/or sets' => 'Flickr comment:' }
def index
if params[:recache] and @@secret == params[:secret]
cache_feeds
expire_fragment(:controller => 'portal', :action => 'index') # next load of index will re-fragment cache
render :text => "Done recaching feeds"
else
@aggregate = read_cache unless read_fragment({})
end
end
private
# This will replace cached feeds in the DB that have the same URI. Be careful not to tie up the DB connection.
def cache_feeds
puts "Caching feeds... (can be slow)"
feeds = @@uris.map do |uri|
feed = FeedTools::Feed.open( uri )
{ :uri => uri, :title => feed.title,
:items => feed.items.map { |item| {:title => item.title, :published => item.published, :link => item.link} } }
end
feeds.each { |feed|
new = CachedFeed.find_or_initialize_by_uri( feed[:uri] )
new.parsed_feed = feed
new.save!
}
end
# Make an array of hashes, each hash is { :title, :feed_item }
def read_cache
@@uris.map { |uri|
feed = CachedFeed.find_by_uri( uri ).parsed_feed
feed[:items].map { |item| {:feed_title => @@title_map[feed[:title]] || feed[:title], :feed_item => item} }
} .flatten .sort_by { |item| item[:feed_item][:published] } .reverse
end
end
It's actually pretty simple but it took me a while to get the balance just right. What you need to do is set up a cron job or other repetitive task that does an HTTP load on http://mywebsite.com/?recache=yes&secret=XXXXXXXX ... every once in a while. You can use wget or curl, or whatever. You might want to recache every minute, five minutes, hour, whatever. Since it's done as a part of the controller there's no nonsense about running backgroundRB, RubyCron and all the other nonsense at HowToRunBackgroundJobsInRails. Yay!
Here's the view:
<div id="feed-stream">
<% cache do %>
<%
lastday = -1
@aggregate.each do |item| %>
<div class="item">
<%
mydate = item[:feed_item][:published].getlocal
if mydate.yday != lastday
%><div class="item_details"><p style="text-align:right"><%= mydate.strftime('%A, %B %e') %></p></div><%
lastday = mydate.yday
end
%>
<div class="item_content">
<%= item[:feed_title] %>
<a href="<%= item[:feed_item][:link] %>"><%= item[:feed_item][:title] %></a>
</div>
</div>
<% end %>
<% end %>
</div>
My cache is all Hashes. I don't cache the FeedTools object because I discovered that even after FeedTools has parsed your feed, accessing the supposedly "final" data is incredibly slow (like maybe 10x or 100x slower than a hash).
Here's the model:
require 'feed_tools'
class CachedFeed < ActiveRecord::Base
validates_presence_of :uri, :parsed_feed
validates_uniqueness_of :uri
serialize :parsed_feed, Hash # note that if this exceeds a certain KB size, it will likely fail (thinking it's a String)
end
And the migration:
class CreateCachedFeeds < ActiveRecord::Migration
def self.up
create_table :cached_feeds do |t|
t.column :uri, :string, :limit => 2048
t.column :parsed_feed, :text, :limit => 128.kilobytes # use for serialized object
t.timestamps
end
end
def self.down
drop_table :cached_feeds
end
end
Well, that's all you need. When I started out to make this I thought I'd find a simple example out there but there wasn't anything. It turns out that there's a number of interesting challenges — picking a parser to deal with difficult feeds, XML, and malformatted XML... to deal with caching ... to deal with background processing. Took me a while to get it all just right.
It powers my own front page ... consider to be under standard ruby open source license. As the vending machine says: Share And Enjoy!
Hi there.
Well, I'm back. I was running this site on really ancient technology — AxKit — so 2001. Now I'm running it on modern technology, i.e. Rails 2. And doesn't it rock. Now I have a cool GUI editor to type into, I have easy programming in ruby, and I have of course polished both my design and my CSS/XHTML skillz considerably in the mean time, hopefully making this all easier to look at and navigate.
So I'm running on SimpleLog here, but it's not "stock". Oh no. Stock SimpleLog right doesn't run on Rails 2, but this one does. Also, I made it even MORE simple than it used to be:
- Support Rails 2.0 (no need to freeze an old rails)
- no themes—annoying to use anyway, and no one was publishing themes either
- replaced the editor/preview panel with WYM on Rails, which is by FAR the best WYSIWYG / GUI editor I've ever found, and the end of a long search for me
...and so on.