There's this project I've been wanting to do for some time now, but haven't been able to due to a much bigger project that keeps dragging out and taking much longer to complete that I originally hoped. I finally decided to just take a break from that other project for a while and hammer this little thing out... and, it's done(ish).

There's still some things that I'd like to add, but all the core functionality is there and working. So, I'd like to finally present you with .... CramDex.

CramDex is a flashcard studying tool. Of course you can build your own decks of cards and in the true spirit of Web 2.001, you can study other people's decks and they can study yours. Sounds pretty bland, but there's more. CramDex doesn't start out flashing all of the cards in your deck at you. It randomly picks a few, and starts you on those. It tracks your progress on each, spending less time on flashcards you know, and more on those you don't. As you prove that you know certain flashcards it starts to introduce more cards, and once you've demonstrated that you've mastered a particular card, it's "set aside" and only rarely shown again (just often enough to re-enforce that knowledge). So you're constantly focusing your efforts on learning new and difficult material, and not wasting time "studying" things you already know.

Initially, new flashcards are presented as a multiple choice question, showing you either the 'Question' side of the card and giving you several possible answers, or the 'Answer' side with several possible questions. After a few of those it starts to give you just the 'Question' side, and a field to type the answer into (spelling, punctuation and capitalization counts). The 'Question' side of the card can either be words (eg. "What's the capital of Mazatlán?"), or a picture. I've got one sample deck in there now that shows maps of US States and you need to identify the pictured state.

So, go check it out, pass the link to friends, and let me know what you think. It's still not very pretty at the moment, but I have to say, I think my design skills are starting to improve... just a little.

Comments: 0 [add comment]
  • ICandy

At my day job we have several Rails apps that we've built and continue to maintain and enhance. We often add features that are usable in all of our applications, so although they're often initially developed in one app or another, they quickly get turned into plugins that we can share across all of our apps.

My boss and fellow Rails developer Aaron Baldwin has been typically hacking on plugins that are strictly internal and wouldn't be of much use to anyone outside of our organization, whereas I've been fortunate enough to get to work on things that are more generic and thus releaseable to the broader community. So, it's particularly noteworthy that Aaron released his first public Rails plugin this week, and this is one of those plugins that, now that I've used it, I don't know how I could have ever coded Rails without it.

Here's what it's for. When writing integration tests you often test that certain elements exist on the page. The problem though, is that when one of these tests fail, all you're told is that an element of some type was expected, but none were found. Was the element really not there? Or is there a typo in your test? Or on the page? Or... what? I've found myself in this scenario desperately wishing I could just see the page that the test was testing in my browser window so I could look for myself.

Enter ICandy. ICandy is a Ruby on Rails plugin that automatically displays the page from failing integration tests in your browser window. You can also manually trigger certain pages to open in your browser, if you desire. It's been tested to work with Safari and Firefox on Mac and Firefox on Ubuntu.

Comments: 0 [add comment]
  • Floriduuh

After all the recent controversy in Florida Floriduuh over updating their K-12 science education standards to start teaching kids actual science (and the strident minority who were offended by that), and the even more recent attempt to completely undermine the new standards and grant teachers the "Academic Freedom" to ignore standards completely and teach whatever the hell they wanted (a battle still going on in several other states), now comes... Wizardy!

Substitute teacher Jim Piculas does a 30-second magic trick where a toothpick disappears then reappears. But after performing it in front of a classroom at Rushe Middle School in Land 'O Lakes, Piculas said his job did a disappearing act of its own. The charge from the school district — Wizardry!

So, on that note, I'd like to propose the state name be officially changed to "Floriduuh", and a new State Motto, "Floriduuh, The Most Retarded Place on Earth" and while we're at it, let's just declare that citizens of Floriduuh, especially anyone educated there, are now officially known as Floridiots. So, go ahead and call your favorite Floridiot and congratulate them... just be sure to speak slowly and use small words.

Update: May 9th 2008

By any reckoning, University of South Florida professor Lorena Madrigal, Ph.D., was the perfect choice to speak for this Friends of Brooker Creek Preserve event.

A respected scholar in the field of human evolution, on a day marking the birthday of the creator of the science of evolution, giving a speech in a setting inextricably linked to the study of biology, which indeed would reflect upon Darwinism, was considered too controversial?

http://www2.tbo.com/content/2008/may/08/me-evolution-darwin-day-big-problem/

Could only happen in Floriduuh! (Well, honestly, it could happen elsewhere (probably has), but it didn't this time, and I've got a great theme going here.) Come on Floridiots, keep the great material comming.

Comments: 2

I recently added attachment_fu to a personal Rails project that was already using GateKeeper and ran into some stumbling blocks. It took me some time to dig into attachment_fu's inner workings and some experimentation to figure out the best way to get to the two plugins to play nice together, so here's some tips for anyone else who might try this themselves someday.

attachment_fu is a Ruby on Rails plugin by Rick Olson that facilitates file uploads and is the successor to Rick's acts_as_attachment plugin. GateKeeper is a Ruby on Rals plugin by yours truly that provides a natural language DSL to easily setup and manage highly complex RBAC permission rules at the model level.

I'm going to use 'Picture' as my attachment_fu model for these examples. Typically with attachment_fu, this model would probably look something like...

class Picture < ActiveRecord::Base
  belongs_to :user
  has_attachment :content_type => :image, :max_size => 50.kilobytes, :thumbnails => {:thumb => 'x42'}
end 

Now, with gatekeeper, you might expect to just add a couple simple permissions so the owner can create, update and destroy the picture and share it with other users, such as....

class Picture < ActiveRecord::Base
  belongs_to :user
  has_attachment :content_type => :image, :max_size => 50.kilobytes, :thumbnails => {:thumb => 'x42'}
  
  ## Permissions ##
  crudable_by_my_user
  readable_by_anyone
  #################
  
end 

Note that the readable_by_anyone permission assumes that you have a 'is_anyone?' User instance method that always returns true.

There are two problems with the above. First, you're going to have issues the first time you try to access the thumbnail version of the image. Second is that the above is using the default :db_file storage method which actually adds a another ActiveRecord model, and which GateKeeper will immediately lock down tight. If you're not using thumbnails or the :db_file storage method, then the above is probably all you need. Everybody else, keep reading.

First, let's address the thumbnail issue, since that's the easiest to deal with. In order to support thumbnails, attachment_fu's instructions tell you to add a 'parent_id' column to your 'pictures' table. attachment_fu then automatically provides a 'parent' association on thumbnails that point back to the original uploaded image. You're going to want thumbnails to just inherit their GateKeeper permissions from the original image, so that anyone with permission to read or delete the original image can do the same to the thumbnails. To do this, just add a 'crudable_as_my_parent' permission to Picture. (Notice that that says as_my_parent, not by_my_parent.) This will allow anyone who can read the original to also read the thumbnail, and anyone who can destroy the original to also destroy the thumbnail.

class Picture < ActiveRecord::Base
  belongs_to :user
  has_attachment :content_type => :image, :max_size => 50.kilobytes, :thumbnails => {:thumb => 'x42'}

  ## Permissions ##
  crudable_by_my_user
  readable_by_anyone
  crudable_as_my_parent
  #################
end 

Ok, that was actually pretty easy. The :db_file storage method is just a little more challenging. If you're using :filesystem or :s3 as your storage medium for uploaded files, then you should be fine since neither of these use extra ActiveRecord models to access the files, so GateKeeper won't get in the way. However, if you're using :db_file, here's what you want to do. First, create a db_file.rb file in /app/models and make it look something like this (changing all three (3) occurances of 'picture' to match whatever your attachment_fu model is).

class DbFile < ActiveRecord::Base
  has_one :picture
  crudable_as_my_picture
  crudable_by_anyone :if => lambda {|db_file| db_file.picture.nil? } #pic already destroyed
end

The first line provides an association for GateKeeper to use to get back to the picture model. The second line tells DbFile to inherit it's GateKeeper permissions from the picture provided by the association. The third line is the tricky one. When pictures are destroyed, Rails will destroy the picture first, then automatically try to destroy the associated db_file (via a ':dependent => :destroy' call). However, when it goes to delete the db_file, there won't be a picture anymore to inherit permissions from, and line two will fail. We can safely assume though that if a db_file is being destroyed and if (and only if) the picture doesn't exist, it too must have just been destroyed by the current user and the db_file should follow suit. You should never have stray :db_file objects left in your database whose associated objects are gone, so there's really no risk in allowing anyone to destroy one in such an orphaned state.

We're almost done, but not quite yet. The last problem is that the DbFile class you just defined will never get loaded by Rails. attachment_fu is going to create it's own DbFile and the permissions you setup will never get loaded or used. So, let's go back to the Picture class and add one line to the very top to force your DbFile class to load before attachment_fu tries to create it's own.

require_dependency 'db_file'
class Picture < ActiveRecord::Base
  belongs_to :user
  has_attachment :content_type => :image, :max_size => 50.kilobytes, :thumbnails => {:thumb => 'x42'}

  ## Permissions ##
  crudable_by_my_user
  readable_by_anyone
  crudable_as_my_parent
  #################
end 

And that's it. GateKeeper and attachment_fu should play nice together now, giving you file upload wonderfulness and model security peace of mind. One last thing I'd like to briefly touch on is the possibility of having more than one attachment_fu model. Let's say you're running a job site that allows users to upload a photo of themselves AND a pdf resume. So, you're going to have both a Picture and a Resume class, and DbFile is going to need associations to both. Note that I haven't actually tried this, but updating your DbFile class that you built above to the following should work just fine. (Continue the pattern for any additional attachment_fu models you want to add to your application.)

class DbFile < ActiveRecord::Base
  has_one :picture
  has_one :resume
  crudable_as_my_picture
  crudable_as_my_resume
  crudable_by_anyone :if => lambda {|db_file| db_file.picture.nil? and db_file.resume.nil? }
end
Comments: 0 [add comment]

Hello Readers, and Happy National Day of Prayer to you.

I'd like to dedicate this post to Madeline 'Kara' Neumann, an 11 year old girl from Weston Wisconsin, who tragically died of untreated diabetes on March 23, 2008, due to her parents insufficient faith and unwillingness to pray harder. President Bush states in his 2008 National Day of Prayer Proclamation, that...

"As we observe this National Day of Prayer, we recognize our dependence on the Almighty, we thank Him for the many blessings He has bestowed upon us, and we put our country's future in His hands."

Clearly, Kara's parents, Leilani and Dale Neumann, failed to recognize their own "dependence on the Almighty," and for this their daughter deserved to die, ... slowly. If they had only prayed harder, called on more friends to help them pray, and sent more emails asking fellow christians around the world to help them pray, maybe this tragedy could have been avoided. Thankfully, these faithless parents are now being charged with second degree reckless homicide and are facing sentences of up to 25 years in prison.

Naturally, there are terrible people in this world trying to undermine the power of prayer, such as the malicious scientists behind this 2006 study showing prayer has no effect, and recklessly subjecting innocent people to crises of faith. Who cares if there's no evidence to support a belief in prayer? There's no harm in believing (except deaths of innocent children), right? And what about those heathens who support a National Day of Reason instead of prayer. How dare they challenge the establishment of a Christian Theocracy to replace the U.S. Government for the purpose of instructing all Americans which god to follow, and how, if they intend to testify on how taxpayer dollars should (or not) be spent. Really, of all the nerve.....

Comments: 0 [add comment]

I just got back from the first Misoula BarCamp, and I think it was a success. We had about 18 people show up, which I think is very good for the first one. One attendee ( a fellow Rails developer ) came all the way from Helena. There were also some people that I was hoping/expecting would be there that weren't so hopefully they'll come to the next, whenever that is. There was a great mix of technical and non-technical discussions and presentations (I gave a little Rails demo). At any rate, big shout out and thanks to Harold for putting the whole thing together. I don't think anybody who went was the least bit disappointed in doing so, despite missing out on the first really nice spring weather in a couple weeks.

Free Image Hosting by FreeImageHosting.net
A few of the attendees looking over the topics board to
pick out which sessions they're going to go to.
Comments: 2

I've noticed in my site logs people are searching the Interweb Tubes for "GateKeeper" combined with other keywords that suggest they're looking for some better examples on how to use it, so I figured maybe I'd write something up to try to help.

GateKeeper is a Ruby on Rails plugin providing a natural language DSL to manage security permissions on instances of ActiveRecord classes at the Model level. Permissions may be assigned based on the current users roles, and/or by their relationships to the object in question. GateKeeper automatically intercepts all attempts to CRUD (Create, Read, Update & Destroy) instances of ActiveRecord classes and verifies the current user has permission before allowing the operation to proceed.

The main requirement of GateKeeper is that it expects that you have a 'User' class, and that it responds to the class method 'current', as in 'User.current'. If you need to use a different class (such as 'Person' or something), that can be defined in your environment.rb, but whatever it's called, it must respond to 'current' and return an object that represents the currently logged in user. So, here's a simple example of how to set that up.

Exactly how you get your users logged in is up to you and still beyond the scope of this post, but I'd recommend a SessionsController and a User.login(username, password) method. So, we'll assume that you have that setup already and that you store the 'id' of the currently logged in user in session, such as 'session[:user_id]'.

The first thing you'll want to do is have a method in your application controller that sets up the current user for the rest of the application...

class ApplicationController < ActionController::Base
  before_filter :setup_user

  def setup_user
    User.current = User.find(session[:user_id])
  end
end

Now, we need to add 'current' and 'current=' methods to User.

class User < ActiveRecord::Base
  
  ## Class Methods ##
  class << self
    
    def current
      @CURRENT_USER || User.new(:username => 'guest')
    end

    def current=(u)
      @CURRENT_USER = u
    end

  end

end

And that's it for User.current. Now, anywhere in your application (Models, Controllers, Helpers, and Views), you can just call User.current, and get an object that represents the currently logged in user. Now we'll go into some basic uses of GateKeeper.

If users can register new accounts on your site, then you'll need to allow 'guest' users permission to create new users. Update your User class with the following (previously shown class methods left out for brevity).

class User < ActiveRecord::Base
  ## Permissions ##
  creatable_by_guest
  #################

  def is_guest?
    username == 'guest'
  end
end

However, if at any point your application tries to save the current user, it could potentially end up creating a user with the username 'guest', which you probably don't want. So, update the above with the following...

  ## Permissions ##
  creatable_by_guest :unless => lambda {|new_user| new_user.is_guest? }
  #################

The 'is_guest?' method above is an example of how to define dynamic 'user roles' for GateKeeper. When you say 'creatable_by_guest', GateKeeper expects to either find a 'is_guest?' instance method on User, or a Role named 'guest' belonging to User.current (see below). Here are some examples of other dynamic user roles that you might want to define for your users.

class User < ActiveRecord::Base
  def is_premium_member?
    premium_membership_expires_on > Time.now
  end
  
  def is_logged_in_user?
    !is_guest?
  end

  def is_popular_user?
    popularity_ratings.size > 42
  end
end

class Foo < ActiveRecord::Base
  ## Permissions ##
  creatable_by_premium_member
  updatable_by_popular_user
  readable_by_logged_in_user
  #################
end

As I stated above, GateKeeper also looks for a Role belonging to User.current with the name of the Role used in the permission. To set this up, you'll want the following...

class CreateRoles < ActiveRecord::Migration
  def self.up
    create_table :roles do |t|
      t.string :name
      t.timestamps
    end
    add_index :roles, :name
    
    create_table :roles_users, :id => false do |t|
      t.integer :user_id
      t.integer :role_id
    end
    add_index :roles_users, :user_id
  end

  def self.down
    drop_table :roles
    drop_table :roles_users
  end
end

class User < ActiveRecord::Base
  has_and_belongs_to_many :roles
end

class Role < ActiveRecord::Base
  has_and_belongs_to_many :users
end

Now, if you were to create a role named 'moderator', and assign it to a few of your users, you could give those users special permissions on certain models, such as updatable_by_moderator . Take note that the role name should be all lower case and underscored so that your permissions look nice. If you have a role named 'SpecialUser', then your permissions would need look like 'readable_by_SpecialUser' which isn't beautiful code, and if the role name has spaces in it, it won't work with GateKeeper at all. 'special_user' is a much more appropriate role name for GateKeeper's purposes.

So, there you have it. I hope that helps some people get over the initial hurdle of setting up GateKeeper. I'd love to hear from anyone using GateKeeper and hear your thoughts and questions.

Comments: 0 [add comment]

That horrific piece of propagandist trash by Ben Stein disguised as a hard hitting documentary that makes Michael Moore look like Jacque Cousteau came out today, and the official reviews are finally coming in...

You know that flatulence like sound that balloons make when you let the air out and the last bit finally escapes before it goes completely limp? That's the sound that Ben Stein's career is making right now. But if you still feel compelled to go see this garbage, here's one more site about --> Expelled <-- that you should read first (created by the National Center for Science Education), and be sure that you offset your price of admission with a truth ticket or two... or three... dozen.

Comments: 0 [add comment]

I've just released ARID 0.5. This is a minor update with some small bug fixes and optimizations.

ARID ( ActiveResourceIntegrationDsl ) is a Ruby on Rails plugin offering easy to use methods for writing integration tests on RESTful Rails apps. With ARID you write simple statements like user.builds_article(:params => {:article => {:subject => 'Hey!', :content => 'Hello World!'}}) which will GET your new_article_path, check that it has a properly formatted form with fields for all the given params, and then will POST the params to articles_path and assert the expected response. You can even pass a block to the above call to perform additional tests. And YES, ARID tests AJAX calls.

More info and installation instructions at http://arid.rubyforge.org.

Comments: 0 [add comment]

I've just release version 0.3.1 of Cross Site Sniper. This is a minor bug fix update.

Cross Site Sniper is a Ruby on Rails plugin that automatically html escapes all string and text fields of ActiveRecord objects. Data is escaped as it is retrieved from the database to protect your site from XSS (cross site scripting) attacks without modifying or corrupting your original data. Convenience methods are provided to access the unescaped data when necessary. Edit forms are prepopulated with unescaped data as well to avoid user confusion and prevent double escaping.

Documentation and install instructions can be found at http://xss.rubyforge.org.

Comments: 0 [add comment]