Cobalt Edge

 
Filed under

Ruby

 

My Setup and Software

I too read Al3x's interview the other day, and like John Nunemaker, figured I'd share my setup, as I enjoy reading what others use and often can pick up a few interesting tools or tidbits.

Unlike Mr. Nunemaker, my desk is too messy, IMHO, to photograph right now :) However, many similarities aside from that. On with it...

I use a 17" MacBook Pro with 4GB RAM as my only machine these days. Like Alex and John, I really like having just a single machine, and I no longer work for a corporation where I'd worry about that. DealBase is cool and wouldn't try to make some wacko claim to some work not relevant (and we've explicitly discussed my use of a single machine, etc.). I have my MBP open on a laptop arm from Ergotron, and then my primary monitor is a 30" Dell. Really love the big monitor. I do my main work o the 30", and then the laptop screen has TweetDeck, iChat, Things, some Fluid apps, and other things that I tend to more glance at, and aren't primary work items.

Further, I use a wireless Apple keyboard, and like John, I just love this thing. I can't tell you how long I'd been looking for a keyboard that was just a keyboard (but with arrow keys). I hate normal keyboards that take up so much extra space on the right side (my mouse side) with stuff I rarely use - which only exacerbates problems with having my arm/elbow canitlevered further out to use the mouse, sometimes causing arm strain after long days of coding. I use Logitech MX Revolution cordless mouse, which I like quite a lot.

Transitioning to music... I use JBL Creature speakers, and listen to a variety of things, or nothing. Pandora, via a Fluid app, iTunes (my own playlists, or various Ambient "radio" stations), etc. Either that, or we have a whole-house NuVo Concerto audio system, so sometimes I have that on either with XM satellite radio, or to a playlist from the iPod we have hooked into it. The NuVo setup is nice because it fills my office with sound a bit better (via in-ceiling speakers), but I have more variety via the computer.

As with Alex and John, I am absolutely in love with my iPhone 3G. It is even better than expected. It has essentially replaced my 80GB iPod in the car, typically because it's more up to date, and I like it's UI better; I can remotely work on servers if I have to via iSSH, play games if I'm bored, use InstaPaper to read things I've set for reading later, sync with Address Book and iCal, and of course Twitter, via Tweetie. So, yes, I use Apple's Address Book and iCal, for great sync, simplicity, etc.

Ok, onto dev stuff. My primary work is on Rails-based web-apps, although I dabble with other things as well. DealBase is my day job, and I'm also involved with Bring Light.

Yet again, like Alex and John, I spend the bulk of my time in TextMate, iTerm (a better Terminal, IMHO), and Safari. And actually, I do my development testing in nightly builds of WebKit/Safari, and all my other browsing in standard Safari. I do pull up Firefox for testing, and to use YSlow and sometimes Firebug (although I've been finding the dev tools in WebKit nightlies work well). I've used Emacs - did so for about a year when working with Linux as my desktop. I ditched it back then in favor of Visual SlickEdit, but these days TextMate just rules. I don't get the Emacs passion - why do you want to press two keys for everything, especially the most common things? Yes, I know, you can setup different bindings, etc., but come on the most basic things like saving, opening, copy, paste, etc. should be "single" key (and by single I mean some meta+key) strokes by default. I do fire up vi all the time at the command line on remote servers, and even occasionally on my MBP for some real quick edit. Also, I spend the bulk of my day in my text editor, so yes, appearance matters, and TextMate kills others. I've also used a lot of IDE's in the past, from IDEA, to Eclipse, to Visual Studio. Visual Studio is actually quite good if you have to suffer in that world, but I find Eclipse just plain crappy. IDEA was great for Java, and their Ruby setup will be something to keep an eye on, but generally, the setup I have now works well.

I have all my code for nearly everything I do (e.g. both private and open source/public) on GitHub, and truly love it. Git has been a huge win, and gives me the best of, as well as improving SVN and Perforce. I'm using GitX for most of my commits and history browsing these days.

I use RSpactor for continuously running our RSpec suite, and we also use RSpec stories (but haven't converted to Cucumber yet). I recently added speech output to RSpactor, and that is my preferred notification instead of Growl. We use Pivotal Tracker for tasks/stories/features as well as bug tracking. We used to use Lighthouse, but having it all in one place was nicer, and Tracker wins big time in my opinion. If you want GitHub post-receive hook for Tracker, I recently whipped that up, and its been a real nice addition. We too use Hoptoad for exception notification, and really like it. Also, New Relic is in use at DealBase. I also like viewing Google Analytics with Analytics Reporting Suite, a slick AIR app.

I really like Navicat as a GUI for database stuff. It's proprietary/pay software, but honestly, it's worth it to me. I can do all this stuff command line fine, but the GUI simply makes it a heck of a lot faster to view the results, quickly re-sort on a column, mess around with queries, etc. Also, it has great SSH support, so I can tunnel into all my server's DB's with ease.

I have CruiseControl.rb setups for all my Rails apps, and make use of CCMenu for a nice little status menu item showing me what's going on with those.

I pretty much can't live without LaunchBar. Same goes for 1Password.

Skitch is quite handy for showing sharing and annotating screen shots, and we use Google Docs and Gmail. Speaking of email, I am a huge fan of Mailplane, which is a Mac app for Gmail. Integration is superb, and I can quickly switch around my 15 or so Gmail accounts with ease. I find it superior to a Fluid app for Gmail, since the integration is better and it handles multiple accounts.

I host most of my own web apps on Slicehost, and DealBase is at EngineYard.

I also use Backpack some, although not nearly as much as I used to, and access it about 99% of the time via Packrat. MarsEdit is my blog authoring tool of choice. NetNewsWire is my RSS reader.

All of my photography and photo processing, etc. are done in Adobe Lightroom. I use the Flickr plugin for it as well.

Various other bits:



  • TextPander

  • WeatherDock

  • Pukka

  • Flickr

  • Del.icio.us

  • xScope - a great screen ruler app

  • Photoshop CS3 (look for my name in the about box too :)

  • JungleDisk - I do some backups with this

  • SuperDuper! Still my favorite backup, although I use TimeMachine too

  • CSS Edit and XyleScope sometimes

  • Last.fm - is running all the time, but I really don't actually make use of it, kinda silly.

  • Acrobat Pro and Reader

  • XCode (or TextMate) if I'm working on an Objective-C/Cocoa app.

  • iStat menus

  • YouControl Tunes

  • p.s. One other bit I can't live without but really isn't computing hardware/software, is my espresso setup. I use an Expobar Brewtus II machine, Macap MC4 stepless doserless grinder and a variety of cups (mostly Nuova Pointe and Illy). I use only totally fresh beans from a variety of places (favorites include Blue Bottle, Ecco Caffe, PT's, 49th Parallel (unfortunately not often, since shipping from Canada makes it a bit cost prohibitive), etc.). Coffelab tamper and Bumper stand and knock box. My espresso bar is kept clean (unlike my desk). The pictures are a bit older, so don't show bottomless portafilter in use these days.

    Whew, that's more than plenty. What's your setup?


    Loading mentions Retweet
    Filed under  //   ContinuousIntegration   CruiseControl   DealBase   environment   Espresso   git   Gmail   iPhone   laptop   Mac   Nuvo   Office   Pivotal Tracker   Rails   RSpactor   Ruby   TextMate  

Comments [0]

Speech / Talking Results for RSpactor

After discovering that autospec was taking up a lot of CPU while it was "idle", I looked for alternatives. I found RSpactor, which doesn't take as much CPU, and is better since it's a dedicated window with nice GUI results and so. My only gripe was that I'd gotten used to the spoken results output I'd rigged up for Autospec.

I really prefer the spoken results because it is not visually distracting, and doesn't require me to be paying attention to the area of the screen where they pop up (I use a 30" monitor, plus the 17" laptop monitor, so I'm not always looking at the right spot for the Growl notices, and I don't like the monitor-wide growls). Lucky for me, RSpactor is open source and is up on GitHub.

I thought it was an Objective-C Cocoa app, but as it turns out it's a RubyCocoa app. I'd built RubyCocoa apps before, so was familiar with that, plus of course know Ruby. It wouldn't have mattered either way (I'm fine working in ObjC as well), but this did make things a slight bit faster.

Anyway, did a quick bit of work and got a new preferences panel for Speech added, and then rigged that up to test results, so that I now have my desired spoken results. A slight improvement comes along in that it (optionally) speaks the number of passing/failing/pending tests - just insert a question mark in the string/phrase you want spoken for each and it'll say the number at that spot.

I've sent a pull request to RubyPhunk, but no guarantees it will get added to the main line. In the mean time, if you're interested, grab it from my RSpactor fork on GitHub. Update: RubyPhunk integrated my changes into the main RSpactor code.

Loading mentions Retweet
Filed under  //   ContinuousIntegration   RSpactor   RSpec   Ruby   RubyCocoa   Testing  

Comments [0]

GitHub Post-Receive Hook for Pivotal Tracker

Over the holiday, I whipped up a quick GitHub Post-Receive Hook for use with Pivotal Tracker. This is just a small web service, implemented in Sinatra. It was my first time using Sinatra, so any suggestions on improvements are of course welcome (as are they in general, this is open source). I've put the code up on GitHub in the somewhat painfully named tracker_github_hook repo.

The service supports multiple GitHub repos and Tracker projects, so you can run a single service that integrates multiple projects. The service will figure out which commits go to which projects based on a config file on the server that associates a GitHub repo URL (make sure to use the http version of the URL, not https), to a Tracker project ID. For example:


tracker_github_hook:
github_url: http://github.com/chris/tracker_github_hook
tracker_api_token: a1234b56789c0defa12b3c4def56a78b
tracker_project_id: 123

You will need to take care of running the service within your particular server setup. I'm personally running it via Thin/Rack, behind Nginx. I have it setup on the same server that runs our continuous integration system, so these two are differentiated by subdomain.

It should be noted, I will not claim this thing is secure. You run it at your own risk, etc.

Aside from getting the service running on your own server, you'll need to add the URL to it as a GitHub post-receive hook for each project you want to integrate. To do that, go to the Admin tab of your GitHub repo, and then the Services tab. At the top you'll see where you put the URL in. The URL is just the root of the service. Also see GitHub's docs on post-receive hooks as it illustrates just how I built this, how to set it up, etc.

Hopefully others find this useful. Or, what I really hope is that the Pivotal guys get with the GitHub guys and add a standard integration service, where it's automatically configured on the Tracker side, and you just need to turn it on on the GitHub side much like the other service integrations.

Loading mentions Retweet
Filed under  //   git   Pivotal Tracker   Ruby   Sinatra  

Comments [0]

Mocked by Default, but Unmocking in Some Cases with RSpec

Uh, ya, another great blog title, but we'll get over it. We use geocoding in our app, and that's a relatively costly operation time wise, especially when you may be doing it hundreds or thousands of times when your test suite runs. I can't stub out the objects that use it in many cases, so I wanted to stub out the actual geocode call unless I truly needed real geocoding (which is only when I'm testing the actual geocoding itself, and thus is a very small part of the test suite).

We use RSpec Specs and Stories and I wanted to mock out the geocoding by default, but unmock it in a few places. I asked about this on the mailing list, Googled and so on, but didn't find a solution that was working. So here is what I wound up doing...

In my spec_helper.rb file, I added:


Spec::Runner.configure do |config|
config.before(:each) do
# Setup fake geocoding unless told not to
unless @do_not_mock_geocoding
fake_geocode = OpenStruct.new(:lat => 123.456, :lng => 123.456, :success => true)
GeoKit::Geocoders::MultiGeocoder.stub!(:geocode).and_return(fake_geocode)
end
end
end

What this does is mock the geocoding unless a test has set the @do_not_mock_geocoding variable to true. One caveat, at least from what I've found, is that you need to set that to true in a before(:all) block in your tests, so that it happens before the before(:each). This is minor, as you can just have something like:


describe "with real geocoding" do
before(:all) do
@do_not_mock_geocoding = true
end

# your tests that want real geocoding
end

The impact this has had on our test suite is tremendous. I had already had some partial mocking of the geocoding in place, but was sweeping the system to put it in because the time it took to run our test suite was out of hand at about 13 minutes! Now that I've got this in, it runs in 2 minutes! Geocoding is used in two of our most core objects, which is why it has such a big impact on the test suite. This is one place mocking has really proved to be a massive value!

Loading mentions Retweet
Filed under  //   RSpec   Ruby   RubyOnRails   Testing  

Comments [0]

Use the Lighthouse API for Mass Ticket Changes

Lighthouse is a nice issue tracker/bug database. We're using at at DealBase and some of my other projects. However, one thing it doesn't have, is a way to set the state of a bunch of tickets at once, or assign a slew of tickets to a certain user, etc. I need to do this regularly, as anytime we do a deploy to our staging environment, I need to go mark any bugs that were set to 'fixed' to now have a state of 'deployed', and then assign them to someone else to be verified. Lighthouse API to the rescue...

You can whip up a quick Ruby script to automate operations like this. The following is the script (tweaked to protect the innocent) I use to mark all tickets that are set to "fixed" to now be "deployed" and assign them to another user for verification. Obviously you can change this pretty easily to affect various other changes. I recommend playing with the Lighthouse API in IRB to see what the attribute names are for Tickets and so on.


It appears Blogger is messing with the GitHub Gist formatting, so click the "view raw" link for a more readable version.

Replace the various values as indicated, and you're off and running. Note, the Lighthouse API is not a RubyGem or such, so you need to just clone it or download it and then store it somewhere and adjust the require at the top of the script accordingly.

Loading mentions Retweet
Filed under  //   Ruby  

Comments [0]

Setting up CruiseControl.rb with/for Git Based Projects

[Updated to refer to official ThoughtWorks CC.rb Git repo.]

I have a new Rails project I'm working on and I use Git/GitHub for source control. It was time to setup continuous integration, and my usual weapon of choice for that is CruiseControl.rb. Here's what I did to get my project setup under CruiseControl.rb with Git, on an Ubuntu 7.10 machine...

Setup for accessing GitHub repo


All I needed to do here was generate an SSH key for my account on the host machine, and then add that key to the allowed keys for my GitHub account.

Prerequisites



  • I setup a builder@mydomain.com email address which will get used by CruiseControl for sending build related emails/notifications.

  • You'll need to determine a port you want CruiseControl to run on, and your strategy for accessing it. For example, I run mine on a port other than port 80, and other than the default 3333. I then proxy that via Nginx, and also use Nginx to password protect access to it (since this is not a public project, etc. This will affect the CC dashboard URL setting specified below. Some notes on this:


    • I did my initial Nginx configuration using err's Nginx config generator. However, this makes a lot of path assumptions, and various other things, so you'll definitely want to go through the resulting file closely. I had a few sites on this server, so it was relatively useful to use this as a base starting point, and then just fix up paths to the access and error logs, and the PID file.

    • Here's a quicky on how to add password protection to an Nginx server (and a specific location).


Install CruiseControl and Do Site Configuration



  1. Cloned the Git version of CruiseControl.rb in location I wanted it (you could also simply download it and expand the tarball): git clone git://github.com/benburkert/cruisecontrolrb.git

  2. The DEPENDENCIES file indicated I needed to have the grit and mime-types gems, so installed those.

  3. Where your projects get stored for CruiseControl.rb is now defined by the CRUISE_DATA_ROOT environment variable, and if you don't set this, it defaults to $HOME/.cruise. I personally changed this to be /var/cruisecontrolrb.
  4. Edit the config/site_configuration.rb (probably need to rename the example version accordingly) to set site-wide settings, such as your email config and so on.

    • For email setup, I use Gmail for domains, so I have a block like this:

      ActionMailer::Base.smtp_settings = {
      :address => "smtp.gmail.com",
      :port => 587,
      :domain => "mydomain.com",
      :authentication => :plain,
      :user_name => "builder@ mydomain.com",
      :password => "password"
      }

    • You'll want to specify the Configuration.dashboard_url setting so URL's work properly.

    • There are a variety of other settings available in the file that you may want to tweak.

Add Project and Configure



  1. Did the usual usual cruise add command to add my project, but with the Git variant: ./cruise add MyProjectName --git-url git@github.com:mylogin/myproject.git (modify the Git project URL for your Git repo of course). Note that you can see all the options by doing a ./cruise add

  2. Create the test database for your project. The easiest way is just to go into $CRUISE_DATA_ROOT/projects/MyProjectName/work and do a rake db:create RAILS_ENV=test. Your first build will have already failed because this hasn't been made, this step hopefully fixes that.

  3. If your log directory isn't in Git, you'll need to go mkdir it, so something like:mkdir $CRUISE_DATA_ROOT/projects/MyProjectName/work/log.

Setup CruiseControl.rb Service/Daemon



  1. Copy the cruisecontrolrb file into /etc/init.d.

  2. I set the port for CruiseControl.rb to run on in the above /etc/init.d/cruisecontrolrb daemon file, by adding "--port 1234" (for example) to the DAEMON_ARGS variable.
  3. Start the CruiseControl.rb daemon as appropriate for your system (e.g. "sudo /etc/init.d/cruisecontrolrb start").

Finally, surf to your cc.rb site on the web and see how your build has done. If you run into build problems, you'll want to look at the cc.rb build logs (if it was your project test/build that failed) which are in the $CRUISE_DATA_ROOT/projects/MyProjectName directory (or rather, the subdirectory in there for the particular build). And Enjoy!

Loading mentions Retweet
Filed under  //   ContinuousIntegration   CruiseControl   git   Ruby  

Comments [0]

Creating Sparkle Appcast via Rake Tasks

I have a RubyCocoa application that self-updates using Sparkle. To do so, you need to create an "appcast" file which contains the version and download information for your application, as well as creating the zip file that holds your app. Then, you of course have to upload this to the server and location that you have specified in the SUFeedURL key value in your Info.plist file of your app. For general instructions on using Sparkle and setting it up, see their Basic Instructions page.

My Rake tasks do not create the zip file. I may enhance it to do this at some point, but so far I haven't needed to, and have had cases where I need to create it myself for various reasons. What the tasks do is to build an appcast.xml file from a YAML file that contains all the necessary information. Note that the name of my app is "Linker", so you'll see that in various spots. The tasks do rely on a simple directory structure:


  • Your app root directory


    • Rakefile

    • appcast


      • version_info.yml

      • build


        • Your app zip files go here (e.g. Linker_0.8.zip, Linker_0.9.zip, etc.)

        • Rake task will create the linker_appcast.xml file here




So, you have a spot you drop your zip files into, and this same dir is where the Rake tasks create the appcast file. The version_info.yml file is where you put the info needed to generate the appcast. It looks like this:


linker-04:
title: Linker 0.4
filename: Linker_0.4.zip
description: Added Sparkle updating mechanism.

linker-05:
title: Linker 0.5
filename: Linker_0.5.zip
description: Added help (see Help menu). Added bookmarklet support/custom URL protocol handling. See the new help for information on how to use the bookmarklet.

Note that you can put HTML into the "description" field, and my Rake task will deal with and preserve that.

Finally, I have two Rake tasks, one for building the appcast, and the other for uploading it and the latest zip file to the server. These each are simply one liners that call a parallel Ruby method within the Rakefile:


namespace :appcast do
desc "Create/update the appcast file"
task :build do
make_appcast
end

desc "Upload the appcast file to the server"
task :upload do
upload_appcast
end
end

The two methods rely on you defining a couple of variables in your Rakefile, adjust these as desired:


APPCAST_SERVER = 'your_appcast_server.com'
APPCAST_URL = "http://#{APPCAST_SERVER}"
APPCAST_FILENAME = 'linker_appcast.xml'

Here are the methods, first the one that builds the appcast, which you'll need to modify for your app:


def make_appcast
begin
versions = YAML.load_file("appcast/version_info.yml")
rescue Exception => e
raise StandardError, "appcast/version_info.yml could not be loaded: #{e.message}"
end

appcast = File.open("appcast/build/#{APPCAST_FILENAME}", 'w')

xml = Builder::XmlMarkup.new(:target => appcast, :indent => 2)

xml.instruct!
xml.rss('xmlns:atom' => "http://www.w3.org/2005/Atom",
'xmlns:sparkle' => "http://www.andymatuschak.org/xml-namespaces/sparkle",
:version => "2.0") do
xml.channel do
xml.title('BWA Linker')
xml.link(APPCAST_URL)
xml.description('Linker app updates')
xml.language('en')
xml.pubDate(Time.now.rfc822)
xml.lastBuildDate(Time.now.rfc822)
xml.atom(:link, :href => "#{APPCAST_URL}/#{APPCAST_FILENAME}",
:rel => "self", :type => "application/rss+xml")

versions.each do |version|
guid = version.first
items = version[1]
file = "appcast/build/#{items['filename']}"

xml.item do
xml.title(items['title'])
xml.description { xml xml.pubDate(File.mtime(file))
xml.enclosure(:url => "#{APPCAST_URL}/#{items['filename']}",
:length => "#{File.size(file)}", :type => "application/zip")
xml.guid(guid, :isPermaLink => "false")
end
end
end
end
end


Looking through that above, you'll want to modify the title and description at least. Now on to the uploader method:

def upload_appcast
remote_dir = "/var/www/apps/bwa/shared/public/updaters/"

Net::SSH.start( APPCAST_SERVER, 'deploy' ) do |session|
cwd = Dir.pwd
Dir.chdir('appcast/build')

shell = session.shell.sync

begin
out = shell.cd remote_dir
raise "Failed to change to proper remote directory." unless out.status == 0

out = shell.ls("-1")
raise "Failed to get directory listing." unless out.status == 0

files = Array.new
out.stdout.each { |file| files
# Look through the list of files and see what we need to upload, as
# compared to what we have locally - but always upload the appcast itself
local_files = Dir.glob('*')
files.delete(APPCAST_FILENAME) # we always upload this
local_files.each do |local_file|
unless files.include?(local_file)
print "Uploading: #{local_file}... "
`scp #{local_file} deploy@#{APPCAST_SERVER}:#{remote_dir}`
puts $?.exitstatus == 0 ? "done." : "FAILED!"
end
end
rescue => e
puts "Failed: #{e.message}"
ensure
Dir.chdir(cwd)
shell.exit
end
end
end


You will of course want to modify the remote_dir, and the login credentials towards the bottom (where it does the scp command). This also relies on you having your SSH keys set up, so you don't have to enter a password when it does the scp.

You could further generalize this obviously, but this is what I have, it works fine, and I haven't needed to extract anything out further. Posting here as per a request, and hopefully it saves someone else a few minutes.

Loading mentions Retweet
Filed under  //   Cocoa   Ruby   RubyCocoa   Sparkle  

Comments [0]

Rails Applications and Gems: Solving the Dependency Problem

There's a post today on the Relevance blog about Frozen Gems Generator. I tried posting a comment there, but it seems to have not gone through, so I'll blog my solution here instead.

Chad Woolley at Pivotal Labs created GemInstaller to solve the problem of specifying exactly what gems you want your Rails app (or other Ruby code) to use. I've dealt with this issue a lot over the course of building Rails apps, and while at first blush I didn't think this was a good solution, I'm now really like it, and use it on most of my projects (basically all the projects I control or can :)

So, why is it better than other solutions, or at least the other solutions I've seen? First, let me give a quick synopsis: it is a simple gem that allows you to create a geminstaller.yml file that specifies the version(s) of gems your app wants. This can be an explicit version, or can use things like >= version, etc. It can then automatically install the gems for you on app launch, on deploy, or just at the command line. The benefits of this solution for a Rails app include:


  • Solves the arhictecture/platform-specific gems problem. I haven't seen any of the other solutions do this, or do it well. Most just punt on it, others require a convoluted process or hacking up your other code. Because geminstaller simply relies on the gems being installed on your system, it will use the proper version for whatever system it is running on. This also ties into the next point...

  • No polluting your source control with gems. This speeds up your source control, as well as your deploys. Further, for architecture specific gems, you now don't have to have every version of each gem in your source control for each platform you need (which is quite likely at least two: your dev boxes (e.g. Macs) and your deployment boxes (Linux), but could be even more).

  • Easier, single location, statement of what gems your app requires. By using the geminstaller.yml file, you have a single place to go see what gems and which versions of those gems your app uses. This is much better than trying to look through your vendor directory, and determine what version of a gem you might have.

  • Great for bootstrapping your development environment. Sure, frozen gems usually solve this too (except for the architecture specific ones!). You can just run geminstaller after pulling down the code and it'll go install all the specific version gems you need.

  • Allows for multiple config files, so that you can build common ones you use across projects, etc. Or even cooler, your plugins or whatever can provide a file to specify what they need and you can integrate that into your config!

  • Easy to install and use. In Rails 2 environments, you can simply drop the few lines needed to use it into its own file in config/initializers. In Rails 1.x, you add these lines to your environment.rb.

  • You determine what level of function you want geminstaller doing in your app: e.g. do you want it automatically installing missing gems or just warning you? Should it put them on the load path so you are guaranteed the proper version loads, or do you want to just use it to bootstrap and live dangerously otherwise ;-)

  • Makes it easier to experiment with new versions of gems. Since you'll have to install the gem anyway (or most solutions need that to freeze them in, but not all), you can experiment by simply changing the version number in your geminstaller.yml file. To undo it, just change the number back. No need to copy the gem into vendor or a private gem repo, etc. Easy.

  • GemInstaller can tell you what gems you have on your system, but are not in your config file, as a way to see what you might need.

Check out the GemInstaller page for more details. I highly recommend this, and thanks Chad for creating it.

Loading mentions Retweet
Filed under  //   Ruby   RubyOnRails   Versioin Control  

Comments [0]

Delete an S3 Bucket Containing Thousands of Files

A backup bucket I had on Amazon S3 (via Jungle Disk) had gotten out of control and was costing me too much. I decided to kill it off completely and take a different approach. So, I wanted to delete the bucket. I thought, easy, I'll just use Interarchy, pick the bucket, hit delete and be done. Nope. Interarchy kept choking on some of the files. I tried a few times. Turned to Transmit, failed as well. So, instead, a few lines of Ruby, via the aws-s3 gem, and I was done, with one caveat.

Assuming you have the aws-s3 gem installed, I just used IRB to do it. First there was setting up the connection, and then finding the bucket in question:


require 'aws/s3'

AWS::S3::Base.establish_connection!(:access_key_id => 'put-yer-key-here', :secret_access_key => 'put-ye-ole-secret-key-here')

# Find the bucket the blunt way, by getting a list of the few I have, then picking the particular one (for me it was the second one in the array):

buckets = AWS::S3::Service.buckets
evil_bucket = buckets[1]

Then, you need to blow everything away. According to the docs, you should just be able to delete the bucket, passing the ":force => true" option, but that didn't work for me - it complained that the bucket didn't exist. So, instead I decided I'd delete everything in the bucket using the Bucket#delete_all call. That appeared to work, but it wasn't actually empty. Then I found that the library only pulls down 1000 files at a time (this is a standard S3 limitation in a listing call - although you'd think the library would realize this and loop until it was truly done), so it was only deleting 1000 files. So, the trick then was to do:


while !evil_bucket.empty?
puts "."
evil_bucket.delete_all
end
evil_bucket.delete

I have the puts in there, simply to observe progress (and it's also sort of fun to see how many files there really were). Obviously this is quick and dirty, but it wound up being far more effective, and nearly as simple as I had expected Interarchy to be. One note, if you do have a lot of files, this is not something that goes quick - it was taking about a minute per 1000 files (i.e. per delete_all call) on my system.

Loading mentions Retweet
Filed under  //   Amazon   Ruby  

Comments [0]

RubyCocoa Rocks

My infatuation with RubyCocoa continues. I've been working on a small app for the Building Web Apps folks. I'd originally been tasked with a feature that was to be done 100% within the web app. However, some of our requirements changed, and the workflow was not efficient enough. So, essentially what we moved forward on was a desktop Mac application that could interface with NetNewsWire, as well as the Building Web Apps site. The desktop app gives us a faster way to add data into the system - rapidly processing a ton of content and injecting what is desired into the web app, yet without getting slowed down by a web app interface. AJAX or Flex, or some other web UI tech wouldn't make it any faster in this particular situation.

Thus, I pursued building a Cocoa app, but this time using RubyCocoa. I've written Objective-C apps before, and spend the bulk of my time in Ruby, but this was my first opportunity to use RubyCocoa. The combination, much like JRuby, Jython and other hybrid systems, gives you "the best of both worlds." There are cons of course (slower, a few Cocoa things you can't do, debugging is harder, etc.), but for the most part, it's really nice.

For me, the infatuation stems from letting me use the aspects of each system that I am either more efficient with, or that are easier for a particular piece of functionality, all yielding a faster, and better end result. I can iterate on the app more quickly, and get a solution to BWA faster. And, one step further in the case of RubyCocoa: having the native OS integration abilities at hand.

What's been great is the ability to use Ruby's more effective (for me at least) string processing, XML processing, and networking features; create a native Mac application - using any cool Cocoa/native features; and support for AppleScript, which was critical for this particular application. This latter feature ruled out using something like Adobe Air.

This evening I setup the second use of AppleScript/Apple Events in this app: registering a custom URL protocol for the app. Applications like Pukka and Mailplane do this. What for? Well, in this case, it allows me to create a web browser bookmarklet, that can send data from the browser to our app. It also means that it works in basically any browser on the Mac (as opposed to AppleScript not working for Firefox). Further, it allows a simple "push a button" in the browser to send the data over to the app, as opposed to having to switch to the app, pick a menu item to pull the data, or horror of horrors, copy-paste.

How do you do this? This article is short and covers how to do it in Objective-C. It's just as easy in RubyCocoa: You need to add an entry into your Info.plist file to specify the name of your URL protocol as described in the article. Then, you need to register your app as a handler for that protocol:


NSAppleEventManager.sharedAppleEventManager.
setEventHandler_andSelector_forEventClass_andEventID_(
self, :getUrl_withReplyEvent, fourcharcode('GURL'), fourcharcode('GURL'))

The fourcharcode method is my way of translating four character codes for use in Ruby. I discussed this in more detail in my last post about RubyCocoa, but here's the actual method for your pleasure:


def fourcharcode(character_code)
character_code.unpack('N').first
end

Ok, so now that you've registered your app to handle its custom URL protocol, you will get an Apple Event sent to you with the URL whenever one is opened. This is handled (as per the parameter in the registration function above), by the getUrl_withReplyEvent method:


def getUrl_withReplyEvent(event, reply)
url = event.paramDescriptorForKeyword(fourcharcode('----')).stringValue
# url now contains the complete URL as a string
# do your processing of the URL/content...
end

That's it. Pretty cool eh? Handling events from NetNewsWire is almost identical (register for them, write a handler function).

And, one more great thing I could integrate: Sparkle. Sparkle is a superb Cocoa library that does automatic application updates. It checks the web for a newer version of your app, downloads it, and installs it. Integrating it is simple, and in fact, depending on your needs, you don't have to write a single line of code. The only code I wrote for it was actually a Rake task to build the appcast and upload it to the server. Slick.

All this could only be done as a native Mac app, which means Cocoa. But, as said above, doing it with RubyCocoa gives me access to all these abilities, yet, I can do all the more heavy string and XML processing I need to do using Ruby, which is much more effective for me. Also, the web services calls and code is a lot easier for me to do in Ruby than Cocoa.

Furthermore, this is plain fun! Unlike some, I feel desktop apps still have a place, but love webapps at the same time. With RubyCocoa I can build super cool Mac apps, but do so in a language I'm happier using, yet have the power of Cocoa available to me. For me, some of the best "applications" these days are such hybrids: a web app that does your primary data storage, and gives you access to the app from "anywhere" (i.e. anywhere you can get to a browser and net connection), but a desktop app to use most of the time for faster interaction, potentially better integration on your desktop system, and so on. It's the same reason I use Mailplane (desktop app for Gmail), or PackRat (desktop app for Backpack). I suspect it's the same reason we're seeing other solutions like Adobe Air, or Google Gears. Technology is so cool, isn't it?!

Loading mentions Retweet
Filed under  //   Adobe Air   Cocoa   Ruby   RubyCocoa  

Comments [0]