Screencast: Mehrseitige Formulare mit Wicked

Mehrseitige Formulare sind nicht immer einfach zu erstellen. Wicket ist ein Plugin das genau diese Nische bedient und die Erstellung vereinfacht. Mit Wicket lassen sich Controller in eine Serie von Schritten verwandeln um seitenweise Daten zu erfassen.

 

Downloads in verschiedenen Formaten:

mp4
m4v
webm
ogg

 

Resourcen:

terminal

[bash]
rails g controller user_steps
[/bash]

user_steps_controller.rb

[ruby]
class UserStepsController < ApplicationController
include Wicked::Wizard
steps :personal, :social

def show
@user = current_user
render_wizard
end

def update
@user = current_user
@user.attributes = params[:user]
render_wizard @user
end

private

def redirect_to_finish_wizard
redirect_to root_url, notice: "Thank you for signing up."
end
end
[/ruby]

user_steps/personal.html.erb

[html]
<h1>Tell us a little about yourself.</h1>

<%= render layout: ‚form‘ do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :date_of_birth %><br />
<%= f.date_select :date_of_birth, start_year: 1900, end_year: Date.today.year, include_blank: true %>
</div>
<div class="field">
<%= f.label :bio %><br />
<%= f.text_area :bio, rows: 5 %>
</div>
<% end %>
[/html]

user_steps/social.html.erb

[html]
<h1>Where can we find you?</h1>

<%= render layout: ‚form‘ do |f| %>
<div class="field">
<%= f.label :twitter_username %><br />
<%= f.text_field :twitter_username %>
</div>
<div class="field">
<%= f.label :github_username %><br />
<%= f.text_field :github_username %>
</div>
<div class="field">
<%= f.label :website %><br />
<%= f.text_field :website %>
</div>
<% end %>
[/html]

user_steps/_form.html.erb

[html]
<%= form_for @user, url: wizard_path do |f| %>
<% if @user.errors.any? %>
<div class="error_messages">
<h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% @user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= yield f %>

<div class="actions">
<%= f.submit "Continue" %>
or <%= link_to "skip this step", next_wizard_path %>
</div>
<% end %>
[/html]

users/new.html.erb

[html]
<h1>Sign Up</h1>

<%= form_for @user do |f| %>
<% if @user.errors.any? %>
<div class="error_messages">
<h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% @user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>

<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>

<div class="actions">
<%= f.submit "Sign Up" %>
</div>
<% end %>
[/html]

users_controller.rb

[ruby]
def create
@user = User.new(params[:user])
if @user.save
session[:user_id] = @user.id
redirect_to user_steps_path
else
render :new
end
end
[/ruby]

models/user.rb

[ruby]
validates_format_of :twitter_username, without: /W/, allow_blank: true
# validates_presence_of :twitter_username, if: :on_social_step?
[/ruby]

Screencast: OmniAuth

OmniAuth ist ein Ruby-Authetifierungs-Framework welches eine standardisierte Schnittstelle zu verschiedenen Authentifizierungs-Providern bietet (z.B. Facebook, OpenID). Mit dem aktuellen OmniAuth-Release 1.0 ist nun auch möglich klassisch über Benutzernamen und Passwort die Authentifizierung durchzuführen, wenn kein externer Anbieter gewünscht oder vorhanden ist. In diesem Screencast zeigt Ryan wie es installiert und verwendet werden kann.

 

Resourcen:

bash

[bash]
rails g model identity name:string email:string password_digest:string
rake db:migrate
rails g controller identities
[/bash]

Gemfile

[ruby]
gem ‚omniauth-identity‘
[/ruby]

config/initializers/omniauth.rb

[ruby]
Rails.application.config.middleware.use OmniAuth::Builder do
# …
provider :identity, on_failed_registration: lambda { |env|
IdentitiesController.action(:new).call(env)
}
end
[/ruby]

models/identity.rb

[ruby]
class Identity < OmniAuth::Identity::Models::ActiveRecord
validates_presence_of :name
validates_uniqueness_of :email
validates_format_of :email, :with => /^[-a-z0-9_+.]+@([-a-z0-9]+.)+[a-z0-9]{2,4}$/i
end
[/ruby]

sessions/new.html.erb

[html]
<p>
<strong>Don’t use these services?</strong>
<%= link_to "Create an account", new_identity_path %> or login below.
</p>

<%= form_tag "/auth/identity/callback" do %>
<div class="field">
<%= label_tag :auth_key, "Email" %><br>
<%= text_field_tag :auth_key %>
</div>
<div class="field">
<%= label_tag :password %><br>
<%= password_field_tag :password %>
</div>
<div class="actions"><%= submit_tag "Login" %></div>
<% end %>
[/html]

routes.rb

[ruby]
resources :identities
[/ruby]

identities_controller.rb

[ruby]
def new
@identity = env[‚omniauth.identity‘]
end
[/ruby]

identities/new.html.erb

[html]
<%= form_tag "/auth/identity/register" do %>
<% if @identity && @identity.errors.any? %>
<div class="error_messages">
<h2><%= pluralize(@identity.errors.count, "error") %> prohibited this account from being saved:</h2>
<ul>
<% @identity.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= label_tag :name %><br>
<%= text_field_tag :name, @identity.try(:name) %>
</div>
<div class="field">
<%= label_tag :email %><br>
<%= text_field_tag :email, @identity.try(:email) %>
</div>
<div class="field">
<%= label_tag :password %><br>
<%= password_field_tag :password %>
</div>
<div class="field">
<%= label_tag :password_confirmation %><br>
<%= password_field_tag :password_confirmation %>
</div>
<div class="actions"><%= submit_tag "Register" %></div>
<% end %>
[/html]

Screencast: Im Browser „In-Place“ Editieren

In-Place Editing bezeichnet die Möglichkeit im Browser direkt Bereiche zu bearbeiten. Diese Funktion wird gerne CMS Systemen verwendet um den Anwender eine einfache Möglichkeit zu bieten um Inhalte ohne Umwege zu verändern und/oder zu erstellen. Ryan zeigt in diesem Screencast wie dies umgesetzt werden kann.

 

Downloads in verschiedenen Formaten:

mp4
m4v
webm
ogg

 

Resourcen:

Gemfile

[ruby]
gem ‚best_in_place‘
[/ruby]

app/assets/javascripts/application.js

[javascript]
//= require jquery.purr
//= require best_in_place
[/javascript]

app/assets/javascripts/users.js.coffee

[javascript]
jQuery ->
$(‚.best_in_place‘).best_in_place()
[/javascript]

users/show.html.erb

[html]
<p>
<b>Name:</b>
<%= best_in_place @user, :name %>
</p>
<p>
<b>Email:</b>
<%= best_in_place @user, :email %>
</p>
<p>
<b>Gender:</b>
<%= best_in_place @user, :gender, type: :select, collection: [["Male", "Male"], ["Female", "Female"], ["", "Unspecified"]] %>
</p>
<p>
<b>Public profile:</b>
<%= best_in_place @user, :public_profile, type: :checkbox, collection: %w[No Yes] %>
</p>
<p>
<%= best_in_place @user, :bio, type: :textarea %>
</p>
[/html]

users_controller.rb

[ruby]
respond_to :html, :json
def update
@user = User.find(params[:id])
@user.update_attributes(params[:user])
respond_with @user
end
[/ruby]

app/assets/stylesheets/users.css.scss

[css]
.purr {
position: fixed;
top: 30px;
right: 100px;
width: 250px;
padding: 20px;
background-color: #FCC;
border: solid 2px #C66;
&:first-letter { text-transform: uppercase; }
}
[/css]

Screencast: Clientseitige Validierung von Formularen

Clientseitige Validierung, also die Überprüfung von eingegebenen Werten innerhalb des Webbrowsers, können dem Benutzer bereits bei der Eingabe über die Korrektheit der Daten informieren. Dies kann unter Umständen den einen oder anderen Korrigier-Zyklus ersparen. Wie dies innerhalb von Rails eingesetzt werden kann zeigt Ryan diese Woche anhand eines gems, welches die Entwicklung erleichtert.

 

Download:

Download(20.2 MB, 8:42)
Alternativer Download für iPod & Apple TV(19.3 MB, 8:42)

 

Resourcen:

 

Quellcode:

[bash]
bundle
rails g client_side_validations:install
[/bash]

[ruby]
# Gemfile
gem ‚client_side_validations‘

# models/user.rb
validates_uniqueness_of :username, :email
validates :email, :email_format => true

# lib/email_format_validator.rb
class EmailFormatValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
unless value =~ /^([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})$/i
object.errors.add(attribute, :email_format, options)
end
end
end

# config/application.rb
config.autoload_paths << "#{config.root}/lib"
[/ruby]

[html]
<!– users/_form.html.erb –>
<%= form_for @user, :validate => true do |f| %>

<!– layouts/application.html.erb –>
<%= javascript_include_tag :defaults, "rails.validations", "rails.validations.custom" %>
[/html]

[text]
# locals/en.yml
en:
errors:
messages:
email_format: "is not formatted properly"
[/text]

[javascript]
/* rails.validations.custom.js */
clientSideValidations.validators.local["email_format"] = function(element, options) {
if (!/^([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})$/i.test(element.val())) {
return options.message;
}
}
[/javascript]

Screencast: jQuery Tokeninput

Autokompletierung ist eine hilfreiche Funktion für viele Anwender. Mit jQuery Tokeninput ist die relativ einfach umzusetzen. Ryan zeigt zudem noch wie es im Zusammenhang mit Rails eingesetzt werden kann.

 

Download:

Download(21.7 MB, 11:09)
Alternativer Download für iPod & Apple TV(20.4 MB, 11:09)

 

Resourcen:

 

Quellcode:

[bash]
bundle
rails g jquery:install
[/bash]

[ruby]
# Gemfile
gem "jquery-rails"

# models/book.rb
class Book < ActiveRecord::Base
attr_accessible :name, :author_tokens
has_many :authorships
has_many :authors, :through => :authorships
attr_reader :author_tokens

def author_tokens=(ids)
self.author_ids = ids.split(",")
end
end

# authors_controller.rb
def index
@authors = Author.where("name like ?", "%#{params[:q]}%")
respond_to do |format|
format.html
format.json { render :json => @authors.map(&:attributes) }
end
end
[/ruby]

[html]
<!– layouts/application.html.erb –>
<%= stylesheet_link_tag "application", "token-input-facebook" %>
<%= javascript_include_tag :defaults, "jquery.tokeninput" %>

<!– books/_form.html.erb –>
<p>
<%= f.label :author_tokens, "Authors" %><br />
<%= f.text_field :author_tokens, "data-pre" => @book.authors.map(&:attributes).to_json %>
</p>
[/html]

[javascript]
// application.js
$(function() {
$("#book_author_tokens").tokenInput("/authors.json", {
crossDomain: false,
prePopulate: $("#book_author_tokens").data("pre"),
theme: "facebook"
});
});
[/javascript]

Screencast: MetaSearch und MetaWhere

MetaSearch und MetaWhere hilft bei Entwicklung komplexe Abfragen ohne SQL in ActiveRecord zu erstellen. In diesem Screencast wird gezeigt wie es eingesetzt werden kann.

 

Download:

Download(16 MB, 8:28)
Alternativer Download für iPod & Apple TV(14.4 MB, 8:28)

 

Resourcen:

 

Quellcode:

[ruby]
# Gemfile
gem "meta_where"
gem "meta_search"

# rails console
Product.where(:price.lt => 5)
Product.where({:price.lt => 5} | {:name.matches => "%video%"})
Product.order(:released_at.desc)
MetaWhere.operator_overload!
Product.where(:price < 5)

# products_controller.rb
def index
@search = Product.search(params[:search])
@products = @search.all
end
[/ruby]

[html]
<!– products/index.html.erb –>
<%= form_for @search do |f| %>
<p>
<%= f.label :name_contains %>
<%= f.text_field :name_contains %>
</p>
<p>
<%= f.label :price_gte, "Price ranges from" %>
<%= f.text_field :price_gte, :size => 8 %>
<%= f.label :price_lte, "to" %>
<%= f.text_field :price_lte, :size => 8 %>
</p>
<p class="button"><%= f.submit "Search" %></p>
<% end %>

<p>
Sort by:
<%= sort_link @search, :name %> |
<%= sort_link @search, :price %> |
<%= sort_link @search, :released_at %>
</p>
[/html]

Screencast: Simple Form

Simple Form ist ein gem zur Unterstützung bei der Entwicklung von Formularen. In diesem Screencast gibt Ryan einen Überblick wie es eingesetzt werden kann.

 

Download:

Download(14.7 MB, 7:50)
Alternativer Download für iPod & Apple TV(13.7 MB, 7:50)

 

Resourcen:

 

Quellcode:

[bash]
bundle_install
rails g simple_form:install
[/bash]

[html]
<%= simple_form_for @product do |f| %>
<%= f.error_messages %>
<%= f.input :name %>
<%= f.input :price, :hint => "prices should be in USD" %>
<%= f.input :released_on %>
<%= f.association :category, :include_blank => false %>
<%= f.input :rating, :collection => 1..5, :as => :radio %>
<%= f.input :discontinued %>
<%= f.button :submit %>
<% end %>
[/html]

[css]
/* stylesheets/application.css */
.simple_form label {
float: left;
width: 100px;
text-align: right;
margin: 2px 10px;
}

.simple_form div.input {
margin-bottom: 10px;
}

.simple_form div.boolean, .simple_form input[type=’submit‘] {
margin-left: 120px;
}

.simple_form div.boolean label, .simple_form label.collection_radio {
float: none;
margin: 0;
}

.simple_form label.collection_radio {
margin-right: 10px;
margin-left: 2px;
}

.simple_form .error {
clear: left;
margin-left: 120px;
font-size: 12px;
color: #D00;
display: block;
}

.simple_form .hint {
clear: left;
margin-left: 120px;
font-size: 12px;
color: #555;
display: block;
font-style: italic;
}
[/css]

Screencast: Mehrstufige Formulare

Je nach nach Umfang und Komplexität ist es notwendig ein Formular in mehrere Schritte aufzuteilen. Diese Woche zeigt Ryan in seinem Screencast, wie dies mit Rails gelöst werden kann.

 

Download:

Download(21.8 MB, 15:14)
Alternativer Downloadfür iPod & Apple TV(20.9 MB, 15:14)

 

Resourcen:

 

Quellcode:

[bash]
script/generate nifty_scaffold order shipping_name:string billing_name:string index show new
[/bash]

[ruby]
# models/order.rb
attr_writer :current_step

validates_presence_of :shipping_name, :if => lambda { |o| o.current_step == "shipping" }
validates_presence_of :billing_name, :if => lambda { |o| o.current_step == "billing" }

def current_step
@current_step || steps.first
end

def steps
%w[shipping billing confirmation]
end

def next_step
self.current_step = steps[steps.index(current_step)+1]
end

def previous_step
self.current_step = steps[steps.index(current_step)-1]
end

def first_step?
current_step == steps.first
end

def last_step?
current_step == steps.last
end

def all_valid?
steps.all? do |step|
self.current_step = step
valid?
end
end

# orders_controller.rb
def new
session[:order_params] ||= {}
@order = Order.new(session[:order_params])
@order.current_step = session[:order_step]
end

def create
session[:order_params].deep_merge!(params[:order]) if params[:order]
@order = Order.new(session[:order_params])
@order.current_step = session[:order_step]
if @order.valid?
if params[:back_button]
@order.previous_step
elsif @order.last_step?
@order.save if @order.all_valid?
else
@order.next_step
end
session[:order_step] = @order.current_step
end
if @order.new_record?
render "new"
else
session[:order_step] = session[:order_params] = nil
flash[:notice] = "Order saved!"
redirect_to @order
end
end
[/ruby]

[html]
<!– orders/new.html.erb –>
<% form_for @order do |f| %>
<%= f.error_messages %>
<%= render "#{@order.current_step}_step", :f => f %>
<p><%= f.submit "Continue" %></p>
<p><%= f.submit "Back", :name => "back_button" unless @order.first_step? %></p>
<% end %>
[/html]

Screencast: Kalender-Funktion

Es gibt verschiedene Möglichkeiten um einem Benutzer ein Datum auswählen zu lassen oder einen Kalender zu präsentieren. In dieser Woche zeigt Ryan einige Beispiele und Lösungen dafür.

 

Download:

Download(15.6 MB, 9:50)
Alternativer Downloadfür iPod & Apple TV(14.9 MB, 9:50)

 

Resourcen:

 

Quellcode:

[bash]
script/plugin install git://github.com/p8/table_builder.git
[/bash]

[ruby]
# articles_controller.rb
def index
@articles = Article.find(:all)
@date = params[:month] ? Date.parse(params[:month]) : Date.today
endv
[/ruby]

[html]
<!– layouts/application.html.erb –>
<%= stylesheet_link_tag "http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/redmond/jquery-ui.css", "application" %>
<%= javascript_include_tag "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js", "http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.1/jquery-ui.min.js", "application" %>

<!– articles/index.html.erb –>
<div id="calendar">
<h2 id="month">
<%= link_to "<", :month => (@date.beginning_of_month-1).strftime("%Y-%m") %>
<%=h @date.strftime("%B %Y") %>
<%= link_to ">", :month => (@date.end_of_month+1).strftime("%Y-%m") %>
</h2>
<% calendar_for @articles, :year => @date.year, :month => @date.month do |calendar| %>
<%= calendar.head(‚Sunday‘, ‚Monday‘, ‚Tuesday‘, ‚Wednesday‘, ‚Thursday‘, ‚Friday‘, ‚Saturday‘) %>
<% calendar.day(:day_method => :published_on) do |date, articles| %>
<%= date.day %>
<ul>
<% for article in articles %>
<li><%= link_to h(article.name), article %></li>
<% end %>
</ul>
<% end %>
<% end %>
</div>

<!– articles/_form.html.erb –>
<%= f.text_field :published_on %>
[/html]

[javascript]
// application.js
$(function() {
$("#article_published_on").datepicker();
});
[/javascript]

[css]
#calendar table {
border-collapse: collapse;
width: 100%;
}

#calendar td,
#calendar th {
font-family: "Lucida Grande",arial,helvetica,sans-serif;
font-size: 10px;
padding: 6px;
border: 1px solid #999;
}

#calendar th {
background: #DDD;
color: #666;
text-align: center;
width: 14.2857142857143%;
}

#calendar td {
background: #FFF;
color: #777;
height: 80px;
vertical-align: top;
font-size: 16px;
}

#calendar .notmonth {
color: #CCC;
}

#calendar #month {
margin: 0;
padding-top: 10px;
padding-bottom: 10px;
text-align: center;
}

#calendar #month a, #calendar #month a:hover {
text-decoration: none;
padding: 0 10px;
color: #999;
}

#calendar .today {
background-color: #D7F2FF;
}

#calendar ul {
margin: 0;
margin-top: 5px;
padding: 0;
list-style: none;
}

#calendar li {
margin: 0;
padding: 0;
font-size: 11px;
text-align: center;
}
[/css]

Screencast: Mehrere Daten gleichzeitig bearbeiten

Mehrere Einträge in Formular gleichtzeitig zu bearbeiten erfordert gewisse Anpassungen bzw. Vorkehrngen. Eine Möglichkeit wie dies gelöst werden kann zeigt Ryan diese Woche in seinem Screencast.

Download

Download (33.6 MB, 13:53)
Alternativer Download für iPod & Apple TV (19.8 MB, 13:53)

Ressourcen:

Quellcode:

[ruby]
# products_controller.rb
def edit_individual
@products = Product.find(params[:product_ids])
end

def update_individual
@products = Product.update(params[:products].keys, params[:products].values).reject { |p| p.errors.empty? }
if @products.empty?
flash[:notice] = "Products updated"
redirect_to products_url
else
render :action => "edit_individual"
end
end

# routes.rb
map.resources :products, :collection => { :edit_individual => :post, :update_individual => :put }
[/ruby]

[html]
<!– views/products/index.html.erb –>
<% form_tag edit_individual_products_path do %>
<table>
<tr>
<th></th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
</tr>
<% for product in @products %>
<tr>
<td><%= check_box_tag "product_ids[]", product.id %></td>
<td><%=h product.name %></td>
<td><%=h product.category.name %></td>
<td><%= number_to_currency product.price %></td>
<td><%= link_to "Edit", edit_product_path(product) %></td>
<td><%= link_to "Destroy", product, :confirm => ‚Are you sure?‘, :method => :delete %></td>
</tr>
<% end %>
</table>
<p>
<%= select_tag :field, options_for_select([["All Fields", ""], ["Name", "name"], ["Price", "price"], ["Category", "category_id"], ["Discontinued", "discontinued"]]) %>
<%= submit_tag "Edit Checked" %>
</p>
<% end %>

<!– views/products/edit_individual.html.erb –>
<% form_tag update_individual_products_path, :method => :put do %>
<% for product in @products %>
<% fields_for "products[]", product do |f| %>
<h2><%=h product.name %></h2>
<%= render "fields", :f => f %>
<% end %>
<% end %>
<p><%= submit_tag "Submit" %></p>
<% end %>

<!– views/products/_fields.html.erb –>
<%= f.error_messages :object_name => "product" %>
<% if params[:field].blank? || params[:field] == "name" %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<% end %>
<% if params[:field].blank? || params[:field] == "price" %>
<p>
<%= f.label :price %><br />
<%= f.text_field :price %>
</p>
<% end %>
<% if params[:field].blank? || params[:field] == "category_id" %>
<p>
<%= f.label :category_id %><br />
<%= f.collection_select :category_id, Category.all, :id, :name %>
</p>
<% end %>
<% if params[:field].blank? || params[:field] == "discontinued" %>
<p>
<%= f.check_box :discontinued %>
<%= f.label :discontinued %>
</p>
<% end %>
[/html]