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!

Ruby FuzzyHash

I came across an interesting way to use a Hash at http://github.com/joshbuddy/fuzzyhash/tree/master.

I may look into this further for Vapor since this is basically what that RadiantCMS extension needs to do.

I've altered the sample code but it does all the explaining:

>> hash = FuzzyHash.new  
>> hash[/^\d+$/] = 'number'  
>> hash[/.*/] = 'something'  
>> hash['chunky'] = 'bacon'  
>> hash['foo'] = 'vader'

>> hash['foo'] #=> 'vader'  
>> hash['food'] #=> 'something'  
>> hash['123'] #=> 'number'

Rails Metal in RadiantCMS

Radiant edge now supports loading Rails Metal from extensions!

I'm really excited to announce that. I had been working on it before the release of 0.8.0, but hadn't had the time to hammer it out before the release. Admittedly, I push some sloppy commits into the main repo, and I should have rebased them and cleaned them up. This was the first opportunity I'd had to look around at the way Rack middlewares are loaded in Rails, so a lot of my effort was just poking around. Gregg Pollack's screencast on Rack & Metal had some helpful tips in it. Check it out if you want to try out the new features in Radiant.

This will make things like checking a login status (like we're doing with Practice Greenheath and the Header Authorize extension) much faster.

More importantly, Vapor(the extension to allow users to write their own redirect rules which also caches all the rules so its nice and speedy) will be moving to metal. I've created a separate branch for this, but I'm considering backward compatibility so that if you've got an older version of Radiant, it'll still operate the same old way (by catching the requests in the SiteController). I'll need to re-evaluate the code before it goes into the master branch, but it works!

Managing ALL of Your Content

We've been working hard to provide great features to our customers that are using Radiant and we've been pushing a lot of that work out into the community. We've still got plenty more in store for our extension development and of course a lot more to contribute to Radiant.

One of the problems any organization might face is the task of keeping track of your content. Of course, you'd look to a content management system like Radiant, but even beyond your content is the content from others. Many organizations have relationships with others and their content is often linked back and forth. As the content changes, some things may get stale and we're working on a way to keep track of that.

Your content isn't just your content. Your site depends on its environment and you'll need to react to any changes in it.

When you're writing content for your site, you shouldn't just be worried about what you've got. You also need to make sure that your target sites (or sites on which you might comment or to which you might send your visitors) are up and running too. Site Watcher is a great way to keep track of what's happening on your own site and we'll be adding more features there, but there will be more to come for tracking the rest of the world too.

I'll be posting more about this in the future as we get ready to release our upcoming projects.

Pretending to Be Different

It seems to be very common to say in many ways that you are different, but when it comes to showing it, many companies must be too afraid to actually be different.

I've seen a TV commercial for the Audi Q5 which claims that it is unmistakable. From the behavior of the actors in the ad I would guess that it's supposed to be unmistakably different, but when it comes time to show how different it is you are shown that it is... black. The other cars, you see, are beige. Never mind that they look the same in every other way.

This ad just plain confuses the point:

Are you doing this? Is your company claiming to be different but not showing your differences?

What I Learned From a Vacuum

How often does someone want to buy a vacuum? It seems like just one of those things you do and then never do again until 20 years later when your vacuum finally dies.

Dyson makes me want to buy their products. I wonder, when I see their advertisements, if I'm the only one that feels that way. Certainly not, but actually wanting a vacuum is outside the realm of what people usually desire when it comes to material things. A new car, new iPod, new camera, sure. But a vacuum?

Perhaps its the fact that I'm already a satisfied owner and bummed out that I bought one so early that I'm missing out on the new features. They certainly handle their marketing well. They describe so clearly why their product is better; then they follow-through.

A few years ago my wife and I vacuumed our house (during some construction) with a shop vac thinking it would do the best job. We followed up with our Dyson and were astounded at what the supposedly powerful shop vac had missed.

alt textI recently received an email from Dyson about their new airmuscle and it got me thinking about how I need to improve. Dyson shows the process and explains in plain words their complex technology. I'm looking at that for some inspiration. Many clients avoid decision-making because of a lack of understanding. Technology is always increasing its pace in involvement in our lives and businesses, but understanding of it isn't... at least not for all.

Whenever I speak with someone about our work and how we might help, I first try to help them understand. Because even if I win your business, if you don't walk away with a better understanding then you've lost something.

More updates for Radiant comments

I'm slowly working on improving the commenting extension for Radiant.

The simple logic spam blocking has been working fine, and I've just updated it to ensure that the answers are not revealed in the HTML. Originally I just dropped the correct answer into a hidden field, but now the correct answer is hashed so that the value in the form is something like fc7272f83a6dbcfea6a3c81d3eb10e2e rather than the actual text.

Enjoy the more secure simple CAPTCHA system!

I've also added the ability to specify the number of comments per page on the admin side with Radiant::Config['comments.per\_page'] = 100. To any contributors, I am adding some features from the wild, but please write specs for your updates!

I hope to simplify the interface for managing comments as well, but that will come in time.

UPDATE

I've also sanitized the content_html so that you're no longer vulnerable to inserted script elements.

What partnership means

I subscribe to a newsletter from Bright Yellow Jacket where they always seem to have good insight (and you can sign up for it too).

In light of Paul's recent articles (Developer As Typist and Developer as (Fashion) Designer) on what a developer is or is not, I found the latest newsletter from Bright Yellow Jacket to be apropos.

Too often, many businesses may find a designer or developer in order to dole out commands rather than explore what a relationship with that vendor may provide. I thought it would be appropriate to re-broadcast the idea:

Two weekends ago, Sean O’Hair won the Quail Hollow Championship. Paul Tesori also won. Who’s Tesori you may ask? O’Hair’s caddy. This is important. As a golfer, you want someone to carry your clubs. It makes for a more enjoyable day of links. But you need more than that. You need a professional, and better yet, one that knows the game. A professional caddy can provide a second opinion; knowledge of the course; feedback on your shot, club choice and approach that is tailored to your skill set. O’Hair’s caddy carries his clubs, he’s also an ex-PGA player, but most importantly his caddy is his partner.

These are important ideas. I don't say this because I'm in a company that provides partnership like that, I'm in a company that provides partnership like that because these are important ideas.

I really like the way the folks at Bright Yellow Jacket created the analogy. When you want a professional job, you don't just hire someone to drag your clubs around for you.

Who says Perl is dead?

Many in the Ruby world might not be aware of the development in Perl 6 but a good friend pointed out an interesting post about Cross-language library loading on Parrot.

If you're not familiar with it, Parrot is "a virtual machine designed to efficiently compile and execute bytecode for dynamic languages." It's written in C, and the 'rakudo' implementation of Perl 6 runs on Parrot.

Take a gander at the rough-around-the-edges Ruby compiler for Parrot on github.

WolframAlpha Needs Work

There's been a lot of hype around WolframAlpha so I thought I'd test it out today.

Here's what I think so far:

  1. Simple facts might be hard to get
  2. The name is a mouthful
  3. The brand is inconsistent
  4. I'm curious about the revenue

Simple facts might be hard to get

I was away from a computer when I first thought to test it, but I had my iPhone on me, so I quickly went to the new site and tapped "number of chromisomes in a chimp" and hit "Go". Oh well, I thought. I mis-spelled chromosomes (easy to do in a web form on the iPhone), but it should give me a proper spelling to click.

Wolfram|Alpha isn't sure what to do with your input.

Hmm, ok. I tried the proper spelling and full name of the animal: "number of chromosomes in a chimpanzee".

Wolfram|Alpha isn't sure what to do with your input.

Perhaps I'm using it wrong. Google got me the answer pretty quickly even though the answer didn't come from Google (but another site on the internet.) So I tried out what was supposed to be a Google-killer: Cuil (improper spelling to be fair).

No results were found for: number of chromisomes in a chimp

Proper spelling? That got me to a place to look.

According to Wolfram: "We are not a search engine. No searching is involved here". That's fine but a search engine got me my answer. It's 48 by the way.

Forbes magazine had this to say:

...Wolfram Alpha's biggest strength may also be its biggest weakness: The datasets behind its calculations are curated by hand—hundreds of them—and somebody plugged them into the system.

Uhh... really!? I guess that's a necessity for a useful and friendly tool but I was still shocked when I read that. Here's my contribution then:

Dear WolframAlpha,

A chimpanzee has 48 chromosomes.

Sincerely,

Jim

WolframAlpha is a mouthful

It doesn't roll off the tongue as much as it gets stuck coming out. That could be a really good thing if the product is really good. A name that's difficult to pronounce may increase the amount that people tend to discuss it... that gets the name around.

Aside from my search, it produces some great things which could be expected from the makers of Mathmatica. But I fear the advent of startup companies with names like DolphinFrankenfurter. Alright, that doesn't have the difficult "lfr" in the name, but it'll do for a me-too company name.

The WolframAlpha name should be more consistent

The logo seems to say WolframAlpha, but everywhere else that it is written in reads Wolfram|Alpha... What's with the pipe and it's inconsistent usage? Is it WolframAlpha, Wolfram Alpha or Wolfram|Alpha?

I'm curious about the WolframAlpha source of revenue?

Forbes magazine also points out that:

It would be more than foolish to assume Wolfram Research can monetize Wolfram Alpha in the way Google has monetized its search engine through its lucrative paid search business...

It would, wouldn't it. Because that's not the same target market. And making money on ads might mean you need to ensure that a lot more information was readily availabile. Such as the number of chromosomes in a chimp.

Wolfram Research has not said how it intends to monetize its new search engine, but a preview of it offered to Forbes hints at one approach: There's a "Featured Sponsor(s)" module hidden in the code, commented out by the developers. The copy used is dummy text, but suggests that one revenue stream for Wolfram Alpha will be from display ads.

Hmm. I already see ads appearing for featured sponsors, so perhaps that's the route they've chosen. But perhaps a better path would be to charge for it. If it's a great service that is worth money then people will likely pay to use it. It works for Lexis Nexis.

How to write a RadiantCMS extension

If you're looking for the short description that boils down the answer: start. Just start writing an extension and you'll know more after that than you do now.

Radiant is a great system for setting up a website. Many developers who contribute to the core or contribute extensions to the community might forget that there may be plenty of users of Radiant who want to do many things with it, but they just don't know how.

That is part of the reason I wrote the Help Extension. Sure, regular users need help understanding how the system works , but people who provide Radiant to their clients could make the entire community better if they could just get past the point where they begin to understand. "Help" is an apt name for the extension, but there may be a downside in that many Radiant developers may not realize that there's plenty of info in there about how to interact with Radiant through an extension.

I do my best to help new developers. I once was a new developer. Scratch that; I still am a new developer.

Start small

Many of our extensions are small in scope. That's a great way to learn and a great way to pave the path to write something more substantial. You can ask for Radiant help on the email lists. Sometimes the community may come to your aid or sometimes the help doesn't come. That may be because of timing (and everyone is busy with other things when you ask for help) or because no one else particularly cares about what you want to do.

Well how do I get help if nobody cares!? Easy. Ask a technical question. But in order to do that, you need to start.

Start building your extension and when you get an error or arrive at an impasse in your code, you'll have plenty of info to either solve the problem yourself or get help from someone else. If nobody cares about your desire to list widgets on your site, someone may be interested in solving a technical problem with you. Developers are like that.

Throw away your code

One of the things I learned in a Drawing class in college was to be willing to throw something away. So many aspiring artists hold on to every piece of work they've done. They might spend hours upon hours on their work and feel like they have reached a pinnacle when the work is done. But they should always be willing to lose the final piece. You should learn something by doing. You should be able to do it again, even if you do it differently the second (or third, or fourth) time around. The same goes for development when you are learning. The benefit of writing code is that you can put it into source control and all those ideas that you first had and threw away, are safe for you to find again.

If you allow yourself to be willing to throw your code away, you'll be helping yourself learn.

Begin again

Solve the same problem twice and you might discover something you hadn't realized the first time around. While this is not an article about the mechanics of writing extensions for Radiant it is about writing extensions and in order to do that, you must first begin.

After all that, this post isn't really about Radiant, it's about not fearing errors, and about not feeling that editing mere code is a daunting task. I want to encourage any would-be developers to be.

Look for saturnflyer in the #radiantcms IRC channel or on the mailing list. Not every experienced developer may help, but most are happy to help. Just come armed with your info: code, error messages, ideas, etc.

View, Edit and Search your RadiantCMS Database

I came across a handy plugin for Rails applications (version 2.2 or higher) that provides a simple way to view, edit and search the database. Read about it at Neeraj Singh's website.

I thought it would be a useful way to view data in radiant, so I quickly hacked together an extension to view the interface of admin_data.

Originally, the plugin was designed to look into your app/models directory and pull all the classes. But because Radiant loads classes from several locations, it was simpler to just alter this code:

model_dir = File.join(RAILS_ROOT,'app','models')
    Dir.chdir(model_dir) { models = Dir["**/*.rb"] }

to look at the database tables and assume you've followed Rails conventions and load class names according to the table names:

models = ActiveRecord::Base.connection.tables.map{|t| t.singularize.to_s }

The upside of doing that is that it will easily integrate into a system like Radiant.

In a quick test, I found that I also needed to provide a way to alter data in the database without calling validations, so I also provided just that: a button to "Save without Validation!"

At the moment, the extension documentation states that our fork is required and that may remain. The benefit of the original design is that admin_data just looks in the file system for the necessary information, and it preserves any non-standard table naming conventions defined in the models. The downside is that Radiant can't use it like that unless there is some configuration for where to load the models.

Both the extension and the plugin need more tests, but it looks to be a simple way to achieve a separate admin view of data without the need to write all the controllers and views.

I've added it to the extension registry so you can just do a script/extension install admin_data but you'll still need to install the plugin.

How to get what you want

An email from the great Campaign Montior landed in my inbox today and contained a link to a helpful article about getting information on why users have unsubscribed from your email campaign.

This is such a simple concept but it seems that so many seem to be unaware of it. I've recommended this same technique to others in regard to just about anything: if you want something, ask for it.

Many not-for-profit organizations understand this, others seem to miss it. Certainly the way that you ask will have an affect on the answers, but not asking is a great way to not get what you want.

  • Before you leave an interview ask for the job.
  • Ask someone specifically to volunteer for your event.
  • Get a great developer on your open-source project by asking.
  • Need funding for an expansion to your school/university/community center/whatever? Ask for it.

People respond to questions, and asking them with a certain approach will help you get your answer. Take a listen to some great advice for managers on how to delegate tasks (it involves asking for help).

Asking for what you need directly affects your future. By not asking, it means you're just waiting for your future to happen to you.