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.

Another way to make navigation

John Long posted a great write-up of different ways to make navigation in Radiant. But there's a certain way that I like to skin that cat which wasn't covered: using <r:find>, <r:children:each> and <r:if_ancestor_or_self>.

See it at this gist

Non-standard extension locations in Radiant

Sometimes I'll have the need to load extensions from a non-standard location.

I might have 2 instances of Radiant installed on the same server which may need similar but different functionality.

I can keep the code updated for both if I symlink their extension directories to "/var/radiant_extenions" for example, but then I'd need to mess with the environment files to specify the loading of extensions with config.extensions = [:this, :that, :the_other] in one and config.extensions = [:this, :that, :custom] in another if the requirements of the sites aren't exactly the same.

Instead, if I know I'll be just running "rake db:migrate:extensions" for the standard extensions I can place those in my shared location and symlink them as "vendor/standard_extensions" and put my site-specific extensions in "vendor/extensions". To get that working all you need to do is add this to each environment and I'm good to go: config.extension_paths << "#{Rails.root}/vendor/standard_extensions"

Rake tasks won't be loaded from there automatically though, so you'd need to be careful about what you put in your set of standards.

In the future, using extensions as gems will be nice, but creating a directory to manage standard extensions is helpful and just running "rake db:migrate:extensions" will load their migrations and bring them up to date in each database.

Code Spelunking: Radiant extension deactivate

A long time ago when Radiant was just a young CMS, it allowed you to activate and deactivate extensions with the click of a button.

This was simply an awesome feature. But there was a problem: it didn't work.

Radiant couldn't really know how you built your extension, and you can do whatever you want in there, so deactivating an extension might not actually deactivate certain parts. I've long wanted to take the time to look into Mixology to see how this extension deactivation feature might be able to make it back in, but the problem will still exist that you can build an extension however you want. Even if Mixology provides a way, it would only work with extensions built in a particular way; it can't solve everything.

Even so, the feature was removed because: it doesn't really matter, and nobody who worked on the core cared enough to put time in to solve a problem around what seemed to be an edge use case. How often do you need to turn off an extension, and does it really need to happen when the application is running? It would be nice, for example, to have a client upgrade or downgrade their account with the click of a button, which would remove or add features. I'm working on several ideas for this, but that'll be a discussion for later.

So what's with the deactivate method in your extensions?

It might as well look like this:

def deactivate
      # Ha ha! Fooled you!
    end

Mostly, because of the generated code in many extensions you'll just have something that says:

def deactivate
      admin.tabs.remove "My Extension"
    end

That deactivate method is there because it was formerly used. It's like finding a dinosaur bone in your back yard, but everyone has a dinosaur bone in their back yard so it's not special.

Is this deactivate method ever used? Lets look:

The Radiant::ExtensionLoader has a method that looks like this:

def deactivate_extensions
      extensions.each &:deactivate
    end

Clearly, this will call the deactivate method on each extension. And the Radiant::ExtensionLoader::DependenciesObserver has this method:

def before_clear(*args)
      ExtensionLoader.deactivate_extensions
    end

Aha! So extensions are deactivated before they are cleared... And they are cleared... um... never? A new DependenciesObserver is created but where is the @observer object used?

This is where we start digging into Rails. Radiant bundles Rails, so we can stay within the source and find that action_controller/dispatcher.rb clears the dependencies. ActionController clears the dependencies and the instance of Radiant::ExtensionLoader::DependenciesObserver has the message before_clear sent to it, which calls each extension to run the deactivate method.

Does this mean that you can use the deactivate method in your extension? Yup. Put whatever you want in there. But still... it won't do anything, or at least not much. Yes, the method is called (go ahead and drop a puts 'hello from the deactivate method' in there and watch your logs) but only in Rails development mode. In production, the dependencies are not cleared, your app just runs.

You can still use it, you could even build a controller for your extension to run the deactivate method and do something with it, but you're on your own as far as the Radiant core is concerned.

So why keep that method in there at all? We even have specs built around it to ensure that it works properly. Sure, this means a bit of code complexity when the feature isn't used, but it's minimal and perhaps deactivate will come back in some way.

At a code sprint weekend over a year ago we discussed this method briefly and decided to leave it in because... well, maybe it will be of use in the future. I'm sure that it will be removed at some point if it is never used but for now keeping it in means that it can more easily come back and extensions using it will merely do things like remove some admin tab only to add it back in with the activate method and only in development mode.

If you've been wondering about that method, well, there you are.

Kicking the Gem Extension Tires

Josh French has been working on loading extensions in RadiantCMS as gems. I walked through the process and pushed out some changes to the Vapor extension to be loaded as a gem.

There's currently no plan to cram this into the official release of the 0.9 version of Radiant, but I'd like to see it happen. I think that extension development and usage is a sticking point for the growth of Radiant and the community of extensions and developers.

Creating the gem was relatively easy using the jeweler and gemcutter gems, and then (after installing the radiant-vapor gem) I required it in my Radiant environment.rb with config.gem "radiant-vapor".

Radiant extensions (by default) have an update task which will copy public files to the project's public directory such as images, stylesheets, and javascript files. The current downside of using extensions that are gems is that the rake tasks are not available from the extension. We'll solve this problem in some way, but currently extensions such as Vapor won't have much of a problem since there are no public files to worry about and even though this task won't be found rake radiant:extensions:vapor:migrate, this one will rake db:migrate:extensions and will make the necessary changes to the database since the migrations are loaded.

More to come in Radiant

The next release of Radiant CMS will be a great one. The interface is definitely nicer and should make running multiple extensions much cleaner with the new navigation scheme.

One annoyance that's been in it for a long time is the lack of a tag reference on the Snippet and Layout screens where they are arguably most needed. I hope to get an update for that in the next release. Radius tags on Pages should be discouraged, in my opinion, for the sake of the end user's understanding of the system so it seems backwards that we'd have a reference on the Page edit screens and not in a place where it's most needed.

If there's something that you think is missing, or a bug that needs fixing, please go vote for it.

RedDot CMS is a POS

Quite possibly the worst experience I've had in working with a content management system is with RedDot CMS. I'm currently working with a client to update their site which is built in RedDot.

The manufacturer of RedDot claims to be "The Content Experts" but perhaps they exclude user experience from that.

I'm using RedDot, which is apparently old and has been replaced with an newer version with a new name. Hopefully they've updated everything about the user experience which is a mess of pop-up windows and cryptic ways to search for and link to images and content. One of the worst offenses, is that hitting the return key after entering text into a form to search for content for a link the page goes blank... Blank, because I hit return, and didn't click on the custom search image button which is the only way it seems to submit the form.

An unofficial blog asks the question, "Is RedDot CMS Dead?". Sadly, no. Even though it's been replaced, there are probably many customers out there of RedDot resellers who must suffer it's existence.

Being a developer and user of RadiantCMS makes me look at this from a particular perspective and will help me guide development of Radiant. With this experience, I'm officially picking a fight with other CMSes. I hope to learn a lot from other solutions and give my customers a better experience, but mostly I think its important to talk more about what is considered "enterprise" and show that the emperor has no clothes.

If you're looking for a CMS, it should be easy to understand and easy to update. If you're considering RedDot, or some vendor that provides it, think again for your own benefit.

Cache key for collections in ActiveRecord

I've been working on the performance of a Rails application and much of my recent work involves simply caching. It was an application that was an excellent proof-of-concept which quickly turned into a production application.

We've got memcached setup and running and now it's a speedy little interface.

But one thing I knew I needed but wasn't sure how to do was cache a group of records. Rails fragment caching is super simple for one record:

<% cache [object] do %>
  ... this will be cached based upon your object.cache_key ...
<% end >

That's fine and dandy until you want to cache the view for not just one object in your loop, but the entire rendered view of @objects. As much as I looked around, I found that nobody has written about this problem. I still wonder if I'm being naive and there is some obvious way to do this that has simply slipped by me.

When I first tackled this, I had just a handful of objects in one particular view and would never have more, so I just concatenated the cache_keys of all of them and I was done. In another instance (with a larger number of objects) I tried something different by prepending another object which could be changing and appending a string: cache [user_role, @project_application,'task_lists'] do. This was painful because it required that I ensure that all the objects down the relationship tree call :touch and update their belongs_to parent until finally my @project_application was updated.

The task lists in this application don't just have tasks, they have attachments and notes too and this just felt like the invalidation of the cache was far too intertwined among the related objects for my liking.

Finally I realized that it was much simpler than that. What I need here is pretty simple: a cache_key for a collection.

A group_key method on a collection would be so much simpler than any convoluted process I attempted. Here's what I did:

def self.group_key
  count = ActiveRecord::Base.connection.select_value("select count(*) from widgets").to_s
  timestamp = ActiveRecord::Base.connection.select_value("select max(updated_at) from widgets").to_s.parameterize.wrapped_string
  count+'-'timestamp
end
That makes 2 simple calls to the database to get a parameterized representation of the most recent count and `updated_at` value for my Widget. If any of my objects change for any reason or if I add a new record, my group_key will be updated. I took this and put together a simple gem called `group_cache_key` which you may find on [gemcutter.org](http://gemcutter.org/gems/group_cache_key). The source is a bit different in that it includes a max `updated_at` and max `created_at` and it doesn't go back to the database for the information:
def cache_key
  if self.empty?
    'empty'
  else
    update_timestamp = max {|a,b| a.updated_at <=> b.updated_at }.updated_at.to_i.to_s
    create_timestamp = max {|a,b| a.created_at <=> b.created_at }.created_at.to_i.to_s
    self.first.class.to_s.underscore+'/'+length.to_s+'-'+create_timestamp+'-'+update_timestamp
  end
end

I hope you find it useful. It'll give you a cache key value of something like widget/2-1253224342-1253311589.

To use it, grab the gem (gem install group_cache_key) and add this to your config/environment.rb:

config.gem `group_cache_key`

Let me know what you think.

Update

I've pushed a newer version of the gem out there which creates a hash of the given ids. The hash is created in the order given, so if you're doing a sort in your interface, you'll get an entirely different cache_key.

Changes in Radiant AdminUI for 0.8.1+

There's a lot of focus on the visual changes to the Radiant interface, but there are some functional changes that should be discussed too. I've started playing with the new AdminUI as I try to understand the changes (since I haven't had time to take part in the development). The first thing I tackled was figuring out how to rearrange the navigation items and it's not too easy to understand what's going on.

First, here's the new structure of the tabs:

NavTab #=> [NavSubItem, NavSubItem, ...]

NavTab is a subclass of Array and it's accessed via admin.nav although admin.tabs will still be available (with a warning of deprecation) for some time.

I'll be posting details like this on the Radiant blog once the code is more developed.

There's debate about where Snippets ought to go: content or design? Do site editors use them, or do designer/developers use them? So that's a good place to start.

In order to do this, I had to generate an extension. Then, in my extension, I added this to the activate method:

admin.nav['content'].delete_if{ |t| t.name.to_s == 'snippets' }
    admin.nav['design'] << admin.nav_item(:snippets, "Snippets", "/admin/snippets")

That's complex, but it moves the "Snippets" link from the "Content" tab to the "Design" tab. It should be simpler; something like this would be nice:

admin.nav['content']['snippets'].nav_tab = admin.nav['design']

but better yet, I'd rather have:

admin.nav_items['snippets'].move_to('design', :after => 'pages')

It doesn't seem too valuable to implement that just to move one thing, but with the new tab/nav structure I would think that there will be a lot more customization by users/developers.

1) Does that syntax appeal to you? (Why or why not?)

To make it even simpler, it might be nice to be able to define structure in an initializer or a yaml file somewhere so that Radiant would load it's default setup, but then read your changes and rearrange the navigation accordingly. I wouldn't want to tackle that until other questions are answered, but it's an idea for the future.

Next, the recent changes only allow you to specify visibility on the main NavTab and not on the NavSubItem.

2) Do you want to be able to specify visibility on the NavSubItem?

Using the above example:

admin.nav_items['snippets'].move_to('design', :after => :pages, :visibility => :admin)

Personally, I think something like this is a must. I see tabs as a way to organize links and not as much of an authorization mechanism. By that I mean that in order to hide a section to all but a specific role, you'd need to put it in it's own tab even thought it might already make sense in a place like "Content".

These recent changes are going to cause a bit of pain for developers to upgrade the numerous extensions out there, but it'll be well worth it in the long run; especially if it's easy to move things around.

Updates in Radiant have been causing some pain do to the major changes being made recently and if you're not well-versed in the core application it can be difficult to keep up. Please post your questions to the mailing list where people are generally very helpful. Although we'd all like to avoid upgrade problems, they sometimes comes from changes in Radiant or changes in Rails, and we are doing what we can to create problem free upgrade paths. But this doesn't mean that the creators or maintainers of the particular extension that you use are yet aware of your problem. Hence, the mailing list is your best option for help.

Thanks very much to John and Sean for hacking away and getting this into the master branch.

UPDATE: There's plenty of discussion going on about this on the development email list, and I've got some related changes that I hope to add to the core here http://github.com/saturnflyer/radiant/commits/tab

Fat Free CRM on Heroku

Heroku is an excellent way to easily deploy Rails applications. But there's a trick to doing it: it's a read-only file system.

To some, this may be old news, but to others who have come across a project using SASS, you might feel like your out of luck. Fat Free CRM, for example is a great new project, and it uses SASS. Try running it on Heroku and you'll start getting frustrated. If you're not familiar with SASS, it automatically generates static CSS files for you in production, meaning it needs write access to the file system.

So, what do you do? The good thing is Heroku has a plugin to help with SASS. But, in this instance, it doesn't seem to work. I've installed it with Fat Free CRM and have pushed up to Heroku, but I get errors so rather than debug the issue you can try another option: http://github.com/mooktakim/heroku_sass_and_cache/tree/master.

Here's a step-by-step. First, if you don't have a Heroku account, signup and install the gem:

$ gem install heroku

Next, get the Fat Free CRM code:

$ git clone git://github.com/michaeldv/fat_free_crm.git

And turn it into a Heroku project

$ cd fat_free_crm
    $ heroku create

You'll need to install a plugin to manage the location for SASS files.

$ script/plugin install git://github.com/mooktakim/heroku_sass_and_cache.git

Read the documentation for the plugin, but you'll need to add this to config/routes.rb:

map.heroku_sass_and_cache

Just drop that on the first line of your routes.rb file and commit all of your changes.

$ git add .
    $ git commit -m "feeling sassy"
    $ git push heroku master

That last bit will deploy your application to Heroku. Fat Free CRM requires that you run the rake crm:setup task.

$ heroku rake crm:setup USERNAME=myusername PASSWORD=mypass EMAIL=my@email.com

After that, you should have a working Fat Free CRM on Heroku. Try heroku open and you'll land at your new login screen.

This will get you going, but you'll find that minor things like storing avatars won't work because they expect to be stored on the file system. Missing avatars aren't an application deal-breaker and I imagine that there might be some options in the future for separate storage options, or even just using Gravatar.

If you have any luck with the official Sass support on Heroku, leave a comment and I'll update the post.

Textpattern on Github

I'm a former user of Textpattern and I'm glad to see it more easily accessible on github. I hope it'll be easier for other users out there to contribute to the project.

My path to abandonment began when I built a real estate management interface on top of Textpattern. It was an interesting experience to say the least. The system is built to provide clear methods like graf() which I'm sure you would guess is a way to display an HTML paragraph element...

And of course, for your developing delight there are other methods like sLink(), eLink(), wLink(), dLink(), and aLink(). I'll leave it to you to guess what features they implement. My favorite is listed in there too.

I am glad, however, that in the very unfortunate event where I might need to use Textpattern again it'll be much more accessible to track other forks and branches. Cheers to the Textpattern community, from a thankful but recovering user.

Why you should use Devver

For good reason, developers share concern over not only a well-tested application, but also a regularly tested application. If you are managing a group of developers and are responsible for the outcome, wouldn't you first find a solution to allow your developers to run the test suite whenever they want? If your team members are running tests whenever they want on your massive application it'll probably slow them down, so of course you'll implement a continuous integration server. But then you're left with the requirement that they checkin their code. That could be a painful situation if they checkin some feature breaking hack. Enter Devver.

I'm participating in the Devver beta. When I got my invitation I immediately starting testing out the RadiantCMS source. While the code base is relatively simple, Radiant has some great test coverage including Cucumber features.

Radiant is a bit of a strange bird when it comes to Rails apps since it has it's own Radiant::Initializer rather than a Rails::Initializer. Devver wasn't quite expecting this, but Dan, Ben, and Avdi were extremely quick to respond to my requests.

There's been a good deal of development around the problem of running a massive test suite for an application. There's CruiseControl or CruiseControl.rb, and Integrity or Inotegration, or RunCodeRun and plenty of others that I'm missing. It seems to me that the problem with those approaches is that you need to checkin your code. In some respects, it's too late to run your tests when you've already checked in the code.

Of course you can use things like ZenTest with autotest. And there's spork for speeding up your tests as well. But a massive test suite that takes a while to run is something you might like to avoid running locally. Offload that to a server! Run rake devver:spec or rake devver:test. It's a very simple process.

You could, of course use testjour if it's a viable option in your development environment, but Devver is still probably much simpler to setup.

Installation is simple.

  1. Download and install the Devver gem
  2. Download and install the Devver Rakefile
  3. Configure Devver to use your API key
  4. Declare your gem dependencies
  5. Run it!

I've not been compensated in any way by Devver. They've taken a great idea (run tests before a code checkin) and made it easy and fast; and I thought you should know about it.

External website resources

I wrote a recent post about managing ALL of your content where I discuss the idea that your content is not just the content on your site.

In my continuing research for better ways to manage content I came across a real gem. Net::DNS which is a helpful tool for resolving DNS and gives you a different perspective than Ruby's Resolv.

If you want to check if a domain is valid then something as simple as this might do the trick:

Net::DNS::Resolver.start("google.com").answer.size > 0

But of course, there's much more to it than that. Check it out!