Triggering the DCI Context

There's been a lot of interest in DCI among Ruby developers, and a lot of good discussion going on among some blogs about application architecture.

I've written about basic information on the concept which touched on some sample code. One main point to make is that our class oriented approach to application architecture is that we end up with junk drawer objects; objects which contain methods for any number of interactions that may be performed at some time during the execution of the application.

Mike Pack's article has been the first to discuss triggers for DCI in Rails (but correct me if I'm wrong). Triggers are the actions that initialize your Context to perform the algorithm.

When you develop your system with DCI one of the goals is to take your business use cases and describe them in your Context. When writing a use case you'll say that you have a specific preconditions, specific actors, and of course specific goals. But we can look to Alistair Cockburn for the full understanding of use cases.

Here are the main points about what a use case should do and some points about how we can apply this to our executable code. In Cockburn's words (with my comments afterward) use cases DO:

Hold Functional Requirements in an easy to read, easy to track text format. We can do this in code. Ruby is widely known for being easy to read and the community values beautiful code. As far as holding functional requirements, that should be very simple for executable code to do.

Represents the goal of an interaction between an actor and the system. The goal represents a meaningful and measurable objective for the actor. This is both an important aspect of your business, and an important aspect of DCI. When we're attempting to achieve our business goals, we should write software that is uniquely designed to do that. The Context is an object that encapsulates this concept.

Records a set of paths (scenarios) that traverse an actor from a trigger event (start of the use case) to the goal (success scenarios). In simple code this is easy enough to do with if/else blocks or case statements, for example.

Records a set of scenarios that traverse an actor from a trigger event toward a goal but fall short of the goal (failure scenarios). An example here might be the above if/else blocks or perhaps a rescue from an exception. A use case describes a complete interaction between the user and your system and it is the responsibility of your Context to implement this.

Are multi-level: one use case can use/extent the functionality of another. This is reflected in DCI in the fact that we want to achieve the vision of Alan Kay, to create a network of interacting objects much like biological cells or computer networks. A Context can trigger other Contexts within.

What you're attempting to do with DCI is not battle junk drawer objects with other junk drawer objects, but to implement business logic in an organized set. Take a specific need and describe it, including variations, in a single Context to coordinate the Data and Interactions. The Context has real meaning and real value to your business, it's not just a place to use extend on your objects.

In Rails, your controllers should handle the user actions that trigger these use cases. You might have multiple ways to trigger a use case. For example in a typical view your user can interact with objects in your system but in admin view another user can do the same with perhaps an alternate scenario allowing him to override certain aspects of the scenario. It makes a lot of business sense to look at this use case and scenarios together, so why not put that code into one place? Why not create Context that explains all of this for us in executable code.

Put your use cases in a set of executable code and trigger it from wherever your interface requires it. Before you attempt this, begin first by writing your use cases. Use cases reveal the needs of the program and also show the value of the DCI Context in centralizing your business needs.

I'm writing about this and more in "Clean Ruby" a book about DCI in Ruby that is a collection of research and tips from my experience in working with large and small applications and making them easy to understand and maintain.

OOP, DCI and Ruby - what your system is vs. what your system does

If you've read my previous article on the tip of the iceberg with DCI then you might be curious to find out more about it. You read a very simple example that was only used to make a point. But for sure, you're left wondering a bit:

  • Why not make some other class to manage this if we want to separate functionality?
  • Is this at odds with the program's "connascence"
  • And another good question came up in the comments: what about performance?

All of these, and more, are good questions. But first you need to get your head around why you would use a pattern like DCI. What does the approach with Data, Context, and Interaction have to do with Object Oriented Programming?

Your first challenge

You need to unlearn what you know to be OOP.

Not quite all of it, but you need to stop and reconsider what it means to be "object oriented."

It's very likely that you've been building programs that are "class oriented" and not "object oriented." Take a moment and look at your latest project and consider if that's true.

Class Oriented Programming

You might call your program "class oriented" if your classes define both what an object is and what it does. If you need something else to happen to or with that object then what's your answer?

Do you add more methods to it's class or do you make a new class of thing to abstract the management of some task?

If you do either of those, it could be called class oriented.

Think about what this comment from Joe Armstrong might mean in Coders at Work: "The problem with object-oriented languages is they've got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle."

Can that be applied to your objects?

Object Oriented Programming

When we program with OOP we care about objects, not only classes.

What DCI attempts to do is separate what the system is from what the system does. With this type of approach you'll get a truer mental model of your program. And your code concepts will be far closer to what you experience in the real world.

"But classes represent what things are! And different things behave in certain ways" you might say. This may, on the outset, seem to be true, but if you think about the things in the real world, they don't do anything unless they are playing some role.

Objects and Roles

The problem with relying upon classes and inheritance to define object behavior is that the behavior is fixed at compile time and your objects are limited in what actions they perform.

For example, you might have a Person class that is the basic model representation of a human being. That person is also a student, so you make a Student subclass. Eventually the student needs to get a job to pay for books and you need to make an Employee class. Should employees inherit from students? No, that's not right. Should the student inherit from employees? That's not right either.

Really, what you have is an object, a person, that needs to play many roles. With DCI, the approach is obvious: when the object needs to play a role, it is given that role.

Obvious Code

Yeah, but how often does this happen? Isn't your example contrived?

This type of inheritance problem may not happen often in your programs, but it does happen and you're likely to run into it at some point. And when it does happen what will you do?

I'll make another object to manage the work. Maybe the Person has many tasks and I can just make a TaskPerformer object to handle that.

While making another object to handle behavior may solve the problem with better encapsulation, your code is less representative of how the real world actually works. By making your code less like the real world, it makes it more difficult for you and others to reason about it's functions. And by introducing an abstract object, you've introduced something that doesn't make sense in the real world. Does a person have a task performer or does a person just perform a task?

The benefit in approaching objects and roles like this is that it makes your code more obvious. In the context of some event, an object is given a role and has methods defined to perform some action. By explicitly assigning roles to objects in a context, your code is instantly more decipherable.

Let's look at a simple example:

current_user.extend Admin
    current_user.grant_permission(other_user)
    current_user.extend Notifier
    current_user.send_thank_you_to(other_user)

In the example code, we see when an object gets a role, and performs some action. Looking at that code, one could assume that the modules used to extend the objects define the methods used.

Compare that code with this:

current_user.grant_permission(other_user)
    current_user.send_thank_you_to(other_user)

Are those methods defined on the class? Are they in another module that's included in the class? Perhaps. And yet you might need to break out grep to look around for def grant_permission in your project to find out exactly what that method does.

By defining these methods directly in an object's class, we're convoluting what the system is with what the system does. By separating the actions in to Roles, we're drawing a clean line between our representation of data (what it is) and the use cases that our system is designed to implement (what it does).

Separate Object from Role

With Ruby, we can easily just define new methods on an object with extend. This gives you the ability to easily break apart your concerns.

Here's something to try: the next time you need to implement a feature begin by writing some pseudo-code. Take the details of what needs to be done and add them as comments. Then gather your objects and assign to them some roles that make sense for what needs to be done.

# A user submits a form to request permission to access our system
    # If there is an available opening
    team.extend AvailabilityChecker
    if team.available_opening?
      applicant = User.find(params[:id])
      applicant.extend Applicant
      # and the user has completed the application, send it to processing queue
      if applicant.completed_application?
        applicant.prepare_for_acceptance
      else
        # If the user hasn't completed the application, ask them to complete it
        render 'edit', :notice => 'Oops! Please finish this.'
      end
    else
      # If there is no available opening, display a message
      redirect_to :index, :notice => "Bummer, dude! We're all out of space."
    end

The sample I gave is completely unreal and off the top of my head; don't read too much into it. But if you take a glance you'll see that it's pretty obvious where I intend these methods to be defined. It's likely that others on my team would find it obvious too.

Another benefit is that I didn't add any new methods to my user class. My code won't infect any other feature or test developed by others and by not adding more methods to the user class I don't add any overhead to understanding it.

Once I have that pseudo-code that describes the behavior, I can comment it out and start writing tests while I re-implement each tested piece.

Try this approach to see whether the experience is good or bad. (Note that this doesn't have to happen in your controller. You might implement this in a separate object, a Context which coordinates the knowledge of these objects and roles).

Does it make your code more obvious? Did it make testing any easier? What did others on your development team think when they saw it?

4 Simple Steps - Extending Ruby Objects - The Tip of the Iceberg with DCI

You've got a few Rails applications under your belt, perhaps several. But something is wrong. As you go, your development slows and your classes become bloated and harder to understand.

Keep your program simple

While you're doing your best to follow the concept of keeping your controllers skinny and your models fat, your models are getting really fat.

The answer is simple: cut the fat.

Your models don't need to have every method they will ever need defined in the class. The reality is that the objects that your application handles only need those methods when they need them and not at any other time. Read that again if you must, because it's true.

Step 1: Evaluate your code and look for a place to separate concerns

This is the point where you look at your code and try to realize what object needs what and when.

For example, if users of your system need to approve a friend request they only need to do it in the context of viewing that request. Your User class or @current_user object doesn't need this ability at any other time.

Step 2: Prepare your test suite

If you want to make your code simpler and easier to understand, write tests. You must do this first.

Even if you're only intending to change one small thing (just one tiny piece), write a test for that. You need a baseline.

Step 3: Create the Object Roles

Take your friend approval (or whatever it is) method or methods and put them in a module.

You might want to drop this into some namespace such as ObjectRole::FriendApprover or if you know your name won't clash with anything else, just go with FriendApprover.

Here's a sample of what this might look like:

module FriendApprover
      def approve(friend_request)
        friend_request.approved = true
        friend_request.save
        increment_friends
        notify_new_buddy(friend_request.user_id)
      end

      def increment_friends
        friend_count += 1
        save
      end

      def notify_new_buddy(buddy_id)
        BuddyMailer.notify_buddy(buddy_id, "We're officially friends!")
      end
    end

It doesn't really matter what my sample code is, you get the picture: take the methods from your User class that do the approval and put them in your FriendApprover module.

The unit tests you had for these methods can now be simplified and applied to the module. The test just needs to check that some object agrees to the contract that the methods expect.

Step 4: Extend your user

Extend your user. Thats little "u" user. Your class doesn't need this module, your object does.

Open up your controller where you usually call current_user.approve(friend_request) and change it to:

current_user.extend FriendApprover
    current_user.approve(friend_request)

That's it.

What you've just done

You've made your code more obvious.

It's only in this context that a user needs to perform this action and this change has limited the scope of those methods to a very concrete area.

  • Your User class is smaller making your cognitive strain easier
  • Your User unit test is smaller
  • You have a clear separation of concerns with your new Object Role module
  • You've inherently made these methods reusable

But what about...

Yes, there's more to it. Of course there's more you can do, but with this simple concept you can do a lot of cleanup of both your code, and your ability to reason about your code.

What is DCI?

For now, I'll leave the description of what DCI is to this article but I'll be writing more about the concepts in Data Context and Interaction.

How delete on a Hash works in Ruby... or how you thought it worked

Are you new to Ruby? Or were you once? Cool, me too.

When you first bump into Hash#delete you think you might be able to use it and just remove the items you don't want, but it doesn't work that way.

You think "I'll just do this:"

hashy = {'this' => 'that', 'something' => 'else', 'secret' => 'I love NKOTB'}
    hashy = hashy.delete('secret')

And bingo you've got your hash without that nasty secret. But what you actually found is that delete will remove that key from the hash and return the deleted value and not the altered hash.

Perhaps this is old news to you, but to new developers this is the moment where they say "Oh, right, it gives me the value that I don't want."

Well here's a quick idea to give you what you want:

class Hash
      def except(which)
        self.tap{ |h| h.delete(which) }
      end
    end

Now you can use the same code above but run:

pre>hashy = {'this' => 'that', 'something' => 'else', 'secret' => 'I love NKOTB'} hashy = hashy.except('secret')

That'll return your hash without the part you want. That could be simplified to this:

hashy = {'this' => 'that', 'something' => 'else', 'secret' => 'I love NKOTB'}
    hashy.except('secret')

because the delete will alter the hash. So you need to be aware that if you want to keep the original hash you'll need to:

hashy = {'this' => 'that', 'something' => 'else', 'secret' => 'I love NKOTB'}
    new_hash = hashy.dup.except('secret')

Enjoy!

Always check in schema.rb

Sometimes developers are unsure about Rails' schema.rb file and whether or not it should be checked in to source control. The answer is simple: yes.

As you are familiar, Rails encourages developers to alter the database state through migrations. One migration adds some table, and the following migrations add more, change something, or remove something. And as you well know, this is a great way to keep developers in sync with each other's changes to the database structure.

With each migration you add and subsequently run, your schema.rb file is altered to represent the final state of your database. Seemingly because this is automatically done for you, some developers think this file should be left out of your project; it should not.

Don't store it just because David Heinemeier Hansson said you should

First things first, this is a terrible reason to add schema.rb to your source control. Often, in an argument about something like this it's easy to look to an authority, find a relevant quote and say "See, he said so." Yes, there is a commit in Rails where he suggests you store schema.rb, but this was done to end an argument, it is not an argument itself.

Authorities are authorities because of their deep understanding of something, not because they command you to do things. But often people will allow authorities to command them to do things because they are authorities. David Heinemeier Hansson is an authority on Rails and his control of the project allowed him to make that commit and force the direction of Rails. If you agree with this commit, agree because of the reasoning, not because DHH said so.

Don't store generated files, except for schema.rb

As a general rule of thumb in compiled languages, you don't store generated files. The reason for this is not because an authority said so or because many consider it to be a good rule to follow, but because you want your code to generate the proper files for correct execution of the application.

If you add your generated files to your source control you may in the future find that you've altered your code in a way that prevents a particular file from being generated at all. But if that file exists regardless of the bug you introduced you're likely to find errors in the application's behavior. Tracking that down may be tough until you realize that you've checked-in your generated files.

Additionally, if you store generated file X in source control but change the code to instead generate file Y, you'll leave unused code in your project possibly giving other developers the misconception that it is important in some way. Worse yet, depending on your application the mere presence of file X could affect the execution of your application. I've heard things like this appropriately called "code turds." If it's not going to be used, it shouldn't be there.

Regardless of all of this, schema.rb doesn't affect the execution of your application so there is no danger in storing it in source control. Avoiding generated files in source control is a good rule to follow, but knowing when to break that rule is important too. Leave the code turds out and schema.rb in.

File churn in source control is not an issue with schema.rb

If you are concerned about the amount of churn (that is, frequent changes) you have with the development of your schema.rb file, then you probably are actively developing your database, or you have a problem elsewhere and need to get your team to work on a clearer picture of what your database should do.

Churn in schema.rb can actually be valuable, however. It's easy to overlook multiple migration files changing things in your database, but in reviewing code commits the amount of churn in an area of schema.rb can reveal problems with your development team.

Conflicts in schema.rb are valuable

If your team is making dueling commits over the purpose of a database field, your problem is not with resolving conflicts in schema.rb, it's with resolving conflicts between and among your developers about the structure of your database. Keeping schema.rb in source control will help to reveal this.

Before you commit any of your files to your master/production/whatever branch, you should

  1. run your tests
  2. pull down and merge any other changes
  3. re-run your migrations if any new ones were pulled down
  4. re-run your tests
  5. commit/push your changes (including schema.rb)

Following those steps ensures that your tests are run against the database that everyone else will have and ensures that the schema.rb file you commit is the latest and most up-to-date. Maybe you don't want to run your tests twice, that's fine, but be sure to run them after you pull down the latest code for the project and merge your changes.

Store it because schema.rb is a representation of the current state of the database

At any point in development, you can look at schema.rb to give you an accurate representation of your database structure. Other developers can checkout the project and run rake db:schema:load and almost instantly they are ready to develop (barring any sample data they need to load).

For a new team member, there is absolutely no need to change or rename anything in the database as may be done with migrations. Your ultimate goal to begin development is to have a database in a desired state. It doesn't matter a bit if a field name was changed from "login" to "username" or if your :text field was once a :string field in your first migration. For your database structure, the final state is all that matters, and schema.rb does this for you.

Application code mixed with migrations can cause problems

Sometimes you may have a need to alter your structure and do something like ProductType.reset_column_information in a migration and add some data. For now, I'll avoid the discussion on whether or not that's appropriate, but if you are doing this a problem may arise when at some point in time you remove the ProductType model, or rename it to Category. In that case, you'll need to go back and maintain your migrations... read that again you'll need to maintain your migrations. This is a pointless exercise: use schema.rb.

This is also an example of why you shouldn't mix database seeding and migrations.

Migrations are slow

Relying on migrations to get up to speed for development is slow and gets slower as your application's database changes and as the number of your migrations increases. Because schema.rb skips over changes and represents the final (and desired) state of your database, it's fast.

Your blank slate production database only needs the final state

A production database only needs the final state assuming, of course, that the database is a blank slate. Running all of the migrations in production to get where schema.rb would be is not necessary. schema.rb weeds out all of the changes for you and gets the job done quickly.

"But what about a database that already has a structure?" you may ask. Then all you need is the migrations that haven't been run; you'll never run rake db:schema:load on an existing production database.

Keep schema.rb with your code

schema.rb in your project's source control adds value for all developers. It loads the desired state quickly, it gives you a clear representation of your database structure, it reveals conflicts and unnecessary churn, it doesn't affect the execution of your application. Add schema.rb to your source control and add value for your team.

Ruby Metaprogramming is Awesome

If you've come to read about how wrong I am about metaprogramming, don't worry, I'm sure I'll follow-up with a post about how bad it is to do metaprogramming and how it causes you real, physical pain.

If, however, you've come to find out about how awesome metaprogramming is, then you can tell by the title of this article that you are in the right place! Now that's authoritative!

The best part about this article is that it is for you! That's right, newbie. I'm not going in-depth, I'm merely going to discuss how a dash of metaprogramming solved an annoying problem.

I do a lot of work with Radiant. And Radiant manages pages well, but there are plenty of extensions that allow you to do other things. You can, for example, add the ability to upload files and whatnot. Or you can add the ability to edit stylesheets and javascripts.

That's pretty cool. Radiant does a good job of managing pages in the pages table. But stylesheets and javascripts are not pages. Some of the samples that Radiant bundles have stylesheets as regular pages in the page tree and are tied to an almost blank layout that sets a particular content-type like text/css. Yuck. So Chris Parrish, the author of SNS, added text_assets to handle other types of content like stylesheets and javascripts.

One of his reasons for creating this extension is the weirdness of having a stylesheet in your page tree: "No more confused users wonder what those fancy pages are." Saaaweet! Thanks, Chris.

All is well until you find out that the SNS extension doesn't let you use your typical Radius tags within javascripts or stylesheets like:

#something { background: url('< r:assets:url title="my_image" />'); }

Nooooooo! Why, God, why!?!

Well, with SNS, you're dealing with a TextAsset and not a Page. But if you're familiar with Radiant, you know that page tags aren't only used on pages, they're used on snippets and layouts too. So what's the deal with that?

All of the radius tags that you use in Radiant are evaluated in the context of a single page. Pages, snippets, and layouts are all used in reference to rendering a Page. But SNS renders a TextAsset and as a result, it doesn't have any of the fancy tags added. Afterall, it's class TextAsset < ActiveRecord::Base and not class TextAsset < Page.

You read that right: text assets are not pages. Bummer, dude.

Well, what if you could include the same modules as are included in the Page model? Then you could use those tags in your stylesheets.

Fire up your console because you're about to see how to do it. In a rails console (for a Radiant instance), try this:

Page.included_modules

And you'll get back a bunch that don't matter for you. Let's simplify that:

>> Page.included_modules.select{|m| m.to_s =~ /Tags$/}
    => [TextileTags, SmartyPantsTags, MarkdownTags, StandardTags]

Awesome. All we need to do is include all of those modules into TextAsset and we're done! Almost.

Since we're dealing with the SNS extension, you'll also see that it includes Sns::PageTags which adds tags to output stylesheets and javascripts. Those, we don't need. So we can filter them out and include them into TextAsset:

TextAsset.class_eval {
      Page.included_modules.select{|m| m.to_s =~ /Tags$/}.reject{|m| m == Sns::PageTags }.each do |mod|
        include mod
      end
    }

Bingo! Now we're in business. Except for the fact that it doesn't work.

So we need to update the context in which these tags are evaluated. Since they are all written expecting to be included in a Page model, the global variables for the tags need to have knowledge of a page.

TextAssetContext.class_eval {
      def initialize(text_asset)
        super()
        globals.page = text_asset # This is the important line that adds the hook for all existing tags for pages.
        globals.text_asset = text_asset
        text_asset.tags.each do |name|
          define_tag(name) { |tag_binding| text_asset.render_tag(name, tag_binding) }
        end
      end
    }

There you have it. All it took was some class_eval and some looping over included_modules.

page tags in stylesheets And now you'll have happy users. Because even though Chris solved the problem of weird stylesheet pages in the page tree, the solution introduced a problem where editors of the site expect the radius tags to simply work everywhere. Now they do.

The above code assumes that the only valuable tags are those whose modules have "Tags" at the end of their names. That's a reasonable expectation, but we can go even further by inspecting the included modules of the included modules to see if Radiant::Taggable is there:

TextAsset.class_eval {
      Page.included_modules.reject{|m| m == Sns::PageTags }.each do |mod|
        if mod.included_modules.any? {|inc| inc == Radiant::Taggable }
          include mod
        end
      end
    }

So go ahead and install the page attachments, or paperclipped, or whatever extension with sns and just do script/extension install sns_page_hook, or better yet: gem install radiant-sns_page_hook-extension. Please let me know if you find any problems with it.

Radiant 0.9 Official Release

Finally, after a long, long wait: 0.9 is out.

It's my first non prerelease gem to push out for Radiant but I'm proud of all the work that I and many, many others did to make it happen. John Long put the UI updates together with the help of a good group. Keith Bingman managed all that went into internationalization and the many contributors that made it happen. William Ross added features such as pagination in the standard Radius tags, and pagination in the admin screens. Josh French gave us the ability to load extensions from gems. John Muhl, our newest core team member increased the general quality in testing for bugs and improving the overall quality of the application.

There are many contributors, I'm just listing some from the top of my head. Thank you to everyone of the contributors, be you bug reporters, committers, or just users who discuss your needs on the mailing list.

While this release is an exciting improvement, I'm even more excited about the future of the project. More to come...

Rack Cache on Heroku with Memcached

The convenience of deploying application on Heroku is attractive especially with their add-ons and the free options that they provide, in particular Memcache.

If you're working with an application which needs to manage it's cache with Rack::Cache, you'll want to have fast responses for your metastore. The meta information about your content is probably the most important part of your cache since it's checked by many clients for the status of the content: is it fresh or stale? See more about How Web Caches Work.

Typically you might setup Rack::Cache like this:

config.middleware.use Rack::Cache, :metastore => 'file:tmp/cache/meta', :entitystore => 'file:tmp/cache/entity'

5MB of Memcache is a decent place to start for free and it's integrated into your application without any effort on your part. So on Heroku you can use Memcache as your metastore like this:

$cache = Memcache.new
    config.middleware.use Rack::Cache, :metastore => $cache, :entitystore => 'file:tmp/cache/entity'

That's simple enough, and it's just as easy if you're deploying Radiant:

$cache = Memcache.new
    config.middleware.use Radiant::Cache, :metastore => $cache

If you want to look at an example of a simple app that does this, there's an easy to understand sample application on github. Enjoy your speedy metastore.

Radiant Page#find_by_url

On a recent project a client asked about overriding Page#find_by_url and when that actually occurs. I think the answer should be explained for everyone working with it.

20 second summary

This is an in-depth look at the method that gathers pages within Radiant. In short, if you want to do special page finding, create a subclass of Page and write your own find_by_url method to adjust the way Radiant behaves. Every page will respond to this method and return appropriate pages according to the requested url. In the admin interface, you can select your special page type to make that page behave as you have specified.

Simple finding

find_by_url is defined both as a class method and an instance method. Let's look at the class method Page.find_by_url from the Page model:

class << self
      def find_by_url(url, live = true)
        root = find_by_parent_id(nil)
        raise MissingRootPageError unless root
        root.find_by_url(url, live)
      end
      # ...
    end

First, it looks for the root page, which is considered the page with no parent_id. If no root page is found it raises a MissingRootPageError exception; otherwise, it calls the instance method find_by_url on the root page.

This class method takes 2 arguments: the url (really the path matched in the routes from request) to be found, and a live flag which defaults to true (more about that later).

Finding the first page

The find_by_url instance method is a bit more complex. Let's take a look:

def find_by_url(url, live = true, clean = true)
      return nil if virtual?
      url = clean_url(url) if clean
      my_url = self.url
      if (my_url == url) && (not live or published?)
        self
      elsif (url =~ /^\#{Regexp.quote(my_url)}([^\\/]*)/)
        slug_child = children.find_by_slug($1)
        if slug_child
          found = slug_child.find_by_url(url, live, clean)
          return found if found
        end
        children.each do |child|
          found = child.find_by_url(url, live, clean)
          return found if found
        end
        file_not_found_types = ([FileNotFoundPage] + FileNotFoundPage.descendants)
        file_not_found_names = file_not_found_types.collect { |x| x.name }
        condition = (['class_name = ?'] * file_not_found_names.length).join(' or ')
        condition = \"status_id = \#{Status[:published].id} and (\#{condition})\" if live
        children.find(:first, :conditions => [condition] + file_not_found_names)
      end
    end

Wow. There's a lot going on there and there's room for some refactoring, but for now let's just walk through it.

First, nil will be returned if the page is virtual?. A page, by default, is not virtual. This is stored in the database in a boolean field, but you may override this in any subclass of Page that you create. For now, let's assume that your page isn't and won't be virtual and we'll get back to what it means.

Next, we clean the url if the clean flag is set to true (which it is by default). clean_url simply ensures that the url being checked is properly formatted and that any doubling of slashes is fixed. So this right//here//// becomes this /right/here/.

The next step shows us why we clean the url. A local variable is setup to compare against the page's url.

my_url = self.url
    if (my_url == url) #...

What is a page's url? It's calculated by the page's slug and the slugs of it's ancestors. In short, if your current page's slug is 'here' and it's parent page is 'right', and that page's parent is the home page (with a slug of '/') then your current page's url is '/right/here/'.

So we check to see that to see if it is the same as the url in the request. But also, in this comparison, we check to see if the live flag is set and is false or if the page is published?.

This live flag is a bit strange in appearance:

my_url = self.url
    if (my_url == url) && (not live or published?)

By default, this not live returns false (since live is true by default and we reverse it with not) so it moves on to published?. You might set live to false in other situations, but for now we'll just go with this.

A page is published? if it's status (as stored in the database) is the 'Published' Status.

So if the incoming url matches the current page's url (which at the first pass is the root or home page), then we return with the current page:

my_url = self.url
    if (my_url == url) && (not live or published?)
      self

Finding deeper pages

If it isn't true that the incoming url and the current page's url are equal, then we move on to the next step:

my_url = self.url
    if (my_url == url) && (not live or published?)
      self
    elsif (url =~ /^#{Regexp.quote(my_url)}([^\/]*)/)

Here it matches the incoming url against a Regexp of the current page's url. When it starts, we're matching the root page which has a url of '/'. If that's the incoming url, it would have been caught in the original if block, but we ended up at the elsif. The Regexp that's used matches the next slug in the incoming url. So if the incoming url is '/right/here/' then it will match the slug 'right'.

From that match, we find the current page's children by their slug (remembering that the current page is the root, with a slug of '/'):

elsif (url =~ /^#{Regexp.quote(my_url)}([^\/]*)/)
      slug_child = children.find_by_slug($1)

If it finds that 'slug_child', then we call find_by_url on that page to loop down the tree to find the final page that we want (which would be the page that responds to the url '/right/here' or in this simple case, the page with a slug of 'here'). If it finds the page, then it returns the found page:

slug_child = children.find_by_slug($1)
      if slug_child
        found = slug_child.find_by_url(url, live, clean)
        return found if found
      end

In that if slug_child block, the slug_child.find_by_url acts as a loop. Because every page responds to this method and will do exactly what is happening here for the root page, each page will search it's children for a slug matching the slug from the incoming url and any found page will likewise call find_by_url to search it's children as well.

There is some room here for some optimization in the way we do a lookup for a page, but for now it works and we can get to the refactoring another time.

When no slug is found: customizing the finder

If the slug_child is not found (and no child matches that slug) then this if slug_child block is never hit and we move to the next step. This is where the magic happens for subclasses of Page:

children.each do |child|
        found = child.find_by_url(url, live, clean)
        return found if found
      end

It asks each child of the current page if it responds to find_by_url and returns any found page.

So even if none of the pages are found by the slug, we still ask the children if they respond to find_by_url. Why would we do this?

The answer lies in one of the included extensions: Archive.

The ArchivePage is a subclass of page which provides it's own find_by_url method. The ArchivePage#find_by_url will check the incoming url for it's details and if it meets certain requirements (namely that there is a standard date format in the url such as 'articles/2010/06/22') then it will find the appropriate page type such as ArchiveDayIndexPage, ArchiveMonthIndexPage or ArchiveYearIndexPage and return the proper page. If none of those are found it just calls super and calls the original Page#find_by_url.

This can act as your router for your custom page types. If you want to return a particular page type, such as a ProductsPage and your url is '/products/1234' then you can create a ProductPage which has it's own find_by_url method and would find your ProductDetailsPage to display a standard view of all of your products based upon the slug '1234' which I'd assume would be a product id, but could be anything you want.

Handling 404

Lastly, if none of this finds any pages to return, Radiant has a FileNotFoundPage page which allows you to easily create your own 404 error message for content that isn't found. You can subclass a FileNotFoundPage page to provide your own behavior there too. But when searching for a match to an incoming url, Radiant will find deeply nested 404 pages. So you can create a FileNotFoundPage as a child of your root page, but you can also create a FileNotFoundPage as a child of your ProductsPage to return an appropriate message to someone looking for '/products/not-a-valid-url'.

Here's the code for that last step:

file_not_found_types = ([FileNotFoundPage] + FileNotFoundPage.descendants)
      file_not_found_names = file_not_found_types.collect { |x| x.name }
      condition = (['class_name = ?'] * file_not_found_names.length).join(' or ')
      condition = "status_id = #{Status[:published].id} and (#{condition})" if live
      children.find(:first, :conditions => [condition] + file_not_found_names)

The live flag comes into play here again and optionally allows you to find pages that are not published. By default live is true, so in this instance we only check for a FileNotFoundPage that is published.

Radiant has a 'development' mode which would find unpublished pages, but that's a subject for another discussion.

I hope this gives you a good understanding of how Radiant finds its content, and how you can easily bend it to behave differently by creating a subclass of Page and writing your own find_by_url method. If I've left anything out or if you want me to cover some other aspect, let me know in the comments.

Combining show_for and stonewall

I'd been doing some thinking about simplifying the display of records in Rails applications and fortunately came across show_for.

show_for is a formtastic-like approach to displaying details about database records. It helps you DRY up your views and can even reflect on associations with code like this:

<% show_for @user do |u| %>
  <%= u.attribute :name %>
  <%= u.attribute :nickname, :in => :profile %>
  <%= u.attribute :confirmed? %>
  <%= u.attribute :created_at, :format => :short %>
  <%= u.attribute :last_sign_in_at, :if_blank => "User did not access yet",
                  :wrapper_html => { :id => "sign_in_timestamp" } %>

  <% u.attribute :photo do %>
    <%= image_tag(@user.photo_url) %>
  <% end %>

  <%= u.association :company %>
  <%= u.association :tags, :to_sentence => true %>
<% end %>

But since I'm working on an application which uses stonewall (which allows you to limit the display of a record's details), I needed the 2 of them to work together.

stonewall allows you to guard the display of your data from your models:

stonewall do |s|
  s.varies_on :aasm_state

  s.guard_attribute :secret_cultivation_details

  # ... role details here
end

It provides a allowed? method which you can use to ask an object if the current user is allowed to see the content.

So I quickly put together show_for_stonewall which will limit the display of certain fields if stonewall prevents it. It was as simple as using alias_method_chain to check that an attribute is not allowed

!allowed?(@object, current_user, attribute_name)

You can edit your show_for translation yaml and add a redacted key for the content to display if a field is not allowed for the current user.

Adventures in Javascript

I wrote a simple Javascript library for myself in 2006 called simpleDOM.

At the time, there was a problem with using innerHTML in Internet Explorer to write content to a page and then select it when traversing the DOM or submit a form with the added content. Not only that, but it's expensive to process and manipulate DOM objects in the browser while they are displayed so this library gave me an easy way to create document fragments and manipulate those to then be injected in the right place.

Over time, I got into working with other libraries and problems like this were solved for me, so I never did much more with it.

I've still got it working in one production site but haven't looked at it in a long time. I thought I'd put it out there on github for posterity.

Enabler: control Radiant sites

I've put together an extension which will allow you to turn on and off your instances of Radiant.

Enabler will allow you to post to your sites to disable and enable it's ability to serve content. You might use this extension if you host sites for your clients and they pay to you a monthly fee for service. If your customer has refused payment or if their payment bounces you can post to the_website.com/admin/disable/your-api-key and the cache will be cleared and the site will serve a simple message that the site is down.

You can change the message by adding a message parameter to your disable post. Just post to the_website.com/admin/disable/your-api-key?message=Please%20pay%20your%20bills

This will allow your customers to continue to login to the admin area but they won't be serving up any public content until their bill is paid and you post back to re-enable the site.

It's built for Radiant 0.9 and you can just gem install radiant-enabler-extension and load it up in your environment.

This isn't an extension you'd use manually and you'd probably want to tie it to a billing service such as Spreedly so that a site is automatically turned off when there is no payment.

To prevent delicious malicious hackers from scanning sites for this extension it will respond with a typical Radiant response for the URL rather than a 403 Forbidden error code as you might expect. If someone posts to your site with an invalid key, Radiant will simply respond with the usual 404 from your FileNotFound page.

If you think it could use a feature to turn off admin access too, just code and send me some pull requests.

Radiant projects and Rails applications

Whenever I talk about Radiant with developers in the Ruby/Rails community a few things come up in the conversation.

  1. Why does it use extensions instead of plugins?
  2. Choosing Radiant means you're choosing to create a Radiant project and not simply integrating a great CMS into another application.

These are 2 issues that will be addressed.

First, I think it makes the most sense for extensions to become more like plugins rather than do some cold-turkey switch over. Commits like this one make extensions much more predictable for Rails developers. Slowly converting extensions means a smoother upgrade path for developers.

Second, while you currently can't easily include Radiant itself as a plugin, we'll be working on that and I'd love to hear ideas about how to do it. Mislav Marohnic has done some work to update Radiant to Rails 3 and I think the future is bright for an easy to use and extend CMS in your Rails projects.

And there's an extension to Radiant which allows you to alter your controllers to use views defined in the Layouts section of the UI. I haven't personally worked with it in a while, so it might need some tweaks to allow plugins to do the same.

As I mentioned in a previous post, Radiant easily loads application plugins just like a regular Rails app, so you're not entirely restricted in your application development to what Radiant provides.

But even when I talk to people about the project and they find that I'm leading the development, the negative comments become a bit more sheepish. But I'd want to hear the complete opposite. If there are problems or complaints with application integration, the project can't grow to solve them unless they are heard. By all means, complain loudly but if you have suggestions write some code too. Brian Doll wrote about his opinion on CMS development and created some code to back it up. His post is embellished a bit, but the squeaky wheel gets the grease (be it your own, or some from the community) so it's good to hear reasoned arguments for different issues. The problem with this, I think, is that I hadn't heard the complaints, but I don't know if they were mentioned on the email list. On the other hand, Rit seems to be trying to solve a problem that isn't a particular case that Radiant is geared toward so the split in philosophy is just fine.

I'm always looking for feedback at DCRUG and talking to people at RubyNation was great. Everyone at B'more on Rails who comes down to DC are always really welcoming and are happy to offer lots of feedback about Radiant, so I'm hoping to get up there for their events too.

Patrick Peak of BrowserCMS fame and I often chat about the approaches to content management at DCRUG and it's a great way to think about systems in a different way. If you're working on a project that needs a CMS, has a CMS, or might involve a CMS, then give me a call at 571-403-0338.

Radiant and plugins

Radiant loads plugins just like any typical Rails application.

If you want to load custom routes, they are loaded from the plugin's config/routes.rb file. Custom controllers and views? They are pulled from the plugin's app directory.

So if you need to pull in an application plugin which does all this for you, you're free to do so. There's still plenty of work to be done with the core to make things simpler, but loading plugins like a regular Rails application is no problem. You're still constrained by the code in ApplicationController if you inherit from that, but you don't necessarily need to inherit from ApplicationController for any particular reason.

But Radiant's extensions can also bring plugins with them. So, for example, if you want to work with the AdminUI in Radiant but want to pull in a plugin's code there, you can work with vendor/extensions/your_extension/vendor/plugins/your_plugin. Yes, that works too, even the config/routes.rb

In both your project's vendor/plugins and your extension's vendor/plugins you can load the interface you need with controllers, views, models, metal, etc.

If you're working with Radiant, you might be able to use some development direction or even a simple consultation to give your plans a head-check. Give me a call at 571-403-0338

Commenting in RadiantCMS with a gem

The comments extension is now a gem.

Add config.gem 'radiant-comments-extension' and do everything else as you would with a regular extension installation.

Update: I should note this in the article too since Jeff asked about this in the comments. Public files are copied into your project with rake radiant:extensions:update_all rather than rake radiant:extensions:comments:update.

GMail User Experience

I was happily tapping away at my keyboard in Gmail when upon hitting the send button I saw this error message asking me if I meant to attach some files.

It says "You wrote 'are attached' in your message, but there are no files attached. Send anyway?"

Even though I wasn't actually referring to any attached files and had no plans to attach any, I was so happy to see that. I've both sent and received plenty of emails with no attachment only to have a follow-up 10 minutes later with a message saying "Oops. I forgot the attachment, here it is."

There's not much of an incentive for Google to spend time working on adding a feature like that and this is the only time I've ever seen it occur so it's not like they need to keep up with anybody in that regard.

A great thing is the sum of tiny details.

Bad interface design at Bank of America

I've been using Bank of America for online banking for quite some time and while their service is usually great, their interface design is sorely in need of some love.

Exhibit A:
I've been using Bank of America for online banking for quite some time and while their service is usually great, their interface design is sorely in need of some love.

Exhibit A:

This example shows 2 very different ways to allow a user to choose a yes or no answer in the same form. "Radio buttons or a select list!? Oh, it's such a tough choice... let's just use both!"

Exhibit B:

You would think from this image that the "Paid Invoices" tab has been selected. It is not. The Unpaid invoices is selected currently. This caused a ridiculous amount of confusion when I first began using this service. I've written to them about this, but apparently it's not enough of a problem to change.

But really, Bank of America, what is going on in your UI design department?
This example shows 2 very different ways to allow a user to choose a yes or no answer in the same form. "Radio buttons or a select list!? Oh, it's such a tough choice... let's just use both!"

Exhibit B:

You would think from this image that the "Paid Invoices" tab has been selected. It is not. The Unpaid invoices is selected currently. This caused a ridiculous amount of confusion when I first began using this service. I've written to them about this, but apparently it's not enough of a problem to change.

But really, Bank of America, what is going on in your UI design department?

JRuby, Rails, and OC4J on OAS... oh the pain

Basic Rails app in version 2.3.4.

I kept running into errors like:

Error loading listener 'org.jruby.rack.rails.RailsServletContextListener', class not found

Setting up JDBC Resources became painful too. OAS, it seems, doesn't really care what you do after you first create your connection details. I kept testing my connection with changes to the settings and it kept running the test with the original information. To fix this, I had to delete the connection and recreate it.

I saw bugs pop up like:

09/09/11 15:56:05.734 dms: Servlet error
    java.lang.OutOfMemoryError: PermGen space
    09/09/11 15:56:07.998 dms: Servlet error
    java.lang.OutOfMemoryError: PermGen space
    09/09/15 11:09:21.756 dms: 10.1.3.4.0 Stopped
    09/09/15 11:09:22.325 Error in bean MBeanServerEjb: Error
    deserializing EJB-session 'MBeanServerEjb': null
    java.io.EOFException
      at java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2502)

It turns out that I'm not alone in this experience. The difference in my case was that we were already giving a gigabyte of memory to our instance and we still saw this problem. So my habit became bouncing the OAS after a few deployments.

Radiant Config in source control

I'm currently maintaining the settings extension which provides a nice interface for editing the Radiant::Config settings.

In general it's a nice extension for people who know what they are doing, but I tend not to use it in production sites. The settings are not likely to be updated often, and I hate to add to an interface where more features are not absolutely necessary.

Of courI'm currently maintaining the settings extension which provides a nice interface for editing the Radiant::Config settings.

In general it's a nice extension for people who know what they are doing, but I tend not to use it in production sites. The settings are not likely to be updated often, and I hate to add to an interface where more features are not absolutely necessary.

Of course you should be backing up your database, but if you want to store settings with your source control and manage updates with deployments you can use 2 lesser known rake tasks in Radiant:

rake radiant:config:export
    rake radiant:config:import

Those will generate and import config/radiant_config.yml file for you which you can use during your deployment process.

UPDATE

I should also mention the extra details. If you have multiple radiant applications running on a server, you can set the RADIANT_CONFIG_PATH to some location where you share your standard settings. Just run:

rake radiant:config:import RADIANT_CONFIG_PATH=/var/custom_radiant/custom_config.yml

And you can put all your standard things like settings for paperclippedse you should be backing up your database, but if you want to store settings with your source control and manage updates with deployments you can use 2 lesser known rake tasks in Radiant:

rake radiant:config:export
    rake radiant:config:import

Those will generate and import config/radiant_config.yml file for you which you can use during your deployment process.

UPDATE

I should also mention the extra details. If you have multiple radiant applications running on a server, you can set the RADIANT_CONFIG_PATH to some location where you share your standard settings. Just run:

rake radiant:config:import RADIANT_CONFIG_PATH=/var/custom_radiant/custom_config.yml

And you can put all your standard things like settings for paperclipped

An army of volunteers with daytime jobs

I love working with Radiant CMS and the community and available extensions are growing by the day. New and enthusiastic people come to the project and are won over. There are a lot of great ideas out there about what it could and what it should become.

Like any project ideas are all over the place, but the actual workers might be hard to find. John Long recently called out for people to get more involved and help out if you love it or even if you hate it but want it to be better.

My favorite image to illustrate the way that Radiant is developed is this one. Radiant developers are weekend warriors (and are apparently really busy doing other things on Thursdays).

If you want things to get better, or to change in any way, blog about it, find bugs and request features, and best of all, vote with your code.