Using Auditable in nested controllers.

[ Posted by James Harton Tue, 08 Dec 2009 22:18:42 GMT ]

After yesterday releasing the initial version of Auditable I have added a few extra features, the most important of which is a Model.auditable? method so you can tell from other parts of the model whether you're dealing with a model that potentially has audit logs.

In my application I will be using a controller called audit_controller.rb which contains only the index method and will be nested below controllers for almost every model that is using Auditable. The first thing you need to do is set up your routes, my application has two controllers called remote_servers_controller and tasks_controller, both of which are auditable. We'll set up nested routes by mapping their resources, however we only want to route the index method because we don't want anyone to be able to change the audit logs. My config/routes.rb file looks like this:

ActionController::Routing::Routes.draw do |map|
  map.resources :tasks do |controller|
    controller.resources :audits, :only => :index
  end
  map.resources :remote_servers do |controller|
    controller.resources :audits, :only => :index
  end
  map.connect ':controller/:action/:id'
  map.connect ':controller/:action/:id.:format'
end

Next we need to create an audits_controller.rb file in app/controllers to handle these requests:

class AuditsController < ApplicationController

  before_filter :find_auditable_parent

  def index
    @audits = @parent.audits.find(:all, :order => 'audits.created_at DESC')
    respond_to do |format|
      format.html # index.html.erb
      format.xml { render :xml => @audits }
    end
  end

  protected

  def find_auditable_parent
    # Use find parent to dynamically figure out
    # what the parent model for this request is
    if self.find_parent
      # Check if that model is auditable.
      if @parent.class.auditable?
        true
      else
        false
      end
    end
  end

end

As you can see we access the parent model's audits method to limit the scope of our find so that we only show audits about that particular model. You'll notice that there is a before_filter applied to this controller calling a method called find_parent_auditable, which is what is used to verify that the @parent variable is auditable. find_parent_auditable also calls self.find_parent which is such a common pattern with nested resources that I wound up writing something generic and including it in my ApplicationController:

def find_parent
  # Dynamically figure out and what the parent
  # model is for a nested resources request.
  params.each do |key,val|
    if key[-3..-1] == '_id'
      # parameter is potentially an FK field.
      potential_model = key[0..-4]
      if !potential_model.empty?
        # only keep processing if the name is not
        # empty.
        begin
          model = Object.const_get potential_model.camelize
          if model.ancestors.member? ActiveRecord::Base
            if val.to_i > 0
              @parent = model.find(val.to_i)
              true
            else
              # no point trying to clone the model if the
              # id doesn't make sense.
              false
            end
          else
            # If the Class 'PotentialModel' is not a subclass of
            # ActiveRecord::Base then return false.
            false
          end
        rescue NameError
          # If there is no Class by the name of 'PotentialModel'
          # then return false.
          false
        end
      end
    end
  end
end

As you can see this function iterates through params looking for variables like remote_server_id, it uses that to attempt to imply the name of the model. It checks the model to see if it has ActiveRecord::Base as an ancestor, in which case it populates @parent with the correct instance of the model and returns true, telling the before_filter that it can allow the request to proceed to the controller.

Now all you need to do is create app/views/audits/index.erb to display your audit logs.

Posted in ,  | Tags , , , ,  | no comments

Auditable: keep audit logs of model changes in Rails

[ Posted by James Harton Tue, 08 Dec 2009 01:45:06 GMT ]

This afternoon I threw together a Rails plugin to keep audit logs of any changes made to your models. It makes use of ActiveRecord's polymorphic relationships to attach an Audit model to every model you specify.

Installing Auditable

From within your Rails application I would suggest using the plugin script to install the latest version of Auditable directly from GitHub and checking for updates regularly.

# ./script/plugin install git://github.com/jamesotron/Auditable.git
Initialized empty Git repository in /Users/jnh/tmp/test/vendor/plugins/Auditable/.git/
remote: Counting objects: 22, done.
remote: Compressing objects: 100% (19/19), done.
remote: Total 22 (delta 4), reused 0 (delta 0)
Unpacking objects: 100% (22/22), done.
From git://github.com/jamesotron/Auditable
 * branch            HEAD       -> FETCH_HEAD
* Generating model and migration...
    exists  app/models/
    exists  test/unit/
    exists  test/fixtures/
    create  app/models/audit.rb
    create  test/unit/audit_test.rb
    create  test/fixtures/audits.yml
    create  db/migrate
    create  db/migrate/20091208005219_create_audits.rb
* Making model polymorphic...
* You probably want to run rake db:migrate now.
* Done.

Auditable will generate a model and migration called Audit, which you can safely remove if you don't want to use the default model. If you want to use the default Audit model then run

# rake db:migrate
(in /Users/jnh/tmp/test)
==  CreateAudits: migrating ===================================================
-- create_table(:audits)
   -> 0.0032s
==  CreateAudits: migrated (0.0035s) ==========================================

to create the table in your database. You can always add any additional fields you may want using a migration.

Enabling Auditable on your models

Enabling an audit log on your model is now as simple as adding a single line to the model definition:

class WorrisomeData < ActiveRecord::Base
  acts_as_auditable
end

acts_as_auditable defaults to the most paranoid setting by basically turning on all auditing by default, you can override them with options that make sense to your installation. Options available are:

  • :using => symbol pointing to the polymorphic model to use for storing audit logs. Defaults to :auditable. Only useful if you are defining your own Audit model.
  • :relation => symbol pointing to the relationship mapping for audit longs. Defaults to :audits. Useful if you want to access the audit log by a method other than Model.audits().
  • :when => [] an array containing symbols describing on which actions to write audit logs. Defaults to [ :accessed, :modified, :saved, :created, :deleted ].
  • :identity => lambda block containing any code needed to retrieve the user identifier. Defaults to lambda { "Unknown user" }. You might want to try something like lamba { ApplicationController.session[:current_user].name } if you are using restful_authentication.
  • :for => [] an array of symbols naming the model fields you wish to audit. Defaults to [ :all ] where :all is a special name symbolising all fields. Note you can never audit :id because it will cause an infinite loop, however Auditable will log if :id is changed.
  • :log_field => A symbol naming the text field in the auditor model to store the log information in. Only useful if you need something more than the default Audit model.
  • :identity_field => A symbol naming the string field in the auditor model to store the user's identity information in. Only useful if you need something more than the default Audit model.

Posted in ,  | Tags , , ,  | 1 comment

Passenger - The Wild Ones

[ Posted by James Harton Tue, 20 Oct 2009 23:25:23 GMT ]

Hi all.

I've been very busy lately working on a number of projects, but the one I am most proud of is our entry for the Orcon Iggy Live Competition embedded below:

Passenger, by The Wild Ones. from James Harton on Vimeo.

The song was recorded and mixed in about 5 hours using Cubase. The drum track is synthetic everything else is real instruments. There is two rhythm guitar parts, played by myself and Matthew, a bass part played by me, lead vocals are Justin and backing vocals by Matthew and I. Justin also added a lead guitar part which made the recording feel much more dynamic.

We were able to shoot the video on the same evening, finishing up around midnight. Due to some unforeseen issues with the DV camera we were using I had a great deal of trouble getting the video off the camera, and wound up having to capture it using an old bt848 video capture card, meaning that quality is a lot lower than what I would have liked, but there's not much to be done about it now.

We shot five takes of the video, three from static angles and two hand-held takes. I used the excellent Kdenlive video editor to sync each take with the audio track and edit the cuts between angles. I also make a lot of use of the Pan and Zoom and Rotate and Skew effects to alter each shot so that it looks like there are a lot more camera angles than there really are.

I would call it a success and now Matthew and I are considering working together to record more music. You can become a fan of The Wild Ones on Facebook and we'll keep you updated on what we're working on.

Posted in  | Tags , , , , , , , , , ,  | no comments

PikeFlows project renamed Culvert.

[ Posted by James Harton Tue, 04 Aug 2009 04:03:00 GMT ]

Mainly because I liked it better. GitHub project page.

Posted in , , ,  | Tags ,  | no comments

PikeFlows updated to Pike 7.8 (much faster too!)

[ Posted by James Harton Mon, 03 Aug 2009 02:11:35 GMT ]

I've spent the last wee while hacking on PikeFlows to fix some of the locking issues present in the initial version. I've made use of Pike 7.8's new getter/setter syntax to give much finer grained locking, meaning that it now runs faster because it's locking each object only for a single atomic transaction.

There are still some unusual behaviours using Public.Network.Pcap (it seems to randomly capture packets on Mac OS X for example), however appears to be working nicely on Linux.

I've rewritten the demo app flow.pike to take command line options, check the top of the file for more information.

Check out the latest source from GitHub.

Posted in , ,  | Tags , , , , , ,  | 1 comment

Stalkr Widget

[ Posted by James Harton Fri, 22 May 2009 04:33:16 GMT ]

I've just thrown together a small widget for Stalkr that allows you to display your contact information on your blog or website. It's really easy to use, just add the following code to your page where you want the widget to be:
<script type="text/javascript" src="http://www.stalkr.cc/widget/jamesotron"></script>
obviously replacing "jamesotron" with your Stalkr login. You can see the output on the top right of this page. Feedback is appreciated.

Posted in , ,  | 2 comments

Hosting for creative projects

[ Posted by James Harton Mon, 18 May 2009 22:38:23 GMT ]

I've been spending a bit of time lately thinking about where to get our creative projects hosted. I hesitate to call projects like Flittr artistic, but they're definitely creative. I think of it as a toy. Currently Flittr is hosted on Marek's VM but since cheap VM's operate on low memory footprints and Marek has his own Mono projects to host I can't host any additional projects on his box, even with Passenger and Ruby Enterprise keeping the memory usage to a miminum.

Of course, given Marek's well documented love for Mono he has suggested we try using IronRuby, which I'm happy to give a go but not on the production system (and I don't have time at the moment to set up a development environment).

That's where Heroku comes in. Heroku is a cloud-based Ruby hosting service, from their site:

Heroku is a platform for instant deployment of Ruby/Rails web apps.

Heroku is a completely novel approach to deploying web applications. Forget about servers; the fundamental unit is the app. Use the Heroku client gem to create and manage apps from the command line. Then deploy your code with Git, and control the running app with the remote Ruby console and rake commands.

Sounds good. What's the catch? Well, for free you get 1 dyno and 5MB of storage. Which is great unless your app uses a lot of resources. Now Flittr uses relatively little CPU and RAM (well, as little as any Rails app can) but has a growing database (currently 68MB) because it stores the state of every Flittr ever sent to someone's browser for repeatability and stores basic image metadata because hitting Flickr for each image is a very costly operation. So, if you have a small Rails app then definitely look at Heroku first. What are your other options? We'll cover them in the next installment.

Posted in , , ,  | no comments

Flittr: Twitter/Flickr mashup

[ Posted by James Harton Tue, 19 May 2009 00:45:12 GMT ]

I have just finished deploying the alpha of Flittr, a toy that mashes up tweets with images on Flickr. I'm using Rails for rapid development and the Twitter4R and Fleakr gems. I'm keen for feedback.

Posted in , , ,  | Tags , , , , , , , , , ,  | no comments

PikeFlows: Pike IP packet flow analysis

[ Posted by James Harton Tue, 19 May 2009 00:48:42 GMT ]

Around the end of last year I wrote a IP and IPv6 flow handler in Pike for some packet analysis I wanted to do.

The code is available here under the GNU LGPL.

It's simple to use, and there is a demonstration app which simply keeps track of all your flows and periodically prints out all your active connections:

PikeFlows screen capture

Just take a look at flow.pike in the archive. The important part being void capture_cb(). It takes the packet from libpcap and explodes it with the Ethernet.Frame module, it then takes a look at the frame type. It passes IP packets to either IP.v4.Packet or IP.v6.Packet and then on to IP.Flow.Engine for processing into a flow. Something I threw in to show how easy it is to work with is de-encapsulation of 6to4 packets from within IPv4 packets.

I should write some documentation, but in the mean time just email me if you have questions. Thanks to Bill for Public.Network.Pcap.

Posted in , ,  | Tags , , , , , , , ,  | no comments