Acts_as_state_machine

When mapping the flow of an application, we usually have various states for a single object. These states are then joined with lines showing how an object can transition from one state to another. Each state will be unique; it may be just simply have a status flag, or it could open up new functionality within the app. For example, an e-commerce application may have a system for package tracking. When customer pays for a product, that product will have a status of 'paid'. Based on the paid status, the seller will be notified that the product needs to be shipped and the customer will gain the ability to write a review.

Traditionally, we would have to create methods that dealt with switching and checking states. Thankfully that has all changed with the wonderful ActsAsStateMachine plugin. What I like best about ActsAsStateMachine is that it has its own methods in handling states and event transitions that won't get lost with other methods within the model. I will go more into detail soon, but first we need to create a quick application to showcase the awesomeness of ActsAsStateMachine!

Let's create a baseball application that will display scores of current games. Each game will be in the database before it starts, allowing users to view if tickets are available. When the game is live, ticket information will not be needed and a scoreboard with all the innings will be displayed instead. When the game is over, only the final score will appear with information for the next game.

To get started, first install the plugin:

ruby script/plugin install http://elitists.textdriven.com/svn/plugins/acts_as_state_machine/trunk/

Then within our Game model, add the following:

acts_as_state_machine, :column => :game_state, :initial => :pending

Here we are simply telling the Game model to act as a finite state machine. We are setting the column that will hold the Game's state as game_state. By default, ActsAsStateMachine will use the state column. This can cause problems since a state column in most applications will hold location information. So by specifying a column name, we can avoid column problems. The last portion will set the initial state of a newly created Game object to pending. It is similar to creating a migration and setting the column to a default value, but if that default value ever needs to change, a new migration would have to be created. By setting a initial value, we can easily change just that one line. Now lets define all the various states for a game. Based on the app's requirements, a game will have three states: Pending, Live and Final. Defining states is as easy as adding:

state :pending
state :live
state :final

Our Game model now has states and with each state definition new methods are created. These methods may come in handy for state-specific validations or can be used in conditionals within the views that will load certain partials. For example, instead of doing @game.state == "live", we can use @game.live?. With all the states for Game defined, it is time to add events that will transition the game from one state to another. ActsAsStateMachine has it's own methods for creating events, which is basically a loop. We start by defining the event loop, giving the event a name and then specifying transitions within.

event :start_game do
  transitions :from => :pending, :to => :live
end

event :end_game do
  transitions :from => :live, :to => :final
end

With these events we can now transition a game object's state by using @game.start_game! and @game.end_game!. Keep in mind each event can hold many transitions, we can combine the two event methods into one as long as the :from portion is different. It wouldn't make much sense in the context of our example, however, because it is linear.

We have had the app up for a few weeks now and baseball fans are really digging the slick up-to-the-minute scores that we update through AJAX calls. Eventually we will run into our first rain delay, which will lead to problems. Fans will start thinking the app has stopped updating and our servers will keep running expensive AJAX updates over a game that has turned idle. It is time to introduce the rain delay state and event, making our simple app less linear.

state :rain_delay
  
event :rainout do
  transitions :from => :live, :to => :rain_delay
end

With a rain delay, a choice will be made by the umpires. If enough innings have been played, the game can end with the current score being final. Otherwise the game can be canceled and be rescheduled at a later date. Let's add the new canceled state and transitions events from rain_delay. Lets also add a new transition into the end_game event for rain_delay games that have played enough innings to be called final.

state :canceled
  
event :end_game do
  transitions :from => :live, :to => :final
  transitions :from => :rain_delay, :to => :final
end

event :cancel_game do
  transitions :from => :rain_delay, :to => :canceled
end

Let's say if a game is canceled it is still in our database for historical purposes and a new game will be created with some data from the canceled game. Let's also say that the reschedule_game method will create a new game from a canceled game. ActsAsStateMachine lets us attach callbacks like state events. Basically we want to run the reschedule_game method whenever a game's state changes to canceled. To accomplish this, we simply add :enter => methodname after defining the state as so:

state :canceled, :enter => :reschedule_game

Now whenever a game's state switches to canceled, a copy will automatically be made through the reschedule_game method. Besides enter, ActsAsStateMachine also has after and exit options. After would run after the state switch has been made and exit will be executed when the object is transitioning away from the state.

ActsAsStateMachine comes in handy when a object's status goes through many changes. I hope I have shown you how easy it is to define states, create transitions and implement ActsAsStateMachine into a model. Here is what our final code looks like:

acts_as_state_machine, :column => :game_state, :initial => :pending

# States
state :pending
state :live
state :final
state :rain_delay
state :canceled, :enter => :reschedule_game

# Transition Events
event :start_game do
  transitions :from => :pending, :to => :live
end

event :end_game do
  transitions :from => :live, :to => :final
  transitions :from => :rain_delay, :to => :final
end

event :rainout do
  transitions :from => :live, :to => :rain_delay
end

event :cancel_game do
  transitions :from => :rain_delay, :to => :canceled
end



Image

A long standing complaint among web designers is the lack of variety in typefaces that can be reliably used and guaranteed to display across all web browsers and operating systems. For the most part, designers are stuck with Arial, Verdana, Georgia, Times, or the standard serif and sans-serif implementations, though this list can be supplemented by expanding the font stack. Granted, the standard typefaces are optimized for web and will render well across the the board at various sizes, with anti-aliasing on or off, but occasionally designers need to spice things up with additional typefaces in an accessible, search-friendly manner without resorting to pre-rendered images. Two options currently exist to help remedy the situation: sIFR and Safari Embedded Fonts.

sIFR (Scalable Inman Flash Replacement)

sIFR has been available for a few years and has had a good reception across the web community. It was created as a way for designers to display any typeface on a web site without requiring the visitor to have that typeface previously installed. It uses a combination of Adobe Flash and Javascript to dynamically replace text, and is completely accessible and search engine friendly. The current stable build of sIFR is version 2, with version 3 currently in development, hopefully to be released in a stable form in the near future.

sIFR requires both Javascript to be turned on and the Flash plugin to be installed. If either of these requirements are not met, the default styles designated by the designer will be used and the visitor will never know the difference. However, the wide adoption of Flash and Javascript will ensure it works for most visitors. sIFR is best used in moderation with elements like headlines, rather than entire blocks of body text.

How to use it

After downloading sIFR, the first step is to open the sifr.fla in Adobe Flash, replace the typeface with one of your choosing, and export the file as a Flash movie. This is explained in more detail in the sIFR Documentation. Next, in the <head> of your HTML document, insert links to the sIFR CSS and Javascript files:

<head>
  .....
  <link rel="stylesheet" href="sIFR-screen.css" type="text/css" media="screen" />
  <link rel="stylesheet" href="sIFR-print.css" type="text/css" media="print" />
  <script src="sifr.js" type="text/javascript"></script>
  <script src="sifr-addons.js" type="text/javascript"></script>
  .....
</head>

Before the the closing <body> tag of your HTML document, insert the Javascript to call the sIFR replacement methods on all the elements you want to transform:

.....
<script type="text/javascript">
if(typeof sIFR == "function"){
	  sIFR.replaceElement(named({sSelector:"body h1", sFlashSrc:"blackletter.swf", sColor:"#000000", sBgColor:"#dddddd", sFlashVars:"textalign=center"}));
  sIFR.replaceElement(named({sSelector:"body h2", sFlashSrc:"blackletter.swf", sColor:"#000000", sBgColor:"#FFFFFF"}));
};
</script>
</body>
.....

The sIFR.replacementElement method takes a number of options such as color, background color, link and hover colors, padding, and several more as described in detail in the sIFR Documentation. You may have to play around with these options and tinker with your CSS to perfect your transformations.

View the Example »

You should see something like:

sIFR Example

Pitfalls

sIFR does a good job of working across a variety of browsers and degrades rather gracefully when it can't be used. That said, there are a few issues that arise with its use. Occasionally sIFR can slow the rendering of a web site, and can also create a visible 'flash' as the text is replaced by Flash. Additionally, visitors won't be able to copy and paste a combination of sIFR text and normal HTML text.

Embedding Fonts with @font-face and Safari

With the recent release of Safari 3.1 from Apple comes the ability for a web designer to reliably display any TrueType font of their choosing on a web site without Flash or Javascript, allowing them to style it with CSS as they wish. Visitors' browsers will seamlessly download the typeface and render the design in all its glory as was intended by the designer. Surprisingly, Internet Explorer has actually supported font-face embedding since version 4, but unsurprisingly, Microsoft's implementation only works with a proprietary .EOT font format.

How to use it

Implementing the @font-face property is actually quite simple. It acts just like any other CSS selector as shown in the example code below. Define a name for your font-family and then provide an absolute or relative url() to the typeface file. The url() acts just like url() for background-image. Once you've defined the typeface, simply use the new font-family within any selector as you would normally.

@font-face {
  font-family: "OldeEnglish";
  src: url(OldeEnglish.ttf) format("truetype");
}

h1 { font-family: "OldeEnglish"; }

View the Example (works only in Safari 3.1+) »

You should see something like:

Safari Embedded Fonts Example

Pitfalls

One major downfall to using embedded typefaces is that the technique currently only works in Safari 3.1. However, other browsers will degrade gracefully and use the next font in the font stack. Unfortunately, a designer may be using different line-height, font-size, letter-spacing, or word-spacing specific custom typeface, making the next typeface in the stack unreadable. No word yet on whether future versions of Firefox, Internet Explorer, or Opera will join in the font embedding party, but a designer can hope.

Copyright issues also arise with @font-face. Quality typefaces from type foundries often cost hundreds to thousands of dollars and don't generally include licenses that allow for legal distribution to the public. Since the fonts used with @font-face are required to be publicly accessible, they could potentially be downloaded by any user who has a peek inside your stylesheet. There are many freely downloadable fonts available on the web, but a majority of those are rather low quality and should probably be avoided anyways.

With Great Power Comes Great Responsibility…

The two tools are a welcome addition to a designers arsenal, especially when it comes to web typography. However, these new tools do open the door for typeface abuse and potentially illegible, tacky designs. Unless that's specifically what you're going for, as a designer you should still use sound judgement and restraint. Just because you can, doesn't mean you should. With that said, go forth and experiment!

Helpful Resources & References



Unknown

A while ago, I was given the task of converting a working Java web application to Ruby on Rails. The word on the street was that Ruby on Rails was great for a 'green' project, lays i.e. starting from scratch - but not so good for a legacy application.

However, since the database I was using already had an auto-increment primary-key column 'id' for every table, I didn't run into that many problems. There was, however, one bit of functionality I puzzled over for a while.

The application was a project tracking application: projects with attached assignments, and each assigment was associated with a document of some sort. The document types weren't really related to each other all that much, except for a few common attributes and the fact that they could be assigned. To protect the names of the innocent, I will pretend the types of items were articles, books and blog posts. If you consider those, they don't have a lot in common. No article or blog post needs an isbn, and no book needs a date posted field. They are all pretty distinct conceptually; the commonality can be found in that each are something an editor might need to work on - i.e. they are all coherent bodies of text.

I looked at the existing Java code. The application had classes Article, Book and Post all derived from Document. But there was no 'documents' table - only articles, books and posts.

Technically this is Multi-table inheritance since each type of object gets its own table. This is not something supported by Rails. Rails supports the idea of Single Table Inheritance (STI): put every record in one table. This often works out very well because it is efficient and simple.

However, I didn't want to force all these records into one table for a variety of reasons. One such reason is I wanted to make sure no Book was created without an isbn - but if I put them all in one table, the isbn field would have to allow for NULL (for articles for instance). Some would argue this an application level concern, but I think if you can guarantee data validity at the database level - and bolster that reliability on the application side - you are much better off than just merely relying on the application.

Another reason was because I've found, when rewriting an application in Rails, the longer I can push forward keeping the original data structure intact the better. That way I can run both applications at the same time as I figure out the functionality of the first. In the ideal world, the functionality of the original app would be fully detailed in a spec somewhere. This project, unfortunately, was deeply rooted in the actual world.

The currently working Java application was using an ORM, just like Rails does. The ORM for this project was something from the Apache group called the ObjectRelationalBridge or OJB for short. I looked into the code to see how it was achieving this multi-table inheritance. Basically it was using a simple table to keep track of what the next unique id should be for a given 'type'. Some databases support sequences that do this, but not MySQL 3.

The name of the table was sequences and it looked roughly like this:

sequences
id type_name value
1 document 5825

Whenever it was time to create a new Article, Book or Post, the database went to this table looking for the 'value' in the row with the 'type_name' = 'document', used that value to assign an id in a table corresponding to the object, and incremented the value. So the next Article would get an id of 5826 which would be stored in the id column of articles, the next Book would get an id of 5827 in the id column of books and so on. Pretty simple.

The other tables were structured something along these lines (this is a simplification):

projects

  • id
  • subject
  • start_date
  • end_date
  • status

assignments

  • id
  • document_id
  • document_type
  • start_date
  • end_date
  • status
  • project_id

articles

  • id
  • title
  • author_id
  • published

books

  • id
  • title
  • author_id
  • isbn

posts

  • id
  • title
  • author_id
  • slug

So how do I go about duplicating the functionality in Rails? Ideally, I'd like not to have to normalize all data into one big table, and ideally, I'd like to do something that doesn't mess with the internals of Rails, simply using the conventions that are already there. Over the years, I've found meddling with the internals of a framework can come back to bite you. Call me paranoid.

First Try

So, working in the most basic Rails idioms, I start off with something like this:

  class Project  < ActiveRecord::Base
  has_many :assignments
end

class Sequence  < ActiveRecord::Base
end

class Article  < ActiveRecord::Base
  before_create :generate_key

  def generate_key
    type_name = "document"
    key = Sequence.find(:first, :conditions => [ "type_name = ?", type_name])
    new_id = key.value + 1
    key.value = new_id
    key.save
    self.id = new_id
  end
end

class Book  < ActiveRecord::Base
  before_create :generate_key

  def generate_key
    type_name = "document"
    key = Sequence.find(:first, :conditions => [ "type_name = ?", type_name])
    new_id = key.value + 1
    key.value = new_id
    key.save
    self.id = new_id
  end
end

class Post  < ActiveRecord::Base
  before_create :generate_key

  def generate_key
    type_name = "document"
    key = Sequence.find(:first, :conditions => [ "type_name = ?", type_name])
    new_id = key.value + 1
    key.value = new_id
    key.save
    self.id = new_id
  end
end

That will take care of those Document objects getting their correct id, then I just have to make the Assignment reference it. This will work fine as a belongs_to since there can only be one match per row.

  class Assignment  < ActiveRecord::Base
  belongs_to :project

  belongs_to :article, :foreign_key => 'document_id'
  belongs_to :book, :foreign_key => 'document_id'
  belongs_to :post, :foreign_key => 'document_id'
end

That's all fine and good, but I wanted to clean up my code for 3 reasons:

  1. I've had to copy and paste the generate_key and before_create :generate_key code 3 times already. Not very DRY.
  2. There is nothing in the code itself to indicate that there is a relationship between a Book, an Article and a Post.
  3. I have to type in belongs_to for each type of object. I figure there must be a way to make this more succinct.

Second Try

First order of business: fix all those redundant generate_key methods. Well, how do you go about adding both class method (before_create) and an instance method to a class? Answer: a Module.

The Rails library likes to use the base.extend(SomeModule) convention by defining ClassMethods and InstanceMethods submodules. This works great and is a good convention to follow. If there are not many instance methods, though, I've found calling module_eval within ClassMethods will work just as well (see below).

  module GeneratedKey

  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods

    def has_generated_key(object_name)
      object_name = object_name

      # add the before_create hook and make sure
      # we can access the object_name in instance
      # methods
      class_eval <<-CODE
           before_create :generate_key

           cattr_accessor :object_name
           @@object_name = object_name
      CODE

      # add the generate_key instance method
      # NOTE: i've skimmed over worrying about Symbol
      # vs. String for clarity
      module_eval <<-CODE
        def generate_key
          type_name = object_name.to_s
          key = Sequence.find(:first, :conditions => [ "type_name = ?", type_name])
          new_id = key.value + 1
          key.value = new_id
          key.save
          self.id = new_id
        end
      CODE

    end
  end

end

This means when I put has_generated_key :some_value in the preface of a class definition, the class will call the generate_key function before it runs create to generate the id value from the sequences table. Now I can set up my classes using inheritance. The only odd thing about that is that I have to set the table name on subclasses (otherwise it will always look for a 'documents' table).

  class Document < ActiveRecord::Base
  include GeneratedKey
  has_generated_key :document
end

class Article < Document
  set_table_name "articles"
end

class Book < Document
  set_table_name "books"
end

class Post < Document
  set_table_name "posts"
end

The only thing left is to clean up that belongs_to :book, belongs_to :article etc. stuff in the Assignment class. Once again, I can just factor it out into a module.

    module GenericAssociations

  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods

    def belongs_to_type(object_name, implementations=[])

      object_name = object_name

      # take the array of implementations and do belongs_to for
      # each one
      implementations.each do |implementation|
        code = <<-CODE belongs_to  :#{implementation},
                                   :foreign_key => '#{object_name.to_s}_id'
        CODE

        class_eval code
      end

      # build up an if for each type of object i.e.
      # if article
      #   return self.article
      # end
      # if book
      #   return book
      # end
      # etc...

      if_loop = ""
      implementations.each do |implementation|
        if_loop += <<-CODE
            if self.#{object_name.to_s}_type == "#{implementation}"
               return self.#{implementation.to_s}
            end
        CODE
      end

      # add the 'document' method
      code = <<-CODE
         def #{object_name.to_s}
            #{if_loop}
         end
      CODE


      module_eval(code)

    end

  end

end

This creates a method belongs_to_type that is called during class definition, taking the type (as a Symbol) and an Array of the allowable subclasses as parameters. It also adds a method whose name is derived from the type (in this case document) that will loop through all the values and pick out which kind it is based on a *_type field value. Now I can just write this code for my Assigment class:

  class Assignment < ActiveRecord::Base
  include GenericAssociations

  belongs_to :project, :foreign_key => 'project_id'
  belongs_to_type :document, [:book, :article, :post]
end

So the only major surgery I might have had to do on the original database design was to add a document_type field to the assigments table. But that's it and, in my case, there was already a field by that name anyway because it was still necessary for the UI. Now I can write code like this to get the Document of an Assignment (and because of duck-typing - any field I need I can just reference when I need it):

   project = Project.find(1)

project.assignments.each do |assignment|
  print assignment.document
end

I can still retrieve only books, or only articles:

     articles = Article.find(:all)
articles.each do |article|
  print article
end

books = Book.find(:all)
books.each do |book|
  print book
end

And I can still create an Article, Book or Post the same way I would any other object:

    article = Article.new
article.save

Download files here: multi_table_article.zip

Note: The download includes a Rakefile in which you need to set up the db, username and password that matches your own. Then run rake. The default task runs some tests using a Ruby version of doctest, something from the Python world I'd like to see used in the Ruby world. The basic idea of doctest is that you can copy the results of an interactive interpreter session (in Ruby's case IRB) into comments in your Ruby code and rerun those transcripts as a form of regression testing. For example:

#doctest Check that 1 + 1 = 2
>> 1 + 1
=> 2
>> 2 + 3
=> 5
=end

I included a slightly modified version so that I could run the test from Rake. For a more official version see http://code.google.com/p/ruby-roger-useful-functions/wiki/DocTest




RSS Feed


CATEGORIES


ARCHIVES


BOOKMARKED


Add to Technorati Favorites