Main Content:

Posts tagged as 'rake'

19 Rake db:backup 0 comments

July 22

I apologise for the quietness around ThinkRefresh recently, but I have been busy on a few projects, however some of them are coming to a close (or rather, an open...), so I have had little chance to post. Anyway, enough excuses, one of the good things to come from my work on these projects is the many things I have developed or discovered a long the way, many of which I hope to post here, so here is the first.

It is always a pain manually backing up your database, and even if your web host does backup for you, they may not keep copies long enough for your liking, so with a helping hand from S3 you can simply install the below rake task as a file in your lib/tasks directory and drop in your AWS keys. I cannot take full credit for the script, so I have quoted those involved within it, all I did was drop in SQLite support, so this was more of a discovery than a development.

One final note before you dig in, if you add support for another database engine or make any changes, please feel free to update the code on gist.github.com/739.

# S3 Backup Task for MySQL & SQLite # Assumes InnoDB tables # If using MySQL database user needs the "reload" permission on the database (for --flush-logs in mysqldump) # # Stores files in Amazon S3 using the excellent AWS Gem: http://amazon.rubyforge.org/ # For information about Amazon S3: http://aws.amazon.com/s3 # # Installation # 1) Install AWS Gem # 2) Enter your S3 Bucket, access_key_id and secret_access_key in this file # 3) Run (rake db:backup) # # Inspired by code from: # http://blog.craigambrose.com/articles/2007/03/01/a-rake-task-for-database-backups # http://www.rubyinside.com/advent2006/15-s3rake.html # http://info-architects.net/2007/08/25/rake-task-for-mysql-backup-to-amazon-s3/ # # Support for SQLite added by Ryan Townsend of http://thinkrefresh.com namespace :db do require "aws/s3" require "ftools" desc "Backup database to Amazon S3" task :backup => :environment do BUCKET = "my.bucket.example" begin AWS::S3::Base.establish_connection!( :access_key_id => "ABC", :secret_access_key => "XYZ" ) db_config = ActiveRecord::Base.configurations[RAILS_ENV] backup_path = "db/backup" File.makedirs(backup_path) case db_config['adapter'] when 'mysql' stored_file = backup_file(backup_path, "#{db_config['database']}.sql.gz") sh "mysqldump -u #{db_config['username']} -p #{db_config['password']} --single-transaction --flush-logs --add-drop-table --add-locks --create-options --disable-keys --extended-insert --quick #{db_config['database']} | gzip -c > #{stored_file}" when 'sqlite3' stored_file = backup_file(backup_path, File.basename(db_config['database'])) File.copy "#{RAILS_ROOT}/#{db_config['database']}", stored_file else puts "Unable to backup databases of type: #{db_config['adapter']}" end if stored_file && File.exists?(stored_file) puts "Created backup: #{stored_file}" puts "Storing file in S3: #{BUCKET}" AWS::S3::S3Object.store(stored_file, open(stored_file), BUCKET) puts "Backup Complete" else puts "Unable to create backup" end rescue AWS::S3::ResponseError => error puts error.response.code.to_s + ": " + error.message end end def backup_file(path, file) File.join(path, "db_#{Time.now.strftime("%Y%m%d%H%M%S")}_#{file}") end end

20 Rails 101: Routing 2 comments

September 24

Routes define what URLs are used to access different parts of your application.

When you run the scaffold generator described in the previous screencast routes are setup for you automatically but they only setup the basics.

In this screencast, I will show you how routes work and how to set them up yourself.

How Routes Work

You'll probably hear the phrase 'RESTful routes' passed around more often than not, REST stands for Representational State Transfer and basically makes use of different types of request to process different actions, this way applications can interact with one another very easily over the standard HTTP protocol.

So how does REST affect you? Well if you are familier with HTML you will know that forms have a method attribute which is set to GET or POST, however there are actually a few more methods available to HTTP but not included in HTML, these include the PUT and DELETE methods, REST uses these to figure out whether you are trying to create, read, update or delete an item.

Create => POST /users Read => GET /users/1 Update => PUT /users/1 Delete => DELETE /users/1

The standard Rails setup needs a few more actions than these, for starters there is the index action which lists all the items in the collection, this can be accessed by GETing the collection URL, in this example at/users. Other methods by default suffix the collection URL or object URL with their name, however this behavior can be overridden - but we'll look into that later.

Index => GET /users New => GET /users/new Edit => GET /users/1/edit

Putting The Magic To Work

The routes file is located within the config directory, by default it contains just two routes, both of which aren't restful and simply act as catch all for non-nested URLs, personally I like to remove these as they let you get away with not setting up your routes properly.

To make our RESTful users section, all you need to type is map.resources :users, pretty simple right?

You can now test this with a rake command, so we'll pop open our terminal and enter rake routes, as you can see there has been multiple routes setup for us.

If you look carefully, you'll notice that each route has a name associated with it, this becomes useful in our views and controllers where we need to reference the URLs dynamically.

You can setup named routes by typing the name of your route after the word map. When setting up routes manually this way you have to specify a string which is parsed for parameters, the parameters are words prefixed with a colon, like symbols in Ruby.

For an example, I'll create a route to disable a user, the route will be called disable_user, and the route will include the user's ID, and it will call the disable method of the users controller.

map.disable_user 'users/:user_id/disable', :controller => 'users', :action => 'disable'

If we switch back to our terminal, you can see that the route is now active.

This can now be called in views and controllers as disable_user_path and disable_user_url, there is no rule for whether to use path or url, but the general consensus is that you should use ..._url in controllers, and ..._path in views.

There is one problem with our route, we were trying to match the user's ID which is a number and at the moment, it will match a word or anything upto the next forward slash, so we can add a constraint to the user id.

To do this add another symbol key to the end of the route with the name of the attribute (in this case user_id) and set it to a regular expression, if you don't know what regular expressions are, don't worry too much for now as they will be covered in a future episode. For now you can set a digit constaint using /\d/.

map.disable_user 'users/:user_id/disable', :controller => 'users', :action => 'disable', :user_id => /\d/

Remember I mentioned those additional HTTP methods before? Well we can constrain by them too, by adding the conditions hash to the end containing only one key value pair, I'll choose "PUT" which describes updating a current record.

map.disable_user 'users/:user_id/disable', :controller => 'users', :action => 'disable', :user_id => /\d/, :conditions => { :method => :put }

If you are following the restful route (no pun intended), you generally won't be writing your own routes like this very often, most of the time you can add them to the map.resources, so if we return back to our users entry, we can add our disable method to the member hash, which means it acts on one object, not the whole collection. If we wanted a disabled action to list all disabled users we can add it to the collection hash.

map.resources :user, :member => { :disable => :put }

Another bonus for using map.resources is you can easily nest resources, so for example if a user has a profile, I would nice to be able to have routes like /users/one/profile, and to save writing out massive lines of routing code, we can just change the current map.resources into a block and drop another map.resource within it, but you don't use map this time, you use the word between the goal posts, in this case 'users'.

map.resources :user, :member => { :disable => :put } do |users| users.resource :profile end

There are a few more special things to note about routes, and those are the root route (again no pun intended), path prefixing and route globbing.

When a user goes to the root URL of your site you often want users to be greeted with a homepage, or dashboard, so to do that simply create a named route called root without a URL string attached to it. In this example, I'll make it point to the index of the users controller.

map.root :controller => 'users', :action => 'index'

Path prefixing allows you to prefix resource URLs with a string such as account, this means that you can group various URLs under one section.

map.resources :user, :member => { :disable => :put }, :path_prefix => 'account'

For route globbing, or in other words catch all routes with an unlimited nesting depth (i.e /random/url/here), you can prefix the parameter with an asterisk instead of a colon and in your controller and view the parameter will return an array of the parts.

map.page '*path', :controller => 'users', :action => 'page'

Summary

To create restful routes, you can use map.resources or resource, depending on whether you want a collection, or singular resource respectively.

For the homepage you should use map.root which negates the URL option, as it defaults to an empty string.

And for other named routes you simply type map followed by the name and the URL as a string.

The options available to you are :member and :collection for adding additional actions to restful routes. There is a :conditions hash for constraints (currently :method is your only available option). For adding base urls behind restful routes use :path_prefix. :controller and action to specify what section the route relates to. And finally the attribute name (e.g. :user_id) which allows you to constrain the parameters with regular expressions.

Download Screencast: Mirror 1 | Mirror 2 | iPhone Size: 18.9mb, Time: 07:34