layout false if request.xhr?
Rather simple actually… since ActionController::Base.layout takes a proc, and calls it upon a request.
class ApplicationController < ActionController::Base
def self.without_layout_on_xhr
layout proc { |controller| controller.in_popup? ? nil : "application" }
end
def in_popup?
request.xhr?
end
end
class BusyWorkController < ApplicationController
without_layout_on_xhr
before_filter :login_required
def new
@work = Busy.new
end
endI’ve abstracted out the conditions for rendering without a template based on the controller instance method #in_popup?. Feel free to change it to whatever you want.
Pathname of the Righteous 2
Lately, I've noticed a lot of scripts taking advantage of File, Dir, FileUtils and even FileTest. Nothing odd, and these are fully warranted for most. They get the job done just fine, end of story.
But Ruby is special. Like little yellow bus special. It's about these things called Object instances, not Class/singleton methods. Maybe you'll agree with me, maybe you won't... but I'm sure you'll enjoy reading more about what Ruby is packaged to offer.
The Golden Child
Pathname is what this article is about. It's part of the standard library which comes included with MRI's Ruby distribution. You might have seen Pathname used like this...
require 'pathname'
$: << Pathname(__FILE__).dirname.join('lib', 'bacon', 'bits').to_s...which is normally this...
$: << File.expand_path(File.join(File.dirname(__FILE__), 'lib', 'bacon', 'bits'))BTW: $: is called the "load path", I'm sure you've seen plenty of errors about it... read here
This is because Pathname represents a full file name path in your filesystem. Only as a Ruby Object instance. See what I mean? Using Ruby this way just makes you want to rub your tummy like Santa after cookies and milk.
Alil Deeper
Most people stop there. They say to themselves, "Man, that looks great but you're not really saving lines of code. I mean, you just daisy chain method calls and every time I use this I have to explicitly require it".
Thinking this way is just wrong, because "All functionality from File, FileTest, and some from Dir and FileUtils is included, in an unsurprising way. It is essentially a facade for all of these, and more".
What does this mean? Well, when you find some crazy file handling method...
def some_files
@file_list = Dir.entries(DOC_PATH)
@file_list.reject! { |entry| entry =~ /^\./ }
@pretty_file_list = []
@file_list.each do |file|
file_path = File.join(DOC_PATH, file)
if ! File.directory?(file_path)
tmp = {}
tmp[:name] = file
tmp[:date] = File.ctime(file_path)
tmp[:size] = sprintf("%.2f", (File.size(file_path) / 1.0.kilobytes)) + " kb"
@pretty_file_list << tmp
end
end
@pretty_file_list.sort! { |x,y| x[:date] <=> y[:date] }
endYou can refactor it like so...
def some_files
@file_list = Pathname(DOC_PATH).children.
delete_if(&:directory?).
sort_by(&:ctime).inject([]) do |file|
{
:name => file.to_s,
:date => file.ctime,
:size => sprintf("%.2f", (file.size / 1.0.kilobytes)) + " kb"
}
end
endPersonally, I would just pass around the actual Pathname instances without building this hash because Pathname provides an immense amount of convenience for handling files wrapped into one library.
The library uses this example...
require 'pathname'
p = Pathname.new("/usr/bin/ruby")
size = p.size # 27662
isdir = p.directory? # false
dir = p.dirname # Pathname:/usr/bin
base = p.basename # Pathname:ruby
dir, base = p.split # [Pathname:/usr/bin, Pathname:ruby]
data = p.read
p.open { |f| _ }
p.each_line { |line| _ }With Strings
Just remember that Pathname returns instances, not Strings. So methods that use #to_s will work like normal.
puts Pathname('.').expand_path
=> "/home/justin/Kinetic/articles"
p Pathname('.').expand_path
=> #<Pathname:'/home/justin/Kinetic/articles'>You can even concatenate String's to Pathname's to build a path, as you would between normal strings.
p1 = Pathname.new("/usr/lib") # Pathname:/usr/lib
p2 = p1 + "ruby/1.8" # Pathname:/usr/lib/ruby/1.8
p3 = p1.parent # Pathname:/usr
p4 = p2.relative_path_from(p3) # Pathname:lib/ruby/1.8
pwd = Pathname.pwd # Pathname:/home/gavin
pwd.absolute? # true
p5 = Pathname.new "." # Pathname:.
p5 = p5 + "music/../articles" # Pathname:music/../articles
p5.cleanpath # Pathname:articles
p5.realpath # Pathname:/home/gavin/articles
p5.children # [Pathname:/home/gavin/articles/linux, ...]However, you will need to call #to_s. For instance, when adding to the load path array, which normally contains expanded path strings.
From up top, notice the #to_s...
$: << Pathname(__FILE__).dirname.join('lib', 'bacon', 'bits').to_sBut when you're requiring you won't need it.
require Pathname(__FILE__).dirname.join('lib', 'bacon', 'bits')In Conclusion
So that's really it... when not to use Pathname? Well, you will want to check to make sure that alternative Ruby implementation support it. Classes like File and Dir definitely will be supported by many, but Pathname might not. Also, if you believe you'll be doing major binary work, heavy File CRUD or possibly need to extend File writing operations... please don't use this. I also believe Windows support has gotten better in Ruby 1.9.1, but its a little flacky in older versions. But who uses Windows?
Simple stuff really, but I hope to see more of you using this instead of all those File classes and their class method calls. Even when you need to do very few things with Pathname, just require it, because I'm sure you'll find some use for it later!
when timeouts aren't timeouts 2
Last Friday, a good chunk of our team spent a while debugging a problem afflicting some sites on one of our shared hosting machines.
After a bunch of red herrings, some TCP dumps determined that the sites in question were all hung up on numerous DNS requests. That led us to the call to a web service we use on the sites in question. As it turns out, the IP geocode service being called, HostIP.info, was completely down. (It’s free; you get what you pay for.)
We assumed this couldn’t be the case, since those calls are wrapped in a Ruby Timeout calls with a maximum of 2 seconds each (Timeout.timeout(2)). Well, apparently, Ruby Timeout just doesn’t work sometimes.
Big props to Andy for sleuthing that down. And bigger props to Philippe Hanrigou for writing that very detailed article where he says, specifically:
“In particular, initiating network connections and/or a broken or slow DNS server will typically block the whole Ruby process while the call completes.”
Bingo! We’re going to try Philippe’s library on these projects and others in the future when we need guaranteed timeouts.
Ruby 1.9.1, The Stuff It Gives Us
We’re still kicking the Ruby hardcore. We’ve just been massively busy locked in the Kinetic Barn HQ this winter. Anyway, here is a list of the latest things we’ve found interesting about the first stable 1.9.x release of MRI Ruby.
Note: Some of these have been in the 1.9 branch for awhile, I’m just taking the moment to get you all acquainted.
Newlines allowed before ternary colon operator (:) and method call dot operator (.)
Before this you might note that you could do this in Ruby, and it was valid.
Person.
set_first_name('Ben').
capitalize.
to_a
# or...
dog = Person.first_name == 'Ben' ?
'Cookie' :
'pookie'You won’t see the former very often, but you can do it if you keep to coding standards of 80-100 columns of text in your source.
I’ve seen and used the latter numerous times, however I usually don’t put a newline after the colon. Eithercase, Ruby makes things a bit more clearer in 1.9 (though these are bad examples of their use cases).
Person
.set_first_name('Ben')
.capitalize
.to_a
# or...
dog = Person.first_name == 'Ben' ?
'Cookie'
: 'pookie'Kernel#methods and #singleton_methods used to return an array of strings but now they return an array of symbols.
I use this daily within Irb to see what methods are in my objects. You used to receive an Array of Strings, of all the methods on the object you ran this against. Now you will get this.
>> String.methods.sort
=> [:, :, :=>, :, :, :, :, :, :__id__, :__send__, :allocate, :ancestors, :autoload,
:autoload?, :class, :class_eval, :class_variable_defined?, :class_variables, :clone,
:const_defined?, :const_get, :const_missing, :const_set, :constants, :display,
:dup, :eql?, :equal?, :extend, :freeze, :frozen?, :hash, :id, :include?,
:included_modules, :inspect, :instance_eval, :instance_method,
:instance_methods, :instance_of?, :instance_variable_defined?,
:instance_variable_get, :instance_variable_set, :instance_variables, :is_a?,
:kind_of?, :method, :method_defined?, :methods, :module_eval, :name, :new,
:nil?, :object_id, :po, :poc, :pretty_inspect, :pretty_print, :pretty_print_cycle,
...]If you know anything about Ruby low level you might guess someone got smart and decided it was better to utilize the Symbol table instead of building these lists of anonymous String instances.
Hash; preserving item insertion order
Yes, Hashes are now ordered… which is pretty odd if you’ve been writing Ruby for awhile now. We all used to utilize Arrays, and I hope we still do. But Hashes are now ordered by the pattern in which you insert keys/values into them.
[1,2,3,4,5].inject({}) { |hash, value| hash["name_#{value}"] = value; hash }
# Ruby 1.8
=> {"name_4"=>4, "name_5"=>5, "name_1"=>1, "name_2"=>2, "name_3"=>3}
# Ruby 1.9
=> {"name_1"=>1, "name_2"=>2, "name_3"=>3, "name_4"=>4, "name_5"=>5}As a community, let’s not go nuts and start replacing our usage of Array’s with Hashes when we needed to persist order. I’m fairly certain the speed of an Array will always trump a Hash.
Named/Coroutine style Regexp
Dave Thomas can give you a better description than I can…
sentence = %r{
(?<subject> cat | dog | gerbil ){0}
(?<verb> eats | drinks| generates ){0}
(?<object> water | bones | PDFs ){0}
(?<adjective> big | small | smelly ){0}
(?<opt_adj> (\g<adjective>\s)? ){0}
The\s\g<opt_adj>\g<subject>\s\g<verb>\s\g<opt_adj>\g<object>
}x
md = sentence.match("The cat drinks water")
puts "The subject is #{md[:subject]} and the verb is #{md[:verb]}"Dir.exists?/.exist?
This one annoyed quite a few people, because before you had to do File.exists?(”/tmp”) to test the existance of a directory. Odd, but fixed.
Negative operators such as ”!”, ”!=” and ”!~” are now overloadable
OOOHHH… more fun for the DSL writers out there! dm-core, I’m looking at you! :)
Rake and RubyGems Made Core
Both Rake and Rubygems are now included in the core library. Hopefully it’s still as easy to upgrade them both!
Thats Not All
Of course this isn’t it, and the entire list is a great read. If anyone else has anything feel free to post… like the difference between proc and lambda now, new Hash key syntax {apple: 1, pear: 2} or anything else… feel free to comment.
Another Sinatra app: RadioPlaying.com 3
I made this last night and early this morning in about an hour and a half:
I’ve been listening to Philadelphia’s Radio 104.5 of late. And since I can never remember song titles, I thought it was nice that they have a page on their web site that lists the last 10 songs played. Unfortunately, it’s not very mobile-friendly. So when I’m stuck in traffic, and want to check what just played, I can use this little site.
All it does is scrape their site with Net/HTTP and Hpricot and outputs the results in an extremely bare-bones version.
Andy likes it, too, and wants to add more stations so who knows what will become of this little app. But, for now, it’s a handy tool I’ll use myself.
Attributr background style
I love incremental improvement.
New in Attributr—you can now choose the background style for your attribution overlay. The original black/white on transparent is still there but I’ve added white on black and black on white options for photos where that is more appropriate.
There are several more ideas I want to flesh out such as:
- Returning other sizes besides the default Flickr Medium.
- Being able to position the attribution in someplace other than the bottom right.
- Choosing different font styles. For some reason, the Coolvetica font I’m using looks a lot jankier when rendered on the servers than when it’s rendered from my local copy running on OS X.
- Providing a list of more comprehensive attribution of all the photos you request so that a “credits” page can be pasted into your presentation, website, blog, whatever.
