Archive for August, 2008
Bill by the hour
After reading Practices of an Agile Developer, I have begun billing only by the hour since about 1 month ago. It was one of the many golden nuggets I gleaned from that book.
Here’s a nice and concise article that basically summarizes why its better to charge at an hourly rate (and not just better for the developer. It’s better for the client too). I’ve already seen much improvement in my clients’ happiness level.
It’s been my experience previously working at Compaq and later starting up our own business six years ago that in a fixed bid situation, either the client gets taken to the cleaners or the developers do. With fixed bid, the requirements of a project have to be so locked down and fine grained that there is no flexibility. If requirements change, costs for the developer can spin out of control. For a developer to make profit, they have to bid the project up really high to cover any unforeseen scope creep. Business as usual (fixed bid) just didn’t make sense to us.
I am stilling giving my clients a general idea on price, but I am not promising them anything. Fixed bids are broken promises - mainly because of damn scope creep, and the fact that what the client wants and what the client comes to expect over time are two different things. It’s imperative to create for the client what he or she has come to expect.
No!Spec letter
This is a useful letter to send to a client who is trying to get spec work out of you.
Adjusting contrast on your mac
press and hold ctrl+alt+cmd then tap “,” and “.” to adjust contrast again and again
Ruby on Rails order by associated model - find.(:order => ”)
1 2 3 4 5 6 7 | def recent_obituaries(limit) Obituary.find(:all, :limit => limit, :include => :services, :order => "services.start_time DESC") end # obituaries has_many :services # services belongs_to :obituary |
Installing Ruby Enterprise on slicehost
I already had Phusion Passenger installed on my slicehost. Now, I wanted to install Ruby Enterprise Edition after hearing how much memory it could possibly save on my slice.
I just followed the instructions here.
cd ~/sources wget http://rubyforge.org/frs/download.php/41040/ruby-enterprise-1.8.6-20080810.tar.gz tar xzvf ruby-enterprise-1.8.6-20080810.tar.gz sudo ./ruby-enterprise-1.8.6-20080810/installer
Then I just hit enter at all the prompts.
At the end I received the following message.
Ruby Enterprise Edition is successfully installed! If you're using Phusion Passenger (http://www.modrails.com), and you want it to use Ruby Enterprise Edition, then edit your Apache configuration file, and change the 'PassengerRuby' option: PassengerRuby /opt/ruby-enterprise-1.8.6-20080810/bin/ruby If you ever want to uninstall Ruby Enterprise Edition, simply remove this directory: /opt/ruby-enterprise-1.8.6-20080810
Change the apache config
cd /etc/apache2 sudo nano apache2.conf # scroll to the bottom of the page # change PassengerRuby /usr/bin/ruby1.8 to PassengerRuby /opt/ruby-enterprise-1.8.6-20080810/bin/ruby
Then I did:
sudo /etc/init.d/apache2 restart
Resources:
- Comparison to thin
- Ruby Enterprise Edition & Phusion
Adding categories to your rails blog
So you’ve created your blog (title, body, etc), and now you want to add categories.
Here’s a handy tutorial for how to add categories to your rails blog. This isn’t the way to do it in Rails 2.0.
Instead follow Akita’s tutorial with comments and posts in Rails 2, but adjust for categories.
Uploading multiple images with paperclip
Firstly, I recommend not using the model name in the field name. Instead do file_etc. Also I recommend against using Attachment as your model name. I ran into this problem:
undefined method `quoted_table_name' for Paperclip::Attachment:Class
Also see, Advanced Rails Recipes, recipe #13 for help on this.
Ok let’s go.
Create the model.
1 | script/generate scaffold Photo file_file_name:string file_content_type:string file_file_size:integer |
Migrate the database in terminal
rake db:migrate
Add the following to app/model/photo.rb
1 | has_attached_file :file, :styles => { :medium => "300x300>", :thumb => "100x100>" } |
Aside: This assumes you are setting this up under the admin folder for app administration purposes.
Create admin folder with photos controller inside
script/generate controller admin::photos(If you get a warning like helpers/photos_helper.rb already exists then just delete that file, and run the generator again.)
Set up controllers/admin/photos_controller.rb to look something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | class Admin::PhotosController < ApplicationController layout 'admin' before_filter :login_required # GET /photos # GET /photos.xml def index @photos = Photo.find(:all) respond_to do |format| format.html # index.html.erb format.xml { render :xml => @photos } end end # GET /photos/1 # GET /photos/1.xml def show @photo = Photo.find(params[:id]) respond_to do |format| format.html # show.html.erb format.xml { render :xml => @photo } end end # GET /photos/new # GET /photos/new.xml def new @photo = Photo.new respond_to do |format| format.html # new.html.erb format.xml { render :xml => @photo } end end # GET /photos/1/edit def edit @photo = Photo.find(params[:id]) end # POST /photos # POST /photos.xml def create @photo = Photo.new(params[:photo]) respond_to do |format| if @photo.save flash[:notice] = 'Photo was successfully created.' format.html { redirect_to(admin_photos_path) } format.xml { render :xml => @photo, :status => :created, :location => @photo } else format.html { render :action => "new" } format.xml { render :xml => @photo.errors, :status => :unprocessable_entity } end end end # PUT /photos/1 # PUT /photos/1.xml def update @photo = Photo.find(params[:id]) respond_to do |format| if @photo.update_attributes(params[:photo]) flash[:notice] = 'Photo was successfully updated.' format.html { redirect_to(admin_photos_path) } format.xml { head :ok } else format.html { render :action => "edit" } format.xml { render :xml => @photo.errors, :status => :unprocessable_entity } end end end # DELETE /photos/1 # DELETE /photos/1.xml def destroy @photo = Photo.find(params[:id]) @photo.destroy respond_to do |format| format.html { redirect_to(admin_photos_url) } format.xml { head :ok } end end end |
Create the following view files under views/admin/photos/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | # _form.html.erb <%= f.error_messages %> <p> <%= f.label :file %><br /> <%= f.file_field :file %> </p> <p> <%= f.submit %> or <%= link_to 'Cancel', admin_photos_path %> </p> # edit.html.erb <h2>Editing photo</h2> <% form_for([:admin, @photo], :html => { :multipart => true }) do |f| %> <%= render :partial => 'form', :locals => { :f => f } %> <% end %> # index.html.erb <h2>Photos <small>(<%= link_to 'Create new photo', new_admin_photo_path %>)</small></h2> <table class="styledtable"> <tr> <th>Photo</th> <th>Sizes</th> <th>Manage</th> </tr> <% for photo in @photos %> <tr class="<%= cycle('even','odd') %>"> <td><%= image_tag(photo.file.url(:thumb)) %></td> <td><%= link_to 'thumb', photo.file.url(:thumb) %>, <%= link_to 'medium', photo.file.url(:medium) %>, <%= link_to 'original', photo.file.url(:original) %></td> <td><%= link_to 'edit', edit_admin_photo_path(photo) %> | <%= link_to 'delete', [:admin, photo], :confirm => 'Are you sure?', :method => :delete %></td> </tr> <% end %> </table> # new.html.erb <h2>New photo</h2> <% form_for([:admin, @photo], :html => { :multipart => true }) do |f| %> <%= render :partial => 'form', :locals => { :f => f } %> <% end %> # show.html.erb <p> <b>photo file name:</b> <%=h @photo.file_file_name %> </p> <p> <b>photo content type:</b> <%=h @photo.file_content_type %> </p> <p> <b>photo file size:</b> <%=h @photo.file_file_size %> </p> <%= link_to 'Back', admin_photos_path %> |
Edit your config/routes.rb file so that photos can route to apps/controllers/admin/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | map.resources :photos map.resources :categories map.resources :manufacturers map.resources :users map.resource :session map.resources :line_items map.resources :orders map.resources :products map.namespace :admin do |admin| admin.resources :products admin.resources :orders admin.resources :manufacturers admin.resources :categories admin.resources :users admin.resource :session admin.resources :photos end |
Add paperclip’s plugin functionality to the model photo.rb
1 2 3 | class Photo < ActiveRecord::Base has_attached_file :file, :styles => { :medium => "300x300>", :thumb => "100x100>" } end |
Restart your server and test that you can add photos by going to http://localhost:3000/admin/photos/new
Did that work. Good. Now we can finally, move onto adding multiple images to another model. I am going to use the product.rb model and products_controller.rb to add these multiple images.
Add the belongs_to and has_many to the models.
1 2 | # app/models/product.rb has_many :photos |
1 2 3 4 5 | # app/models/photo.rb belongs_to :product # paperclip has_attached_file :file, :styles => { :medium => "300x300>", :thumb => "100x100>" } |
Add references to the photos model
script/generate migration add_product_references_to_photosAnd populate that file like so
1 2 3 4 5 6 7 8 9 10 11 12 13 | class AddProductReferencesToPhotos < ActiveRecord::Migration def self.up change_table :photos do |t| t.references :product end end def self.down change_table :photos do |t| t.remove_references :product end end end |
rake db:migrate
Then I am going to follow Railscast Episode 73: complex forms to add multiple images.
————————
UPDATE:
Use advanced Rails Recipes # 13
DON’T FORGET THE MULTIPART FOR UPLOADING IN YOUR FORM!!! Recipe #13 fails to mention this since it’s not dealing with images.
1 2 3 | <% form_for([:admin, @product], :html => { :multipart => true }) do |f| %> <%= render :partial => 'form', :locals => { :f => f } %> <% end %> |
————————
Add photos.build to new action of apps/controllers/admin/products_controller.rb
1 2 3 4 5 6 7 8 9 10 | # admin/products_controller.rb def new @product = Product.new 3.times { @product.photos.build } respond_to do |format| format.html # new.html.erb format.xml { render :xml => @product } end end |
Create _photo.html.erb partial under /app/views/admin/products/
1 2 3 4 5 6 7 | # _photo.html.erb <% fields_for "product[photo_attributes][]", photo do |photo_form| %> <p> <%= photo_form.label :file %><br /> <%= photo_form.file_field :file %> </p> <% end %> |
Add collection partial to /app/views/admin/products/_form.html.erb
1 | <%= render :partial => 'photo', :collection => @product.photos %> |
And that’s it. That will work.
Though there are still some unresolved issues. Take a look at Ryan’s Railscasts (except for episode 75. See Advanced Rails Recipe #13 instead.) on complex forms to add ajax, and also make sure you add dependent destroy to your model. Otherwise, when you delete a product the photos won’t be deleted.
How to install ImageMagick on Tiger
Just install from source.
I needed this in order to use paperclip the rails plugin.
