Archive

Posts Tagged ‘rails’

devise limit one session per user at a time

February 23rd, 2012 rupert Comments off

A user can only be signed in a single session at a time. This means if he logged in to computer A then afterwards he logged in to computer B, then computer A times out. The original question was solved in http://stackoverflow.com/questions/7068919/devise-limit-one-session-per-user-at-a-time

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  protect_from_forgery
 
  before_filter :authenticate_user!, :check_concurrent_session, :store_location
 
  ... 
  def check_concurrent_session
    if is_already_logged_in?
      sign_out_and_redirect(current_user)
    end
  end
 
  def is_already_logged_in?
    current_user && !(session[:token] == current_user.login_token)
  end
 
  def after_sign_out_path_for(resource)
    loggedout_path
  end
 
end

app/controllers/sessions_controller.rb

class SessionsController < Devise::SessionsController
 
  skip_before_filter :check_concurrent_session
 
  def create
    super
    set_login_token
  end
 
  private
  def set_login_token
    token = Devise.friendly_token
    session[:token] = token
    current_user.login_token = token
    current_user.save
  end
 
end

app/controllers/static_controller.rb

class StaticController < ApplicationController
  skip_before_filter :authenticate_user!
end

app/views/sessions/new.html.erb

<div id="application">
 
 <nav id="secondary">
    <ul>
      <li class="current"><%= link_to "Log In", new_user_session_path %></li>
      <%- if devise_mapping.recoverable? && controller_name != 'passwords' %>
        <li><%= link_to "Forgot Password", new_password_path(resource_name) %></li>
      <% end -%>
    </ul>
  </nav>
 
  <section id="content">
 
    <%= semantic_form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
      <section>
        <%= f.input :username %>
      </section>
 
      <section>
        <%= f.input :password %>
      </section>
 
      <%= f.buttons do %>
        <%= f.commit_button :label => "Login", :button_html => { :class => "button primary submit"} %>
      <% end %>
 
      <br/>
    <% end %>
 
    <%= render :partial => 'layouts/devise/devise_error_messages' %>
 
  </section>
 
</div>

app/views/static/loggedout.html.erb

<section id="content">
 
  <h1>Logged Out</h1>
 
  <hr/>
 
  <p>This is not an error page but an indication that you have lost your session.</p>
 
  <p><b>So why are you here?</b></p>
 
  <ul>
    <li>- You have successfully logged out after clicking the "Logout" button.</li>
    <li>- You logged in to another machine so we logged this session out. We don't want to have multiple logins everywhere for security purposes.</li>
    <li>- You have been inactive for a while, we logged this session out.</li>
  </ul>
 
  <p><b><%= link_to "Login", new_user_session_path, :class => "button" %></b></p>
</section>

config/routes.rb

  devise_for :users, :controllers => { :sessions => "sessions" }
 
  ..
  match "loggedout" => "static#loggedout"
 end

db/migrate/20120223022102_add_login_token_to_users.rb

class AddLoginTokenToUsers < ActiveRecord::Migration
  def self.up
    PgTools.restore_default_search_path
 
    change_table "users" do |t|
      t.string "login_token"
    end
  end
 
  def self.down
    PgTools.restore_default_search_path
 
    change_table "users" do |t|
      t.remove "login_token"
    end
  end
end
Categories: rails Tags: ,

freebsd + apache + rvm + rails + passenger

November 22nd, 2011 rupert No comments

1. Install apache22

% cd /usr/ports/www/apache22
% make config
% make install clean
% vim /etc/rc.conf
apache22_enable=YES
/usr/local/etc/rc.d/apache22 start

2. Install rvm via multi user install from http://beginrescueend.com/rvm/install/

Login as root.
%  bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)
Downloading RVM from wayneeseguin branch stable
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  799k  100  799k    0     0   109k      0  0:00:07  0:00:07 --:--:--  199k
 
Installing RVM to /usr/local/rvm/
    RVM system user group 'rvm' exists, proceeding with installation.
 
# RVM:  Shell scripts enabling management of multiple ruby environments.
# RTFM: https://rvm.beginrescueend.com/
# HELP: http://webchat.freenode.net/?channels=rvm (#rvm on irc.freenode.net)
# Screencast: http://screencasts.org/episodes/how-to-use-rvm
 
# In case of any issues read output of 'rvm requirements' and/or 'rvm notes'
 
Installation of RVM in /usr/local/rvm/ is almost complete:
 
  * First you need add all users that will be using rvm to 'rvm' group,
    anyone using rvm will be operating with `umask g+w`.
 
  * To start using RVM you need to run `source /etc/profile.d/rvm.sh`
    in all your open shell windows, in rare cases you need to reopen all shell windows.
 
  * Optionally you can run `rvm tools rvm-env ruby bash` which will generate 
    shebang wrappers for easier selecting ruby in scripts.
 
# root,
#
#   Thank you for using RVM!
#   I sincerely hope that RVM helps to make your life easier and more enjoyable!!!
#
# ~Wayne

3. Add your users to rvm group

# vim /etc/group
....
rvm:*:1003:root,rupert

Load rvm script to current shell by calling source /etc/profile.d/rvm.sh or add it to /etc/profile and re-login

#vim /etc/profile
source /etc/profile.d/rvm.sh

After relogging in, simply type rvm and it should spit out something. If the command is not found, then you are doing something wrong.

4. Install 1.9.3

% rvm install 1.9.3
Installing Ruby from source to: /usr/local/rvm/rubies/ruby-1.9.3-p0, this may take a while depending on your cpu(s)...
 
ruby-1.9.3-p0 - #fetching 
ruby-1.9.3-p0 - #downloading ruby-1.9.3-p0, this may take a while depending on your connection...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  3 8604k    3  340k    0     0  27173      0  0:05:24  0:00:12  0:05:12 64100

Use ruby1.9.3

% rvm --default use 1.9.3-p0
% gem install bundler -V

5. Install passenger3

This will install system wide passenger gem which is installed on 1.9.3-p0 gemset.

% gem install passenger -V
% cd /usr/local/rvm/gems/ruby-1.9.3-p0/gems/passenger-3.0.11/bin
% ./passenger-install-apache2-module
--------------------------------------------
The Apache 2 module was successfully installed.
 
Please edit your Apache configuration file, and add these lines:
 
   LoadModule passenger_module /usr/local/rvm/gems/ruby-1.9.3-p0/gems/passenger-3.0.11/ext/apache2/mod_passenger.so
   PassengerRoot /usr/local/rvm/gems/ruby-1.9.3-p0/gems/passenger-3.0.11
   PassengerRuby /usr/local/rvm/wrappers/ruby-1.9.3-p0/ruby
 
After you restart Apache, you are ready to deploy any number of Ruby on Rails
applications on Apache, without any further Ruby on Rails-specific
configuration!
 
Press ENTER to continue.
 
 
--------------------------------------------
Deploying a Ruby on Rails application: an example
 
Suppose you have a Rails application in /somewhere. Add a virtual host to your
Apache configuration file and set its DocumentRoot to /somewhere/public:
 
   <VirtualHost *:80>
      ServerName www.yourhost.com
      DocumentRoot /somewhere/public    # <-- be sure to point to 'public'!
      <Directory /somewhere/public>
         AllowOverride all              # <-- relax Apache security settings
         Options -MultiViews            # <-- MultiViews must be turned off
      </Directory>
   </VirtualHost>

6. Edit httpd.conf to load the passenger module

To use a rvm gemset by passenger read “Rails app with custom .rvmrc and a system wide passenger install” https://rvm.beginrescueend.com/integration/passenger/

LoadModule passenger_module /usr/local/rvm/gems/ruby-1.9.3-p0/gems/passenger-3.0.11/ext/apache2/mod_passenger.so
PassengerRoot /usr/local/rvm/gems/ruby-1.9.3-p0/gems/passenger-3.0.11
PassengerRuby /usr/local/rvm/wrappers/ruby-1.9.3-p0/ruby
<VirtualHost 192.168.69.3:80>
   ServerAdmin rupert@2rmobile.com
   ServerName myserver
   ServerAlias myserver
 
   DocumentRoot "/path/to/myapp/public"
   <Directory "/path/to/myapp/public">
 
   #DocumentRoot "/path/to/myapp/current/public"
   #<Directory "/path/to/myapp/current/public">
      Options Indexes MultiViews
      AllowOverride None 
      Order allow,deny
      Allow from all 
   </Directory>
 
   CustomLog /var/log/apache22/myapp-error.log combinedio
   LogLevel warn 
</VirtualHost>

We need a railsapp/config/setup_load_paths.rb to use our custom .rvmrc

if ENV['MY_RUBY_HOME'] && ENV['MY_RUBY_HOME'].include?('rvm')
   begin
      rvm_path     = File.dirname(File.dirname(ENV['MY_RUBY_HOME']))
      rvm_lib_path = File.join(rvm_path, 'lib')
      $LOAD_PATH.unshift rvm_lib_path
      require 'rvm'
      RVM.use_from_path! File.dirname(File.dirname(__FILE__))
   rescue LoadError
      # RVM is unavailable at this point.
      raise "RVM ruby lib is currently unavailable."
   end
end
 
# Pick the lines for your version of Bundler
# If you're not using Bundler at all, remove all of them
 
# Require Bundler 1.0 
ENV['BUNDLE_GEMFILE'] = File.expand_path('../Gemfile', File.dirname(__FILE__))
require 'bundler/setup'
 
# Require Bundler 0/9
# if File.exist?(".bundle/environment.rb")
#   require '.bundle/environment'
# else
#   require 'rubygems'
#   require 'bundler'
#   Bundler.setup
# end

Visit this stackoverflow question http://stackoverflow.com/questions/5680341/how-to-load-passenger-from-apache-with-rvm-and-unique-gem-sets

% git clone rupert@server:/path/to/your/myapp.git
% cd myapp

9. Run bundle

% bundle install -V
Fetching source index for http://rubygems.org/
....

Errors that you may encounter

Note: The rvmrc located in ‘../releases/20120208034235′ could not be loaded, likely due to trust mechanisms. Please run ‘rvm rvmrc {trust,untrust} “../releases/20120208034235″‘ to continue, or set rvm_trust_rvmrcs_flag to 1. (RVM::ErrorLoadingRVMRC)

% vim /etc/rvmrc
umask g+w
rvm_trust_rvmrcs_flag=1
Categories: freebsd Tags: , ,

Rails Note #17: cucumber + factory_girl + has_many associations

February 24th, 2011 rupert Comments off

1. It seems everytime :section is created, the association :app_profile is created as well. Now, there is an existing uniqueness constraint to both :app_profile and :sections.

Say we have an app_profile for “Hotel 1″.

Now app_profile, “Hotel 1″, have two sections “Section 1″, “Section 2″.

Now the behaviour that we want is you cannot add “Section 1″ again. “Section 3″ is fine.

What is the factory definition that I need to specify for :section so that it will create an association if needed but will use an existing association if it already exists?

Validation failed: App name has already been taken (ActiveRecord::RecordInvalid)
./features/step_definitions/seed_steps.rb:6:in `/^there is a section called "([^"]*)"$/'
features/editing_existing_sections.feature:10:in `And there is a section called "Test Section 2"'

2. Now I don’t know if this is ideal but the only way I worked around this is by assigning the factory build instance into an instance variable using “@”, thus it will be available through the whole request.

cucumber_factory_girl_association_thumb.png

Referene: Getting Started with factory_girl

Categories: rails Tags: , ,

Rails Note #16: rails3, devise, paperclip, uploadify, formtastic

February 10th, 2011 rupert Comments off

Setup

dev:rails3 rupert$ rails new photogallery
dev:rails3 rupert$ cd photogallery
dev:photogallery rupert$ rm public/index.html
dev:photogallery rupert$ vim Gemfile 
  1 source 'http://rubygems.org'
  2 
  3 gem 'rails', '3.0.3'
  4 
  5 # Bundle edge Rails instead:
  6 # gem 'rails', :git => 'git://github.com/rails/rails.git'
  7 
  8 gem 'sqlite3-ruby', :require => 'sqlite3'
  9 gem 'devise', :git => "git://github.com/plataformatec/devise.git", :branch => "master"
 10 gem 'formtastic', '~> 1.1.0'
 11 gem 'paperclip'
 12 gem 'mime-types', :require => 'mime/types'

Paperclip needs imagemagick to work. No need to install by source

sudo apt-get install imagemagick
dev:photogallery rupert$ bundle install
Updating git://github.com/plataformatec/devise.git
Fetching source index for http://rubygems.org/
Using rake (0.8.7) 
Using abstract (1.0.0) 
Using activesupport (3.0.3) 
Using builder (2.1.2) 
Using i18n (0.5.0) 
Using activemodel (3.0.3) 
Using erubis (2.6.6) 
Using rack (1.2.1) 
Using rack-mount (0.6.13) 
Using rack-test (0.5.7) 
Using tzinfo (0.3.23) 
Using actionpack (3.0.3) 
Using mime-types (1.16) 
Using polyglot (0.3.1) 
Using treetop (1.4.9) 
Using mail (2.2.14) 
Using actionmailer (3.0.3) 
Using arel (2.0.6) 
Using activerecord (3.0.3) 
Using activeresource (3.0.3) 
Using bcrypt-ruby (2.1.4) 
Using bundler (1.0.7) 
Using orm_adapter (0.0.4) 
Using warden (1.0.3) 
Using devise (1.2.rc) from git://github.com/plataformatec/devise.git (at master) 
Using formtastic (1.1.0) 
Using paperclip (2.3.8) 
Using thor (0.14.6) 
Using railties (3.0.3) 
Using rails (3.0.3) 
Using sqlite3-ruby (1.3.2) 
Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.

Devise

dev:photogallery rupert$ rails g devise:install
      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml
 
===============================================================================
 
Some setup you must do manually if you haven't yet:
 
  1. Setup default url options for your specific environment. Here is an
     example of development environment:
 
       config.action_mailer.default_url_options = { :host => 'localhost:3000' }
 
     This is a required Rails configuration. In production it must be the
     actual host of your application
 
  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:
 
       root :to => "home#index"
 
  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:
 
       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>
 
===============================================================================
dev:photogallery rupert$
dev:photogallery rupert$ rails g devise User
      invoke  active_record
      create    app/models/user.rb
      invoke    test_unit
      create      test/unit/user_test.rb
      create      test/fixtures/users.yml
      create    db/migrate/20110210032416_devise_create_users.rb
      insert    app/models/user.rb
       route  devise_for :users
  1 class User < ActiveRecord::Base
  2   # Include default devise modules. Others available are:
  3   # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
  4   devise :database_authenticatable, :registerable,
  5          :recoverable, :rememberable, :trackable, :validatable
  6 
  7   # Setup accessible (or protected) attributes for your model
  8   attr_accessible :email, :password, :password_confirmation, :remember_me
  9 
 10   has_many :pictures
 11 end
dev:photogallery rupert$ vim app/controllers/home_controller.rb
  1 class HomeController < ApplicationController
  2   def index
  3   end
  4 end
dev:photogallery rupert$ mkdir -p app/views/home
dev:photogallery rupert$ vim app/views/home/index.html.erb
  1 <h1>Welcome to Photo Gallery</h1>
  2 <%= render :partial => 'shared/header' %>
dev:photogallery rupert$ mkdir -p app/views/shared
  1 <ul>
  2   <% if user_signed_in? %>
  3     <li><%= link_to "Log Out", destroy_user_session_path %></li>
  4     <li><%= link_to "My Pictures", pictures_path(:user_id => current_user.id) %></li>
  5   <% else %>
  6     <li><%= link_to "Log In", new_user_session_path %></li>
  7     <li><%= link_to "Sign Up", new_user_registration_path %></li>
  8   <% end %>
  9 </ul>
dev:photogallery rupert$ vim config/routes.rb
  1 Photogallery::Application.routes.draw do
  2   devise_for :users
  3 
  4   resources :pictures
  5 
  6   root :to => "home#index"
  7 
  8 end

Paperclip

dev:photogallery rupert$ vim config/environments/development.rb
  1 # Be sure to restart your server when you modify this file.
  1 Photogallery::Application.configure do
  2   # Settings specified here will take precedence over those in config/application.rb
  3 
  4   # In the development environment your application's code is reloaded on
  5   # every request.  This slows down response time but is perfect for development
  6   # since you don't have to restart the webserver when you make code changes.
  7   config.cache_classes = false
  8 
  9   # Log error messages when you accidentally call methods on nil.
 10   config.whiny_nils = true
 11 
 12   # Show full error reports and disable caching
 13   config.consider_all_requests_local       = true
 14   config.action_view.debug_rjs             = true
 15   config.action_controller.perform_caching = false
 16 
 17   # Don't care if the mailer can't send
 18   config.action_mailer.raise_delivery_errors = false
 19 
 20   # Print deprecation notices to the Rails logger
 21   config.active_support.deprecation = :log
 22 
 23   # Only use best-standards-support built into browsers
 24   config.action_dispatch.best_standards_support = :builtin
 25 
 26   Paperclip.options[:command_path] = "/usr/local/ImageMagick/bin"
 27   Paperclip.options[:swallow_stderr] = false
 28 end
 29
dev:photogallery rupert$ rails g model Picture
      invoke  active_record
      create    db/migrate/20110210032526_create_pictures.rb
      create    app/models/picture.rb
      invoke    test_unit
      create      test/unit/picture_test.rb
      create      test/fixtures/pictures.yml
dev:photogallery rupert$ vim db/migrate/20110210032526_create_pictures.rb
  1 class CreatePictures < ActiveRecord::Migration
  2   def self.up
  3     create_table :pictures do |t|
  4       t.string :caption_title
  5       t.text :caption_description
  6       
  7       t.references :user
  8       
  9       t.timestamps
 10     end
 11   end
 12   
 13   def self.down
 14     drop_table :pictures
 15   end
 16 end
dev:photogallery rupert$ rails g migration AddPaperclipToPictures
      invoke  active_record
      create    db/migrate/20110210032654_add_paperclip_to_pictures.rb
dev:photogallery rupert$ vim db/migrate/20110210032654_add_paperclip_to_pictures.rb
  1 class AddPaperclipToPictures < ActiveRecord::Migration
  2   def self.up
  3     add_column :pictures, :image_file_name, :string
  4     add_column :pictures, :image_content_type, :string
  5     add_column :pictures, :image_file_size, :integer
  6     add_column :pictures, :image_updated_at, :datetime
  7   end
  8 
  9   def self.down
 10     remove_column :pictures, :image_file_name
 11     remove_column :pictures, :image_content_type
 12     remove_column :pictures, :image_file_size
 13     remove_column :pictures, :image_updated_at
 14   end
 15 end
dev:photogallery rupert$ vim app/models/picture.rb
  1 class Picture < ActiveRecord::Base
  2   belongs_to :user
  3 
  4   has_attached_file :image,
  5                     :styles => {
  6                       :thumb => ["100x100>", :jpg],
  7                       :pagesize => ["500x400>", :jpg],
  8                     },
  9                     :default_style => :pagesize,
 10                     :url => "/images/photogallery/:id/:style/:basename.:extension",
 11                     :path => "/wwwroot/images/photogallery/:id/:style/:basename.:extension"
 12 
 13   validates_attachment_presence :image
 14   validates_attachment_size :image, :less_than => 10.megabytes
 15 end
dev:images rupert$ mkdir -p /wwwroot/images/photogallery
dev:images rupert$ ln -s /wwwroot/images/photogallery photogallery
dev:photogallery rupert$ rake db:migrate
(in /Volumes/rupert/projects/rails3/photogallery)
==  DeviseCreateUsers: migrating ==============================================
-- create_table(:users)
   -> 0.0084s
-- add_index(:users, :email, {:unique=>true})
   -> 0.0679s
-- add_index(:users, :reset_password_token, {:unique=>true})
   -> 0.0007s
==  DeviseCreateUsers: migrated (0.0772s) =====================================
 
==  CreatePictures: migrating =================================================
-- create_table(:pictures)
   -> 0.0007s
==  CreatePictures: migrated (0.0008s) ========================================
 
==  AddPaperclipToPictures: migrating =========================================
-- add_column(:pictures, :image_file_name, :string)
   -> 0.0005s
-- add_column(:pictures, :image_content_type, :string)
   -> 0.0003s
-- add_column(:pictures, :image_file_size, :integer)
   -> 0.0003s
-- add_column(:pictures, :image_updated_at, :datetime)
   -> 0.0003s
==  AddPaperclipToPictures: migrated (0.0016s) ================================
dev:photogallery rupert$ rails g controller pictures
      create  app/controllers/pictures_controller.rb
      invoke  erb
      create    app/views/pictures
      invoke  helper
      create    app/helpers/pictures_helper.rb
dev:photogallery rupert$ vim app/controllers/pictures_controller.rb 
  1 class PicturesController < ApplicationController
  2   def index
  3     @user = User.find(params[:user_id])
  4     @pictures = @user.pictures
  5   end
  6 
  7   def create
  8     #You can specify a sleep here to mimic a long response
  9     #sleep 5
 10     newparams = coerce(params)
 11 
 12     current_pictures = Picture.where(:user_id => params[:user_id]).all
 13 
 14     @picture = Picture.new(newparams[:picture])
 15     @picture.user_id = current_user.id
 16 
 17     if @picture.save
 18       flash[:notice] = "Picture was successfully created"
 19 
 20       respond_to do |format|
 21         format.html {redirect_to pictures_path(:user_id => @picture.user_id)}
 22         format.json {render :json => { :result => 'success', :picture => picture_path(@picture) } }
 23       end
 24     else
 25       flash[:alert] = "There is an error in saving the picture."
 26       respond_to do |format|
 27         format.html {redirect_to pictures_path(:user_id => @picture.user_id)}
 28         format.json {render :json => { :result => 'error', :error => flash[:alert] } }
 29       end
 30     end
 31   end
 32   
 33   def show
 34     @picture = Picture.find(params[:id], :include => :user)
 35     @total_pictures = Picture.find(:all, :conditions => { :user_id => @picture.user.id})
 36     render :template => 'pictures/show'
 37   end
 38   
 39   def destroy
 40     picture = Picture.find(params[:id])
 41     user_id = picture.user_id
 42     picture.destroy
 43     flash[:notice] = "Picture was successfully deleted"
 44     redirect_to pictures_path(:user_id => user_id)
 45   end
 46     
 47   private
 48     def coerce(params)
 49       if params[:picture].nil?
 50         h = Hash.new 
 51         h[:picture] = Hash.new
 52         h[:picture][:user_id] = params[:user_id]
 53         h[:picture][:image] = params[:Filedata]
 54         h[:picture][:image].content_type = MIME::Types.type_for(h[:picture] [:image].original_filename).to_s
 55         h
 56       else
 57         params
 58       end 
 59     end
 60 end

Uploadify

dev:photogallery rupert$ vim config/initializers/session_store.rb
  1 # Be sure to restart your server when you modify this file.
  2 
  3 Photogallery::Application.config.session_store :cookie_store, :key => '_photogallery_session'
  4 
  5 # Use the database for sessions instead of the cookie-based default,
  6 # which shouldn't be used to store highly confidential information
  7 # (create the session table with "rails generate session_migration")
  8 # Photogallery::Application.config.session_store :active_record_store
  9 Rails.application.config.middleware.insert_before(
 10   ActionDispatch::Session::CookieStore,
 11   FlashSessionCookieMiddleware,
 12   Rails.application.config.session_options[:key]
 13 )
dev:photogallery rupert$ mkdir app/middleware
dev:photogallery rupert$ vim app/middleware/flash_session_cookie_middleware.rb
  1 require 'rack/utils'
  2  
  3 class FlashSessionCookieMiddleware
  4   def initialize(app, session_key = '_session_id')
  5     @app = app
  6     @session_key = session_key
  7   end
  8  
  9   def call(env)
 10     if env['HTTP_USER_AGENT'] =~ /^(Adobe|Shockwave) Flash/
 11       req = Rack::Request.new(env)
 12       env['HTTP_COOKIE'] = [ @session_key,
 13                              req.params[@session_key] ].join('=').freeze unless req.params[@session_key].nil?
 14       env['HTTP_ACCEPT'] = "#{req.params['_http_accept']}".freeze unless req.params['_http_accept'].nil?
 15     end
 16  
 17     @app.call(env)
 18   end
 19 end
dev:photogallery rupert$ rm -rf public/javascripts/*
dev:photogallery rupert$ cd doc/
dev:doc rupert$ wget http://www.uploadify.com/wp-content/uploads/Uploadify-v2.1.4.zip
--2011-02-10 14:58:26--  http://www.uploadify.com/wp-content/uploads/Uploadify-v2.1.4.zip
Resolving www.uploadify.com... 67.205.57.45
Connecting to www.uploadify.com|67.205.57.45|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 237327 (232K) [application/zip]
Saving to: `Uploadify-v2.1.4.zip'
 
100%[===========================================================================================================================================>] 237,327     88.9K/s   in 2.6s    
 
2011-02-10 14:58:29 (88.9 KB/s) - `Uploadify-v2.1.4.zip' saved [237327/237327]
dev:doc rupert$ unzip Uploadify-v2.1.4.zip
dev:doc rupert$ cd jquery.uploadify-v2.1.4/
 
dev:jquery.uploadify-v2.1.4 rupert$ mkdir -p ../../public/javascripts/uploadify
dev:jquery.uploadify-v2.1.4 rupert$ mkdir -p ../../public/uploadify
 
dev:jquery.uploadify-v2.1.4 rupert$ cp uploadify.css ../../public/stylesheets/
 
dev:jquery.uploadify-v2.1.4 rupert$ cp jquery.uploadify.v2.1.4.js ../../public/javascripts/uploadify/
dev:jquery.uploadify-v2.1.4 rupert$ cp swfobject.js ../../public/javascripts/uploadify/
 
dev:jquery.uploadify-v2.1.4 rupert$ cp -Rf uploadify.swf ../../public/uploadify/
dev:jquery.uploadify-v2.1.4 rupert$ cp -Rf cancel.png ../../public/uploadify/
 
dev:photogallery rupert$ cd ../../public/javascripts/
wget https://github.com/rails/jquery-ujs/raw/master/src/rails.js --no-check-certificate
dev:photogallery rupert$ vim app/views/layouts/pictures.html.erb
  1 <!DOCTYPE html>
  2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  3 <head>
  4   <title>Photo Gallery</title>
  5   
  6   <%= stylesheet_link_tag 'formtastic/formtastic' %>
  7   <%= stylesheet_link_tag 'formtastic/formtastic_changes' %>
  8 
  9   <%= stylesheet_link_tag 'uploadify' %>
 10 
 11   <%= javascript_include_tag 'https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js' %>
 12   
 13   <!-- 
 14     Taken from https://github.com/rails/jquery-ujs/raw/master/src/rails.js
 15     Do not remove jquery.rails.js as the delete links will not work 
 16   -->
 17   <%= javascript_include_tag "jquery.rails.js" %>
 18   
 19   <%= csrf_meta_tag %>
 20 </head>
 21 <body>
 22 <div class="container">
 23   <% flash.each do |name, msg| %>
 24     <hr/>
 25     <%= content_tag :div, msg, :class => "flash_#{name}" %>
 26   <% end %>
 27 
 28   <%= render :partial => 'shared/header' %>
 29 
 30   <hr/>
 31 
 32   <%= yield %>
 33 
 34 </div>
 35 
 36 </body>
 37 </html>
dev:photogallery rupert$ vim app/views/pictures/index.html.erb
  1 <h1><%= "Manage #{pluralize(@user.pictures.size, 'Picture')}" %></h1>
  2 
  3   <div class="upload_form">
  4     <%= form_for Picture.new(:user_id => @user.id), :html => {:multipart => true } do |f| %>
  5       <h2>Step 1: Choose your images</h2>
  6       <%= f.file_field :image %>
  7 
  8       <%= f.hidden_field :user_id, "value" => @user.id %>
  9 
 10       <h2>Step 2: Upload</h2>
 11       <%= f.submit 'Upload' %>
 12     <% end %>
 13 
 14     <% if simple_upload_form? %>
 15 
 16       <span class="simple_upload_link">
 17         If you want to see the Browse flash button above, click on the
 18         <%= link_to "Flash Upload", pictures_path(:user_id => @user.id) %>
 19       </span>
 20 
 21     <% else %>
 22 
 23       <!-- Important: Please see uploadify. It contains javascript functions to provide the BROWSE button, submit to a user with content_type json, and process the response. See     more details in uploadify -->
 24       <%= render :partial => "uploadify" %>
 25 
 26       <span class="simple_upload_link">
 27         If you cannot see the Browse flash button above, click on the
 28         <%= link_to "Simple Upload", pictures_path(:user_id => @user.id, :simple => 1) %>
 29       </span>
 30 
 31     <% end %>
 32   </div>
 33 
 34 <div class="clear"></div>
 35 
 36 <hr/>
 37 
 38 <div class="picture_container">
 39   <ul class="thumbs noscript">
 40     <%= render @pictures %>
 41   </ul>
 42 </div>
dev:photogallery rupert$ vim app/views/pictures/_uploadify.html.erb 
  1 <%= javascript_include_tag "uploadify/swfobject.js", "uploadify/jquery.uploadify.v2.1.4.js" %>
  2 
  3 <script type="text/javascript" charset="utf-8">
  4 <%- session_key = Rails.application.config.session_options[:key] -%> 
  5 $(document).ready(function() {
  6   
  7   $('#picture_image').click(function(event){ 
  8     event.preventDefault();
  9   }); 
 10   
 11   $('#picture_image').uploadify({
 12     buttonText: 'Browse',
 13     width: 110,
 14     uploader : '/images/uploadify/uploadify.swf',
 15     cancelImg : '/images/uploadify/cancel.png',
 16     multi : true,
 17     auto : true,
 18     script : 'pictures',
 19     //Function 'onComplete' below will process response from pictures_controller 'create'
 20     //format.json {render :json => { :result => 'success', :picture => admin_picture_path(@picture) } }
 21     onComplete : function(event, queueID, fileObj, response, data) { 
 22       var data = eval('(' + response + ')');
 23       if(data.result == 'success'){
 24         $.getScript(data.picture);
 25       }
 26       else{
 27         alert(data.error);
 28         //We can have a <hr/> before alert or notice using jquery
 29         $('#alert').html(data.error)
 30       }
 31     },
 32     scriptData : {
 33           '_http_accept': 'application/javascript',
 34           'format' : 'json',
 35           '_method': 'post',
 36           '<%= session_key %>' : encodeURIComponent('<%= u cookies[session_key] %>'),
 37           'authenticity_token': encodeURIComponent('<%= u form_authenticity_token %>'),
 38           'user_id' : '<%= @user.id %>'
 39         }
 40   });
 41   
 42   $('#picture_submit').click(function(event){ 
 43       event.preventDefault(); 
 44       $('#upload_photo').uploadifyUpload(); 
 45     });
 46     
 47 }); 
 48 </script>
dev:photogallery rupert$ vim app/helpers/pictures_helper.rb
  1 module PicturesHelper
  2   def simple_upload_form?
  3     if params[:simple] == nil
  4       return false
  5     else
  6       return true
  7     end
  8   end
  9 
 10   def link_to_picture(picture)
 11     link_to(
 12       image_tag( picture.image.url(:thumb), :size => '80x80', :border => 0 ),
 13       picture.image.url(:pagesize),
 14       {
 15         :class => "thumb",
 16         :title => "#{picture.image_file_name}",
 17         :name => "#{picture.image_file_name}",
 18         :rel => "nofollow"
 19       }
 20     )
 21   end
 22 
 23   def link_to_web_photo(photo)
 24     link_to(
 25       image_tag( photo.thumb_path, :size => '80x80', :border => 0 ),
 26       photo.full_path,
 27       {
 28         :class => "thumb",
 29         :title => "#{photo.thumb_path}",
 30         :name => "#{photo.thumb_path}",
 31         :rel => "nofollow"
 32       }
 33     )
 34   end
 35 end
dev:photogallery rupert$ vim app/views/pictures/_picture.html.erb
  1 <li>
  2   <p>
  3     <%= link_to_picture(picture)%><br/>
  4     <%= picture.caption_title %>
  5   </p>
  6   <%= link_to "Delete Picture", picture_path(picture), :confirm => "Are you sure?", :method => :delete %>
  7 </li>
dev:photogallery rupert$ vim app/views/pictures/show.js.erb
  1 $('h1').html('Manage <%= pluralize(@total_pictures.count, "Picture") %>');
  2 $('ul.thumbs').append('<%= escape_javascript(render @picture) %>');
dev:photogallery rupert$ vim app/views/pictures/edit.html.erb
  1 <% title 'Edit Caption' %>
  2 
  3 <%= render "shared/error_messages", :target => @picture %>
  4 
  5 <p style="text-align:center"><%= image_tag @picture.image.url(:pagesize) %></p>
  6 
  7 <%= semantic_form_for @picture do |f| %>
  8   <%= f.inputs do %>
  9     <%= f.input :caption_title %>
 10     <%= f.input :caption_description %>
 11   <% end %>
 12   
 13   <%= f.buttons do %>
 14     <%= f.commit_button %>
 15     <%= f.cancel_button pictures_path(:page_id => @picture.page.id) %>
 16   <% end %>
 17   
 18 <% end %>

Download photogallery.tar.gz

Rails Note #15: Rails 3, Devise, Cucumber

February 9th, 2011 rupert Comments off

1. Installed rvm (Ruby Version Manager). See Episode 200. Rails3 Beta and RVM

rupert:tsa rupert$ rvm ruby-1.9.2-p0
rupert:tsa rupert$ ruby -v
ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-darwin10.5.0]

To use ruby-1.9.2-p0 as default

rupert:rails3 rupert$ rvm ruby-1.9.2-p0 --default
rupert:rails3 rupert$ ruby -v
ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-darwin10.5.0]

2. Install rails3

gem install rails

3. Create a rails app tsa (the sample app)

rails new tsa -d mysql

4. Start webrick. Yup, script/server is gone

rupert:tsa rupert$ rails server -e development
=> Booting WEBrick
=> Rails 3.0.3 application starting in production on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2010-12-06 20:47:27] INFO  WEBrick 1.3.1
[2010-12-06 20:47:27] INFO  ruby 1.9.2 (2010-08-18) [x86_64-darwin10.5.0]
[2010-12-06 20:47:27] INFO  WEBrick::HTTPServer#start: pid=1348 port=3000

Note: Upon checking http://127.0.0.1:3000/ I get a “Routing Error”

5. Update your GemFile with the necessary gems.
Let’s use devise as a login authentication system.

source 'http://rubygems.org'
 
gem 'rails', '3.0.3'
 
# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'
 
gem 'mysql2'
gem 'devise', :git => "git://github.com/plataformatec/devise.git", :branch => "master"

Then run bundle install. This will install devise gem.

rupert:tsa rupert$ bundle install
Fetching git://github.com/plataformatec/devise.git
remote: Counting objects: 10326, done.
remote: Compressing objects: 100% (3745/3745), done.
remote: Total 10326 (delta 6603), reused 9755 (delta 6163)
Receiving objects: 100% (10326/10326), 1.24 MiB | 314 KiB/s, done.
Resolving deltas: 100% (6603/6603), done.
Fetching source index for http://rubygems.org/

Where are the gems installed?

rupert:tsa rupert$ bundle show devise
/Users/rupert/.rvm/gems/ruby-1.9.2-p0/bundler/gems/devise-b50fd1a72e71

6. Create the necessary databases in mysql. See config/database.yml

CREATE DATABASE tsa_development;

7. Run devise generator.

rupert:tsa rupert$ rails generate devise:install
      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml
 
===============================================================================
 
Some setup you must do manually if you havent yet:
 
  1. Setup default url options for your specific environment. Here is an
     example of development environment:
 
       config.action_mailer.default_url_options = { :host => 'localhost:3000' }
 
     This is a required Rails configuration. In production it must be the
     actual host of your application
 
  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:
 
       root :to => "home#index"
 
  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:
 
       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>
 
===============================================================================

8. Generate a controller in Rails3.

rupert:tsa rupert$ rails generate controller main index
      create  app/controllers/main_controller.rb
       route  get "main/index"
      invoke  erb
      create    app/views/main
      create    app/views/main/index.html.erb
      invoke  test_unit
      create    test/functional/main_controller_test.rb
      invoke  helper
      create    app/helpers/main_helper.rb
      invoke    test_unit
      create      test/unit/helpers/main_helper_test.rb

Update routes.rb for the root_url

  root :to => "main#index"

9. Create a User model with devise.

rupert:tsa rupert$ rails generate devise User
      invoke  active_record
      create    app/models/user.rb
      invoke    test_unit
      create      test/unit/user_test.rb
      create      test/fixtures/users.yml
      create    db/migrate/20101206104321_devise_create_users.rb
      insert    app/models/user.rb
       route  devise_for :users
rupert:tsa rupert$

10. Run migration for User model

rupert:tsa rupert$ export RAILS_ENV=development
rupert:tsa rupert$ rake db:migrate
(in /Users/rupert/projects/rails3/tsa)
==  DeviseCreateUsers: migrating ==============================================
-- create_table(:users)
   -> 0.0805s
-- add_index(:users, :email, {:unique=>true})
   -> 0.1368s
-- add_index(:users, :reset_password_token, {:unique=>true})
   -> 0.1484s
==  DeviseCreateUsers: migrated (0.3662s) =====================================

11. Using cucumber

sudo port install libxml2 libxslt

12. Update Gemfile

group :test, :development do
  gem 'rspec'
  gem 'rspec-rails'
  gem 'cucumber'
  gem 'cucumber-rails'
  gem 'capybara'
  gem 'launchy'
end

13. Rspec

rupert:tsa rupert$ rails g rspec:install
      create  .rspec
      create  spec
      create  spec/spec_helper.rb
      create  autotest
      create  autotest/discover.rb

14. Cucumber

rupert:tsa rupert$ rails g cucumber:install --rspec --capybara
      create  config/cucumber.yml
      create  script/cucumber
       chmod  script/cucumber
      create  features/step_definitions
      create  features/step_definitions/web_steps.rb
      create  features/support
      create  features/support/paths.rb
      create  features/support/env.rb
       exist  lib/tasks
      create  lib/tasks/cucumber.rake
        gsub  config/database.yml
        gsub  config/database.yml
       force  config/database.yml

15. Write your first feature

Feature: Home Page
  In order to make sure the home page works
  As a normal user
  I want to see the home page
 
Scenario: Show Main Sections
  When I go to the home page
  Then show me the page
  Then I should see "Login"

From Harold Jimenez:

“The Given step is where you set up the context of your scenario. Every scenario starts with a blank slate, so it is important to create a state in your application for example by creating data in the database, or by navigating to a specific page.

The When step is where you exercise the application in order to accomplish what needs testing. In the case of a web app like twiddr, this is usually where you fill in forms, press buttons, click links, or otherwise interact with the system in some way.

Finally, the Then step is where you verify the result, and it’s where we check that the correct pages are rendered, that we see a success or error message, or anything that could help us verify that the prior action was successful. As we move along with creating our own features, this will become much clearer.”

16. See it fail in cucumber

rake features  (or)
cucumber (or)
bundle exec cucumber features/home.feature
rupert:tsa rupert$ cucumber
Using the default profile...
F--
 
(::) failed steps (::)
 
Can't find mapping from "the home page" to a path.
Now, go and add a mapping in /Users/rupert/projects/rails3/tsa/tsa/features/support/paths.rb (RuntimeError)
./features/support/paths.rb:26:in `rescue in path_to'
./features/support/paths.rb:20:in `path_to'
./features/step_definitions/web_steps.rb:24:in `/^(?:|I )go to (.+)$/'
features/home.feature:7:in `When I go to the home page'
 
Failing Scenarios:
cucumber features/home.feature:6 # Scenario: Show Main Sections
 
1 scenario (1 failed)
3 steps (1 failed, 2 skipped)
0m0.062s

17. Defining “the home page” in the support/paths.rb

    when /the home page/
      root_path

and in the routes.rb

root :to => "home#index"

Other examples:

when /the profile page for "([^\"]+)"/
  user = User.find_by_twiddr_name!($1)
  user_path(user)

Note: When you see the error

undefined local variable or method `node' for #<Capybara::Driver::RackTest::Node:0x00000101151ad8> (NameError)"

You need to comment in features/support/env.rb

#require 'cucumber/rails/capybara_javascript_emulation'

For more info, read https://rspec.lighthouseapp.com/projects/16211-cucumber/tickets/674

18. Then show me the page will open up the browser

file://localhost/Users/rupert/projects/rails3/tsa/tsa/tmp/capybara/capybara-20101209222149.html

19. Automatic Testing with “autotest”. When you save a file, autotest automatically runs cucumber. To avoid autotest infinite loop with cucumber, make sure your IDE (i.e rubymine) does not save automatically in the file system. You can exclude files and directories from autotest with a “.autotest” file

Autotest.add_hook :initialize do |at|
  at.add_exception(%r{^\./\.git})
  at.add_exception(%r{^\./db})
  at.add_exception(%r{^\./log})
  at.add_exception(%r{^\./tmp})
  at.add_exception(%r{^\./.idea})
  at.add_exception(%r{^\./rerun\.txt})
  at.add_exception(%r{^\./Gemfile\.lock})
end

Now you can run, autotest from the terminal. Save a file and see autotest run.

autotest

20. If you have javascript AJAX calls included in cucumber scenarios, you need to append them with @javascript tag. Below is a sample which selects “Australia” from the “Country” and waits for the ajax request to finish before selecting the “State” drop down. More info on capybara github.

@javascript
Scenario: Creating a Fleet
  And I select "Australia" from "Country"
  And I wait for the ajax request to finish
  And I select "Victoria" from "State"

How does the step “I wait for the ajax request to finish” looks like? http://stackoverflow.com/questions/7286254/cucumber-wait-for-ajaxsuccess

When /^I wait for the ajax request to finish$/ do
  start_time = Time.now
  page.evaluate_script('jQuery.isReady&&jQuery.active==0').class.should_not eql(String) until page.evaluate_script('jQuery.isReady&&jQuery.active==0') or (start_time + 5.seconds) < Time.now
    sleep 1
  end
end
Categories: rails, ruby Tags: , ,