Making Configuration Files with YAML: Revised

So back in July 2007 I posted a blog on making configuration files with YAML, and I’ve been noticing a lot of readership on the old article. Because it seems that a lot of people are reading it I felt it was important to show how I apply this nowadays.

First I put my config.yml file within the /config/ directory within my Rails application. It looks something like this:

development: &non_production_settings
  :google_analytics:
    :api_key: "[Enter Google ID]"
  :site:
    :title: "[Title]"
    :address: "http://localhost:3000/"

test:
  <<: *non_production_settings

production:
  :google_analytics:
    :api_key: "[Enter Google ID]"
  :site:
    :title: "[Title]"
    :address: "[Address]"

Then, I create a new file called load_config.rb within the /config/initializers directory. You can name the file whatever you want – that’s just what I call it. This is where the actually YAML loading is going to happen – and this is what it looks like:

raw_config = File.read(RAILS_ROOT + "/config/config.yml")
APP_CONFIG = YAML.load(raw_config)[RAILS_ENV]

Now any time I want to all one of these variables I just call it like:

<%= APP_CONFIG[:site][:title] %>
Advertisement

Optimizing the RedCloth Helper

In my previous post I posted several small Rails tips, one of which was a cleaner RedCloth helper. Unfortunately, this helper requires that the textile is parsed each time the page is loaded, and that can get nasty. So it our item is a text field called details, just add a details_html field to your model… then create a private conversion method that you call using a before_filter.

Something like:

before_filter :convert_details

def convert_details
  return if self.details.nil?
  self.details_html = RedCloth.new(self.details).to_html
end 

Then just display details_html in your view instead. This way the textile only gets converted when you save and make changes to it.

Rails Tip Roundup

I’ve been working on a few new projects lately and wanted to share a few little tips I’ve started doing.

Conditional Buttons for Shared Forms

Something that I like to do is use a shared form for both my edit and new views. Doing this however means I need to be friendly to the user interface and make sure the submit button is properly instructional. So for example if I have a Customer model with an instance variable @customer my button would look like this:

<%= f.submit((@customer.new_record? ? "Create" : "Update") + " Customer") %>

With this I’m checking to see if the @customer instance variable belongs to a new record and if so it’s outputs Create, otherwise it’s an Update button.

Cleaner RedCloth Helper

There is the built-in textile helper that comes with Rails, it’s carage return rendering is a bit lame so most people typically upgrade their RedCloth gem and using something like:

<%= RedCloth.new("My copy that requires formatting").to_html =>

I prefer to instead create an application level helper for redcloth (some people might instead overwrite the textile helper here, but I find that can be confusing to some people looking at your code for the first time). My helper looks like this:

def redcloth(str)
   RedCloth.new(str).to_html
end

So now when I want to redcloth something I just call:

<%= redcloth("My copy that requires formatting") %>

Simple Little Permalink

When I have a simple object that I want to create more user-friendly URLs for, I’ll create a basic permalink. In the instance of the same Customer model from tip 1 above, I like to use the customer name as the permalink. To do this of course the name has to be unique so make sure you are validating it’s uniquiness above all. Then I create a permalink column in the database table and write something like this in my model.

def name=(value)
  write_attribute :name, value
  write_attribute :permalink, value.gsub(/\s/, "-").gsub(/[^\w-]/, '').downcase
end

def to_param
  permalink
end

This uses the value of the name that is entered, clears it of puncuation, replaces spaces with hyphens and drops the casing. The first line makes sure it still remembers it needs to write the value itself to the name column in the model.

Now it’s still important that you confirm that the permalink is unique too, but I’ll let you do that on your own.

So that’s it – I hope you guys find it useful.

Rolling with Rails 2.0RC1

I’ve never been a fan of using rake to download EdgeRails. To me it never seemed to make sense that you would make a Rails project generated by one version and then download the Edge version into the plugins directory. So with that in mind I’m going to tell you how I roll out EdgeRails projects and in turn show you how to download Rails 2.0RC1 and generate a project with it.

Before I get into it though, I’m expecting that you have a development environment already running. I expect that you already have rake, ruby, rails, svn, and all that other stuff installed. If you don’t, please see one of the thousand Internet posts on how to do that or the many chapters in books that cover it. Okay, now that we are on the same page – let’s get moving.Let’s open up your command line terminal. Go into wherever you create your projects in Rails and make a new directory for your new project:

mkdir -p new_project/vendor

The -p variable creates both the new_project directory and the vendor directory inside it (depends of course on your OS, you could always just create both manually). Go into the directory:

cd new_project

Now, run SVN and checkout 2.0RC1 into the vendor/rails directory.

svn co http://dev.rubyonrails.org/svn/rails/tags/rel_2-0-0_RC1 vendor/rails

Now from the project directory (new_project) run the rails script within the vendor directory.

ruby vendor/rails/railties/bin/rails .

That will execute the rails script on the current directory. You basically just did the same thing as executing “rails new_project”. That’s it, you’ve now checked out Rails 2.0RC1 and created the base project. Now get started…Have fun.

Update:

This morning (November 29th) the Rails team has released RC2 so if you are wanting to check out RC2 – use the svn checkout command of:

svn co http://dev.rubyonrails.org/svn/rails/tags/rel_2-0-0_RC2 vendor/rails

Making Configuration Files with YAML

Update: A newer version of how I load YAML into configuration files in my projects can be found here.

Since being introduced to YAML I’ve loved using it for configuration files in both Ruby and Ruby on Rails. YAML means “YAML Ain’t Markup Language”. Yes, there is an infinite loop in the title – it’s programmer humor. Those of you familiar with Ruby on Rails are somewhat familiar with YAML because that is the format of the database.yml configuration file. So this is a sample of what a YAML file typically looks like:

development:
	adapter: mysql
	database: project_development
	username: root
	password:
	socket: /tmp/mysql.sock

This is a snippet out of the database.yml file. But one of the great things about Ruby is that writing code to read YAML is extremely simple. Let’s say I want to make a website configuration file, this is what that might look like:

config:
	title: My Rails Website
	author: Santa Claus
	email: email@company.com
	css_file: default.css

The hardest part is thinking about what type of information you want to store in your configuration, the Ruby is extremely easy. Check it out…

First we require the YAML library:

require 'yaml'

Ok, now we can make a read_config method:

def read_config
	config = YAML.load_file("config.yaml")
	@title = config["config"]["title"]
	@author = config["config"]["author"]
	@email = config["config"]["email"]
	@css_file = config["config"]["css_file"]
end

Seriously, that’s it (of course you need to execute the method). We just load the YAML into a local variable. Then we spider down the YAML document using the local variable that we assigned the loaded YAML document. So where you see “config” in quotes that is referring to the “config:” within the YAML document. Of course, were I say “config.yaml” you would actually put the path to your specific YAML document. One the information is loaded, I pull the data I want into a few instance variables making them accessible to my views (in Rails).

Update:

A more efficient way to do this would be to loop through the hash that is created by the read_config method and just set the key to an instance variable, like so:

config["config"].each { |key, value| instance_variable_set("@#{key}", value) }

Update: A newer version of how I load YAML into configuration files in my projects can be found here.

Cleaning up my Ruby Fizzbuzz

As I become more familiar with Ruby and Rails I’m of course going to start to understand better ways to do a snippet of code. Here is an updated script that is a little leaner:

(1..100).each do |i|
  fb = []
  fb << "Fizz" if (i % 3) == 0
  fb << "Buzz" if (i % 5) == 0
  fb << i if (i % 3) != 0 and (i % 5) != 0
  puts (fb.join "")
end

I am still tring to review my notes, so I just ask that those of you awaiting my review of the Web 2.0 Expo please continue to be patient.

Preparing for CSS Naked Day with Ruby on Rails

So you are sitting there around the house watching reruns of Smallville and Seinfeld and you think, “I wish I could jump onto the CSS Naked Day bandwagon with my Ruby on Rails application”, well you are in luck. Conditional statements in Rails are a piece of cake and with logical expressions like “unless” we save ourselves a ton of code.

In our layout when we call our stylesheet link tag we would just add some conditional code to the end like this:

<%= stylesheet_link_tag "default"
unless (Time.now.month == 4) and (Time.now.day == 5) %>

And that’s it, show the stylesheet unless it’s April 5th. The carriage return is for the blog, this can go on one line. I do have to say, be nice to your users here, you just might break the shit out of some of your UI without your CSS. Most Rails applications have more then just a blog on them so I suggest not doing this on a production application (do I really need to be saying this?).

DRYing Up Our Testimonial Partial

So for those of you that ran through the and created your very own random testimonial helper, I’m going to create another help to and were are going to make a few updates to our partial. First we have to do a few things:

  1. Rename our _random_testimonial.rhtml partial to _testimonial.rhtml
  2. Open our partial and remove the random_ from lines 2 and 3 leaving testimonial.[object]
  3. Open our application_helper.rb and edit out random_ on the line rendering the partial.
  4. Give the code a test

Everything should be just fine. All we have done is removed the usage of “random_” when it comes to using our partial. All the other code is the same here. So why would we do such a thing? Well… what if we had a page that we wanted to show all of our testimonials on? We already have a perfectly good partial view – we might as well take advantage of it. So in my testimonials_helper.rb I make another helper as such:

def all_testimonials
  @testimonials = Testimonial.find(:all)
  render(:partial => 'shared/testimonial', :collection => @testimonials)
end

Notice how I’m using the same partial here. Now in my testimonials controller I’ll just create a blank index method. The in my view testimonials/index.rthml I’ll just call the <%= all_testimonials %> helper.

Now I can use the both helpers with the same partial depending on the output I’m trying to active.

Now granted, we could use a method supporting polymorphism to DRY this up a little more, but I’ll let you experiment more with that yourself, when it comes to displaying testimonials on a page I don’t think I’m going to take the code THAT far.

Solving the Fizzbuzz Puzzle with Ruby

So back in February Jeff Atwood over at codinghorror.com was talking about a puzzle to give prospective new-hires when interviewing them entitled “Fizzbuzz”. You can read more about it here.

So with a little thought I decided to solve the fizzbuzz puzzle using Rudy and an Array.

count = 0
100.times do
  count += 1
  fb = []
  fb << "Fizz" if (count % 3) == 0
  fb << "Buzz" if (count % 5) == 0
  fb << count if (count % 3) != 0 and (count % 5) != 0
  puts (fb.join "")
end

For those of you that are curious as to why I used a local variable of count rather then the index identifier, is because the article by Jeff Atwood requests a loop from 1-100 and Ruby starts looping at 0.