Screencast: Squeel

Squeel bietet eine umfangreiche DSL an um SQL Abfrage in Ruby zu erstellen. Es baut auf ARel auf und bietet somit auch Zugriff auf dessen Features.

 

Downloads in verschiedenen Formaten:

mp4
m4v
webm
ogg

 

Resourcen:

terminal

[bash]
rails g squeel:initializer
rails c
[/bash]

rails console

[ruby]
Product.where{released_at < 3.months.ago}
Product.where{released_at.lt 3.months.ago}
Product.where{released_at.lt(3.months.ago) & price.gt(20)}
Product.where{released_at.lt(3.months.ago) | price.gt(20)}
[/ruby]

models/product.rb

[ruby]
def self.search(query)
where do
(released_at <= Time.zone.now) &
((discontinued_at == nil) | (discontinued_at > Time.zone.now)) &
(stock >= my{low_stock}) & (name =~ "%#{query}%")
end
end

def self.low_stock
2
end
[/ruby]

Screencast: APIs schützen

Es gibt viele Möglichkeiten den Zugriff auf APIs zu schützen. In diesem Screencast zeigt Ryan erst einen möglichen Schutz über HTTP Basic Auth und später über Tokens die als Parameter übertragen werden.

 

Downloads in verschiedenen Formaten:

mp4
mp4
webm
ogg

 

Resourcen:

terminal

[bash]
rails g model api_key access_token
curl http://localhost:3000/api/products -I
curl http://localhost:3000/api/products -u ‚admin:secret‘
curl ‚http://localhost:3000/api/products?access_token=123‘ -I
curl http://localhost:3000/api/products -H ‚Authorization: Token token="c576f0136149a2e2d9127b3901015545"‘
[/bash]

api/v1/products_controller.rb

[ruby]
# http_basic_authenticate_with name: "admin", password: "secret"
before_filter :restrict_access

private

# def restrict_access
# api_key = ApiKey.find_by_access_token(params[:access_token])
# head :unauthorized unless api_key
# end

def restrict_access
authenticate_or_request_with_http_token do |token, options|
ApiKey.exists?(access_token: token)
end
end
[/ruby]

models/api_key.rb

[ruby]
before_create :generate_access_token

private

def generate_access_token
begin
self.access_token = SecureRandom.hex
end while self.class.exists?(access_token: access_token)
end
[/ruby]

Screencast: Versionierung von REST APIs

APIs sollten konsistent sein. Dies ist allerdings schwer zu bewerkstelligen wenn verschiedene Formate unterstützt werden und/oder die APIs sich weiterentwickeln. In diesem Screencast zeigt Ryan wie eine API-Versionierung über die URL oder HTPP-Header realisiert werden können.

 

Downloads in verschiedenen Formaten:

mp4
m4v
webm
ogg

 

Resourcen:

terminal

[bash]
rails g migration change_products_released_on
rake db:migrate
cp -R app/controllers/api/v1 app/controllers/api/v2
curl -H ‚Accept: application/vnd.example.v1‘ http://localhost:3000/api/products
[/bash]

routes.rb

[ruby]
require ‚api_constraints‘

Store::Application.routes.draw do
namespace :api, defaults: {format: ‚json‘} do
scope module: :v1, constraints: ApiConstraints.new(version: 1) do
resources :products
end
scope module: :v2, constraints: ApiConstraints.new(version: 2, default: true) do
resources :products
end
end

resources :products
root to: ‚products#index‘
end
[/ruby]

lib/api_constraints.rb

[ruby]
class ApiConstraints
def initialize(options)
@version = options[:version]
@default = options[:default]
end

def matches?(req)
@default || req.headers[‚Accept‘].include?("application/vnd.example.v#{@version}")
end
end
[/ruby]

app/controllers/api/v1/products_controller.rb

[ruby]
module Api
module V1
class ProductsController < ApplicationController
class Product < ::Product
# Note: this does not take into consideration the create/update actions for changing released_on
def as_json(options = {})
super.merge(released_on: released_at.to_date)
end
end

respond_to :json

def index
respond_with Product.all
end

def show
respond_with Product.find(params[:id])
end

def create
respond_with Product.create(params[:product])
end

def update
respond_with Product.update(params[:id], params[:product])
end

def destroy
respond_with Product.destroy(params[:id])
end
end
end
end
[/ruby]

Screencast: Das Rails API Gem

Applikationen die eine reine API zur Verfügung erstellen kommen immer häufiger vor und haben einen anderen Anspruch als konventionelle Web-Applikationen. Sie benötigen in der Regel keine/andere Views um Daten zur Verfügung zu stellen. In Rails kann für Applikationen die hauptsächlich APIs anbieten das Rails API Gem eingesetzt werden. Ryan zeigt in dieser Woche wie es verwendet werden kann.

 

Downloads in verschiedenen Formaten:

mp4
m4v
webm
ogg

 

Resourcen:

terminal

[bash]
gem install rails-api
rbenv rehash
rails-api new todo
rails g scaffold task name
rake middleware
diff api_middleware.txt full_middleware.txt
[/bash]

tasks_controller.rb

[ruby]
include ActionController::MimeResponds
include ActionController::Helpers
include ActionController::Cookies
include ActionController::ImplicitRender

# GET /tasks
# GET /tasks.json
def index
@tasks = Task.all

# respond_to do |format|
# format.json { render }
# format.xml { render xml: @tasks }
# end
end
[/ruby]

views/tasks/index.json.rabl

[javascript]
collection @tasks
attributes :id, :name
[/javascript]

config/application.rb

[ruby]
config.middleware.insert_after ActiveRecord::QueryCache, ActionDispatch::Cookies
[/ruby]

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]