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]

Security: Sicherheitsupdate für Mail (betrifft Rails)

Mikel Lindsaar hat eine neue Vesion des mail gems veröffentlicht, dass eine mögliche Sicherheitslücke mit der „sendmail delivery“ Methode schließt. Die Lücke ermöglicht unter bestimmten Umständen das Ausführen von Kommandos auf dem betroffenen System. Rails Applikationen welche die „sendmail delivery“ Methode nicht verwenden, sind nicht davon betroffen.

Betroffene Version:      2.2.14 und früher
Korrigierte Version:         2.2.15 und danach

 

Auswirkung
——————————————————————

Angreifer können unter bestimmten Bedingungen eine modifizierte Email verfassen, um Shell-Kommandos auf dem betroffenen System auszuführen. Benutzer früherer Version des gems (kleiner 2.2.14) wird empfohlen Ihr System baldsmöglich zu aktualisieren bzw. anzupassen.
Ab Mail-Version 2.2.15 ist der Fehler korrigiert und das Gem steht auf RubyGems.org zum Download bereit.

Bei der Verwendung von Bundler, sollte das Gemfile entsprechend angepasst…

[ruby]
gem "mail", "~> 2.2.15"
[/ruby]

…und danach die Aktualisierung über folgenden Befehl angestossen swerden

[bash]
$ bundle install
[/bash]

 

Workarounds
——————————————————————
Das Anpassen der Versand-Methode nach SMTP oder File behebt die Lücke in der Applikation ebenfalls.
Details wie SMTP oder File verwendet werden kann gibt es unter folgenden Links:
http://rdoc.info/github/mikel/mail/master/Mail/SMTP
http://rdoc.info/github/mikel/mail/master/Mail/FileDelivery

 

Patch
——————————————————————
Wenn ein komplettes Update nicht möglich oder nicht gewünscht ist, kann auch den entsprechenden Patch benutzen.
Der Patch steht hier zum Download bereit:
https://github.com/mikel/mail/raw/master/patches/20110126_sendmail.patch

Screencast: Authentifizierung

Authentifizierung wird in vielen Applikationen, wenn nicht in den meisten, gebraucht. Das die Implementierung nicht Schwierig sein muss, zeigt Ryan in dieser Woche in seinem Screencast.

 

Download:

Download(22.6 MB, 15:26)
Alternativer Download für iPod & Apple TV(22.3 MB, 15:26)

 

Resourcen:

 

Quellcode:

[bash]
rails g controller users new
rails g model user email:string password_hash:string password_salt:string
rake db:migrate
rails dbconsole
rails g controller sessions new
[/bash]

[ruby]
# Gemfile
gem "bcrypt-ruby", :require => "bcrypt"

# models/user.rb
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation

attr_accessor :password
before_save :encrypt_password

validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :email
validates_uniqueness_of :email

def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end

def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end

# users_controller.rb
def new
@user = User.new
end

def create
@user = User.new(params[:user])
if @user.save
redirect_to root_url, :notice => "Signed up!"
else
render "new"
end
end

# sessions_controller.rb
def new
end

def create
user = User.authenticate(params[:email], params[:password])
if user
session[:user_id] = user.id
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
end

def destroy
session[:user_id] = nil
redirect_to root_url, :notice => "Logged out!"
end

# application_controller.rb
helper_method :current_user

private

def current_user
@current_user ||= User.find(session[:user_id]) if session[:user_id]
end

# routes.rb
get "log_out" => "sessions#destroy", :as => "log_out"
get "log_in" => "sessions#new", :as => "log_in"
get "sign_up" => "users#new", :as => "sign_up"
root :to => "users#new"
resources :users
resources :sessions
[/ruby]

[html]
<!– users/new.html.erb –>
<h1>Sign Up</h1>

<%= form_for @user do |f| %>
<% if @user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% for message in @user.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :email %><br />
<%= f.text_field :email %>
</p>
<p>
<%= f.label :password %><br />
<%= f.password_field :password %>
</p>
<p>
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</p>
<p class="button"><%= f.submit %></p>
<% end %>

<!– sessions/new.html.erb –>
<h1>Log in</h1>

<%= form_tag sessions_path do %>
<p>
<%= label_tag :email %><br />
<%= text_field_tag :email, params[:email] %>
</p>
<p>
<%= label_tag :password %><br />
<%= password_field_tag :password %>
</p>
<p class="button"><%= submit_tag "Log in" %></p>
<% end %>

<!– layouts/application.html.erb –>
<div id="user_nav">
<% if current_user %>
Logged in as <%= current_user.email %>.
<%= link_to "Log out", log_out_path %>
<% else %>
<%= link_to "Sign up", sign_up_path %> or
<%= link_to "log in", log_in_path %>
<% end %>
</div>

<% flash.each do |name, msg| %>
<%= content_tag :div, msg, :id => "flash_#{name}" %>
<% end %>
[/html]

Screencast: Notifications in Rails 3

Rails 3 bringt Unterstützung für die Klasse ActiveSupport::Notifications mit und über „subscribe“ kann man über Notifications informiert werden. Ryan stellt in dieser Woche vor wie Notifications eingesetzt werden können.

 

Download:

Download(19.3 MB, 10:22)
Alternativer Download für iPod & Apple TV(17.5 MB, 10:22)

 

Resourcen:

 

Quellcode:

[ruby]
# config/initializers/notifications.rb
ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, start, finish, id, payload|
PageRequest.create! do |page_request|
page_request.path = payload[:path]
page_request.page_duration = (finish – start) * 1000
page_request.view_duration = payload[:view_runtime]
page_request.db_duration = payload[:db_runtime]
end
end

ActiveSupport::Notifications.subscribe "products.search" do |name, start, finish, id, payload|
Rails.logger.debug "SEARCH: #{payload[:search]}"
end

# models/product.rb
ActiveSupport::Notifications.instrument("products.search", :search => search)
[/ruby]

Screencast: Offline Applikationen Teil 2

In dieser Woche zeigt Ryan in seinem zweiten Teil zu Offline-Applikationen wie z.B. HTML5 Storage, zum speichern von Offline-Daten genutzt werden kann.

 

Download:

Download(27.2 MB, 14:51)
Alternativer Download für iPod & Apple TV(25.1 MB, 14:51)

 

Resourcen:

 

Quellcode:

[bash]
curl https://github.com/jquery/jquery-tmpl/raw/master/jquery.tmpl.js > public/javascripts/jquery.tmpl.js
curl https://github.com/wycats/jquery-offline/raw/master/lib/jquery.offline.js > public/javascripts/jquery.offline.js
curl https://github.com/wycats/jquery-offline/raw/master/lib/json.js > public/javascripts/json.js
[/bash]

[ruby]
# items_controller.rb
respond_to :html, :json

def index
@items = Item.all
respond_with(@items)
end
[/ruby]

[html]
<!– layouts/application.html.erb –>
<%= javascript_include_tag :defaults, "jquery.tmpl", "json", "jquery.offline" %>

<!– items/index.html.erb –>
<script type="text/html" id="item_template">
<li>${item.name}</li>
</script>
<ol id="items">
<li><em>Loading items…</em></li>
</ol>
[/html]

[javascript]
// application.js
$(function() {
if ($.support.localStorage) {
$(window.applicationCache).bind("error", function() {
console.log("There was an error when loading the cache manifest.");
});
if (!localStorage["pendingItems"]) {
localStorage["pendingItems"] = JSON.stringify([]);
}
$.retrieveJSON("/items.json", function(data) {
var pendingItems = $.parseJSON(localStorage["pendingItems"]);
$("#items").html($("#item_template").tmpl(data.concat(pendingItems)));
});
$("#new_item").submit(function(e) {
var pendingItems = $.parseJSON(localStorage["pendingItems"]);
var item = {"data":$(this).serialize(), "item":{"name":$("#item_name").val()}};
$("#item_template").tmpl(item).appendTo("#items");
pendingItems.push(item);
localStorage["pendingItems"] = JSON.stringify(pendingItems)
$("#item_name").val("");
sendPending();
e.preventDefault();
});
function sendPending() {
if (window.navigator.onLine) {
var pendingItems = $.parseJSON(localStorage["pendingItems"]);
if (pendingItems.length > 0) {
var item = pendingItems[0];
$.post("/items", item.data, function(data) {
var pendingItems = $.parseJSON(localStorage["pendingItems"]);
pendingItems.shift();
localStorage["pendingItems"] = JSON.stringify(pendingItems)
setTimeout(sendPending, 100);
});
}
}
}
sendPending();
$(window).bind("online", sendPending);
} else {
alert("Try a different browser.");
}
});
[/javascript]

Screencast: Offline Applikationen Teil 1

Eine Webapplikation auch offline verfügbar zu haben bringt einige Vorteile. Ryan zeigt diese Woche wie dies mit rack-offline bewerkstelligt werden kann. Er zeigt auch auf welche Dinge man achten muss, um Fehler zu vermeiden.

 

Download:

Download(18.8 MB, 10:44)
Alternativer Download für iPod & Apple TV(17.5 MB, 10:44)

 

Resourcen:

 

Quellcode:

[ruby]
# Gemfile
gem "rack-offline"

# config/routes.rb
match "/application.manifest" => Rails::Offline

# config/application.rb
ENV["RAILS_ASSET_ID"] = "" # disable timestamps at end of asset files for offline browsing

# config/environments/development.rb
config.cache_classes = true # to temporarily simulate production
[/ruby]

[html]
<!– layouts/application.html.erb –>
<html manifest="/application.manifest">
[/html]

[javascript]
// application.js
$(function() {
$(window.applicationCache).bind("error", function() {
alert("There was an error when loading the cache manifest.");
});
});
[/javascript]