I had to do a little digging, but setting up SSL on your server from Slicehost after launching it with the Deprec gem is not so bad. The issues caused by deprec that make these instructions for setting up SSL on Ubuntu Feisty not work are:

  1. Deprec install Apache2 to a non-standard location. Which means file locations are all mixed up.
  2. Deprec does not install the a2enmod method for Apache. This causes an error ("a2enmod not found") when it comes time to enable SSL in the Apache config files.

No big deal, we can handle this. First step is to SSH into your slice and run the command

sudo apt-get install apache2.2-common

Now you should have a2enmod at your disposal. Follow all the instruction from the SliceHost wiki for setting up SSL on Ubuntu Feisty with Apache, but with three caveats:

  1. replace all references to
    /etc/apache2
    with
    /usr/local/apache2
  2. to restart Apache server, run
    sudo /etc/init.d/httpd restart
    instead of
    sudo /etc/init.d/apache2 force-reload
    Optionally, you may also run this from your home computer instead
    cap apache_restart
  3. you have no ports.conf, so add port 443 to your httpd.conf
    Listen 80
    Listen 443

The rest of SliceHost's excellent tutorial with these minor tweaks should have you up and running in no time.

In this article, I will show you how to install a Mephisto Blog on your server so that it is accessible through a subdomain such as "blog.yoursite.com". While this tutorial is written specifically for installing Mephisto on a VPS Slice on Slicehost, I've divided it into sections so that the directions can be easily followed to install any blogging platform on any VPS or dedicated server that uses Apache and Mongrel.

This took me about 7 hours and 3 beers to figure out and get working on my server (RateMyStudentRental.com), but hopefully, with my help you'll be up and running in a mere half hour and 1 beer (provided you're not underage of course). And on to the tutorial!

Basically, here are the steps that need to be performed:

  1. Install the blog on your computer and get it running as a separate application
  2. Open your first beer and get comfortable
  3. SSH into your server and set up a new svn repository for the app on your existing server right alongside the existing repository for your existing app
  4. Capify your new Mephisto blog on your computer (if you’re not using Capistrano, you’re really missing out)
  5. Import app from your computer into your repository, check it back out and deploy to server right alongside your existing app
  6. Configure Apache server to redirect subdomain traffic to the blog’s directory on the server rather than the directory of your existing app, using Virtual Hosts
  7. Drink one more beer to celebrate (or maybe hold off until after your first post)

Hopefully now you have a bit better idea of what we’re about to do. So, let’s do it now.

1. Install blog and get it running on your computer as a separate app

If you are going to use a platform other than Mephisto, follow the appropriate instructions to get it running on your computer and skip to Step 2.

Now, for Mephisto, the install directions from their site are as follows:

  1. Create a database named mephisto (or one of your choosing).
  2. Copy config/database.example.yml to config/database.yml
  3. Edit database.yml and set your database credentials.
  4. Upload the entire mephisto directory to your webserver.
  5. Make sure you upload the tzinfo gem (see above) with the directory, or that your host has it preinstalled.
  6. Run rake db:bootstrap from a terminal of some sorts (use rake db:bootstrap RAILS_ENV=production to be sure you’ve bootstrapped the production database.)

However, when I tried this, I had problems getting Step 6 to work. To solve the problem, I had to upgrade my rake, gems, and rails to the latest versions (rake 8.1, gems 1.0.1, and rails 2.02), using the following commands

sudo gem update rake
sudo gem update
sudo gem update rails

Oh and make sure the tzinfo gem is installed on both your computer and the server

sudo gem install tzinfo

Then I had to uninstall the stable version of Mephisto (6.1) and instead install the latest and greatest build (7.2) from the Mephisto svn trunk repository

svn co http://svn.techno-weenie.net/projects/mephisto/trunk

Now go into your favorite mysql tool (or terminal) and create a new database for your blog. Then open up database.sample.yml in the config folder and configure your database info to match your new database’s name, user and password, then save it as database.yml. Go back to the directory of your app in terminal and run

rake db:bootstrap

Hopefully your tables got created properly. This is where all my original problems occurred though. If you are getting “no method found” or “permission denied” errors, make sure that you are using the latest rake and rails versions (some important changes happened between rake v7 and rake v8, same goes for Rails 1.2 and Rails 2.0 that will cause the bootstrap rake file to not execute properly with the former versions). Now, you should be able to go to that directory in your terminal and get it running from your computer

ruby script/server

In your browser, navigate to http://localhost:3000/admin, login and make sure all is working.

2. Get Beer

Alright, you’re doing good. We’ve made some real progress, so it’s time to pop open that first beer. Remember though, we’re going for quality here, so it better be something good like Stella Artois or Guiness, none of that domestic crap.

3. Setup SVN

If you are using Slicehost as my app (www.ratemystudentrental.com) is, then hopefully you used the deprec gem along with Capistrano to deploy your original app. If you did, then rejoice, and skip to Step 4. If not, check out this wiki to get Capistrano and the deprec gem working.

Otherwise, you hopefully remember enough about your original deployment that you can do it again with this blog app. But basically, you will need to SSH into your server, navigate to the parent directory of your existing app, create a new directory for your blog right beside it, then create a svn repository for it right beside your existing app’s repository (wherever that is), svn import your blog into the new svn repository, and then check it back out to your computer.

4. Capify your Blog App

If Cap and deprec are already on your computer, issue the following command from your blog app directory in terminal

cd /path/to/railsapp
deprec --apply-to . --name projectname --domain yoursite.com

Note that the domain is just yoursite.com, not blog or blog.yoursite.com. Now, open up the newly created config/deploy.rb and fill in the details. The lines you need to uncomment and fill in are:

1
2
3
4
set :domain, "yoursite.com"
set :application, "projectname"
set :deploy_to, "absolute/path/to/your/new/project/on/server"
set :user, "yourusernameonserver"

Unless you already have an svn repository for your blog setup somewhere, uncomment this line too


role :scm, domain

Then uncomment the appropriate apache and mongrel config lines as shown in my deploy.rb file below. You may need to change the directory of your apache2 config files (you should probably SSH to your server, and look around to find out where your apache2 .conf files are). Mine are in /usr/local/apache2/conf, though a lot of servers have them at /etc/apache2/conf.


set :apache_conf, "/usr/local/apache2/conf/apps/#{application}.conf"

Make sure that you tell apache to serve your blog to different proxy ports than your existing app.


set :apache_proxy_port, 4000

My app was using the 8000 ports, so I put my blog on the 4000 ports. Also, change the number of mongrel instances to the appropriate value. You can typically count on 50-60MB of memory use per mongrel instance, so don’t overdo it. My app is running on a 512MB Slice at slicehost with 4 mongrel instances (4 x 60MB = 240MB). I figured 2 mongrels should be good enough for the Mephisto blog (2 x 60MB = 120MB). 240 + 120 = 360MB < 512 MB, so we’re good.


set :apache_proxy_servers, 2

Also, if you have a SSH certificate set up on your computer, so that you don’t have to enter your username and password into the terminal everytime you go to SSH into your server, you’ll need the following line added at the end with the path on your computer to your SSH certificate


ssh_options[:keys] = %w(/home/yourcomputerusername/.ssh/id_rsa)

Here is what my ‘deploy.rb’ looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
require <i>deprec/recipes</i>

# =============================================================================
# ROLES
# =============================================================================
# You can define any number of roles, each of which contains any number of
# machines. Roles might include such things as :web, or :app, or :db, defining
# what the purpose of each machine is. You can also specify options that can
# be used to single out a specific subset of boxes in a particular role, like
# :primary => true.

set :domain, "ratemystudentrental.com"
role :web, domain
role :app, domain
role :db,  domain, :primary => true
role :scm, domain

# =============================================================================
# REQUIRED VARIABLES
# =============================================================================
# You must always specify the application and repository for every recipe. The
# repository must be the URL of the repository you want this recipe to
# correspond to. The deploy_to path must be the path on each machine that will
# form the root of the application path.

set :application, "msrblog"
set :deploy_to, "/var/www/apps/#{application}"

# XXX we may not need this - it doesn't work on windows
set :user, 'yourserverusername'
set :repository, "svn+ssh://#{user}@#{domain}#{deploy_to}/repos/trunk"
set :rails_env, "production"

# Automatically symlink these directories from current/public to shared/public.
# set :app_symlinks, %w{photo, document, asset}

# =============================================================================
# SPECIAL OPTIONS
# =============================================================================
# These options allow you to tweak deprec behaviour

# If you do not keep database.yml in source control, set this to false.
# After new code is deployed, deprec will symlink current/config/database.yml 
# to shared/config/database.yml
#
# You can generate shared/config/database.yml with 'cap generate_database_yml'
#
# set :database_yml_in_scm, true

# =============================================================================
# APACHE OPTIONS
# =============================================================================
set :apache_server_name, domain
# set :apache_server_aliases, %w{alias1 alias2}
# set :apache_default_vhost, true # force use of apache_default_vhost_config
# set :apache_default_vhost_conf, "/usr/local/apache2/conf/default.conf"
set :apache_conf, "/usr/local/apache2/conf/apps/#{application}.conf"
set :apache_ctl, "/etc/init.d/httpd"
set :apache_proxy_port, 4000
set :apache_proxy_servers, 2
set :apache_proxy_address, "127.0.0.1"
# set :apache_ssl_enabled, false
# set :apache_ssl_ip, "127.0.0.1"
# set :apache_ssl_forward_all, false
# set :apache_ssl_chainfile, false


# =============================================================================
# MONGREL OPTIONS
# =============================================================================
set :mongrel_servers, apache_proxy_servers
set :mongrel_port, apache_proxy_port
set :mongrel_address, apache_proxy_address
set :mongrel_environment, "production"
set :mongrel_config, "/etc/mongrel_cluster/#{application}.conf"
# set :mongrel_user_prefix,  'mongrel_'
# set :mongrel_user, mongrel_user_prefix + application
# set :mongrel_group_prefix,  'app_'
# set :mongrel_group, mongrel_group_prefix + application

# =============================================================================
# MYSQL OPTIONS
# =============================================================================


# =============================================================================
# SSH OPTIONS
# =============================================================================
ssh_options[:keys] = %w(/home/yourcomputerusername/.ssh/id_rsa)
# ssh_options[:port] = 25

5. Migrate Blog to Server

And now for the exciting part, migrating everything to your server. Don’t forget to really enjoy your beer throughout this step, but pace yourself. You don’t want to be incoherent while playing around with your server. In your terminal from the directory of your blog app, run this command

cap setup

Now, if you did not already have a svn repo somewhere, run this command to set that up on your server

cap setup_scm

Your terminal should have spit out the address to your new svn repo. At this point, navigate to the directory of your blog app on your computer and go one level above. You should have a new folder now called projectname_machine or projectname.test or something like that (I don’t remember exactly). Anyway, that is now your new working directory. You can safely delete your original directory projectname folder from your computer or archive it, and rename the new directory back to projectname.

Now go into the new directory, reopen config/deploy.rb, and replace the line


set :repository, "svn+ssh://#{user}@#{domain}#{deploy_to}/repos/trunk"

with the new explicit path that terminal gave you for the svn address (basically, the variables will just be replaced with their actual values).


Mephisto-specific

If you are using Mephisto, go back to terminal and issue

cap deploy

You will now need to SSH into your server. Navigate to the current directory of your newly deployed blog app, and issue the command

rake db:bootstrap RAILS_ENV=production

Again, if you have problems, make sure that your server also has completely updated rake, gems, and Rails as you did with your computer. Your blog app should now be on your server and under revision control.


Non-Mephiso instructions

Note that if you’re using a blog platform other than Mephisto, you’ll instead run

cap deploy_with_migrations

assuming you have the blog app running on your computer with the proper tables and migrations set up.


Now, Everybody!

cap restart_apache
cap restart mongrel_cluster

6. Configure Apache2

You should now be about ¾ done with your beer by this time. Put your beer aside for this step, because it can be a little confusing, and differs depending on your exact server setup and configuration. My server is running Ubuntu 7.04 Feisty with Apache2 and Mongrel, so that’s what I’ll be dealing with specifically here.

The idea is that you’re going to be modifying your server’s configuration files so that it knows to look at the HTTP header of incoming requests and route requests of the appropriate subdomain to the directory of your blog app rather than your main app. Technically, what we’re doing is setting up a Name-based Virtual Host. You can read more about name-based vhosting here.

First, find your Apache2’s httpd.conf file. Most of the time you can find it in /etc/apache2/conf, but my server was setup by deprec, which put it in /usr/local/apache2/conf. In this directory, look to see if you have a folder called apps. If you do, then go in there and you should have two .conf with the titles of your original app and your new blog app. Deprec did that for you.

If you didn’t use deprec, then you’ll just have the original one, which you will need to copy and rename the copy to your blog app name. Either way, now you open up the projectname.conf file and change the following lines:

ServerName www.yoursite.com
DocumentRoot /var/www/apps/projectname/current/public

To

ServerName blog.yoursite.com
DocumentRoot /var/www/apps/projectname/current/public 

Also, if you have any ServerAlias lines, delete those.

If you did not already have this file and copied it over, you will need to go back into your conf directory, open up httpd.conf and tell it to look for the new projectname.conf.

For more help setting up your .conf file, check out this code for setting up Mephisto to work with multiple sites.

If you did not have an apps folder in /usr/local/apache2/conf, then you will need to add this to your httpd.conf.


ServerName blog.yoursite.com
DocumentRoot /var/www/apps/projectname/current/public

The ‘*’ above may also be your specific IP address, but on anything after apache 1.3.12, ‘*’ works just fine. Now all you have to do is add an A Record to tell the interwebs to send all requests for your new subdomain to your IP address. You may need to find out how to do this with your server peeps. Here’s a good resource for messing with DNS Records.

For Slicehost, this is how you do it… go to manage.slicehost.com and login to your account. Go to the DNS tab. Go to Records for your domain. Click new record. Now, you’re going to set up a new A Record. In the ‘Name’ box, enter the subdomain that is setup for your blog in the apache2 .conf file. Mine is blog. In Data, enter your IP address. Then make the other to entries match whatever the numbers are for your www A Record that should already be set up for your Slice. My numbers are 0 and 3600 respectively. Then add it and you should be in business!

Navigate to subdomain.yoursite.com/admin login. If you get any Mephisto errors, you might have a problem with folder permissions on your server. If this is the case, SSH into your server, navigate to projectname/current, and issue the command:

sudo chmod –R 777 public

You may also need to do this for your themes directory if you plan on modifying your themes at all

sudo chmod –R 777 themes

7. W00t!

Now you’re done! Chug the rest of that beer, write your ‘Hello World’ blog post and take the rest of the night off!

Microsoft has just announced full FastCGI support for it's websever software - Internet Information Services (IIS). Though primarily a PHP announcement (the FastCGI Extension was developed by Microsoft in partnership with Zend), it does offer Ruby developers a glimmer of hope for running Ruby on Rails on Windows servers.

"By supporting the open standard, Microsoft has made it possible for PHP and other CGI compliant languages to be hosted efficiently and effectively on Windows Server 2003 and IIS."

The important part here for Rails developers is in the middle of that statement - and other CGI compliant languages. We find from reading the Using FastCGI to Host PHP Applications on IIS 6.0 and IIS 5.1 guide on the same website under the "What is FastCGI?" heading that:

"The IIS FastCGI support enables IIS to host normal CGI programs like PHP, or Ruby On Rails, using the FastCGI protocol, offering high-performance and stability for production deployment of such application frameworks."

This new FastCGI Extension will be available for webhosts to use on older versions of IIS 6 and 5.1, so there will be no excuse anymore for Windows hosts not to support Ruby on Rails.

"This FastCGI Extension release is supported on IIS 6 on Windows Server 2003 for a fully scalable production environment and on IIS 5.1 on Windows XP in order to support developers who build their Web applications on Windows client machines."

At the end of the article lies the true gem:

"Looking ahead, betas of Windows Server 2008, already include the FastCGI Extension as a completely integrated feature of Internet Information Services IIS 7.0 (IIS7)."

This means the capability to run Ruby and Ruby on Rails (via the FastCGI extension) will be available on IIS out of the box, in all future versions. May Ruby on Rails developers around the world rejoice.

Raising errors in controllers

September 21st, 2007

All the cool people raise errors now in their controllers and models as opposed to using save and checking to see if it returned nil or the object we're dealing with. For example, in the create action, you would having something like this:
1
2
3
4
5
6
7
def create
  @book = Book.new params[:book]
  @book.save!

  flash[:notice] = 'Book was successfully created.'
  redirect_to @book
end
By doing this, you can stick this in your application controller (extracted from Beast):
1
2
3
4
5
6
7
8
  private
    def rescue_action(exception)
      exception.is_a?(ActiveRecord::RecordInvalid) ? render_invalid_record(exception.record) : super
    end
    
    def render_invalid_record(record)
      render :action => (record.new_record? ? 'new' : 'edit')
    end
And any create controller that raises and ActiveRecord::RecordInvalid exception will directly to the new view. The same applies for the update controller:
1
2
3
4
5
6
7
def update
  @book = Book.find params[:id]
  @book.update_attributes! params[:book] 

  flash[:notice] = 'Book was successfully updated.'
  redirect_to @book
end
When a PUT is made, it is sent to the update method in your controller and then book will render to the edit view because of the line of code we stuck in our application controller:

render :action => (record.new_record? ? 'new' : 'edit')
This DRYs up controllers significantly as it gets rid of all the render :action => 'new' and render :action => 'edit' lines I used to see all over my code. Raising exceptions is also good for show, edit, update and destroy actions. By utilizing the find method and supplying it with a single parameter ID, it will raise an ActiveRecord::RecordNotFound exception. In rails edge, it will rescue this exception and automatically redirect to the default 404 page located in public/404.html and give the client the appropriate 404 status. Try it out on your controllers and feel the DRYness!
The title of this post is the exact search I found on Google recently that pointed to my site. It forwards to places that don't really answer this question.. so I thought I'd answer it quickly. script/generate migration will show you what files it generates in the console (and any generation script for that matter) so you can look through the list of files it generates and delete them manually. As far as I know, there is no reverse for this generator (or any generators for that matter) and you must manually remove the files it created one-by-one. I hope this answers the anonymous Googler's question (and you come back here to find the answer)!

Rspec and autotest

September 17th, 2007

I thought I’d mention this briefly as rspec is becoming a very popular way to test. I was manually running my tests until I was recently pointed in the direction of auto-test with spec_server. This significantly increases the feedback loop for when your tests pass and fail; increasing productivity exponentially. The steps are as follows:

Install ZenTest:

sudo gem install ZenTest

Install rspec for rails:

http://rspec.rubyforge.org/documentation/rails/install.html

Open up a terminal and open run script/spec_server from the root of your rails application. Then open up another terminal and run autotest -rails (also inside of the root of the rails application). Edit a spec to make it fail, save it and watch autotest tell you it failed! I know you can get this to work with growl (I’m on Ubuntu so I don’t use growl). If someone with OS X experience can comment on how to get growl integrated and perhaps provide an alternative for linux, I will post it up.

David Chelimsky announced today that an experimental version of the story runner for rspec has been committed to the trunk. This move is clearing the path which rspec is taking towards merging the rspec and rbehave frameworks. The code example he gave for this new feature is below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# The following is based on an experimental Rails-Story adapter
# in RSpec's trunk at rev2480.
#
# You run it by standing in RAILS_ROOT and saying:
#   >ruby stories/add_person.rb
#
# Please forgive the lack of RESTful convention (get '/people/create' instead of '/people/new') -
# this works with the existing example app, which is, well, OLD.


# in RAILS_ROOT/stories/helper.rb
ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
require 'spec/rails/story_adapter'

# in RAILS_ROOT/stories/add_person.rb
require File.join(File.dirname(__FILE__), "helper")

Story "Add Person", %{
  As an admin
  I want to add people to the system
  So that I show how many people use my system
}, :type => RailsStory do
  Scenario "Successfully add person" do
    Given "no people in the system" do
      Person.destroy_all
    end
    
    When "creating a new person named", "Dan" do |name|
      post "/people/create", :person => {:name => name}
    end
    
    Then "viewer should see", "/people/list" do
      follow_redirect!
      response.should render_template("people/list")
    end
    
    Then "list should include", "Dan" do
      response.should have_text(/Dan/)
    end
  end
  
  Scenario "Redirect to create form on failed create" do
    Given "no people in the system" do
      Person.destroy_all
    end
    
    When "creating a new person with no name" do
      post "/people/create", :person => {:name => nil}
    end
    
    Then "viewer should see", "/people/create" do
      assert_template "people/create"
    end
    
    Then "list should not include", "Dan" do
      response.should_not have_text(/Dan/)
    end
  end
end

Ruby hoedown

August 29th, 2007

mmmm I love conferences. Below is a link to some (or all?) presentations at Ruby Hoedown.

http://rubyhoedown2007.confreaks.com/

I just ran
script/generate migration add_type_to_users
in my terminal and it automatically generated a migration with the code generated for me.
1
2
3
4
5
6
7
8
9
class AddTypeToUsers < ActiveRecord::Migration
  def self.up
    add_column :users, :type, :type, :null => :no?, :default => :maybe?
  end

  def self.down
    remove_column :users, :type
  end
end

Am I being n00b, or is this a new feature in edge rails? Does this work for columns not reserved for rails?

UPDATE:

On further inspection, this is a new thing in edge rails and it does work for other columns. While running the following:

script/generate migration add_name_to_users
I got the following migration:
1
2
3
4
5
6
7
8
9
class AddNameToUsers < ActiveRecord::Migration
  def self.up
    add_column :users, :name, :type, :null => :no?, :default => :maybe?
  end

  def self.down
    remove_column :users, :name
  end
end
For a bit more experimentation I tried:
script/generate migration add_name_string_to_users
1
2
3
4
5
6
7
8
9
class AddNameStringToUsers < ActiveRecord::Migration
  def self.up
    add_column :users, :name_string, :type, :null => :no?, :default => :maybe?
  end

  def self.down
    remove_column :users, :name_string
  end
end
Unfortunately it seems you cannot specify type. I also tried to see if you could do multiple columns by trying:
script/generate migration add_name_and_date_to_users
and got the following:
class AddNameAndDateToUsers < ActiveRecord::Migration
  def self.up
    add_column :users, :name_and_date, :type, :null => :no?, :default => :maybe?
  end

  def self.down
    remove_column :users, :name_and_date
  end
end

It seems that the implementation is pretty primitive. Obviously I'm using a bad approach as I should be looking at the source to see if any additional implementation does exist without my knowing, but I was hoping that the most intuitive thing for me would implemented already. I think it would be cool to see this syntactic sugar get more advanced. Comments? Opinions?

NOTE: The code that is generated by the migration does not work automatically. You have to change the stubbed symbols placed inside the migration to whatever you need. It's more of a guide than a performance enhancer (which IMO think that it should be both).

1
2
3
4
5
6
7
8
9
10
def self.up
  @first_query = true
  sql_file = File.dirname(__FILE__) + "/../some_crazy_file.sql"
    
  IO.readlines(sql_file).join.split(/;\nINSERT INTO /).each do |q|
    q = "INSERT INTO " + q unless @first_query
    execute q
    @first_query = false
  end
end
Is there a better way? I have found that this is also very picky. Your insert queries must follow up the line right after. It would was fun to create this, but not fun to look at :-/. Maybe there is a plugin somewhere?

UPDATE: My friend Brandon, who chose not to comment on this blog post (shame on him!) said that running the sql file in command line could work as a more elegant solution there. The only problem I have with this approach is whether or not the rake task fails if the operation fails. So you code would like like this:

1
2
3
def self.up
  result = `mysql db_name < some_crazy_file.sql`
end

The only thing left is to make sure that result returns a favorable result, and if it doesn't then fail the migration. I'm not exactly sure how to do this (to busy to investigate). Any takers?

Anvil is growing rapidly and keeps getting cooler everyday! It now has support for MVC architecture (models are missing right now, but will be implemented soon) and it boasts a cool new way to shuffle away some of the code in your application view to other views like partials in rails. From the example I posted in the previous post, you can now do:

app/views/application.rb
1
2
3
anvil "Hammer" do |app|
  app.frame "Hamr", :render => 'hamr', :title => "Hamr", :maximize => true
end
app/views/frames/hamr_frame.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Create Menu Bar
frame.menu_bar "Main" do |menu_bar|
  menu_bar.menu "&File", :render => 'file'
  menu_bar.menu "&Edit", :render => 'edit'
  menu_bar.menu "&Search", :render => 'search'
  menu_bar.menu "&Help", :render => 'help'
end
    
frame.text_control "Editor", 
  :size => { :width => 100, :height => 200 }, 
  :style => [ :multiline, :rich, :rich2 ]

frame.create_status_bar(2)
frame.set_status_text("Hamr World!", 0)
app/views/menus/file_menu.rb
1
2
3
4
5
6
7
8
9
10
item.add(:new)
item.add(:open)
item.separator
item.add(:save)
item.separator
item.add(:preview)
item.add(:print)
item.separator
item.add(:close)
item.add(:quit)
This new way makes your code much neater. Support for putting all of the code into your application.rb file is still there, but having the ability to shuffle it away is very nice. Anyway, check out the projects: anvil.rubyforge.org and hamr.rubyforge.org.
Anvil just announced its conception onto rubyforge yesterday as plans are being set to organize how the new gem is going to be created. The gem is going to be a framework that wraps around the Wx::Ruby project in a way that is more concise and ruby-esk. The concepts are inspired by ruby on rails and Shoes (a project for allowing you to create little applications that act like web browsers). Here is a sample of the some of the code from the sample application called Hammer, a ruby/anvil-based text editor included in the applications folder of the repository:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
anvil "Hammer" do |app|
  app.frame "Hammer", "Hammer", :maximize => true do |frame|
    
    # Create Menu Bar
    frame.menu_bar do |menu_bar|
      
      # File Menu
      menu_bar.menu "&File" do |item|
        item.add(:new)
        item.add(:open)
        item.separator
        item.add(:save)
        item.separator
        item.add(:preview)
        item.add(:print)
        item.separator
        item.add(:close)
        item.add(:quit)
      end
      
      # Edit Menu
      menu_bar.menu "&Edit" do |item|
        item.add(:undo)
        item.add(:redo)
        item.separator
        item.add(:cut)
        item.add(:copy)
        item.add(:paste)
        item.add(:delete)
        item.separator
        item.add(:selectall)
        item.separator
        item.add(:preferences)
      end
      
      # Search Menu
      menu_bar.menu "&Search" do |item|
        item.add(:find)
        item.add(:replace)
      end
      
      # Help Menu
      menu_bar.menu "&Help" do |item|
        item.add(:about)
      end
    end
    
    frame.text_control :size => { :width => 100, :height => 200 }
    
    frame.create_status_bar(2)
    frame.set_status_text("Hammer World!", 0)
    
  end
  
end
The project is in need of talented and eager individuals to help it get off of its feet. If you are interested in contributing, check it out!
Alright, for anyone reading this blog to learn about Rails (hopefully, all of you), I'll just go ahead and give the spoiler. This particular post contains nothing specifically Rails related. I just wanted to precede all of my problems that you will no doubt be seeing on this site with the disclaimer that I am a certified noob. I own a web design business (www.nobswebdesign.com), and have programmed everything to date in PHP and javascript with some AJAX sprinkled in. That is, until my most recent project. So, for the last 5 weeks, I've been learning this new language, and so far so good. Yes, there may be that occasional night where I spend 3 hours staring at an "if" statement that I can't get to work. But other than that, I'm nearly done with the project. That could never happen in any other language. I'm loving it. But be forewarned, you are likely to see some pretty obvious questions raised in my posts. Hopefully, they'll help someone.
Going backwards with RSpec is a pain. By backwards I mean, write the specs after the specifications (my application) already exist. It feels like I'm re-writing all the code I just wrote. I can see the value in writing the specifications as we bring more team members on board and now my mindset is such that I feel like I'm writing documentation to support them as we introduce them to the code base. The real pain started to set in when I had this small problem. The problem I'm having is that rspec is returning an error every time it encounters a method in the view that takes advantage of the polymorphic helpers AND does not pass the appropriate instance variables. Rails by default will allow you to call these methods without passing arguments to it. Here is the error:
1
2
3
4
5
6
7
8

ActionView::TemplateError in 'Edit Artist Page should render the edit artist form'
label_artist_url failed to generate from {:action=>"show", :controller=>"artists"} - you may have ambiguous routes, or you may need to supply additional parameters for this route.  content_url has the following required parameters: ["labels", :label_id, "artists", :id] - are they all satisfied?
On line #3 of app/views/artists/edit.rhtml

    1: <h1>Editing artist</h1>
    2: 
    3: <% form_tag label_artist_path, :method => :put do %>
By providing label_artist_path with its arguments this error goes away:

label_artist_path(@label, @artist)
This to me however adds unnecessary noise to the already easy clutter erb syntax and I would much rather just use this method without having to pass its arguments. There is also another option: stub out the label_artist_path method. My problem with this is that I feel like I'm not really testing my views fully and it will be hard to maintain considering I use these methods everywhere in my views. I could write a module that would include these methods, but this tends to be a big no-no in the rspec philosophy as you are hiding away some of the specifications. I'm thinking the best solution is to patch whatever the problem is in rspec, but I'm not sure where to start. I had a long discussion about this with David Chelimsky about this topic on the rspec mailing list and continued on the irc channel. I even posted a bug report on it here. We were trying to pinpoint where in rails these instance variables get sent to the helpers. He made the suggestion of adding the following code into my before method:

(class << @controller; self; end).send :include, ActionView::Helpers::UrlHelper
Which gives a completely different error:
1
2
3
4
5
6
7
8
9

ActionView::TemplateError in 'Edit Artist Page should render the edit artist form'
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.url_for
On line #3 of app/views/artists/edit.rhtml

    1: <h1>Editing artist</h1>
    2: 
    3: <% form_tag label_artist_path, :method => :put do %>
For some reason the controller is not being set and that is basically where we left off. If anyone has any solutions, please comment! I will keep this post updated.
Finally the much anticipated (by those few lucky enough to have heard about it) Grasp Ruby on Rails blog has arrived! The goal of this blog is to be like every other good Ruby on Rails related blog: complain about some problem we encountered and then describe the steps we took to solve them. We're in the process of collecting anyone who is proficient in enough rails to contribute to this blog. If you are interested, comment!