Thursday 11 October 2012

Has Many Through Relationship

Many-to-Many



There are two ways to build a many-to-many relationship.

The first way uses a has_many association with the :through option and a join model, so there are two stages of associations.

A has_many :through association is often used to set up a many-to-many connection with another model. This association indicates that the declaring model can be matched with zero or more instances of another model by proceeding through a third model.

For example, consider a medical practice where patients make appointments to see physicians. The relevant association declarations could look like this:


class Physician < ActiveRecord::Base
  has_many :appointments
  has_many :patients, :through => :appointments
end

class Appointment < ActiveRecord::Base
  belongs_to :physician
  belongs_to :patient
end

class Patient < ActiveRecord::Base
  has_many :appointments
  has_many :physicians, :through => :appointments
end

has_many :through

The collection of join models can be managed via the API.  for example, if you assign

physician.patients = patients

new join models are created for newly associated objects, and if some are gone their rows are deleted.

Automatic deletion of join models is direct, no destroy callbacks are triggered.

The has_many :through association is also useful for setting up "shortcuts" through nested has_many associations. For example, if a document has many sections, and a section has many paragraphs, you may sometimes want to get a simple collection of all paragraphs in the document. You could set that up this way:

class Document < ActiveRecord::Base
  has_many :sections
  has_many :paragraphs, :through => :sections
end

class Section < ActiveRecord::Base
  belongs_to :document
  has_many :paragraphs
end

class Paragraph < ActiveRecord::Base
  belongs_to :section
end

With :through => :sections specified, Rails will now understand:

@document.paragraphs


Friday 5 October 2012

Understand Rails Authenticity Token!


Understand Rails Authenticity Token!


What happens:
When the user views a form to create, update, or destroy a resource, the rails app would create a random authenticity_token, store this token in the session, and place it in a hidden field in the form. When the user submits the form, rails would look for the authenticity_token, compare it to the one stored in the session, and if they match the request is allowed to continue.
Why this happens:
Since the authenticity token is stored in the session, the client can not know its value. This prevents people from submitting forms to a rails app without viewing the form within that app itself. Imagine that you are using service A, you logged into the service and everything is ok. Now imagine that you went to use service B, and you saw a picture you like, and pressed on the picture to view a larger size of it. Now, if some evil code was there at service B, it might send a request to service A (which you are logged into), and ask to delete your account, by sending a request to http://serviceA.com/close_account. This is what is known as CSRF (Cross Site Request Forgery).

If service A is using authenticity tokens, this attack vector is no longer applicable, since the request from service B would not contain the correct authenticity token, and will not be allowed to continue.
Notes: Keep in mind, rails only checks POST, PUT, and DELETE requests. GET request are not checked for authenticity token. Why? because the HTTP specification states that GET requests should NOT create, alter, or destroy resources at the server, and the request should be idempotent (if you run the same command multiple times, you should get the same result every time).
Lessons: Use authenticity_token to protect your POST, PUT, and DELETE requests. Also make sure not to make any GET requests that could potentially modify resources on the server.


Thursday 4 October 2012

Evil Twin Controllers

Evil Twin Controllers


As an application grows and matures, the number of things that occur in its controllers increases. Even if you remain diligent and move as much functionality from the controllers into the models as possible, there will inevitably be functionality that remains in the controllers, such as alternative formats available for APIs (JSON, XML, and so on).

For Example, examine the following controller for Songs, which exposes an XML API:

class SongsController < ApplicationController
  before_filter :grab_album_from_album_id

  def index
    @songs = songs.all
    respond_to do |format|
      format.html
      format.xml { render :xml => @songs }
    end
 end

  def show
    @song = songs.find(params[:id])
    respond_to do |format|
      format.html
      format.xml { render :xml => @song }
    end
  end  

  def new
    @song = songs.new
    respond_to do |format|
      format.html
      format.xml { render :xml => @song }
    end
  end 

  def edit
    @song = songs.find(params[:id])
  end 

  def create
    @song = songs.new(params[:song])
    respond_to do |format|
      if @song.save
         format.html do
            redirect_to(@song, :notice => 'Song was successfully created.')
         end
         format.xml do 
           render :xml => @song, :status => :created, :location => @song
         end
       else
         format.html { render :action => "new"}
         format.xml do
           render :xml => @song.errors, :status => :unprocessable_entity
         end
       end
    end
  end

  def update
    @song = songs.find(params[:id])
    respond_to do |format|
      if @song.update_attributes(params[:song])
        format.html do
           redirect_to(@song, :notice => "Song was successfully updated.")
        end
       format.xml { head :ok }
      else
         format.html {render :action => "edit" }
         format.xml do
           render :xml => @song.errors, :status => :unprocessable_entity
         end
      end
    end
  end

  def destroy
    Song.find(params[:id]).destroy
    respond_to do |format|
     format.html { redirect_to(songs_url)}
     format.xml { head :ok}
    end
  end

  private

  def songs
   @album? @album.songs : Song
  end

  def grab_album_from_album_id
    @album = Album.find(params[:album_id]) if params[:album_id]
  end
end


Rails 3 introduced a new set of methods called responders that abstract the boilerplate responding code so that the controller becomes much simpler. In the following example, the preceding Songs controller is rewritten using responders:

class SongsController < ApplicationController
  respond_to :html, :xml
  before_filter :grab_album_from_album_id

  def index
    @songs = songs.all
    respond_with(@song)
  end

  def show
    @song = songs.find(params[:id])
    respond_with(@song)
  end

  def new
    @song = songs.new
     respond_with(@song)
  end

  def edit
    @song = songs.find(params[:id])
    respond_with(@song)
  end  

  def create
    @song = songs.new(params[:song])
     if @song.save
        flash[:notice] = "Song was successfully created."
     end
     respond_with(@song)
  end

  def update
    @song  = songs.find(params[:id])
     if @song.update_attributes(params[:song])
      flash[:notice] = "Song was successfully updated."
     end
     respond_with(@song)
  end

  def destroy
    @song = Song.find(params[:id])
    @song.destroy
    respond_with(@song)
  end

  private

   def songs
     @album ? @album.songs : Song
   end

   def grab_album_from_album_id
     @album = Album.find(params[:album_id]) if params[:album_id]
   end
end


Paperclip Sample App


Paperclip Sample App

Step : 1
Lets First Set Your RVM

Step: 2
Let's get started by creating a new Rails application:

$ rails new paperclip-sample-app -d mysql
create create README.rdoc create Rakefile create config.ru create .gitignore create Gemfile create app create app/assets/images/rails.png create app/assets/javascripts/application.js create app/assets/stylesheets/application.css create app/controllers/application_controller.rb create app/helpers/application_helper.rb create app/mailers create app/models create app/views/layouts/application.html.erb create app/mailers/.gitkeep create app/models/.gitkeep create config create config/routes.rb create config/application.rb create config/environment.rb create config/environments create config/environments/development.rb create config/environments/production.rb create config/environments/test.rb create config/initializers create config/initializers/backtrace_silencers.rb create config/initializers/inflections.rb create config/initializers/mime_types.rb create config/initializers/secret_token.rb create config/initializers/session_store.rb create config/initializers/wrap_parameters.rb create config/locales create config/locales/en.yml create config/boot.rb create config/database.yml create db create db/seeds.rb create doc create doc/README_FOR_APP create lib create lib/tasks create lib/tasks/.gitkeep create lib/assets create lib/assets/.gitkeep create log create log/.gitkeep create public create public/404.html create public/422.html create public/500.html create public/favicon.ico create public/index.html create public/robots.txt create script create script/rails create test/fixtures create test/fixtures/.gitkeep create test/functional create test/functional/.gitkeep create test/integration create test/integration/.gitkeep create test/unit create test/unit/.gitkeep create test/performance/browsing_test.rb create test/test_helper.rb create tmp/cache create tmp/cache/assets create vendor/assets/javascripts create vendor/assets/javascripts/.gitkeep create vendor/assets/stylesheets create vendor/assets/stylesheets/.gitkeep create vendor/plugins create vendor/plugins/.gitkeep run bundle install


Step : 3

In your gem file add paperclip gem for image upload

gem "paperclip", :git => "git://github.com/thoughtbot/paperclip.git"



Step : 4


After write add paperclip in your gem file

$ bundle install


Step : 5

I will just create a new "user" model with string columns for the name and email address:



$ rails g scaffold user name:string email:string

   invoke  active_record
      create    db/migrate/20121004063219_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/unit/user_test.rb
      create      test/fixtures/users.yml
      invoke  resource_route
       route    resources :users
      invoke  scaffold_controller
      create    app/controllers/users_controller.rb
      invoke    erb
      create      app/views/users
      create      app/views/users/index.html.erb
      create      app/views/users/edit.html.erb
      create      app/views/users/show.html.erb
      create      app/views/users/new.html.erb
      create      app/views/users/_form.html.erb
      invoke    test_unit
      create      test/functional/users_controller_test.rb
      invoke    helper
      create      app/helpers/users_helper.rb
      invoke      test_unit
      create        test/unit/helpers/users_helper_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/users.js.coffee
      invoke    scss
      create      app/assets/stylesheets/users.css.scss
      invoke  scss
      create    app/assets/stylesheets/scaffolds.css.scss



Step : 6
Now we need to generate the database columns necessary for Paperclip on our new model object using rails


rails g paperclip user avatar
      create  db/migrate/20121004063230_add_attachment_avatar_to_users.rb


Step: 7
create table and run migration

$ rake db:create

$ rake db:migrate


==  CreateUsers: migrating ====================================================
-- create_table(:users)
   -> 0.7840s
==  CreateUsers: migrated (0.7841s) ===========================================

==  AddAttachmentAvatarToUsers: migrating =====================================
-- change_table(:users)
   -> 0.8870s
==  AddAttachmentAvatarToUsers: migrated (0.8872s) ============================




Step: 7

In your user model add attr_accessible of avatar

class User < ActiveRecord::Base
  attr_accessible :email, :name, :avatar
end


Step: 8
You can see that Paperclip generator created columns in the users table called "avatar_file_name", "avatar_content_type", "avatar_file_size", and "avatar_updated_at". Now we have our database schema setup.  The next step is to just modify the code that was generated for us by the scaffolding and make the changes necessary for paperclip.

The first thing to do is to add a line to the user model and indicate that it has a file attachment called "avatar". To do this, open app/models/user.rb and just add this one line:


class User < ActiveRecord::Base
  attr_accessible :email, :name, :avatar
  has_attached_file :avatar
end



And then edit the new user form (app/views/users/new.html.erb) and add a file field to use to upload files. There are actually two code changes you need to make: 

First you need to set the HTML form to encode the uploaded file data (and other fields) using MIME multiple part syntax, and then second you need to actually add the file upload field. Here's the finished new.html.erb file with these two changes in bold:

<%= form_for(@user, :html => {:multipart => true}) do |f| %>


and  second, add this p tag


<div class="field">
   <%= f.label :avatar %><br />
   <%= f.file_field :avatar %>
  </div>



Step: 9

Now if we run our application we can upload an image file and attach it to a user:





if we submit the form, the image file will be uploaded to the server and saved on the file system. By default, paperclip saves files inside a "system" folder it creates in your Rails app's public folder. 



Step: 10
I'm almost done; now i just need to display the uploaded image somewhere; the simplest thing to do is just to add an image tag to the users show page.


<p>
  <b>Avatar:</b>
  <%= image_tag @user.avatar.url %>
</p>




Step: 11
now we can see the image for our new user