Best Practices: a strong case for attr_accessible part 2

Posted by trevor Wed, 24 May 2006 22:31:58 GMT

This is a followup of part 1 which you should read before continuing here.

So… are the requirements satisfied? No.

It’s possible for any user to delete a project that they don’t actually own. More specifically, it’s possible for a user to arbitrarily assume ownership of any project they know the id of. After that, the project is theirs to do with as they choose.

The code that allows you to assume project ownership is here:

class UserController < ApplicationController
  def save
    current_user.update_attributes(params[:user])
  end
end

class User < ActiveRecord::Base
  attr_protected :is_administrator
  has_many :projects
end

Spotted it yet?

Read more...

Posted in code | 9 comments

Best Practices: a strong case for [redacted] part 1

Posted by trevor Wed, 24 May 2006 03:10:00 GMT

Tonight I decided to tip over my blog-post idea pickle jar and see if anything interesting fell out. This is the first one that didn’t get crumpled up and thrown in the trash (gotta love that pickle jar).

Instead of just blathering on I’d like to show you a few lines of code and invite you to answer a pop quiz:

class UserController < ApplicationController
  def save
    current_user.update_attributes(params[:user])
  end
end

class ProjectController < ApplicationController
  def destroy
    @project = Project.find(params[:id])
    if ( @project.user_id == current_user.id ||
      current_user.is_administrator? )
      @project.destroy
    end
  end
end

class User < ActiveRecord::Base
  attr_protected :is_administrator
  has_many :projects
end

class Project < ActiveRecord::Base
  belongs_to :user
end

Before anyone gets too bent out of shape – don’t worry about the stuff you don’t see. Everything you need is there.

Here’s the requirements we’ve been given:

  1. Users should not be able to grant themselves administrative capability
  2. Only administrators or the project’s owner can delete a project

And here’s the pop quiz: are those requirements satisfied? Why or why not?

Bonus points (for what they’re worth) will be given if you can tell me what I’m going to make “a strong case for” in the title of this post.

I’ll post my answers tomorrow (Wednesday) at 10:00 PM GMT.

[Edit] someone asked so I’ll clarify: the current_user method in the controllers always returns the correct user.

[Edit] another clarification: we can assume that actions not shown in ProjectController such as edit/save perform the same checks as ProjectController#destroy before doing anything.

[Edit] Part 2 is available now.

Posted in code | 11 comments

In The Smoking Room

Posted by trevor Wed, 26 Apr 2006 05:59:40 GMT

Note: there’s cussing in this post – you’ve been warned. And yes, I have shamelessly lifted from Daring Fireball.

Read more...

1 comment

pandora.com is uncanny

Posted by trevor Thu, 13 Apr 2006 19:24:00 GMT

I’m not sure how much I buy into the whole Pandora Music Genome Project thing.

Still, this morning I gave it a try and I have to say, it’s uncannily accurate at figuring out my tastes – it’s been quite a few songs since I had to click “I don’t like it”.

What’s even better is that apart from my starting point (Iron and Wine by the way) all but one of the tracks played were from acts I’d never heard before.

And whether I buy into it or not, it’s pretty interesting to click “Why did you play this song?”

Based on what you’ve told us so far, we’re playing this track because if features mellow rock instrumentation, a subtle use of vocal harmony, mixed acoustic and electric instrumentation, a vocal aesthetic and a minor key tonality.

Seriously? Not because 734 other people who bought ‘Woman King’ also bought this album? Go figure.

Obligatory desk shot

Posted by trevor Wed, 08 Mar 2006 23:02:36 GMT

Over the past few days I’ve been tweaking the mechanics of how I work. One thing that’s been bugging me is they way I have to slouch over the keyboard of my PowerBook to get a good look at the screen.

So today I picked up a Griffin iCurve stand and an Apple Bluetooth keyboard.

icurve

While I’m not a fan of the keyboard (I prefer the one on the PowerBook – this one has too much travel), I really enjoy having my screen raised closer to eye-level. Plus it makes it easier to glance out the window and watch the antics of this year’s herring run.

RutBusting - start at the end

Posted by trevor Fri, 03 Mar 2006 12:10:05 GMT

I’ve been in a tremendous rut. Not the one or two day glitch affair that happens from time to time but an honest to goodness, slow… grind… to… a… complete… stop.

Along the way I discovered that there’s no heroic effort that can save you from an epic rut, no massive push or flash of brilliance that will get you back into a state of flow, nothing that will somehow erase all the hours burned while staring blankly at the screen.

To get out of a genuine rut you need to do something much smaller and very specific.

Start at The End

A runner visualizes crossing the finish line, a climber visualizes grabbing a hold. Nothing earth-shattering here: you concentrate on the end goal and work backwards, mapping out how to get there.

Just make sure you really know what “the end” really is.

As a software developer, I thought “the end” was code and this is partly what kept me in my rut. “Just write some code, dammit!” I was wrong, of course, because while in my rut I wrote quite a lot of code. It’s just that all of it was crap so I deleted it as fast as I wrote it. Okay, maybe not all of it was crap – when you’re in a rut everything sucks regardless of whether it really does suck.

It wasn’t until a few hours ago that it occurred to me that whatever I’m working on is never considered finished until it’s been checked-in to source control. My “the end” is svn commit -m and being able to type that command makes all the difference.

Practice getting to The End

Once you know what “the end” is, you need to practice getting there over and over to remind yourself what it feels like. You need a series of tiny victories to rebuild your confidence and prove to yourself that you’re not a complete waste of skin.

Tonight I chose three minor issues with our code to fix. All of them were a simple search-and-replace so they didn’t require much thinking at all. Putting effort into something so trivial seems kind of pointless, especially when you consider how far behind I am on the task that was stuck with me.

But I got to type svn commit -m three times and it felt great.

So, for me at least, today’s catchy buzzphrase is(with a tip of the hat to Glengarry Glen Ross):

Always Be Committing

I’m out of this rut – I can just tell.

Posted in code

Rails gets better support for namespaces.

Posted by trevor Mon, 30 Jan 2006 17:51:52 GMT

If you’ve ever worked on a large rails project, that one /app/models directory can start to look a bit daunting filled with a ton of files. In my opinion it contributes to a fear of adding classes.

A recent changeset adds better support for auto-loading of classes within modules. It also adds support for marking classes as Reloadable even if they don’t inherit from Rails core classes like ActiveRecord::Base.

This is seriously good news because it makes organizing your classes that much easier, encouraging you to simplify your architecture by (paradoxically) adding classes.

Posted in code

FCGI woes yield SCGI plugin

Posted by trevor Thu, 17 Nov 2005 05:01:00 GMT

FCGI hates me. It’s been hating me for quite a few days now. I finally took then hint, installed scgi_rails and everything works great.

Well, it’s not quite that simple. One issue I (and others, it would seem) had with Apache mod_scgi is that if you SCGIMount to ’/’ then you don’t get page caching – something that makes Typo hum along rather nicely.

A quick hack or two later and I have an easy-to-use plugin that makes page-caching possible with scgi.

The subversion repository for the plugin is here:

http://opensvn.csie.org/protocool/trunk/plugins/scgi_bin/

Everything you need to know is in the README file. If you’re going to use it with Typo you’ll need to pay special attention to the last section of the README file.

Update

I should point out that the page cache issue only seemed to affect me with Apache 2. Now I’m starting to wonder if there’s a bug in the Apache 2 mod_scgi.

Posted in code

Plugin Conventions Redux

Posted by trevor Mon, 31 Oct 2005 18:59:00 GMT

Last week Jamis responded to my post about plugin naming conventions.

If any of the rest of this post is going to make sense you’d better check out the above links first.

Anyhow, there’s been a bit more mailing-list to-ing and fro-ing about the fact that Jamis’ suggestion has problems, which I’ll write about today.

When is this an issue?

This is something that we should get out of the way here and now.

If your plugin is just mixing-in methods for a DSL (excellent link, by the way) like a new ActiveRecord ‘Acts’ – or you have some other situation where you only have have a couple of files to ‘require’, it’s a lot simpler and clearer to just require those files explicitly in your init.rb. For example:


plugins/enumerations_mixin/init.rb
plugins/enumerations_mixin/lib/proto_cool/enumerations/acts_enumerated.rb
plugins/enumerations_mixin/lib/proto_cool/enumerations/has_enumerated.rb
plugins/enumerations_mixin/lib/proto_cool/enumerations/virtual_enumerations.rb

My init.rb file will look like this:


require 'proto_cool/enumerations/acts_enumerated'
require 'proto_cool/enumerations/has_enumerated'
require 'proto_cool/enumerations/virtual_enumerations'
ActiveRecord::Base.class_eval do
  include ProtoCool::Enumerations::ActsEnumerated
  include ProtoCool::Enumerations::HasEnumerated
end

Complex Hierarchies

But what if your plugin has a lot of files to require? You may be tempted to organize your requires as Jamis suggested in his email. After all, it looks a lot like how rails organizes its own require calls. A stripped down example of a complex hierarchy might be:


lib/jamis
lib/jamis.rb
lib/jamis/buck
lib/jamis/buck.rb
lib/jamis/buck/my_module.rb
lib/jamis/buck/my_other_module.rb
lib/jamis.rb would contain: require 'jamis/buck'

and lib/jamis/buck.rb would contain:


require 'jamis/buck/my_module'
require 'jamis/buck/my_other_module'

The idea is that you could simply refer to Jamis::Buck::MyModule or Jamis::Buck::MyOtherModule without having to do any explicit require calls in the code that first references those modules. Rails will automagically “require 'jamis'” the first time you reference the Jamis module.

The problem with this approach is that once “require 'jamis'” has been called, all subsequent calls will have no effect, even if there are many jamis.rb files in the $LOAD_PATH. If Jamis releases another plugin that relies on automagic requires his full set of classes won’t be loaded.

Strategies

There are a number of strategies you can use to work around this. First of all, you can just use explicit requires in your init.rb file. You can still use Jamis’ method for everything in your module namespace that is unique to the current plugin though. Using the above example you’d just get rid of jamis.rb and in your init.rb file you’d have a single “require 'jamis/buck'”.

Another strategy would be to use load instead of require for all files that you (as a plugin author) know will appear in many places in the $LOAD_PATH. You could keep your jamis.rb file but in your init.rb you would simply say “load 'jamis.rb'”.

Pie in the Sky

There’s something slightly non-intuitive about the above strategies though. I had one of those “wouldn’t it be nice if…” moments and knocked together some code that allows this:

in lib/jamis.rb

module Jamis
  include Dependencies::AutoRequire
  auto_require 'buck'
end
in lib/jamis/buck.rb

module Jamis::Buck
  include Dependencies::AutoRequire
  auto_require 'my_module'
  auto_require 'my_other_module'
end
in init.rb

load 'jamis.rb'

There’s a couple of ‘nice’ features here. First of all, if you auto_require something, it will use the module name to construct the full require call. So “Jamis::Buck.auto_require 'my_module'” will result in a “require 'jamis/buck/my_module'” call.

Second, if you “auto_require 'buck'” and there is both a buck directory and a buck.rb file the require will be treated differently. The resulting require call guarantees your particular buck.rb file is loaded even if there is another buck.rb file in the $LOAD_PATH that has already had “require 'jamis/buck'” called against it.

You may be wondering why I didn’t just add the auto_require method to the Module class. I could have, but it makes me feel dirty.

The funny thing is, in order for this to be useful to me, I’ll either have to ship and… um… require a file that contains Dependencies::AutoRequire with each of my plugins – or I’ll have to convince the rails core team that Dependencies::AutoRequire would be beneficial to more than just me. Well, I think it’s funny…

Posted in code

Rails Plugins: Will somebody PLEASE think of the children?

Posted by trevor Wed, 26 Oct 2005 16:33:00 GMT

First of all, if plugins are new to you then you should definitely look at Jamis Buck’s guide to plugins before going any further.

Okay, so by now you know everything about plugins right?

So, I had one of those ‘uh-oh’ moments last night.

The Issue

I was worried that putting my acts_as_enumerated into ActiveRecord::Acts::Enumerated was a bad idea. I emailed Jamis and asked if plugin authors should avoid any rails module namespaces and he said:

That’s a really interesting question. I think I would answer “Yes”, but I’m not sure why. :)

It feels more natural to me if people follow a naming convention similar to how things are done in Javaland, where modules are namespaced by their vendor. So:

TrevorSquires::Acts::Enumerated

or something :)

He went on to point out that right now there are no guidelines or best practices.

Considering the number of plugins that have already been written it’s probably a good idea to start thinking about this now.

The Opinion

So here’s my opinion, presented in a way that I hope everyone from newbies (like me), to seasoned pros can understand. I’d appreciate feedback from others on this.

A plugin author has two namespaces to be concerned about:

  • $LOAD_PATH
  • Modules

The two namespaces kind-of overlap, but they don’t have to. I have a file called /lib/active_record/virtual_enumerations.rb that defines a ActiveRecord::VirtualEnumerations module and re-opens the ruby Module class. The name of the file doesn’t have to dictate its contents.

But the name of the file plays a role in being able to load your code in the first place – via the require keyword. If there are two plugins that have a /lib/chunky_bacon.rb file then require 'chunky_bacon' will load the first one found in $LOAD_PATH.

Now, as long as your init.rb does a require of all necessary files during initialization this won’t be an issue because during init your plugin’s /lib directory is the first one in $LOAD_PATH.

However, it may become an issue if you, or any other package that happens to require 'chunky_bacon', decide to defer require calls until a later time.

Even though for most people the $LOAD_PATH won’t be an issue, I believe plugin authors should be encouraged to use a directory structure, unique to themselves, within their /lib directory. Using someone else’s plugin for an example:


/lib/techno_weenie/acts_as_paranoid.rb

Not only does it solve potential $LOAD_PATH problems but it also serves as a reminder that you really ought to make your Module namespaces unique as well (as Jamis said above).

Comments?

Posted in code

Older posts: 1 2 3 4