Screencast: Exceptions Testen

Eine Applikation ist nicht immer frei von Bugs. Einige Fehler treten erst auf wenn das Projekt bereits online ist. Wie mit solchen Fällen umgegangen werden kann und wie man anschließend den Fehler in seine Integrationtest einschließen kann, zeigt Ryan Bates in dieser Folge seiner Screencasts.

Download (14.4 MB, 10:01)
Alternative download für iPod & Apple TV(11.2 MB, 10:01)

Quellcode:

[bash]
script/generate integration_test exceptions
rake test:integration
[/bash]

[ruby]
# test/integration/exceptions_test.rb
class ExceptionsTest < ActionController::IntegrationTest
fixtures :all

test "POST /products" do
post "/products", "commit"=>"Submit", "product"=>{"name"=>"Headphones", "price"=>"-2"}
assert_response :success
end

test "GET /products/8/edit" do
product = Product.first
get "/products/#{product.id}/edit"
assert_response :success
end
end
[/ruby]

Screencast: Pickle mit Cucumber

Neue Woche, neuer Screencast. In dieser Woche behandelt Ryan Pickle, eine Erweiterungs-Gem für Cucumber. Nebenbei gibt es ein bisschen Knowhow zu diffs.

Download(32.5 MB, 16:43)
alternativer download für iPod & Apple TV (21.7 MB, 16:43)

Resourcen:

Quellcode:

[bash]
rails store
sudo rake gems:install RAILS_ENV=test
script/generate cucumber
script/generate pickle
script/generate rspec_model product name:string price:decimal
rake db:migrate
rake db:test:clone
script/generate rspec_controller products show
cucumber features -q
[/bash]

[ruby]
# config/environments/test.rb
config.gem "rspec", :lib => false, :version => ">=1.2.9"
config.gem "rspec-rails", :lib => false, :version => ">=1.2.9"
config.gem "webrat", :lib => false, :version => ">=0.5.3"
config.gem "cucumber", :lib => false, :version => ">=0.4.3"
config.gem "pickle", :lib => false, :version => ">=0.1.21"

# product_steps.rb
Then(/^I should see products table$/) do |expected_table|
html_table = table_at("#products").to_a
html_table.map! { |r| r.map! { |c| c.gsub(/<.+?>/, “) } }
expected_table.diff!(html_table)
end
[/ruby]

[bash]
# display_products.feature
Feature: Display Products
In order to purchase the right product
As a customer
I want to browse products and see detailed information

Scenario: Show product
Given a product exists with name: "Milk", price: "2.99"
When I go to the show page for that product
Then I should see "Milk" within "h1"
And I should see "$2.99"

Scenario: List products
Given the following products exist
| name | price |
| Milk | 2.99 |
| Puzzle | 8.99 |
When I go to path "/products"
Then I should see products table
| Milk | $2.99 |
| Puzzle | $8.99 |
Then show me the page
[/bash]

Screencast: Formtastic Teil 2

In der letzten Woche hat Ryan Bates Formtastic vorgestellt. In seinem neuen Screencasts geht er tiefer in den Funktionaumfang ein und erklärt vorgeschrittene Themen wie: many-to-many Beziehungen, Pflichtfelder und der Gestaltung von Formularen.

Download (19.7 MB, 9:11)
Alternativer Download für iPod & Apple TV(12.6 MB, 9:11)

Resourcen:

Quellcode:

[bash]
script/generate nifty_scaffold problem name:string
rake db:migrate
script/generate nifty_scaffold symptom animal_id:integer problem_id:integer –skip-controller
script/plugin install git://github.com/redinger/validation_reflection.git
[/bash]

[ruby]
# models/animal.rb
class Animal < ActiveRecord::Base
attr_accessible :name, :category_id, :born_on, :female, :problem_ids
belongs_to :category
has_many :symptoms
has_many :problems, :through => :symptoms
validates_presence_of :name, :born_on
end

# models/problem.rb
class Problem < ActiveRecord::Base
attr_accessible :name
has_many :symptoms
has_many :animals, :through => :symptoms
end

# models/symptom.rb
class Symptom < ActiveRecord::Base
attr_accessible :animal_id, :problem_id
belongs_to :animial
belongs_to :problem
end

# config/initializers/formtastic_config.rb
Formtastic::SemanticFormBuilder.inline_errors = :none
[/ruby]

[javascript]
/* formtastic_changes.css */
form.formtastic fieldset ol li p.inline-hints {
font-style: italic;
font-size: 11px;
}
[/javascript]

[html]

<!– views/animals/_form.html.erb –>
<% semantic_form_for @animal do |f| %>
<%= f.error_messages %>
<% f.inputs do %>
<%= f.input :name, :hint => "Use the owner’s name if none is provided" %>
<%= f.input :born_on, :start_year => 1900 %>
<%= f.input :category, :include_blank => false %>
<%= f.input :female, :as => :radio, :label => "Gender", :collection => [["Male", false], ["Female", true]] %>
<%= f.input :problems, :as => :check_boxes, :required => false %>
<% end %>
<%= f.buttons %>
<% end %>
[/html]

Screencast: Formtastic Teil 1

Formtastic, ein Hilfmittel um die Arbeit mit Views zu vereinfachen, ist das heutige Thema in dem Screencst von Ryan Bates.

Download(19.7 MB, 10:41)
Alternativer Download für iPod & Apple TV (12.7 MB, 10:41)

Resourcen:

[bash]
rails vet
sudo rake gems:install
script/generate nifty_layout
script/generate nifty_scaffold category name:string description:text
rake db:migrate
script/generate formtastic_stylesheets
script/generate nifty_scaffold animal name:string category_id:integer born_on:date female:boolean
[/bash]

[html]
<!– application.html.erb –>
<%= stylesheet_link_tag ‚application‘, ‚formtastic‘, ‚formtastic_changes‘, :cache => "base" %>

<!– views/animals/_form.html.erb –>
<% semantic_form_for @category do |f| %>
<%= f.inputs %>
<%= f.buttons %>
<% end %>

<!– views/animals/_form.html.erb –>
<% semantic_form_for @animal do |f| %>
<% f.inputs do %>
<%= f.input :name %>
<%= f.input :born_on, :start_year => 1900 %>
<%= f.input :category, :include_blank => false %>
<%= f.input :female, :as => :radio, :label => "Gender", :collection => [["Male", false], ["Female", true]] %>
<% end %>
<%= f.buttons %>
<% end %>
[/html]

Screencast: Gemcutter & Jeweler

Neue Woche, neuer Screencast. In dieser Woche zeigt Ryan wie man Gemcutter, ein Service für Gem-Hosting, und Jewelery, Ein Tool zur Verwaltung von Gems, einsetzt.

Download(18.8 MB, 7:24)
Alternativer Download für iPod & Apple TV(10.5 MB, 7:24)

Ressourcen:

[bash]
sudo gem update –system
sudo gem install gemcutter
gem tumble
gem build uniquify.gemspec
gem push uniquify-0.1.0.gem
sudo gem install jeweler
rake –tasks
rake version:write
rake version:bump:minor
rake gemcutter:release
[/bash]

[ruby]
# Rakefile
begin
require ‚jeweler‘
Jeweler::Tasks.new do |gemspec|
gemspec.name = "uniquify"
gemspec.summary = "Generate a unique token with Active Record."
gemspec.description = "Generate a unique token with Active Record."
gemspec.email = "ryan@railscasts.com"
gemspec.homepage = "http://github.com/ryanb/uniquify"
gemspec.authors = ["Ryan Bates"]
end
Jeweler::GemcutterTasks.new
rescue LoadError
puts "Jeweler not available. Install it with: sudo gem install jeweler -s http://gemcutter.org"
end

# uniquify.gemspec
Gem::Specification.new do |s|
s.name = %q{uniquify}
s.version = "0.1.0"

s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
s.authors = ["Ryan Bates"]
s.date = %q{2008-11-09}
s.description = %q{Generate a unique token with Active Record.}
s.email = %q{ryan@railscasts.com}
s.extra_rdoc_files = ["lib/uniquify.rb", "README.rdoc"]
s.files = ["lib/uniquify.rb", "Rakefile", "README.rdoc", "Manifest", "uniquify.gemspec"]
s.has_rdoc = true
s.homepage = %q{http://github.com/ryanb/uniquify}
s.rdoc_options = ["–line-numbers", "–inline-source", "–title", "Uniquify", "–main", "README.rdoc"]
s.require_paths = ["lib"]
s.rubyforge_project = %q{uniquify}
s.rubygems_version = %q{1.2.0}
s.summary = %q{Generate a unique token with Active Record.}

if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 2

if current_version >= 3 then
else
end
else
end
end
[/ruby]

Screencast: Bilder ausschneiden

In seinem heutigen Screencast zeigt Ryan Bates wie man mit Jcrop und Paperclip eine Applikation bauen kann, mit der Benutzern die Möglichkeit gegeben werden kann Bilder zu beschneiden.

Download:
Download(46.3 MB, 14:07)
Alternativer Download für iPod & Apple TV (22.4 MB, 14:07)

Resourcen:

Quellcode:

[ruby]

# models/user.rb
class User < ActiveRecord::Base
has_attached_file :avatar, :styles => { :small => "100×100#", :large => "500×500>" }, :processors => [:cropper]
attr_accessor :crop_x, :crop_y, :crop_w, :crop_h
after_update :reprocess_avatar, :if => :cropping?

def cropping?
!crop_x.blank? && !crop_y.blank? && !crop_w.blank? && !crop_h.blank?
end

def avatar_geometry(style = :original)
@geometry ||= {}
@geometry[style] ||= Paperclip::Geometry.from_file(avatar.path(style))
end

private

def reprocess_avatar
avatar.reprocess!
end
end

# users_controller.rb
def create
@user = User.new(params[:user])
if @user.save
if params[:user][:avatar].blank?
flash[:notice] = "Successfully created user."
redirect_to @user
else
render :action => "crop"
end
else
render :action => ’new‘
end
end

def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
if params[:user][:avatar].blank?
flash[:notice] = "Successfully updated user."
redirect_to @user
else
render :action => "crop"
end
else
render :action => ‚edit‘
end
end

# lib/paperclip_processors/cropper.rb
module Paperclip
class Cropper < Thumbnail
def transformation_command
if crop_command
crop_command + super.sub(/ -crop S+/, “)
else
super
end
end

def crop_command
target = @attachment.instance
if target.cropping?
" -crop ‚#{target.crop_w.to_i}x#{target.crop_h.to_i}+#{target.crop_x.to_i}+#{target.crop_y.to_i}’"
end
end
end
end
[/ruby]

[html]
<!– views/users/crop.html.erb –>
<% title "Crop avatar" %>
<% content_for(:head) do %>
<%= stylesheet_link_tag "jquery.Jcrop" %>
<%= javascript_include_tag "jquery.Jcrop.min" %>
<script type="text/javascript" charset="utf-8">
$(function() {
$(‚#cropbox‘).Jcrop({
onChange: update_crop,
onSelect: update_crop,
setSelect: [0, 0, 500, 500],
aspectRatio: 1
});
});

function update_crop(coords) {
var rx = 100/coords.w;
var ry = 100/coords.h;
$(‚#preview‘).css({
width: Math.round(rx * <%= @user.avatar_geometry(:large).width %>) + ‚px‘,
height: Math.round(ry * <%= @user.avatar_geometry(:large).height %>) + ‚px‘,
marginLeft: ‚-‚ + Math.round(rx * coords.x) + ‚px‘,
marginTop: ‚-‚ + Math.round(ry * coords.y) + ‚px‘
});
var ratio = <%= @user.avatar_geometry(:original).width %> / <%= @user.avatar_geometry(:large).width %>;
$("#crop_x").val(Math.round(coords.x * ratio));
$("#crop_y").val(Math.round(coords.y * ratio));
$("#crop_w").val(Math.round(coords.w * ratio));
$("#crop_h").val(Math.round(coords.h * ratio));
}
</script>
<% end %>

<%= image_tag @user.avatar.url(:large), :id => "cropbox" %>

<h4>Preview:</h4>
<div style="width:100px; height:100px; overflow:hidden">
<%= image_tag @user.avatar.url(:large), :id => "preview" %>
</div>

<% form_for @user do |f| %>
<% for attribute in [:crop_x, :crop_y, :crop_w, :crop_h] %>
<%= f.hidden_field attribute, :id => attribute %>
<% end %>
<p><%= f.submit "Crop" %></p>
<% end %>
[/html]

Screencast: Include versus Joins

Ryan Bates stellt in seinem neuen Screencast die Unterschiede zwischen :inlude und :join vor.

Download:
Download(22.8 MB, 11:37)
Alternativer Download für iPod & Apple TV(16.2 MB, 11:37)

Ressourcen:

Quellcode:

[ruby]

# script/console
ActiveRecord::Base.logger = Logger.new(STDOUT) # to show logs
c = Comment.all(:joins => :user, :conditions => { :users => { :admin => true } })
c.first.users
c = Comment.all(:include => :user, :conditions => { :users => { :admin => true } })
c.first.users
User.all(:joins => :comments, :select => "users.*, count(comments.id) as comments_count", :group => "users.id")
g = Group.first
Comment.all(:joins => {:user => :memberships}, :conditions => { :memberships => { :group_id => g.id } })

# models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
end

# models/user.rb
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, :through => :memberships
has_many :comments
end

# models/membership.rb
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :group
end

# models/group.rb
class Group < ActiveRecord::Base
has_many :memberships
has_many :users, :through => :memberships

def comments
Comment.scoped(:joins => {:user => :memberships}, :conditions => { :memberships => { :group_id => id } })
end
end

# comments_controller.rb
def index
@comments = Comment.all(:joins => :user, :conditions => { :users => { :admin => true } }, :order => "comments.created_at desc")
end

# users_controller.rb
def index
@users = User.all(:joins => :comments, :select => "users.*, count(comments.id) as comments_count", :group => "users.id")
end
[/ruby]

[ruby]
<!– views/groups/show.html.erb –>
<%= render @group.comments %>

<!– views/users/index.html.erb –>
<%= pluralize user.comments_count, "comment" %>
[/ruby]

Screencast: Unbenutze CSS finden

Mit der zeit werden CSS Dateien größer und größer. Leider wird dabei auch viel nicht mehr benötigter Code mitgeschleppt. Ryan Bates zeigt in seinem Screencast wie man nicht mehr benötigten CSS findet.

Download:
Download (18.9 MB, 8:04)
Alternativer Download für iPod & Apple TV (10.8 MB, 8:04)


Weitere Resourcen:

Quellcode:

[bash]
sudo gem install aanand-deadweight
[/bash]

[ruby]
# lib/tasks/deadweight.rake
begin
require ‚deadweight‘
rescue LoadError
end

desc "run Deadweight CSS check (requires script/server)"
task :deadweight do
dw = Deadweight.new
dw.stylesheets = ["/stylesheets/application.css"]
dw.pages = ["/", "/feeds", "/about", "/episodes/archive", "/comments", "/episodes/1-caching-with-instance-variables"]
dw.ignore_selectors = /flash_notice|flash_error|errorExplanation|fieldWithErrors/
puts dw.run
end
[/ruby]

Screencast: Seed Data

Rails-Migration-Scripte sind sehr hilfreich bei der Erstellung von DB-Tabellen und deren gleichzeitige Konfiguration. Allerdings es häufig auch nötig bei der Migration auch Daten einzutragen und/oder bestehende Daten anzupassen.

Ryan Bates zeigt in seinem Screencast wie man dies lösen kann.

Downloadlinks:
Download (13.5 MB, 7:56)
Alternativer download für iPod & Apple TV (9.3 MB, 7:56)

Weitere Ressourcen:

Quellcode:

[bash]
script/generate model operating_system name:string
script/generate model country name:string code:string
rake db:migrate
rake db:seed
[/bash]

[ruby]
# db/seeds.rb
require ‚open-uri‘
require ‚active_record/fixtures‘

["Windows", "Linux", "Mac OS X"].each do |os|
OperatingSystem.find_or_create_by_name(os)
end

Country.delete_all
open("http://openconcept.ca/sites/openconcept.ca/files/country_code_drupal_0.txt") do |countries|
countries.read.each_line do |country|
code, name = country.chomp.split("|")
Country.create!(:name => name, :code => code)
end
end

Fixtures.create_fixtures("#{Rails.root}/test/fixtures", "operating_systems")
[/ruby]

Screencast: 7 Sicherheitstips

Auf Screencasts.com ist heute ein weiterer Screencast erschienen. In dieser Folge werden sieben Tips beschrieben um die Sicherheit innerhalb Rails-Applikationen zu erhöhen.

Downloadlinks:

Download (22.2 MB, 14:53)

Alternativer Download for iPod & Apple TV(16.9 MB, 14:53)

Weitere Ressourcen:

  • Rails Security Guide
  • Full episode source code

Links und Quellcodes zu den einzelnen Beispielen:

1 Mass Assignment:

[ruby]

# script/console
p = Project.find(2)
p.update_attributes(:task_ids => [4])
p.tasks

# models/project.rb
attr_accessible :name, :photo
[/ruby]

2 File Uploads
Disabling Script Execution with Apache

[ruby]
# models/project.rb
validates_attachment_content_type :photo, :content_type => [‚image/jpeg‘, ‚image/png‘]
# more security required
[/ruby]

3 Filter Log Params
Episode 9: Filtering Sensitive Logs

[ruby]
# application_controller.rb
filter_parameter_logging :password
[/ruby]

4 CSRF Protection
Cross-site Request Forgery
Rails authenticity token with jQuery

[ruby]
# application_controller.rb
protect_from_forgery
[/ruby]

5 Authorizing Ownership

[ruby]
# projects_controller.rb
def show
@project = current_user.projects.find(params[:id])
end
[/ruby]

6 SQL Injection
SQL Injection
Episode 25: SQL Injection

[ruby]
# projects_controller.rb
def index
@projects = current_user.projects.all(:conditions => ["name like ?", "%#{params[:search]}%"])
end
[/ruby]

7 HTML Injection (XSS)
Cross Site Scripting
Episode 27: Cross Site Scripting

[ruby]
<!– projects/show.html.erb –>
<%=h task.name %>
[/ruby]