s

Posts Tagged with "tutorial"

Deploying a Merb application with the RailsMachine gem

Feb 07, 2009  -  Comments

I recently launched a monthly bill/task tracking application I've been working on in my spare time. I used Merb so I could get some experience with the framework.

Plexus was kind enough to donate some server space on a RailsMachine server. Luckily, they recently added Passenger support to their awesome RailsMachine gem, so all I needed was to add a Rack config file to run my app on Passenger.

require 'rubygems'
require 'merb-core'
 
Merb::Config.setup(:merb_root => File.expand_path(File.dirname(__FILE__)),
                   :environment => ENV['RACK_ENV'])
Merb.environment = Merb::Config[:environment]
Merb.root = Merb::Config[:merb_root]
Merb::BootLoader.run
 
run Merb::Rack::Application.new

After that, I only needed to update the Capistrano deploy file to work with Merb and Passenger.

require 'railsmachine/recipes'
 
# The name of your application. Used for directory and file names associated with
# the application.
set :application, "listode"
 
# Target directory for the application on the web and app servers.
set :deploy_to, "/var/www/apps/#{application}"
 
# Primary domain name of your application. Used as a default for all server roles.
set :domain, "listode.com"
 
# Login user for ssh.
set :user, "deploy"
set :runner, user
set :admin_runner, user
 
# Rails environment. Used by application setup tasks and migrate tasks.
set :rails_env, "production"
 
# Automatically symlink these directories from curent/public to shared/public.
set :app_symlinks, %w{graphs}
 
set :deploy_via, :remote_cache
 
# =============================================================================
# ROLES
# =============================================================================
# Modify these values to execute tasks on a different server.
role :web, domain
role :app, domain
role :db, domain, :primary => true
role :scm, domain
 
 
# =============================================================================
# APPLICATION SERVER OPTIONS
# =============================================================================
set :app_server, :passenger # :mongrel or :passenger
 
# =============================================================================
# SCM OPTIONS
# =============================================================================
set :scm, :git # :subversion or :git
set :repository, "git@github.com:travisr/#{application}.git"
 
# =============================================================================
# CUSTOM CONFIGURATION
# =============================================================================
# action to symlink database file
namespace :deploy do
  desc "Symlink database config file."
  task :symlink_db do
    run "ln -nfs #{shared_path}/system/database.yml #{release_path}/config/database.yml"
  end
end
 
# Overwrite the default deploy.migrate as it calls:
# rake RAILS_ENV=production db:migrate
desc "Use datamapper to call autoupgrade instead of db:migrate."
deploy.task :migrate do
 run "cd #{release_path}; rake db:autoupgrade MERB_ENV=production"
end

after 'deploy:update_code', 'deploy:symlink_db'

The custom section at the bottom sets up a symlink to my databases.yml file since I don't keep that in my git repo. I also have to override the migration action to use DataMapper's db:autoupgrade.

Tagged: merbpassengercapistranorails machinetutorial

Changing Paperclip File Storage Location

Jan 11, 2009  -  Comments

For a while at Plexus, we've been using FileColumn for all our image/file upload attachment needs. It's worked out really well, but when we saw Paperclip we thought it might be a better choice.

Paperclip is super easy to setup and use, but we found ourselves wanting to slightly change the default way it stored attachments. We were used to the way that FileColumn created its folder structure. It would make a folder named for the model (singular) that the attachment(s) were part of and a folder for each attachment (singular). Say we had a BlogPost model with an image attachment and a file attachment. FileColumn would make the following two folders: public/blog_post/image and public/blog_post/file.

Paperclip operates a little differently. It creates a folder (plural) in public for each attachment. If you had the same setup as before, Paperclip would create the following two folders: public/images and public/files. Can you see the immediate problem with this? We already have a public/images folder in our default Rails file structure, so this might get a little confusing. Another problem arises if we have several models with an image attachment. Paperclip would store them all in the same folder. This would be ok 99% of the time. See, Paperclip gets images by their id and name, so even if two models have image attachments and the same id, as long as the name is different, we're ok. But, if you somehow add a different image with the same name, to a model with the same id, then it would get overwritten. Not very likely, but still possible.

The great thing about Paperclip is that you can change the default way it stores its attachments. You can just past a few extra options to the has_attached_file call in your model. As of the current version of Paperclip, they have fixed the file structure problem by tweaking the default storage path. They added a system subfolder in the public folder. This has a two-fold benefit. First, we don't have the problem before of having an attachment named image. Second, this works great for Capistrano because the system folder is already symlinked from the public folder, so you don't have to worry about adding symlinks to your deploy file.

Anyway, back to my point of changing the default file storage path. You can just add the url (which tells where to retrieve the files) and path (which tells where to save the file) options to your has_attached_file call in the model. We add the :class option to include the model_name as a folder.

has_attached_file :image,
  :styles => {:thumb => '120x120>', :large => '640x480>' },
  :default_style => :thumb,
  :url => "/system/class/attachment/id/style/basename.extension",
  :path => ":rails_root/public/system/:class/:attachment/:id/:style/:basename.:extension"

NOTE: Thanks to commenter Steve Bartz for pointing out that current versions of Paperclip have the default path set to ":rails_root/public:url". Because of this, you can leave out the :path option.

So, now our folder structure for the original example would be public/system/blog_posts/images and public/system/blog_posts/files. Much better!

Just make sure you put the whole path in the :path option using the :rails_root variable.

Tagged: railsrubypapercliptutorial

Emulating RJS with Merb/JQuery/Haml

Dec 21, 2008  -  Comments

Ok, Merb is great. Rails is equally as great. RJS is great. JQuery and Haml are just outstanding. But I found that Merb not doing RJS-like behavior a little daunting in the current application that I'm working on. Of course, Merb does javascript, and you can send Ajax requests, but it's not quite as easy to accomplish RJS-type interactions with the current page. I did quite a bit of googling, and all I ever came up with was a very interesting presentation from Yehuda Katz about using JQuery with Merb. It didn't completely answer my question about getting RJS functionality in a Merb app. So, I set about trying to actually figure something out on my own!

Let's say I have an action called mark_as_complete that I can call for several items on a page, have it make a database call, then update the item without having to leave the page. The first step is to make the form for each item submit via AJAX instead of a regular HTTP POST request. Thanks to Ryan Bates' RailsCast on JQuery for help on that. All I have to do is give the form a class of remote, then this function will cause it to be submitted via AJAX (as well as any form on the site with the same class name):

// function to send the jQuery form object via AJAX
jQuery.fn.submitWithAjax = function() {
  this.submit(function() {
    // be sure to add the '/posts/taggedwith/tutorial/2/.js' part so that it knows this is format:js
    $.post(this.action + '/posts/taggedwith/tutorial/2/.js', $(this).serialize(), null, "script");
    return false;
  })
  return this;
};

$(document).ready(function() {
  // once the page has completely loaded,
  // make forms with the class "remote" submit via AJAX
  $("form.remote").submitWithAjax();
});

Now that's done, I need to figure out how to get the RJS stuff to fire correctly. I've already set the format of the request as .js, so the controller knows it's an AJAX request. With Merb, you have to add the provides :js line to your action so that it knows to accept js requests.

def mark_as_complete(id)
  provides :js
  # do work, son
  # set instance variable to indicate success/failure
  if stuff_done
    @complete = true
  end
  render
end

Now for the funky part. Getting my Haml template to update the page. Well, if you just name a view mark_as_complete.js.haml, then the application will use it. The hard part was figuring out what to do in the file. I could call javascript in the file and get that to work (alert(), for example), but the following bit of code did NOT work:

- if @complete
  $("#div_name_#{@record.id}").html('complete');
  $("#notes_#{@record.id}").replaceWith('

#{@record.notes}

'); - else alert("An error occurred processing your request.");

Nothing happened and I couldn't figure out why. The javascript was being run, but the ruby code inside it wasn't being evaluated. Turns out it's because I'm using Haml. I suspect that if I were using erb, then I could surround my ruby code with output blocks(<%= %>) and it would work(though I haven't tested it).

After experimenting with all sorts of variations on the above code, I finally decided to try and put the javascript code into Haml ruby output blocks(so the ruby code would be evaluated). Low and behold, the following actually worked, despite being ugly and less readable.

- if @complete
  = "$('#div_name_#{@record.id}').html('complete');"
  = "$('#notes_#{@record.id}').replaceWith('

#{@record.notes}

');" - else alert("An error occurred processing your request.");

Notice that the alert() call isn't in an output block because it doesn't have any ruby code that needs to be evaluated.

This solution worked for me, but I'm sure there is a better way to accomplish this. If anyone has a better approach, please leave it in the comments.

Tagged: rubyhamljquerymerbrjstutorial

Stop! Haml Time!

Nov 23, 2008  -  Comments

Ok, first of all I have to apologize for the title of this post, but I couldn't resist the pun.

Anyway, as the title suggests, I've recently converted all of my erb pages on this site to haml. Haml is a DSL templating language uses to generate well-formed XHTML. The documentation site claims that you can become familiar with the syntax in about 20 minutes. I think, as long as you already know XHTML, you can pick it up in about 10 minutes. The concept is pretty simple. Each tag should be on its own line, and everything contained in that tag should be indented below it. If a tag doesn't have any other tags contained in it, then the content can be on the same line as the tag. Also, div is the default tag, so you can just pass an id or class name to output a div.

%p This is a paragraph tag.
%ul
  %li First list item
  %li Second list item
#id_name Content in div
.class_name Content in div

Will convert to (with beautiful formatting):

<p>This is a paragraph tag.</p>
<ul>
  <li>First list item</li>
  <li>Second list item</li>
</ul>
<div id="id_name">
  Content in div
</div>
<div class="class_name">
  Content in div
</div>

It's really easy to include rails evaluation and output blocks too.

- @posts.each do |post|
  %p
    Written by
    = post.author
  %p= post.date

Notice that there is no need for end when a block is done. When haml sees that the indentation has ended, it automatically ends the block.

You can also pass any attributes to an HTML tag via a ruby hash.

%img{:src => "/images/picture.png", :alt => "Picture", :title => "Picture"}

I think the coolest thing about Haml is how it can assign a class and unique id to an element simply by passing an ActiveRecord object within square brackets.

%div[@post]

Will output:

<div class="post" id="post_18"><div>

If you'd like to learn more, you can take a look at the online documentation, or you can look at the code for my site on Github.

Tagged: rubyxhtmlhamltutorial

RESTful Routes Demystified

Sep 14, 2008  -  Comments

RESTful application design is a rails-standard way to structure your CRUD actions. It simplifies your controllers into 7 actions: index, show, new, create, edit, update, and destroy. Here are some basics for working with RESTful routes.

The simple way to get the default routes:

ActionController::Routing::Routes.draw do |map|
  map.resources :products
end

Route Options

Several options can be passed to the route to customize it. You can use the path_prefix option to customize the appearance of your URLs. The name_prefix option is used to change the way the routes are called. The controller option is used to specify a custom controller.

# start all of your URLs with /admin
# '/admin/projects/id/edit/index.html'
map.resources :products, :path_prefix => '/admin/index.html'

# start all of your routes with 'mng_'
# mng_products_path or mng_product_path(:id)
map.resources :products, :name_prefix => 'mng_'

# if you want your URLs to say '/products/id/index.html'
# but your controller is called 'store_products'
map.resources :products, :controller => 'store_products'

Adding Routes for Custom Actions

The resources method will generate the routes for the default REST actions, but what if you want to add your own actions to your controller? There are two options for adding custom routes: the member option is for routes that require an id to be passed, and the collection option is for actions that don't require an id. With each option, you must pass a hash with the action name and the HTTP method.

# return_policy_products_path => '/products/return_policy/index.html'
# submit_question_products_path => '/products/submit_question/index.html'
map.resources :products, :collection => {:return_policy => :get, :submit_question => :post}

# warranty_product_path(:id) => '/products/id/warranty/index.html'
map.resources :products, :member => {:warranty => :get}

Nested Routes

You can nest routes for objects that are related. Say the products have customer reviews with a has_many relationship. You can nest the resources call within the products route definition.

# product_reviews_path(:product_id) => '/products/product_id/reviews/index.html'
# product_review_path(:product_id, :id) => '/products/product_id/reviews/id/index.html'
# edit_product_review_path(:product_id, :id) => '/products/product_id/reviews/id/edit/index.html'
map.resources :products do |products|
  products.resources :reviews, :controller => 'customer_reviews'
end

Those are the basics, but there are a LOT more possibilities. If you're interested, there is a really great guide to all things routes.

Tagged: railsroutestutorial