February 23, 2007 · 1 comment
Rails is great. I love it. I want it to have my babies. But it's not so good at making folks who are stuck in the world of PageMaker and Dreamweaver feel at home. I have some clients who have bricks of html and they just want it to go web-side. Some are actually pretty web-savvy but they don't have any interest in learning rhtml. Or Liquid. They don't want to re-write their html in markdown or textile. And they certainly don't want every revision of a page to go through me and have me charge them just for Railsifying their document. They want to make a page in one of their WYSIWYG editors and post it online. That's it.
Any app is bound to get to a size where it needs an 'About' page, a Privacy Policy, or a Terms of Service. These are not worth their own controllers and often should be managed by the site's owners - not the developer. Ergo: the SimplePages plugin.
Update: The plugin now uses page caching - so you get all the benefits of a db-powered Rails app and all the performance of a static page!
View Plugin in SVN
note: Simple Pages requires the new-and-awesomeproved Engines plugin. Engines is now lightweight and allows any plugin to behave more like a Django app (a feature sorely needed in Rails).
What is SimplePages?
SimplePages provides a full controller/model/view/helper/migration/test stack for managing plain html pages that are editable right from your browser. You paste in your html and everybody else sees your Rails layout with the html inside. It's cake.
How to install SimplePages:
ruby script/plugin install -x http://svn.rails-engines.org/plugins/engines
ruby script/plugin source http://svn.6brand.com/projects/plugins/
ruby script/plugin install -x simple_pages
Add the following line to your config/routes.rb file:
ActionController::Routing::Routes.draw do |map|
# all your important routes
map.from_plugin :simple_pages
# some lower-priority routes
end
And, finally, install the migration with the following command:
ruby script/generate plugin_migration
If you navigate to yoursite.com/pages you'll find you can now start doin' stuff.
Enhancements you should do:
One of the first things you might notice is that errors start popping up with helpful messages. They'll guide you to figuring out how to integrate your authentication scheme into the plugin and they'll remind you if you forgot to do any of the necessary installation. The most important change you should make is to add a method in application.rb called can_manage_pages? and have it test for whether the current user should be allowed to do stuff.
Version control your pages!
SimplePages has built-in support for techno-weenie's acts_as_versioned. Simply install his plugin (and run SimplePage.create_versioned_table if you've already installed SimplePages) and now you'll have access to all changes you make for your pages.
Tags:plugins
I've had a weird situation over here at StudioDanger headquarters with plugins going haywire. I had my app working real slick and then installed the new Engines plugin and the slickness continued unabated. A subsequent installation of the simply_helpful plugin gave me a crazy error:
"undefined method 'dom_class'"
There's this line in simplyhelpful where ActionView::Helpers::FormHelper is monkey-patched to call domclass and dom_id. These should be available because in the plugin's init.rb we see the SimplyHelpful helper injected into ActionController as a helper
require 'simply_helpful'
ActionController::Base.send :include, SimplyHelpful::RecordIdentificationHelper
ActionController::Base.helper SimplyHelpful::RecordIdentificationHelper,
SimplyHelpful::RecordTagHelper
At some point 'self' became an unmodified version of ActionController while simply_helpful was looking for it's special helper functions. Why did it to that? I spent three hours trying to figure it out. I have no idea.
You can patch it though. Just change domid and domclass to SimplyHelpful::RecordIdentifier.domid and SimplyHelpful::RecordIdentifier.domclass. That'll do the trick.
Tags:plugins
Gone are the days when you could just throw a few CSS definitions in something called ‘style.css’ and call it a good day’s work. With the arrival of semantic html where none of the styling is done inline it has become important to practice good coding habits while creating CSS styles.
It was embarrasingly recently that I had the habit of putting all my CSS into one file. I didn’t realize that it’s the same bad habit of making programs with only one (usually massive) file of source code. Learning to partition the code properly is a natural development step and it’s just as important for CSS as Ruby, Java, C, or any other.
How I do CSS:
—-
What I’ve found is I have a few default CSS files named after the following pattern:
- application.css: usually will overwrite bad default values (like margins on forms)
- structure.css: the layout of the blocks that make up my layout (body, #wrap, #head, #main, #foot, etc.)
- fonts.css: font-family, font-size, etc. for all the popular styles
- forms.css: whether using label/input pairs or definition lists I need lots of styling for forms
- colors.css: super important to keep all color stuff together so you can both keep the colors in harmony and replace this file with some alternate color theme
These worked for a while but then I realized that I had little pieces of code that were needed that didn’t belong in any of these. I need to specify that a certain logo floats to the right or a users profile just happens to need a different background color.
The answer to this comes in using CSS files kinda like Rails uses partials. Some encapsulized code that has usefulness in a certain portion of your app. I did this at first by making stylesheets named after each of my controllers. This worked for about three seconds until I realized that I needed some conditional way to include them.
Conditionally including CSS files
—-
Including stylesheets conditionally turned out to be a fairly easy task. I’ll outline the solution here using extractions from my code:
1
2
3
4
5
6
|
class ApplicationController < ActionController::Base
attr_accessor :stylesheets
before_filter {|c| c.stylesheets ||= []; c.stylesheets << c.class.name.sub('Controller','').downcase }
end |
This adds an instance var @stylesheets that starts as an array. It is given, by default the name of the currently active controller. note: this prints the derived class’s name, not ‘application’.
This means that you can now add stylesheets to the soon-to-be-rendered view anywhere in your code you want.
1
2
3
4
5
6
|
class UsersController < ApplicationController
# if you have a stylesheet that governs just one action
def show
stylesheets << 'users_show'
end
end |
And it’s surprisingly simple to output the required code to the browser. Just add one line to your app/views/layouts/application.rhtml file:
1
2
3
4
5
6
7
|
<%= stylesheet_link_tag 'application', :media => 'all' %>
<%= stylesheet_link_tag 'structure'', :media => 'all' %>
<%= stylesheet_link_tag 'fonts'', :media => 'all' %>
<%= stylesheet_link_tag 'forms', :media => 'all' %>
<%= stylesheet_link_tag 'colors', :media => 'all' %>
<%= @stylesheets.collect { |file| stylesheet_link_tag file }.join("\n") if @stylesheets %>
<%= javascript_include_tag :defaults %> |
This is so simple that there’s no real point in turning it into a plugin but if anybody wants it that way just let me know.
Update: The version I ended up using in my app did some testing for the existence of the file before it included it as a stylesheet. I decided it was more important to avoid the 404 errors (especially the way they cruft up the logfile) than it was to have the app disk-optimized. So here’s my new application_controller:
1
2
3
4
5
6
7
8
9
10
|
class ApplicationController < ActionController::Base
attr_accessor :stylesheets
before_filter do |c|
c.stylesheets ||= []
klass = c.class.name.sub('Controller','').downcase
c.stylesheets << klass if File.exists?("#{RAILS_ROOT}/public/stylesheets/#{klass}.css")
end
end |
Tags:plugins