MPOV

occasional posts about programming, tech, and the dreaded etcetera

Authority on Rails (Gem for Declaring User Authorization)

CanCan is a wonderful plugin for Rails that allows you to define all your authorization logic in one place. For small apps, it works well. But as my app’s authorization needs grew more complex, I realized I needed a different approach to declaring and testing authorization.

So I went looking… It turns out that the Authority gem is exactly what I was looking for:

  • Authority splits out auth logic into individual “Authorizers”. Each one handles authorization for a single model (or multiple models that behave the same way), with individual methods for each action.
  • Authority doesn’t try to do too much – it gives you an organized way to check authorization in regular Ruby code, explicitly, without having to write implicit rules.

For our app, authorization got much simplified with regular “if” statements (compare to this):

AlbumAuthorizer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class AlbumAuthorizer < ApplicationAuthorizer
  def readable_by?(user)
    # belongs to me
    if resource.owner == user
      true
    # belongs to a friend
    elsif Person === resource.owner and user.friend_ids.include?(resource.owner.id)
      true
    # belongs to a group I'm in
    elsif Group === resource.owner and user.member_of?(resource.owner)
      true
    # is marked public
    elsif resource.is_public?
      true
    # I'm an admin
    elsif user.admin?(:manage_pictures)
      true
    end
  end
end

I like having my authorization logic split into separate classes – it seems to be a cleaner approach.

Also, you can have one authorizer depend on another, like so:

PictureAuthorizer
1
2
3
4
5
6
class PictureAuthorizer < ApplicationAuthorizer
  def readable_by?(user)
    # ask the resource's parent "album" if this user can read it
    resource.album.readable_by?(user)
  end
end

Now, there’s a lot that Authority does not do:

  • Authority doesn’t build SQL for you. Unlike CanCan’s accessible_by, Authority doesn’t give you a way to query all records accessible by the user. Our solution was to build that SQL ourselves, which isn’t difficult.
  • Authority doesn’t give you a way to load and authorize your resources in your controllers. For that, I built load_and_authorize_resource as a more generic solution. (More about that in the next blog post.)

…with these things absent, Authority has a narrow job that it does very well.

CanCan is still a wonderful tool that I will likely use on a future project, but Authority takes a different approach and is a great tool to have in the toolbelt!

Find Photos on Your Hard Drive to Upload to Flickr.

If you’re like me, you have thousands of photos on Flickr, and thousands more scattered accross your laptop, external drive, and phone.

What’s been uploaded to Flickr and what hasn’t? Who knows!

I hacked up a quick solution in Ruby and called it Flickr Upload Set.

diagram

It’s built as a web app with Sinatra, but it’s not meant to run on a server – just on your local computer.

Here’s how it works:

  1. Launch the Ruby app and open your browser to localhost:3000.
  2. Navigate to a folder that contains photos.
  3. Flickr Upload Set makes some calls to the Flickr API to search for photos with the same name under your account.
  4. Files found on Flickr are grayed out on the screen.

screenshot

More details can be found on the project page on GitHub.

Check it out. Let me know if it’s useful to you!

What Is Quality?

A lightning talk (5-minute) presentation I gave at TulsaWebDevs:

What is Quality

Note: I used to have this presentation embedded here, but it was scrolling the page and interfering, so now it’s just a link.

JavaScript Gotchas

Here are some common time sinks encountered when building a JavaScript app, along with some tips to avoid them.

Note: these tips were originally shared as a part of my TulsaWebDevs presentation following 2012 Startup Weekend Tulsa.

Bind

In JavaScript, scope is resolved during a function’s execution – not its definition. When working with classes, you expect that this will point to the class, but it often won’t.

Example:

1
2
3
4
5
6
7
8
9
var Todo = Backbone.View.extend({
  events: {
    'click input': 'check'
  },

  check: function() {
    console.log(this);
  }
});

Solution 1:

Use _.bindAll()

1
2
3
4
5
6
7
8
9
10
11
12
13
var Todo = Backbone.View.extend({
  events: {
    'click input': 'check'
  },

  initialize: function() {
    _.bindAll(this, 'check');
  },

  check: function() {
    console.log(this);
  }
});

Solution 2:

Use CoffeeScript =>

1
2
3
4
5
6
class Todo extends Backbone.View
  events:
    'click input': 'check'

  check: =>
    console.log this

Callback Spaghetti

As your app grows more complicated, your code will start to look like this (unless you work hard to avoid it):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
before(function (done) {
  server.server(options, function (s, db, providers) {
    //clear db and add a test user - "testuser"
    db.user.remove({}, function () {
      db.notification.remove({}, function () {
        providers.provider1.insertBulk(item1, item2, item3],
          function (err, result) {
          providers.provider2.insert([item1, item2, item3]
            function (err, result) {
            providers.provider3.insert([item1, item2, item3]
              function (err, result) {
              providers.provider4.insert([item1, item2, item3],
                function (err, result) {
                s.listen();
                done();
                })
              });
            });
          });
        });
      });
  });
});

While I have no perfect solution, here are some tips:

1. Split callbacks out into separate methods on the class:

1
2
3
4
5
click: function() {
  $.get('/foo', function(data) {
    // do something
  });
}

…becomes:

1
2
3
4
5
6
7
click: function() {
  $.get('/foo', this.clickCallback);
}

clickCallback: function(data) {
  // do something
}

Rinse, repeat.

2. Use the async or Seq library

Waterfall:

1
2
3
4
5
6
7
8
9
10
11
12
13
async.waterfall([
  function(callback){
    callback(null, 'one', 'two');
  },
  function(arg1, arg2, callback){
    callback(null, 'three');
  },
  function(arg1, callback){
    callback(null, 'done');
  }
], function (err, result) {
 // done
});

forEach:

1
async.forEach(files, this.saveFile, this.complete);

Supervisor Pegging CPU

Supervisor monitors files for changes; if you have many files, your CPU starts to become pegged.

Solution: Ignore the node modules directory and any other directories not containing source code:

1
supervisor -i data,node_modules app.js

Supervisor doesn’t reload configs

Solution: um, be aware of this fact, and just ctrl-c and start supervisor again when you change a config. :-)

Object is not a function

This error is the bane of my existence. It happens in lots of places, for many different reasons, but here are a few that I always try to check first:

  • module.exports is not set
  • when using cs, forgetting a comma, e.g. fn bar 2 instead of fn bar, 2
  • setting a property on your object with the same name as a method

Backbone - visibility into view(s)

If you’re building a Backbone.js app, do yourself a favor, and set the main app view as window.app_view or something similar. Set other views as subviews on the main view.

This will allow you to inspect the app from FireBug after everything is up and running.

Sometimes console.log() lies - object changes after being logged

In FireBug, doing a console.log on an object can be misleading if the object changes soon after it is logged. FireBug will update the nested properties of the object.

Solution: to be sure, you should console.log(obj.foo.bar) to see the actual value of the property at the time it is logged.

Migrate Posterous Blog to Jekyll

Posterous is set to shut down on April 30th. For the past month, I’ve been dreading the move from Posterous to whatever.

Today I bit the bullet and decided to migrate my Posterous blog to Jekyll. There are many reasons I chose Jekyll – independence from free hosted services is probably the biggest reason.

I chose to use Octopress, which extends Jekyll with a set of sane defaults and a good structure for customizing layout, CSS, etc.

To migrate the Posterous blog posts, first I downloaded an archive from the Posterous website. Inside the downloaded zip file, there is a wordpress_export_1.xml file, which Jekyll knows how to import.

Lastly, I chose to download all Posterous-hosted images, because I expect that Twitter might not host Posterous assets on Amazon S3 forever. Better safe than sorry. Here is the script that I used to do that work:

Yay, now my blog isn’t at the mercy of big corporations deciding how to make more money. Never mind that I just embedded a Gist above :-)

Note: I later realized all the images were in the original zip file I downloaded from Posterous. But odly, the filenames were a bit different and the wordpress xml file didn’t link to them.

Super Simple Clipboard History for Linux.

I recently switched from Gnome to i3 on my main laptop. So far, I’m enjoying the simplicity of piecing together my own desktop environment from small, specialized tools.

One thing I missed from Gnome was my clipboard history tool, Gpaste. I’ve come to expect that my previous dozen or more clipboard copies are a keystroke or two away and not lost forever.

While tweaking my i3 setup, I realized it would be possible to replicate some or even all of the functionality of GPaste using simple unix tools:

  • Ruby (could be another scripting language, or just Bash script, but I’m more familiar with Ruby)
  • xclip
  • dmenu

An hour or so later, I had this:

Upon startup, my .xinitrc file starts up clipd, which checks the clipboard every second for a new string. (Note, this only works for textual data at the moment.) It stores the last 100 unique items in the .clipboard-history file in my home directory.

The other piece to this is my .i3/config file, which has a keybinding for win+v.

This opens up dmenu with all the available clips. Once a selection is made via dmenu, the clip is stored in the clipboard for pasting the usual way.

It’s not overly clever or advanced, but it seems to work.

Happy hacking!

Tulsa Hackathon Kick-off

So far the Tulsa Hackathon, an all-night programming drive benefiting needy Tulsa non-profits, is going underway and going well.

We had an excellent catered meal, project presentations were made, and teams have been formed. People seem to be figuring out team member responsibilities and getting started on the various projects.

I’ll write a couple more blog posts as the night progresses. Here are pics thus far:

Hacking Well Underway

The Tulsa Hackathon is steaming ahead. The night is still young!

People have gone from standing to sitting, staring intently at backlit screens and making things happen.

Here are some more pics…