Lifejot.com re-launched
October 24th, 2007
Man we've been busy!
We threw our hat in the Rails Rumble ring with our buddies Mike and El. We didn't win but 48 hours of pure coding was a rush and our app worked. Good times.
We've been working on a few different projects, too. Lifejot.com is our recently re-launched shopping list maker. JasonC started this one a couple years ago using .net. It was working fine but we needed to add some features and the site needed a facelift. There was no way we were going to hassle with doing it in .net. Converting this app to Rails was a piece of cake.
Lifejot lets a user create unlimited shopping lists. Usually these lists are created by store but you can create them however you like; Shopping list, Christmas list, Wish list, any list will do. Each list has drag-and-drop reordering. Most of the forms are ajax-loaded and most parts of the main list page are also ajax-refreshed. Lists can be printed, emailed, and merged or copied. It's a great improvement over v1!
We're also working on some ideas to monitize the site. I don't think we'll ever charge a user but we think there are many opportunities to partner up with services and, of course, advertisers. We'd love to hear if anyone has any other ideas.
Installing Nginx, Mongrel Clusters, MySQL, Rails, Ruby on Ubuntu 6.06
September 23rd, 2007
This is my experience setting up a slice from Slicehost to run Unbuntu 6.06, Nginx, Mongrel Clusters, Rails, and MySQL. This was my very first time setting up a server with this stack; no guarantee this is the absolute 'correct' way, but it's the way I got it working.
Lock down the server a bit.
Root and Sudo Access
Okay, first thing's first! SSH into your new fresh Unbuntu 6.06 install with root and your root password using your IP.
First thing we're going to do is a bit of cleanup on the server. Once logged in as root, change the password from the default that was given.
passwd
Set up a new user with sudo access and then use that instead of root. Replace [new user] with your new user name.
adduser [newuser]
Open the /etc/sudoers file with the following command:
visudo
Add the new user to the sudoer access by adding the following under 'user privilege specification':
[newuser] ALL=(ALL) ALL
SSH Access
We want to lock down SSH a bit before we continue and remove root access to SSH as well as change the port you are SSHing too. The default port is 22, but you can pick whatever floats your boat.
nano /etc/ssh/sshd_config
This brings up the SSH configuration. Change 'PermitRootLogin' to 'no' and change 'Port 22' to 'Port 30000' or whatever port of your choosing. After this is done, save, reload SSH, and switch users to the new user you created.
/etc/init.d/ssh reload su [newuser]
If this does not work, you can always restart the system and reSSH back in. To do that use the following:
shutdown -r now
More information about locking down your slice can be found at http://articles.slicehost.com/2007/9/4/ubuntu-lts-setup-page-1
Installing ruby, mysql, rails, etc.
Apt-Get and Your Sources.list
Uncomment your sources.list so you can grab the appropriate packages needed.
sudo nano /etc/apt/sources.list
Uncomment the following and save:
deb http://us.archive.ubuntu.com/ubuntu dapper universe deb-src http://us.archive.ubuntu.com/ubuntu dapper universe
Update your sources, etc:
sudo apt-get update sudo apt-get dist-upgrade
Start Installing!
Install the build essentials needed to run many of the programs you are about to install:
sudo apt-get install build-essential
Grab mysql, ruby, and many of the helper programs that make it all work.
sudo apt-get install ruby ri rdoc mysql-server ruby1.8-dev irb1.8 libdbd-mysql-perl libdbi-perl libmysql-ruby1.8 libmysqlclient15off libnet-daemon-perl libplrpc-perl libreadline-ruby1.8 libruby1.8 mysql-client-5.0 mysql-common mysql-server-5.0 rdoc1.8 ri1.8 ruby1.8 irb ri rdoc libpcre3-dev libopenssl-ruby openssl zlib1g zlib1g-dev libssl-dev
If any of those give you flack, remove it from the install and try again. Once installed, add some linking to the appropriate directories so everything knows where ruby, and others are located.
sudo ln -s /usr/bin/ruby1.8 /usr/local/bin/ruby sudo ln -s /usr/bin/ri1.8 /usr/local/bin/ri sudo ln -s /usr/bin/rdoc1.8 /usr/local/bin/rdoc sudo ln -s /usr/bin/irb1.8 /usr/local/bin/irb
Give MySQL a root password, since the default is blank.
sudo mysqladmin -u root password [yourrootsqlpassword]
performance enhancements for mysql
You can add a few enhancements to your MySQL to make it use less memory. I grabbed these from the slicehost help tutorial. I'm not going to go into details about what they do, but if you want to learn more you can read their tutorial.
Open the mysql config file.
sudo nano /etc/mysql/my.cnf
Add the following:
language = /usr/share/mysql/english skip-external-locking skip-locking
Also add and make these changes under 'Fine Tuning'.
# * Fine Tuning # key_buffer = 16K max_allowed_packet = 1M thread_stack = 64K thread_cache_size = 4 sort_buffer=64K net_buffer_length=2K
Save and restart mysql.
sudo /etc/init.d/mysql restart
Installing Ruby Gems
You will need to grab the latest version of ruby gems from their website. At the time of this writing it is 0.9.4 so change that accordingly. Start by creating a folder to extra the ruby gems program to:
cd ~ mkdir sources
Grab ruby gems files from their website and decompress them.
wget http://rubyforge.org/frs/download.php/20989/rubygems-0.9.4.tgz tar xvzf rubygems-0.9.4.tgz
Install ruby gems.
cd rubygems-0.9.4 sudo ruby setup.rb
Installing Rails & Mongrel Cluster
Once you have ruby gems installed, you can now use 'gem install [program]', so we'll use this to grab rails, mongrel, and others.
sudo gem install rails --include-dependencies
Grab mongrel and mongrel cluster using ruby gems. Choose yes for all dependencies.
sudo gem install mongrel mongrel_cluster
Now we need to make sure if the server dies and restarts, that the mongrel clusters will restart automatically. Copy the init file over to /etc/init.d/ by typing this all on one line:
sudo cp /usr/lib/ruby/gems/1.8/gems/mongrel_cluster-1.0.2/resources/mongrel_cluster /etc/init.d/mongrel_cluster
Next, add a path statement to mongrel_cluster file just above the CONF_DIR variable:
nano /etc/init.d/mongrel_cluster
Add:
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local:/usr/local/sbin:/usr/local/bin
Change the USER from:
USER=mongrel
to
USER=www-data
Ubuntu has a built in user called www-data that we will use. Finally, let's modify permissions and make sure we boot mongrel on startup:
sudo chmod +x /etc/init.d/mongrel_cluster sudo update-rc.d mongrel_cluster defaults
Installing Nginx
We will need to install nginx from source if you are on ubuntu 6.06 and even if you are running 7.x you should do the same to make sure you are running the latest version.
cd ~ cd sources
Grab the latest nginx tar; currently 0.5.31 but check their website for the latest.
wget http://sysoev.ru/nginx/nginx-0.5.31.tar.gz
Decompress it.
tar -zxvf nginx-0.5.31.tar.gz cd nginx-0.5.31
Build it. Make it. Install it.
./configure --sbin-path=/usr/local/sbin --with-http_ssl_module make sudo make install
Now you’ve got it built and sitting in /usr/local/sbin/ and ready to go. Next we need to set it up to run upon boot up like our mongrel clusters in case of an unfortunate reboot. Fortunately notrocketsurgery.com has built a init.d file for us to use. Run each of the following lines:
cd ~ cd sources sudo wget http://notrocketsurgery.com/files/nginx -O /etc/init.d/nginx sudo chmod 755 /etc/init.d/nginx sudo update-rc.d nginx defaults
Installing Subversion(SVN)
Now we are ready to pull in a project we've been developing on. The easiest way is to just check out the project from SVN and keep it updated that way. Grab subversion; yes to all dependencies.
sudo apt-get install subversion
Create a location to put your projects. For this example we'll store them in /var/www. You may need to create it.
cd var sudo mkdir www cd www
Check out your project from SVN.
sudo svn co [path to svn repo] [project-name]
Assign the folder and files the appropriate owner to be shown on the web. From inside the /var/www folder type:
sudo chown -R www-data:www-data [your checked out rails app]
Configure your mongrel clusters
We now need to set up the mongrel cluster configuration. This will produce a file called mongrel_cluster.yml. Each site you have NEEDS to have a file called this located in its /config folder. For the port you can use whatever you want, and it will increment the port by 1 for each mongrel you set up. In this example, we are setting up 3 clusters, by specifying 3 on the -N option. This will use ports 8000,8001,8002. Feel free to use more if you like.
cd /var/www/[your rails app] sudo mongrel_rails cluster::configure -e production \ -p 8000 -N 3 -c /var/www/[yourrailsapp] -a 127.0.0.1 \ --user www-data --group www-data
Now let's create a symlink to that file from within /etc where all our configs live. When our system starts up it looks for mongrel cluster configs in the /etc/mongrel_cluster folder. We need to put all our mongrel_cluster.yml's into this folder by creating symlinks. For more then 1 site, make sure you call your file mongrel_cluster.yml in the site's config folder, but when you symlink it you can name it something else.
sudo mkdir /etc/mongrel_cluster cd /etc/mongrel_cluster/ sudo ln -s /var/www/[your rails app]/config/mongrel_cluster.yml
Add user to mongrel_cluster.yml
nano /etc/mongrel_cluster/mongrel_cluster.yml
Right next to group add 'user: www-data
Configure your Nginx
We need to make some changes to our nginx.conf to make sure all is set up correctly.
sudo nano /usr/local/nginx/conf/nginx.conf
A demo file can be found here: http://webchicanery.com/code/nginx.conf.txt thanks to WebChicanery. They have a great write up about installing and configuring Nginx that help me greatly. You can find it here. http://webchicanery.com/2007/08/07/slicehost-nginx-mongrel-part-2/
In the nginx.conf make sure all your log paths, processes, ports are the same as what is in your mongrel_cluster.yml.
Set up your rails app
Create your db in mysql.
mysqladmin -u [username] -p create [database_name]
Migrate your application using the production environment. Navigate to your application folder.
sudo rake db:migrate RAILS_ENV=production
Stopping/Starting Nginx and Mongrel Clusters
After making changes to the nginx.conf you will need to stop and restart it. Apparently 'restart' does not work, so you will need to kill the process.
sudo kill [nginx pid]
Then start ngnix again.
sudo /etc/init.d/nginx start
To restart mongrel_clusters run the following:
sudo /etc/init.d/mongrel_cluster restart
Mongrel cluster can also take stop|start instead of restart. One last way to restart this is to restart your entire system. If everything is set up correctly, both nginx and mongrel clusters should automatically start when the system starts since we added them to the /etc/init.d folder.
Closing
I hope this helps! If you see anything that is completely incorrect let me know and I'll update the post. Like I said, this is based on my initial install.
Here are some of the websites that helped me not only during the install but also in writing this post:
- Slicehost Articles and tutorials - http://articles.slicehost.com/
- WebChicanery - Part 1- http://webchicanery.com/2007/08/06/slicehost-nginx-mongrel-part-1/
- WebChinanery - Part 2 - http://webchicanery.com/2007/08/07/slicehost-nginx-mongrel-part-2/
- http://brainspl.at/rails_stack.html
- http://symbiotix.net/blog/entry/15
- http://www.urbanpuddle.com/articles/2007/05/09/install-ruby-on-rails-on-ubuntu-feisty-fawn
- http://www.urbanpuddle.com/articles/2006/06/10/install-ruby-rails-on-ubuntu-dapper-drake
Fun with Exception Notifier
August 1st, 2007
Fun? Perhaps I exaggerate. I’ve not had any fun with Exception Notifier today. In fact, I’ve had a lot of NOT fun! So I’m here to help you have less anti-fun than I had.
Exception Notifier (here’s a tutorial) is a great way to get Rails to email you when a critical error occurs in your app. It automatically ignores 404-like errors which is probably good most of the time.
I started by installing the plugin earlier today which is totally straight-forward…
script/install plugin exception_notification
Pretty easy, right? Right.
The README tells you all you need to know about how to configure it…or so you would think.
Personally I like to try my shiz out on my development box before I just throw someone else’s code (granted, Jamis is no slouch) up on my production site. So, I commence with the configuring and forcing my app to error out. I even try hitting my site from a totally different computer so it’ll ignore my “local” request. I get lots of errors, lots of trace info, but no email. Nothing even looking like an attempt.
Digging through the code I find some stuff about ignoring local requests so I figure I should hack that to allow my request to appear non-local. If you’re not a Rails noob (I am) you’ll have already figured out my problem by what I just said. You see, when you’re in development mode, EVERY REQUEST IS LOCAL by default. It doesn’t matter if you are hitting the site from around the globe, if you’re in development mode, it’s a local request. Normally that’s great but when you’re trying to test your freakin exception mailer, IT SUCKS!
Fortunately all problems were solved simply by editing development.rb. Change the line that saysconfig.action_controller.consider_all_requests_local = true
to false and BOOM, exception mailing! This is assuming, of course, that you have the ActionMailer stuff configured properly (which was another part of my problem).
By the way, there’s also a line in development.rb that looks like
config.action_mailer.raise_delivery_errors = false
I like to set that to true so I can see the whole conversation between the app and the mail server.
By the way, I’m using Google Apps for my email server (which I love) but it takes some massaging to get Rails to mail to it. There are a number of articles out on how to do it.
Oh, and if you happened to read the tutorial I posted at the beginning of the article, including the comments, you would notice that what I just told you was in there. Guess who didn’t read it all the way through? Ya. G’night.
Setting up acts_as_paranoid
July 28th, 2007
- Navigate to your rails project folder and run the following command ruby script/plugin install http://svn.techno-weenie.net/projects/plugins/acts_as_paranoid/
- Create a migration(s) for all your database tables that do not contain a deleted_at (datetime) column that you wish to use this with by running ruby script/generate migration add_deleted_at_column
- In the migration use the add_column :table_name, :column_name, :datetime syntax, and the opposite, remove_column for migrating backwards.
- Add acts_as_paranoid under the model name of all your models you wish to use this with.
- Test it out and you should be populating the field deleted_at now instead of deleting the entire row.
has_many_polymorphs saves lives
April 26th, 2007
So here I am tearing through my tickets when all of a sudden my ticket shredder jams. On what? Jason wants me to do some fancy filtering of a list that requires some :joins, some :conditions, and it has to work through a has_many :through join that’s already there. Oh, and since the items are also tagged it has to use acts_as_taggable, too.
Is he asking too much? I don’t know. I guess filtering a list by a search value, date created, a related person object, and a tag isn’t asking too much. I mean it’s for our users, right?
So being the code monkey I am, I plunge forward. I cleared the jam on my shredder, lubed it up a little and started ‘er up again.
My initial solution was, as it was with everything related to search and filtering of my objects so far, to just add another custom finder to acts_as_taggable.rb. I mean I already had hacked that thing to the point that it didn’t resemble the original file much. And I know, I shouldn’t have been putting things RIGHT into that file anyway but overriding it’s methods somewhere else in case I ever updated the plugin…I know.
Here is an example of one of my acts_as_taggable hacks…
def find_tagged_with_and_conditions(list,conditions,page=1,page_size=10)
find_by_sql([
"SELECT distinct #{table_name}.* FROM #{table_name}, tags, taggings " +
"WHERE #{table_name}.#{primary_key} = taggings.taggable_id " +
"AND taggings.taggable_type = ? " +
"AND taggings.tag_id = tags.id AND tags.name IN (?) and #{conditions}" +
" order by created_at desc " +
" limit " + ((page -1) * page_size).to_s + ", " + page_size.to_s,
acts_as_taggable_options[:taggable_type], list
])
end
I know it’s terrible. I know I’m breaking all kinds of rules. But hey, it’s our first “real” rails app. We’re still trying to scrub off the ASP.Net.
It’s pretty obvious what that does. It lets me get a set of results based on a tag, some conditions, and it implements some paging. Pretty straightforward once you get past all the sacrilege of how I’m doing it. It IS semi-DRY since it will work on any model that I’ve set as acts_as_taggable. So it’s…OK.
Where I run into a problem is when I have to do that same thing but through a has_many :through join model. You see I have People and I have Invitations and they join through Invitees. Invitees has some other data in it (which is why I’m not just using HABTM). The filtering I need to do is on a list of Invitations but one of the criteria is that I need to filter by who the invitation was sent to, an Invitee. Thanks Jas!
I started looking around for a solution to tagging that didn’t use acts_as_taggable. Some say it’s deprecated. I don’t know. I ran across Evan Weavers blog entry about has_many_polymorphs and how it could be used for tagging. I read through all the comments and a couple related blog entries and it looked like it would help me get where I needed to go. I jumped on IRC#polymorphs (the IRC channel for the plugin) to ask around. cdcarter was the only one in the channel that was available. He was great moral support! He suggested that I just make a branch in Subversion and hack away on the branch with HMP (has_many_polymorphs). Sounded good to me! And hey, I had never made a branch before. I was learning all kinds of new stuff!
HMP installed easily enough. Evan’s even gone through the trouble of including a generator that will make the tagging parts for you. Since I already had some of the parts I didn’t go that route but it looks like the way to go when starting from scratch. The only glitch I ran into was that I had to add require 'active_record' to my extension lib so that it would recognize ActiveRecord::Base Not sure what is going on there but it’s working.
tag=Tag.find_by_name(@tags_filter)
@invitations=tag.invitations.find(:all,
:conditions=>@conditions,
:include=>:people,
:offset => (@page -1) * @per_page,
:limit => @per_page,
:order => 'invitations.created_at DESC')
I have some more work to do to DRY it up but it has simplified my code tremendously. Thanks Evan!
I have to also thank the guys over at The Rails Way for reminding me of the power of join models and the “right” way to use them.
Nomadic Functions
April 24th, 2007
I was browsing around last night looking for a good JavaScript color picker and came across this little website called Nomadic Functions. Contains a bunch of small, specific JavaScript functions are are fairly useful. One of which, is a snazzy DHTML color picker!
Few other helpful scripts they have are:
- AJAX Star Rating
- Dynamic Fonts
- Sphere Color Picker
- Table Organize
- Table Sort
The UI Guy
April 21st, 2007
And I'm the UI Guy...currently working as a UI Development Mgr at one of those 'bigger' companies where UI tends to clash with 'marketing'. =)
This pretty much says it all!
Let's get this train-wreck a-rollin...
April 17th, 2007
Alright, maybe I judge myself too harshly. Time will tell.
Jason and I are working on a project that we'll be talking about. We're on our first true Rails project and it's caused its share of headaches. We'll share some of what we're learning while getting this hog up and running.
We've also got a list of other projects we'll be starting soon and telling you all about.
We've got business junk to deal with that someone out there might want to hear about, too.
Next time on 2 Jasons....Jason will eat a live snake! Don't miss it!