Archive for category Rails

Working with private RubyGems in Rails 3

I have several gems that contain proprietary code that I do not want public to have access to.    Rubygems.org is great for gem hosting, but they do not allow security restricted gems, since it defeats the purpose of having an open community for ruby gems.  There are several options if you want to host your own gems and restrict access to them.  Here are some of the options that I considered

1. Install gemcutter and use that for a gem server.

2. Install the geminabox sinatra app.  This is a lightweight gem server with a web interface.

3. Use github.  They no longer build gems, but you can still install from github using Bundler (or a plain rake task).

I started playing around with the first 2 options, but I ultimately decided to just use the github solution.  Why waste resources setting up a private server when my code is already stored on github with security.  I am assuming you know how to set up a project on github, password protect it and push your gem source to github.

Git’r tagged

Probably one of the coolest parts of working with gems is being able to install specific versions of a gem in your project.. which is much easier than using vendor plugins.  In order to keep track of your versions you need to work with git tags.  You could also do this with branches, but it would get a bit hairy if you created a new branch for every patch version.  A good example of the proper way to set this up is the Ruby on Rails source on github – > http://github.com/rails/rails.  Click the drop-down where it says “Switch Branches”.  You can see they have a branch for every minor & major version 1.2, 2.0, 2.1, 2.2, 2.3, and 3.0 (master).  But notice that you do not see branches for minor versions like 2.3.8.  They use git tags to keep track of each version instead.  Click “Switch Tags” on the rails source github page.  Here you can see they created a separate tag for each version.  You will want to do the same with your private gems that you host on github.

Jeweler to the rescue

Luckily, jeweler has already done most of the work for us.  Install jeweler first:

gem install jeweler

Jeweler provides some nice shortcuts for creating, building and installing your gems. Take a look at the documentation for more info. I am only going to touch on the versioning that is included with this gem. After you create a project with Jeweler, you get a rakefile that gives you building and versioning tasks. Lets say you make a bunch of changes in your gem project, commit and push them to github and you are ready to bump this gem to a new version. You can use these Jeweler rake tasks to accomplish that:

rake version:bump:patch
#you can also use version:bump:minor and version:bump:major.. 1.2.3 -> 1=major, 2=minor, 3=patch
rake github:release
rake git:release

Jeweler also has a rake task called “release”. This will accomplish the 3 tasks above, but it also tries to push the gem to gemcutter. I could not find a way to turn that off in the documentation (although it may be possible.. I have not looked at the source in depth). I opted to write some custom tasks that combine these 3 tasks into a single task. So I added this to my rakefile:

namespace :version do
  desc "create a new version, create tag and push to github"
  task :github_and_tag do
    Rake::Task['github:release'].invoke
    Rake::Task['git:release'].invoke
  end

  task :patch_release do
    Rake::Task['version:bump:patch'].invoke
    Rake::Task['version:github_and_tag'].invoke
  end

  task :minor_release do
    Rake::Task['version:bump:minor'].invoke
    Rake::Task['version:github_and_tag'].invoke
  end

  task :major_release do
    Rake::Task['version:bump:major'].invoke
    Rake::Task['version:github_and_tag'].invoke
  end
end

The first tasks takes care of the git tag and github commit/push. The other 3 tasks use the first task and add the version bump for major, minor or patch. So now if I want to do a new patch version, I just have to execute one rake task.

rake version:patch_release

That is half of the equation. The other half is installing the gem from github in your Rails 3 project with Bundler.

Bundler takes care of the rest

Bundler ships with Rails 3 and its purpose is gem dependency management. I really like this tool so far, and the development seems to be moving along very fast. I learned a little trick when I was trying to see if something was fixed in Rails Edge. You can specify github as the source of the gem and it will pull the source code from github, build it and install it for you. You can even specify the tag which makes life even easier. So all I have to do is add one line to the Gemfile in my Rails 3 project and install the bundle. Check this out:

Gemfile

gem ‘mygem’, :git => ‘git@github.com:myuserid/mygem.git’, :tag=>”v1.0.2″
bundle install

Now that is easy!

Tags: , , , , ,

Replacing link_to_remote with UJS in Rails 3 (Prototype)

Rails 3 has adopted Unobtrusive Javascript and moved all the _remote functions to a plugin.   They did this in an effort to remove all the inline spaghetti code produced by the helpers and provide a more modular way to deal with Javascript & Ajax. I must admit I like the convenience of the old helpers, but its not really the best way to do things if you are dealing with a complex app that uses alot of Javascript.  So anyways, you will get the following error if you try to use the link_to_remote function:  undefined method ‘link_to_remote’

In order to use UJS in your Rails 3 app, you need to do the following:

1. Use the new javascript file, rails.js along with your favorite javascript framework

In your layout, you can use the default javascript tag to include this and prototype:
The only problem with this method is that it may add javascript files that you don’t need and this adds to page load time.  I like to take advantage of the Google hosted javascripts.  Most people already have them cached and chances are Google will serve the files faster than your hosting company.  I am using some of the scriptaculous libraries so I include that tag too.   Here is what I use:
Make sure you put the rails js tag after prototype.  I wasted 30 minutes figuring that out.

Rails 3 makes it easy to use jQuery or Mootools if you prefer them over Prototype.  Just include them instead.


2. Use the forgery protection meta tags: <%= csrf_meta_tag %>

Rails uses these to prevent XSS attacks.  I don’t know the details, so I will not go into it.. but you need to have this helper which spits out 2 meta tags:
Don’t hard code these.  The helper provides a unique token.

 

3. Change your link_to_remotes to link_to and use the :remote parameter

Lets say you have a list of widgets and people can vote Yes or No as to whether they like the widget.  After they vote, it updates the box with a highlight and changes the text to show you the vote result.  Here is my big ugly Rails 2 version of this:

Here is what you do in Rails 3  (please ignore the ugly path.. we are renovating):

You now use a regular link_to tag and pass the :remote parameter as true.

 

4. Use a js.erb file instead of render :text=>”blah blah”

The link_to tag does not accept :update, :complete, etc., like the link_to_remote tag did.  You can drop in a js.erb file to handle this stuff instead.  I determine the text and color of the highlight in my controller action. /myapp/app/views/widgets/vote.js.erb

Rails sends this back to your page and the javascript is executed.  This is a simplistic solution and the first one I found to produce the same behavior as my original Rails 2 helper.  There may be better ways to do this, but for now this works for me.  Here are some resources that I found useful when figuring this out:

Ryan Bates screencast was very helpful and most of this stuff was “inspired” from that.  Props Ryan, good stuff as usual -> http://railscasts.com/episodes/205-unobtrusive-javascript

Simone Carletti has a good overview on UJS in Rails 3, and is more geared towards jQuery – http://www.simonecarletti.com/blog/2010/06/unobtrusive-javascript-in-rails-3/

Another jQuery example – http://joshhuckabee.com/jquery-rails-3

Tags: , , ,

Performance Exercise #1: Rails Cache = $Cash

As part of my Rails 3 upgrade I am focusing on performance in my app.  The most obvious place to start is caching.  I have a few action caches scattered here in my app, but I neglected to use sweepers in order to cache just about everything.  Rails Sweepers allow you to observe an object in a controller method and expire cache if something changes in your model that warrants a cache expiry.  If your Rails site is mainly a CMS that is 90%+ read-only, you will greatly benefit from caching in Rails.  My app is a public facing read-only site, except for back-end administration stuff.  We add/update/delete content maybe 50-100 times, but most of the content is on pages that can be cached separately.  As a result, we will see a huge performance increase by using action and fragment caching.  It does not look like a whole lot has changed from Rails 2 to 3 according to the edge guides, so you can probably use these techniques on both.

Note: I am using the default caching mechanism that ship with Rails 3 beta4.  Since there are so many out-of-date plugins right now that are not fully compatible with Rails 3, I figured I would not use a plugin for caching.

Types of Caching in Rails

  1. Page Caching – caches page and bypasses application server
  2. Action Caching – caches an action, but still runs through the application server.  application_controller.rb is still executed, but the action in your specific controller is cached
  3. Fragment Caching – caches a fragment of code.  Executes application_controller.rb, widgets_controller.rb.  The caching happens in the view only.

I can’t use Page Caching in my app, because we have some users that login to an account.  If you try page caching with an app like this, then your users will not be able to see dynamic elements of the app that relate to their user account.. and even worse, you risk caching a page for a logged in user, such as an administrator.  Since my app is public facing and has admin accounts, this is not an option.  enter Action Caching…

Action Caching

Action caching is probably your best bet if you have an app with public and private pages.  Action caching will allow you to detect if a user is logged in and decide to cache or not cache.  Lets start with the basics of an action cache:

This will cache your show page, but the cool thing is that it caches each individual object.  So you will have a separate cache for  http://yourapp.com/widgets/1 AND   http://yourapp.com/widgets/2.  This is great, but we are still caching the same page for users who are logged in and users who are not logged in.  This will cause problems, so we need to add a condition to caches_action and a convenience method in the application_controller.  Take a look:

In the application controller, I setup my @user variable based on whether they are logged in. Then I added a cacheable? method. If the user is blank (meaning not logged in), then the action is cacheable, otherwise it is not cacheable (because the user is logged in). Then I am able to use the cacheable? method in the widgets_controller like so:


caches_action allows you to use an if condition. If the :if condition is true, then it caches the action. You must pass a Proc object so that the code is executed at runtime. If this method did not use a Proc, then the code would be executed when the rails code initializes on app server startup.. so basically it would return the same thing every time and it would be useless. The “x” in proc just uses the current object, which is your widgets controller… so the Proc has access to the cacheable? method. Now your actions will be cached if a user is not logged in.  This is a plus for my app, because most of the people that visit my site are public users.  That is great and all, but what if something changes on one of my pages??  I don’t want to use a cached version.  The content on the page will not be fresh.  So what do I do??  This is where sweepers come in to play. Rails Sweeper – A class that observes a model for changes and expires cache based on what changes occur.  You can tell the Sweeper what cache to expire for each type of change. Here is an example class that will show you whats up:
This class is a subclass of ActionController::Caching::Sweeper, which provides it with all the necessary mechanisms to access your controller code and expire the cache.  Notice the first line after the class definition.  You have to include this, so the sweeper knows which Model class to keep an eye on.  Next you see the callback methods.. after_update, etc..  Basically this method says, after you update a Widget instance (widget),  run this code.  The widget variable is the specific widget object that you just updated.  So if you updated widget with the id of 7, you would have access to that variable, just as if you had written “widget = Widget.find(7)”.   This class also has a private method called “expire_cache_for” that all of the callbacks use.  You don’t have to use this private method and you can put any code you want inside the callbacks.  Most of the time it makes sense to use a private method that expires multiple action caches, though.  So in this example, if you create, update or destroy a method, the cache will be expired for that particular object’s show page and the index page as well.

I was wondering where to put the sweeper when I was coding it.  Since I am using Rails 3, I just created a new directory inside myapp/app/ called “sweepers”.  I drop all of my sweepers in here and Rails 3 picks up these classes automatically.  No matter where you place it, you want to make sure these classes get loaded when your server starts.

The sweeper classes are only half of the equation though when dealing with sweeping cache.  You must also add a line to the controller where you want to use the sweeper.  In this example it makes sense to add it to the “widgets_controller.rb”, since that is where the widgets get created,updated, and destroyed.  Take a look at my example:

I added “caches_sweeper” to my WidgetsController. This method takes the sweeper name (WidgetSweeper or :widget_sweeper), and an optional : only parameter. I made the mistake of trying to use : except instead of : only.  caches_sweeper does not know what : except is, so don’t even bother trying.  I’ll put that one on my Rails wishlist.  : only tells the cache sweeper class to only run on these actions.  If you do not use : only, then the sweeper will run on every action in the controller. So thats all you have to do to use action caching with sweeping.   Now lets learn about fragment caching….

Fragment Caching

Now that you have your public facing pages cached, you think to yourself… why should the public have all the fun??  What about my admins that are logged into the system?  Do they get no $cache love?? There are bound to be parts of the application that your logged in users can share, which are not user-specific.  This is where you can use fragments.  Since you don’t want to cache the entire action, you can cache parts of the view.  The only downside to this is that your controller action will be fully executed.  This will probably not be much of a problem if you are using the new Rails 3 query API.  It has lazy loading features built in, so that the database call is only used when it is needed.  If you are just setting up an object or collection without calling any methods on it, the lazy loading feature will not make a database call until the view or helper uses that instance variable.  So my point is, fragment cache in the view will not result in any unnecessary database calls in Rails 3.  Fragment caching is pretty simple.  Take a look at this example html.erb page:

This is about all there is to a fragment cache. You use a block and pass the name to it. If you don’t provide a name to the cache, then it will use “/controller/action” as the default name. You can also get fancy and pass a hash instead like this:

This is helpful if you have more than one fragment in a view and you want to expire them separately. action_prefix will append to the end of the name so you keep them separate. You expire fragment cache in a similar manner to the action caches. Just pass the name or a hash… like so:

Now its time to get crazy with this. My users that are logged in should not cache the same fragments as the non-account users. A helper can achieve that. Check it out:

You can use this helper in place of “cache” in your view like so:

This will use a cache method if the user is logged in, and just yield the block if they are not logged in. So only our logged-in users create and use this cache.

If you want to get really crazy, you can probably cache on a per user basis by appending their user_id to the name. I haven’t tried this and it would probably result in a full cache store for a site with a large user base… but there are endless possibilities when you use helpers with these cache blocks. Same goes for the Proc stuff in action cache.

Sweeping can also be used with the fragment cache and you can use an existing sweeper. Just insert the expire_fragment calls where needed.

So thats about it. These are good techniques to speed up your app without sacrificing fresh content.  A word of caution though:  Analyze your application before and after you add caching.  You need to figure out exactly where the cache expiration should happen and which pages need action vs. fragment caching.  You don’t want to risk stale content on a page because you did not look at all the use cases.  There is no real science to figuring this out.  You just need to look at all of your models and controllers carefully to decide where to use it and think of any odd cases that require cache expiration that may not relate to the same model/controller.  A good example is a storefront with the newest products on the home page.  In this example, you would not only need to expire cache on the product pages, but also the homepage to keep it up to date with any changes & additions that may occur to the products.

So does cache = $cash?? Hell yeah it does. Less database calls = Less CPU cycles = more users on less hardware = less money spent on hosting.

Tags: , , , ,

Setting up Rails 3 on Rackspace Cloud Servers

QUICK UPDATE 03-14-2011 – PLEASE READ – This tutorial was written when Rails 3 was in beta. Some of this info may be outdated. This tutorial will be updated soon, but here are a few notes until I get to it:  For steps 6 & 7, use RVM and install 1.9.2 using RVM. This should install rubygems, so you can skip #7.   For step #8, install without the –pre flag… it will install the current official release (3.0.5 or higher).  I am not using Passenger at this time, so I can’t comment on how well that will work.  You may need to include the bundler Capistrano recipes with your deploy.rb.  This will bundle install with the –deployment flag.

I could not wait patiently for the official release of Rails 3, so I am working on a Rails 3 upgrade now… and part of the process is moving my site from co-location hosting over to Rackspace Cloud.  I am a big fan of the Rackspace Cloud and while most of my websites are hosted there now, I was waiting for my co-location contract to run up before I moved over my highest traffic website.  Here are the steps I used to set up my server. I will show you how to install all the software and create a blank Rails 3 app. Then I will show how to pull an existing Rails 3 app from github and deploy it with Capistrano.

Note: I am not doing this, but you may want to go the route of installing RVM if you will be running multiple applications or ruby versions on your production server.

My Rails stack looks like this:

  • CentOS 5.5
  • Ruby 1.9.2 preview 3
  • Rubygems 1.3.7
  • Rails 3 beta 4
  • Passenger 2.2.15 with Nginx
  • memcached
  • MySQL

Here are the steps I used.  Please leave a comment if you have any questions or if something needs to be changed.

1. Login or signup for Rackspace Cloud Servers (not Cloud Sites)
2. Build a new server – Hosting -> Cloud Servers -> Add Server -> Create a CentOS 5.5 server

(YMMV with other linux flavors using my steps)

3. Login and change your root password
4. Create a new user and add them to the sudoers file.

It is important that you create a new user instead of doing everything as root.  You will most likely run into permission problems with Passenger and Bundler if you do this as root.  Passenger will run as ‘nobody’ if ‘root’ owns the rails app root directory.  It will not have access to root’s local gem bundle. Also, go ahead and disable root login from sshd

useradd app_user
passwd app_user
vi /etc/sudoers  #open sudoers in your favorite text edit
# add the following line below "root ALL=(ALL) ALL" :
# app_user ALL=(ALL) ALL
# save file and exit

#disable root login from ssh, so nobody is able to brute force a root login
vi /etc/ssh/sshd_config
#uncomment "PermitRootLogin yes" and change it to "PermitRootLogin no"
/etc/init.d/sshd restart

#logout and login or su to your new user
su app_user
cd ~
5. Update EPEL and install linux software dependencies
sudo rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm
sudo yum -y install gcc gcc-c++ make zlib zlib-devel openssl openssl-devel git expect pcre pcre-devel readline-devel mysql mysql-devel libxml2 libxml2-devel libxslt libxslt-devel
6. Install Ruby 1.9.2 preview 3 (compatible with Rails 3 beta 4)
wget ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.2-preview3.tar.gz
tar -xvf ruby-1.9.2-preview3.tar.gz
cd ruby-1.9.2-preview3
./configure
make
sudo make install
ruby -v #just checking to make sure it installed.. output = ruby 1.9.2dev (2010-05-31 revision 28117) [x86_64-linux]
cd ..
7. Install Rubygems 1.3.7
wget http://production.cf.rubygems.org/rubygems/rubygems-1.3.7.tgz
tar -xvf rubygems-1.3.7.tgz
cd rubygems-1.3.7
sudo ruby setup.rb
cd ..
8. Install Ruby on Rails 3 Beta 4

If the official release is out you can remove the –pre. This should install a bunch of gems related to rails 3 beta 4 (23 gems on my system)

sudo gem install rails --pre --no-ri --no-rdoc
9. Create a new directory for your rails applications and create a blank rails app

In rails 3 you need to use the new parameter after the rails command in order to create a new app

mkdir rails_apps
cd rails_apps
rails new myapp
10. Install passenger with Nginx
sudo gem install passenger
sudo passenger-install-nginx-module
# This will guide you thru nginx/passenger install.. hit enter to check dependencies
# choose option 1. Yes: download, compile and install Nginx for me. (recommended)
# I installed in default /opt/nginx.. just hit enter to install default
# hit enter at the end and you should be finished and back to the command line prompt
11. Update your Nginx configuration file so you can run myapp with passenger.

Example Nginx config file:  /opt/nginx/conf/nginx.conf

#I found it necessary to change the user in order to have the correct permissions.
user  app_user;
worker_processes  1;
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
error_log  logs/error.log  info;

events {
  worker_connections  1024;
}

http {
  #Note: rubygems installs gems in the 1.9.1 dir even though I am using 1.9.2... not sure why??
  passenger_root /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.15;
  passenger_ruby /usr/local/bin/ruby;
  # passenger recommends 30 max pool size if you have a 2GB box that is dedicated to app server and
  # less than that if you have a DB server running on it
  passenger_max_pool_size 30;

  include       mime.types;
  default_type  application/octet-stream;
  sendfile        on;
  #tcp_nopush     on;
  keepalive_timeout  65;
  tcp_nodelay        on;
  gzip  on;
  gzip_min_length  1100;
  gzip_buffers     4 8k;
  gzip_types       text/plain;
  gzip_comp_level 2;
  gzip_types      text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;

  server {
    listen       80;
    server_name  localhost; #putting in localhost just for testing.  You should set this to your domain name: yourdomain.com
    root /home/app_user/rails_apps/myapp/public;
    passenger_enabled on;  #MAKE SURE YOU HAVE THIS LINE OR IT WILL NOT FORWARD TO PASSENGER!
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
       root   html;
    }
  }
}
12. Start Nginx, which will also start Passenger
sudo /opt/nginx/sbin/nginx
13. Open up iptables for port 80 and restart iptables.

Also, you might want to look into enable SELinux for some added security on your system.

sudo vi /etc/sysconfig/iptables
# Add the following line towards the bottom below the port 22 ACCEPT
# -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
# save and close
# restart iptables
sudo /etc/init.d/iptables restart

You should be able to get to your Rails app now and see the default rails index.html -> Welcome aboard. You’re riding Ruby on Rails! ……

This is great and all, but I am more interested in running my existing app in production, rather than creating a new one. I am guessing you will not be developing on the server, so we need to get things set up with github & capistrano and deploy our app that way. This assumes that you already have a project in github and you have some git knowledge. This also assumes that you already have a database server setup and that your database.yml is pointing to that server. I will not cover database setup here.

Here is what you do:

14. Generate a new ssh key on the server to use with github
ssh-keygen -t rsa -C "youraddress@email.com"
#use the default dir and enter a solid password
15. Add the key to github

Full instructions about creating a key and adding to github here: http://help.github.com/linux-key-setup/

16. Add entry into known_hosts file for github

This is necessary to pull with capistrano. I think the easiest way to do this is to clone your github project. It will create the known_hosts file and add the entry for you. Then you can just delete the project right after you clone it. No need to keep it, since you will be using the Capistrano setup.

git clone git@github.com:your_username/your_project.git
# type yes to accept the key entry
# enter your passphrase
# it will clone.. now delete it
rm -Rf your_project/
OPTIONAL: Out of habit I have all my rails apps in the same place on my servers.

I have a dir called /rails_apps. I am going to create that now, but you can use what you already have set up if you like.  I also need to change the dir to be owned by “app_user”

sudo mkdir /rails_apps
sudo chown app_user:app_user /rails_apps/
17. Go ahead and create your project and release directory for Capistrano to use.
mkdir /rails_apps/myapp
mkdir /rails_apps/myapp/releases

IMPORTANT! It is necessary to update your nginx configuration if you are using Capistrano.  Since cap uses the “current” directory and links to a release directory, your nginx config should change the “root” entry in the server block

open /opt/nginx/conf/nginx.conf and change the “root” entry to “root /rails_app/myapp/current/public;”  Then you can restart nginx like this:

sudo killall -9 nginx
sudo /opt/nginx/sbin/nginx
18. Capistrano Setup

Now you are ready to move on to Capistrano setup. I do all development locally on my Mac, commit the code with git and push it to my repository at github. In order to use Capistrano to deploy, you will need to “gem install capistrano” on your Mac (or windows box.. say it ain’t so!) and add 2 files to your Rails project. /myapp/Capfile and /myapp/config/deploy.rb. Here are my files:

Capfile

load 'deploy' if respond_to?(:namespace) # cap2 differentiator
Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }

load 'config/deploy' # remove this line to skip loading any of the default tasks

deploy.rb – most of this config came from the passenger capistrano recipe.

set :application, "myapp"

#github stuff
set :repository,  "git@github.com:your_github_id/myapp.git"
set :scm, :git
set :scm_username, "your_github_id"
set :scm_passphrase, "your_github_passwd"

set :use_sudo,    false
set :deploy_to,   "/rails_apps/#{application}"

#server login
set :user, "app_user"
set :password, "server_password_here"

ssh_options[:forward_agent] = true

# will be different entries for app, web, db if you host them on different servers
server "123.456.12.34", :app, :web, :db, :primary => true

namespace :deploy do
  task :start, :roles => :app do
    run "touch #{current_release}/tmp/restart.txt"
  end

  task :stop, :roles => :app do
    # Do nothing.
  end

  desc "Restart Application"
  task :restart, :roles => :app do
    run "touch #{current_release}/tmp/restart.txt"
  end
end

Now you are ready to deploy. The whole point of using Capistrano is so you don’t have to login to your servers to push code, do restarts, etc.. So on your mac, you will run the “cap” commands for deployment.

This is a 3 step process.  Run these from the command line on your development box while in your rails app root directory (dir that contains Capfile):

  1. cap deploy:check   #this will check for dependencies
  2. cap deploy:setup  #this will create your shared directories.. log, pids, system
  3. cap deploy  #this will pull your code from github and put it in a new release directory, create a link to “/rails_apps/myapp/current” directory for that release and restart your passenger server

Now, take a look at your app on the server.  If you are lucky it will be running as expected, but I highly doubt it if you have gems that need to be installed.  Lets use bundler to get the necessary gems installed.  If you are Rails 3 savvy, you know that all your gem dependencies should be in /myapp/Gemfile, and that you can use Bundler (which is awesome BTW.. thnx wycats) to install them easily.

Passenger gives me a real pretty screen that says: “Could not find gem ‘mysql (= 2.8.1, runtime)’ in the gems available on this machine. (Bundler::GemNotFound)”

My Gemfile contains that gem, but it is not on my server.  So to get Bundler to install it, you will need to navigate over to your rails app root directory on the server. I mentioned earlier that you should not set up your rails app with the ‘root’ account on your linux box, and this is the problem that I ran into. When I installed with bundler, it put the bundled gems in /root/.bundle/…. and the web server could not access the gems, because passenger runs as ‘nobody’. If you run the server as ‘app_user’ or whatever you account is, then you should bundle install with the same account, so you dont have permission problems.

cd /rails_apps/myapp/current
bundle install  #no need for sudo here.  You want them to install in your user's home dir
19. Memcached Setup

Next on my list was getting memcached working. I use memcached as my cache store. In /myapp/config/environments/production.rb, I have the following line:

config.cache_store = :mem_cache_store

So passenger was complaining about not having memcached -> no such file to load — memcache

First I need to install memcached on the server. You can use a seperate server for memcached but you need to update the line in production.rb, to point to that server. It uses localhost by default.
In order to get memcached to work, you have to install libevent first

cd
wget http://www.monkey.org/~provos/libevent-1.4.13-stable.tar.gz
tar -xvf libevent-1.4.13-stable.tar.gz
cd libevent-1.4.13-stable
./configure && make
sudo make install
cd ..
wget http://memcached.googlecode.com/files/memcached-1.4.5.tar.gz
tar -xvf memcached-1.4.5.tar.gz
cd memcached-1.4.5
./configure && make
sudo make install
#you will get errors if you do not do the next 2 steps.  Create libevent config file, add a line to it, and then run ldconfig
sudo vi /etc/ld.so.conf.d/libevent-i386.conf  #create this file and add one line ->
                                                              # /usr/local/lib/
sudo /sbin/ldconfig
#start your memcached server up
sudo /usr/local/bin/memcached -u nobody &
#you should add this last command to /etc/rc.local so the memcached server starts if there is a reboot

Now your memcached server is running, but you still have to install the gem. Let’s add it to our Gemfile, git commit, & git push on our Mac, run “cap deploy” and then bundle install again on the server

#Gemfile located at myapp/Gemfile. on your dev box
group :production do
gem “memcache-client”
end

One note of caution here.. if you are already in /rails_apps/myapp/current, you need to cd again. If I remember correctly I ran into the problem of being in an old release directory. I guess when the link was update, my shell did not catch the update and was pointing current to the previous release directory.

cd /rails_apps/myapp/current
bundle install

You should have a working Rails app with memcached now. I did run into one gotcha when I was doing this. I had a legacy Rails app that I was upgrading and the following line was in environments/production.rb:

config.action_view.cache_template_loading = true

This line needs to be removed if you get the following error -> “undefined method `cache_template_loading=’ for ActionView::Base:Class”

Now open up your app in your browser and feel all the Rails 3 goodness! You are a rock star!

Hopefully YMDNV ;-)   Everything seems to be working so far on Rails 3 and I have a fairly complex app with quite a few plugins.  I think the hardest part about adopting Rails 3 is the fact that plugins are not ready.  I spent a good bit of my time troubleshooting and patching (yeah.. some monkey stuff) plugins.  You can read more about my upgrade experiences in this post.

Tags: , , , , ,

Rails 3 Upgrade.. my experience

UPDATE 1 (6/29/2010): Today I purchased a copy of “Rails 3 Upgrade Handbook” by Jeremey McAnally, who wrote the rails_upgrade plugin.  I would highly recommend reading this book as your starting point for Rails 3 upgrade, as I wish I read this before I started my upgrade.  He goes into alot of detail about what you need to change.  The steps differ some from what I wrote in this post, and you might find it easier to follow his steps.

This is going to be quite a long post and I will probably be updating it daily for the next few weeks.  Now that Rails 3 RC is right around the corner, I figured it would be a good time to upgrade CouponShack.com.  I have been meaning to refactor my code for quite a while and clean up the mess I have created in the last 3 years.   I am finally getting around to it and I thought upgrading to Rails 3 would be a good start, so here is my experience upgrading to Rails 3, from Rails 2.3.2.  I am writing some of this after the fact, so hopefully I didn’t leave out any steps:

Current stable setup on CouponShack.com

  • Co-location hosting in my city
  • CentOS 5
  • nginx
  • Mongrel 1.1.5
  • Rails 2.3.2
  • Ruby 1.8.7

Proposed upgrade

  • Rackspace Cloud Servers hosting
  • Rails 3 beta4 (upgrade to RC when ready)
  • Ruby 1.9.2dev
  • New app server… looking at Passenger right now

The upgrade list may change depending on how everything pans out.  I was planning on moving over to Rackspace Cloud (where most of my other apps are currently), so I figured I would go with Rails 3 as a part of that move.

Note: I use Mac OSX 10.5.8 and Textmate for development

Another Note: A good place to start is to read the Rails 3 Release Notes.  This document explains the new features, upgrade path, and has alot of good links to blog posts by the rails developers that relate to the new features.  My blog post reiterates alot of this info while showing you how it relates to my specific upgrade.  I recommend reading as much as you can about Rails 3 before you get your feet wet with the upgrade… I know that helped me.

Upgrade Steps

Step 1 – Install RVM

RVM will allow you to run multiple versions of Ruby on your development box.  This is a must if you are upgrading to Rails 3 and still want to use Rails 2.  Follow the instructions on the RVM website and install it from github.

Step 2 – Install Ruby 1.9.2

After RVM is installed, you will want to install Ruby 1.9.2 using RVM.

rvm install 1.9.2

It will take a few minutes.  On my system it installed ruby-1.9.2-preview3. After its done you can switch to 1.9.2 by using this command.

rvm 1.9.2

Now your Mac is using 1.9.2 instead of the system ruby (you can switch back with “rvm system” if you need to code some rails 2 stuff again)

Step 3 – Install Rails 3

Using Ruby 1.9.2 on your system, you can now install Rails 3.  One thing to note here is that you now have a clean slate for all your gems.  RVM conveniently stores your gems in a directory under your account ~/.rvm/gems/ruby-1.9.2-preview3/gems.  This will allow you to keep your Rails 2 gems separate, but that means that you have to reinstall any gems that you might be using in your Rails 2.3 app.  You will probably get plenty of errors when you finally start up your app as a result of the missing gems… I will get into that later.  So here is all you need to do to install the Rails 3 beta 4.

sudo gem install rails --pre

You should be set now with Rails 3.  You can do a version check just to be sure with “rails -v”.

Step 4 – Generate a blank Rails 3 application

NOTE: If you don’t have your app in a source control repository, it may be a good idea to also make a full backup of your app directory before you start changing things.

I found it useful to have a blank Rails 3 app to use as a reference when upgrading my 2.3 app.  You will most likely need to drop some of the files in your 2.3 app or at least copy/paste some of the code.  Create the new app with the rails command in your terminal.

rails new blank_app

Step 5 – Get the rails_upgrade plugin and run it

This is an officially supported plugin from Rails.  Get it here: http://github.com/rails/rails_upgrade Jeremy McAnally has instructions on how to install rails_upgrade as a plugin.  I remember seeing this was opted as a plugin instead of a gem because it was easy to install and delete from your application.

I ran the check rake task first after I installed this plugin.  It gave me quite a large list of things I need to change.  Most of the changes involved the new ActiveRecord Query API, which is pretty awesome btw.  I just ran down the list one by one and changed what it was complaining about.  I re-ran it after every section to make sure the changes were correct.  There are a few other Rake tasks to add the new Gemfile, add the application.rb and fix your routes, but I opted to do that manually in order to learn more about the changes.

I am guessing you may spend alot of time updating queries and scopes, so check out these blogs from some of the Rails gurus for more info on what has changed in Rails 3 and when the old stuff will be deprecated:

http://m.onkey.org/2010/1/22/active-record-query-interface

http://hasmanyquestions.wordpress.com/2010/01/17/let-your-sql-growl-in-rails-3/

Step 6 – Remove your old scripts and add the new rails script

Rails 3 has replaced all the scripts in /app_root/script with a single script called rails.  I deleted all of the files in the script directory and copied the file from my blank application in step 4 and put the file in my script directory.  I think it makes much more sense for rails to have one script file.  This file can start the server, generate scaffolding,  start your console etc..  To see a full list of commands use “rails –help” inside your application somwhere.

Step 7 – Upgrade your environment files (rails_upgrade may take care of this)

The individual environment files in config/environments/ are also a little different in Rails 3.  I am not sure if rails_upgrade takes care of this, but I did not use that rake task, so I just updated the files manually.  You will need to add a block around your configuration in each of these files.  Mine looks something like this for development.rb:

Couponshack::Application.configure do
  config.cache_classes = false
  #.....
end

Also, make sure you update your environment.rb file if rails_upgrade didn’t do it for you.  Refer to the blank application in step 4 for the code… its only a couple of lines now.

Step 8 – Add application.rb (rails_upgrade will do this for you)

If you want to do this manually to see whats new, check out the application.rb file in your blank app.  There are examples in the comments.  For those that don’t know, application.rb is sort of a replacement for environment.rb.  This file is for application specific configuration and it makes sense that they would remove this from environment.rb, since application and environment configuration are really 2 completely separate things.. app configuration is not specific to an environment.

Step 9 – New routes (rails_upgrade will do this for you)

Rails 3 has new syntax and features for the routes file.  Look at config/routes.rb in the blank app you created in step 4.  The comments provide you with an explanation of the new route syntax.  Some of the highlights include shortened syntax for controller/action.  Instead of :controller=>”mycontroller”, :action=>”myaction”, you would write – :to=>”mycontroller#myaction”.   And new syntax for mapping..  instead of “map.connect” or “map.name_of_route”, you just use “match” (and :as=>”name_of_route”).  I would recommend doing this reading all the comments in the new file and doing it manually so you can learn the new syntax, unless of course you have hundreds of routes.

Step 10 – Take care of them Gems (rails_upgrade can do this too)

Along with application specific configuration, Gem config was also removed from environment.rb in favor of the Gemfile which is in the application root directory.  Again, take a look at a blank Rails 3 app to see what this file should look like.  The gemfile makes gem dependency management easy by using the new bundler.  Bundler checks your system for installed gems based on the Gemfile and can install them for you automatically.  If you try to start up your app and you are missing a gem dependency, you will see something like this:

Could not find gem 'mechanize (>= 0, runtime)' in the gems available on this machine.
Try running `bundle install`.

The Gemfile and Bundler are great additions to Rails 3.  For more info check out the bundler home page for more info on what it can do.

I know many of you will run into problems with existing Gems that are not quite Rails3 ready.  Hopefully most of the popular gems and plugins will be finished with the upgrade by the time the official release comes out… probably wishful thinking.  A good resource for troubleshooting problems with Gem and plugin compatibility with Rails 3 is this website:  RailsPlugins.org – Lots of comments have been posted on this site and more than likely someone has already experienced the upgrade problem that you may be having.  It was helpful in troubleshooting a few of the plugin bugs I found.

Step 11 – Beware: “h” helper in views is the default now

This is turned on by default now.  So you don’t have to do crap like this anymore: <%= h @blog.title %>.  The “h” is not necessary, so HTML will be escaped by default.  If you have any content that includes HTML coming from your database, you will need to wrap that with the “raw” method like so: <%= raw(@blog.html_enabled_content) %>

Step 12 – config.ru

Rack-based servers use this file to start the app, so you will probably want to add this if you have not already.  Again, copy the file from the blank app and change the application name.

Step 13 – Start up your app locally and pray

Mine did not work at all.  I use mongrel on my dev box and I had trouble running it manually (mongrel_rails start).  Then I used the rails server command and it seemed to work fine.  You can start it up by using the new rails script file.  All you have to do is type:

rails s

This will start up the server locally and you are ready to take a look at your app.

As far as a I remember, these are the steps necessary for my app to start running on my Mac.  If I left out anything, please comment on this post.  The rest of this post will go into detail on some of the problems I am having and how I solved them, as well as the upgrade path on my production server.  At the time of this writing I am planning on using Nginx in front of Passenger for my production environment.

Problems that I ran into (mainly plugins)

Problem 1 – recaptcha plugin

I was using this in vendor/plugins.  It when I tried to access pages that used this helper, it was complaining about “builder” not being available or something to that effect.  I monkeypatched the code in /plugins/vendor/recaptcha/lib/recaptcha.rb – added the following line to the top:

require 'builder'

recaptcha_tags worked in the views after I added that line.

Problem 2 – will_paginate errors

I was getting this annoying error:

undefined method `paginate' for #<Class:0x16c739c>

activerecord (3.0.0.beta4) lib/active_record/base.rb:1041:in `method_missing'

I included will_paginate in my Gemfile with this line:

gem "will_paginate", :git => "git://github.com/mislav/will_paginate.git", :branch => "rails3"

That did not fix it.  Same error.  I finally realized that I screwed up application.rb when I was upgrading.  I was missing the bundler require call, so the initializers in the railtie were never executed.  Make sure you have this in your application.rb, or your dependencies will probably crap out.

# If you have a Gemfile, require the gems listed there, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(:default, Rails.env) if defined?(Bundler)

Problem 3 – equals sign with blocks

When running rails_upgrade, it warned me of several blocks that needed an equals sign. This is new to Rails 3 and it makes sense now that I understand where to use it.  Basically if it spits out a tag or any output to the view, then you should use equals, otherwise if its an .each iterator or something like that that does not produce output don’t use it.  Some of these were false positives like the following:

<% @blogs.each do |blog| %>

Check out Ryan Bates screencast which shows when to use and not use the equals.  I am not sure if this has been fixed in the rails_upgrade plugin, but its something worth noting.  It was spitting out the printed representation of the objects when I used an equals with it.. not pretty ;-)

Problem 4 – my routes.rb is too bloated

rails_upgrade does a very good job of translating your routes.rb into the new syntax for the most part.  This is not a huge deal, but the only part I wanted to change was the way the collection section is written.  My routes.rb after the upgrade was hundreds of lines long.  My legacy code (which needs to be big-time refactored) has lots of collections for my resources.  This was a result of Rails 1 code being merged with Rails 2 code.. I used the collection hash in my resources to add any controller/action that was not RESTful.. and I had lots from my original Rails 1 app.. mostly custom views which could have been consolidated in the show or index action.  But anyways, all these collection members produce something that looks like this:

resources :blogs do
  collection do
    get :action1
    get :action2
    get :action3
    #...... etc.........
  end
end

After experimenting I found out that Rails 3 also accepts this form:

resources :blogs do
  collection do
    get :action1, :action2, :action3 #etc...
  end
end

Just personal preference, but I find this form much easier to manage because of the large number of collection actions that I have.. refactoring them is a whole different story.

Problem 5 – colon in case statement (caused by new Ruby version)

I use the little known, “date_time_text_field_helpers” plugin.  Long name, but came in handy for handling some of my date/time fields.  When I tried to start my app server with Rails 3 it gave me the following error:

/rails_apps/couponshack/vendor/plugins/date_time_text_field_helpers/lib/date_time_text_field_helpers/instance_tag.rb:110: syntax error, unexpected ':', expecting keyword_then or ',' or ';' or '\n' (SyntaxError)
when :hour, :minute, :second : 2

So I looked at instance_tag.rb at line 110 and I see a syntax that I did not recognize in a case statement. I rarely use case statements and I don’t know much about them, but apparently Ruby 1.8x allowed the use of the colon character. This has been deprecated in Ruby 1.9. I changed the colons to “then”. Another problem is the html escaping. I had to update a call to content_tag and pass escape=false. You can check out the changes in my github fork:
http://github.com/johnmcaliley/date_time_text_field_helpers/commit/ba14af26781ac2576f20f0195703fad2c9657082#diff-0

Works just fine now.

more to come…


Tags: , ,

Git Tutorial Part 1: Working with branches and release versions of your Rails app

This week I am working with branches in git for the first time.  Up until now I just used my master branch for everything which works ok since I am the only developer on most of my apps, but now I realize branches will make my life alot easier in more than a few situations.  I am working with my brother on a project where it is necessary for us to use branches, so he started me on the path to git branch knowledge.  Here is my workflow so far.. and most of this relates to working with a Ruby on Rails web app that has stable milestone releases and a master branch for the future version.

I guess lets start with the basics of initializing git and using Github to host your code.  I am assuming you have git installed and you are using Github to host your main repository.. if no, proceed to Google.  Also, I am assuming you already have a project.. Rails or other.

ONE BIG NOTE: REALIZE THAT JUST BECAUSE YOU CREATE A BRANCH OR MAKE A CHANGE AND COMMIT IT ON YOUR LOCAL MACHINE, THAT DOES NOT MEAN THAT IT SENDS IT TO GITHUB OR THE MAIN REPOSITORY!!!  I WAS TOTALLY CONFUSED ABOUT THAT THE FIRST TIME I USED GIT.  LEARN TO PUSH AND PULL!

Go to your project directory, initialize git, add all the files and then make your first local commit with a message

cd /your_app
git init
git add *
git commit -m 'initial commit'

Now you are set up with your local repo. Next you need to login to (or create) your github account. You will create a new repository and name it the same thing as your local app. They will give you instructions similar to what I am telling you here after it is set up. This will add an origin to your local git config and then push all the local files that you just committed to that origin. If you have problems getting this to work, take a look in the github help about security and setting up your ssh keys here

git remote add origin git@github.com:your_username/your_app.git
git push origin master

You will get the following output

Counting objects: 22, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (19/19), done.
Writing objects: 100% (22/22), 3.73 KiB, done.
Total 22 (delta 3), reused 0 (delta 0)
To git@github.com:your_username/your_app.git
* [new branch] master -> master

What you see here is that your files were transferred to github and a new branch was created called “master”. The new branch was created automatically since there were no branches in this github project. To see what branches you have available on your local machine type:

git branch
* master

Your local machine knows about one branch and it is called master. Lets do some more branching now. My project is already at Version 1.0 but I am just now adding it to Github. Version 1.0 is stable and I may or may not make fixes or small additions to it. I am planning out my development roadmap and some major changes are going to happen in V2. So I don’t want to have to worry about changing and adding a bunch of crap and then realize that I need a minor fix on V1.. that would be a mess because I would have a codebase that was moving toward V2 and I would have to hack and hack in order to deploy that small change back to version 1.. what a mess. Thats where branching comes in. We need a Version 2 branch so we can keep things separate. To create another branch on your local machine do this:

git branch version-1_0-stable

Lets see if it worked..

git branch

OUTPUT:

* master
  version-1_0-stable

Yep.. there is our new branch. But notice that we are still on the master branch. Git puts a asterisks beside the current branch. In order to switch branches on our local machine, we need to use the checkout command.

git checkout version-1_0-stable

OUTPUT:

Switched to branch 'version-1_0-stable'

If you run “git branch” again, you will see a star beside version-1_0-stable. Wouldn’t it be easier if you could branch and switch all in one command? Oh yeah.. you can. Why didn’t I just say that in the first place?? Who knows, why I do alot of stuff… Here is how you delete your branch and then create it with the one-liner. Note that you can’t delete a branch you are currently on, so you must checkout back to master first:

git checkout master
git branch -D version-1_0-stable
git checkout -b version-1_0-stable

By throwing in that -b flag, you are telling git to create the branch and then switch to it all in one move.

Ok, cool, so I got another local branch. Why don’t I see the new branch in Github?? Because I have been working with git branches locally. Github is not aware of these changes because I have not pushed anything to it. You can see what github has by logging in and clicking on the “Switch Branches” dropdown in your project or by using the -r flag on your local box -> “git branch -r”. So now we need to tell Github about this branch.

git push origin version-1_0-stable

OUTPUT:

Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:your_username/your_app.git
 * [new branch]      version-1_0-stable -> version-1_0-stable

Frickin awesome dude… I gots a new branch and github has it too. So I have 2 branches that I can work in separately. Most of my work will be in the master branch which is on the path to version 2, but if something craps out in V1 and I have to put in a bug fix, I just “git checkout version-1_0-stable”, make my changes, commit them, and push them to the the V1 branch. Then I could easily deploy the new code without worry about V2 code getting in the way. Lets try that out:

You are already using version-1_0-stable. So go to your editor of choice and change a file. You must commit the changes locally first. This will commit to only the current branch, so master will not get these changes since you are on version-1_0-stable. You can commit your files several ways. Here are the 2 ways I do it.
1. Commit all local changes at the same time and use -v to view the changes

git commit -a -v

2. Commit a single file if you have other outstanding changes that you don’t want committed yet. Just use the relative path to a single file (or * wildcard it for a whole dir).

git commit -v path/to/your/file

Now your local machine knows about the committed files, but not Github. You have to push.

git push

OUTPUT:

warning: You did not specify any refspecs to push, and the current remote
warning: has not configured any push refspecs. The default action in this
warning: case is to push all matching refspecs, that is, all branches
warning: that exist both locally and remotely will be updated.  This may
warning: not necessarily be what you want to happen.
warning:
warning: You can specify what action you want to take in this case, and
warning: avoid seeing this message again, by configuring 'push.default' to:
warning:   'nothing'  : Do not push anything
warning:   'matching' : Push all matching branches (default)
warning:   'tracking' : Push the current branch to whatever it is tracking
warning:   'current'  : Push the current branch
Counting objects: 14, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (7/7), done.
Writing objects: 100% (9/9), 815 bytes, done.
Total 9 (delta 5), reused 0 (delta 0)
To git@github.com:your_username/your_app.git
   d8b9507..862d326  version-1_0-stable -> version-1_0-stable

Look at all those warnings. WTF is git talking about?? They want us to type more options in?? Well.. I actually think this is crap, but there is probably a good reason behind the default behavior. By default it is going to push all branches that it finds on your local box over to the origin server. So for instance, if you change and commit something on master, switch branches to V1 and change and commit something on that branch too, then git will push both of them. That could potentially be dangerous if you were not ready to push master or V1. So in order to fix that you need to pass your git config some options to make it only push the current branch you are working in.

git config push.default current

If you look at /your_app/.git/config, then you will see it added some options to your config.

[push]
        default = current

Next time you issue the git push command the warning goes away and it only pushes the current branch. Thanks for not being an asshole this time git… I really appreciate it.

So we are looking pretty good, but there is one more thing that is bugging me. What if I make a change to V1 and I want the changes to show up in V2 also. The bug is most likely in V2, unless I removed that feature or completely re-coded it. So how do I get them darn changes over to me other branch?? Move back to master (V2), maybe check the differences so you don’t totally mess it up, and then issue the merge command:

git checkout master
git diff master version-1_0-stable
git merge version-1_0-stable

If you have any conflicts, the merge might not work and it will tell you to fix them. Here is the message “Automatic merge failed; fix conflicts and then commit the result.” So how do I fix the conflicts? Go to the files that it marked as conflicting. In my case, my LICENSE file was conflicted so it said:

Auto-merging LICENSE
CONFLICT (content): Merge conflict in LICENSE

So I look at the file and git has put some junk in there that shows where the conflicts are and which branch they are coming from. Just edit the file to make it work and get rid of the junk (<<<<<<<<<<).

<<<<<<< HEAD:LICENSE Copyright (c) 2010 John McAliley  ======= Copyright (c) 2009 John McAliley >>>>>>> version-1_0-stable:LICENSE

Hmm.. its 2010, the file should say that, so I edit and make it look like this:

Copyright (c) 2010 John McAliley

Then I do a “git commit -a” and the conflict has been resolved. If you issue a “git merge version-1_0-stable” then it will tell you its up to date. Cool, I guess we are done. Not so fast chief!!! Don’t forget to push it to Github

git push

Now we done! The change in V1 is also in V2. Go drink a beer. You deserve it.

I think that about covers my workflow as it stands now aside from pulling using the rebase command. Yehuda Katz has an excellent tutorial on his Git workflow regarding pulling, pushing and resolving conflicts (although he does not go into branching). I have only been doing branching and versioning in git for a week, so I am sure some of you git masters might have a better workflow. If so, don’t be stingy.. leave a comment and show people a better way. Next tutorial will cover tagging as related to minor version changes in the master before it is actually considered stable. Check out the ruby on rails source in github if you can’t wait. They use branches and tagging for major and minor releases (including betas in the master branch)

Tags: , , , , ,

Custom Rails config file containing application constants

NOTE: I am not using this method now.  Take a look at settingslogichttp://github.com/binarylogic/settingslogic

I have used several methods to store app config such as putting them in environment.rb or including a module in the lib directory, but I stumbled across a method today on Stack Overflow that I really like. There are three easy steps involved in setting up a config file in Ruby on Rails for your application constants using this method.

1. Create a new yaml called app_constants.yml in /rails_app/config/ like so

defaults: &defaults
  my_const1: some value
  my_const2: some other value

development:
  <<: *defaults

test:
  <<: *defaults

production:
  <<: *defaults

2. Create a new file under /rails_app/config/initializers called app_constants.rb like so:

APP_CONFIG = YAML.load_file("#{RAILS_ROOT}/config/app_constants.yml")[RAILS_ENV]

3. Access your config variables using the APP_CONSTANT variable like so:

my_const1 = APP_CONFIG['my_const1']

The reason this works is that rails runs the code in the initializer you just created when you start up your app server. It sets the APP_CONFIG constant to the YAML content using the load_file method. This will return you a nice hash that you can access your application constants with. Simple and powerful at the same time. If anyone has a better suggestion to do this (not including the database), or sees any issues with this, please leave a comment. Thanks!

Tags: , ,

Using the collection parameter in a Ruby on Rails partial

Here is one my brother showed me the other day that I was not aware of. More RTFM and less Cowboy Coding.. I know, I know.. its just my nature. So here is the use case: You have a collection of objects that you want rendered in a partial. The way I used to do it is like this:

#controller code
@widgets = Widget.all

# view code
<%= render :partial=>"widgets/widget" %>

#partial code
<% @widgets.each do |widget| %>
  This widget is named <%=widget.name %> 

<% end %>

Now that is kinda ugly doing the iteration in the partial like that. Now for a cleaner way. You pass the @widgets to the partial with the :collection hash.

#controller code
@widgets = Widget.all

# view code
<%= render :partial=>"widgets/widget", :collection=>@widgets %>

#partial code
  This widget is named <%=widget.name %> 

This code will execute the partial multiple times.. once for each widget in the widget collection. That looks damn pretty. One caveat, though.. In the partial, the name of the local variable needs to be the same as the name of the partial. So if you have widgets/some_widget, the code would look like this:

  This widget is named <%=some_widget.name %> 

If you want to use another variable name you just add the :as hash, like this:

<%= render :partial=>"widgets/some_widget", :collection=>@widgets, :as=>:widget %>

Then you can use widget as the local variable name instead of some_widget. Got it? Good.

Tags:

star symbol in front of Ruby def argument

you may have seen something like this and wondered what the star symbol inside the argument parenthesis was:

def some_method(*args)
  #cowboy code goes here
end

The star symbol in front of args means that this method can accept an unlimited number of arguments. This is commonly used in Ruby on Rails when extracting options from the method arguments:

options = args.extract_options!

extract_options! is an ActiveSupport method that pulls out the option hashes from the method arguments.

Tags: ,

Ruby on Rails dynamic finder – find_or_create method

Here is another one I was not aware of. I knew you could use dynamic finders to look for one or more attributes, but I did not realize you could do a find and create at the same time (well.. not really both of them at once, but you know what I mean)

Blog.find_or_create_by_title("Some Blog")

If there is a blog with title = “Some Blog”, then it will pull this blog out of the table. If the blog does not exist, a new Blog is created with the title “Some Blog”. Alternately you can use find_or_initialize_by_name and it will not save the object to the database immediately.. it will be available in the scope of your code, just as if you called blog = Blog.new(:title=>”Some Blog”)

This could come in handy in certain situations and save you a line of code. Daddy like

Tags: ,