Screencast: Active Records Reputation System

Wenn der Durchschnittswert für ein Bewertungssystem oder die Summe von abgegebenen Stimmen berechnet werden müssen, dann lohnt sich ein Blick auf das activerecord-reputation-system gem. Ryan zeigt in diesem Screencast wie es verwendet wird und wie eine selbsterstellte Basis-Version gebaut werden kann.

 

Downloads in verschiedenen Formaten:

mp4
m4v
webm
ogg

 

Resourcen:

terminal

[bash]

rails g reputation_system
rake db:migrate
[/bash]

Gemfile

[ruby]

gem ‚activerecord-reputation-system‘, require: ‚reputation_system‘
[/ruby]

models/haiku.rb

[ruby]

has_reputation :votes, source: :user, aggregated_by: :sum
[/ruby]

models/user.rb

[ruby]

has_many :evaluations, class_name: "RSEvaluation", as: :source

has_reputation :votes, source: {reputation: :votes, of: :haikus}, aggregated_by: :sum

def voted_for?(haiku)
evaluations.where(target_type: haiku.class, target_id: haiku.id).present?
end
[/ruby]

config/routes.rb

[ruby]

resources :haikus do
member { post :vote }
end
[/ruby]

haikus_controller.rb

[ruby]

def index
@haikus = Haiku.find_with_reputation(:votes, :all, order: "votes desc")
end

def vote
value = params[:type] == "up" ? 1 : -1
@haiku = Haiku.find(params[:id])
@haiku.add_or_update_evaluation(:votes, value, current_user)
redirect_to :back, notice: "Thank you for voting"
end
[/ruby]

_haiku.html.erb

[html]

| <%= pluralize haiku.reputation_value_for(:votes).to_i, "vote" %>
<% if current_user && !current_user.voted_for?(haiku) %>
| <%= link_to "up", vote_haiku_path(haiku, type: "up"), method: "post" %>
| <%= link_to "down", vote_haiku_path(haiku, type: "down"), method: "post" %>
<% end %>
[/html]

application.html.erb

[html]

<%= current_user.reputation_value_for(:votes).to_i %>
[/html]

models/haiku_vote.rb

[ruby]

validates_uniqueness_of :haiku_id, scope: :user_id
validates_inclusion_of :value, in: [1, -1]
validate :ensure_not_author

def ensure_not_author
errors.add :user_id, "is the author of the haiku" if haiku.user_id == user_id
end
[/ruby]

models/haiku.rb

[ruby]

def self.by_votes
select(‚haikus.*, coalesce(value, 0) as votes‘).
joins(‚left join haiku_votes on haiku_id=haikus.id‘).
order(‚votes desc‘)
end

def votes
read_attribute(:votes) || haiku_votes.sum(:value)
end
[/ruby]

models/user.rb

[ruby]

def total_votes
HaikuVote.joins(:haiku).where(haikus: {user_id: self.id}).sum(‚value‘)
end

def can_vote_for?(haiku)
haiku_votes.build(value: 1, haiku: haiku).valid?
end
[/ruby]

haikus_controller.rb

[ruby]

def index
@haikus = Haiku.by_votes
end

def vote
vote = current_user.haiku_votes.new(value: params[:value], haiku_id: params[:id])
if vote.save
redirect_to :back, notice: "Thank you for voting."
else
redirect_to :back, alert: "Unable to vote, perhaps you already did."
end
end
[/ruby]

_haiku.html.erb

[html]

| <%= pluralize haiku.votes, "vote" %>
<% if current_user && current_user.can_vote_for?(haiku) %>
| <%= link_to "up", vote_haiku_path(haiku, value: 1), method: "post" %>
| <%= link_to "down", vote_haiku_path(haiku, value: -1), method: "post" %>
<% end %>
[/html]

application.html.erb

[html]

<%= current_user.total_votes %>
[/html]

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: Globalize3

Rails bringt bereits standardmäßig die Möglichkeit mit eine mehrsprachige Applikation zu bauen. Dazu werden Textbausteine in Yaml-Dateien erstellt und in der Applikation verwendet. Dies reicht in den meisten Fällen aus, aber ist nur bedingt geeignet um große und dynamisch erstellte Textmengen zu verwalten. Hier hat Globalize3 seine Stärken. Globalize3 ist ein Gem mit welchem mehrsprachige Texte in Datenbanken verwaltet werden kann. Es ist relativ einfach einzubinden und zu verwenden . Ryan zeigt in diesem Screencast wie es funktioniert.

 

Downloads in verschiedenen Formaten:

mp4
m4v
webm
ogg

 

Resourcen:

Gemfile

[ruby]
gem ‚globalize3‘
[/ruby]

terminal

[bash]
rails g migration create_article_translations
rake db:migrate
rails c
[/bash]

rails console

[ruby]
I18n.locale
Article.first.name
I18n.locale = :wk
Article.first.update_attribute(:name, "Ahhyya")
I18n.locale = :en
[/ruby]

db/migrate/create_article_translations.rb

[ruby]
class CreateArticleTranslations < ActiveRecord::Migration
def up
Article.create_translation_table!({
name: :string,
content: :text
}, {
migrate_data: true
})
end

def down
Article.drop_translation_table! migrate_data: true
end
end
[/ruby]

models/article.rb

[ruby]
translates :name, :content
[/ruby]

config/application.rb

[ruby]
config.i18n.fallbacks = true
[/ruby]

Screencast: Pagination mit Kaminari

Wenn größere Datenmengen angezeigt werden müssen, ist häufig nötig diese auf mehrere Seiten aufzuteilen. So kann der Benutzer durch die Daten blättern und die interessanten Bereiche aufrufen. In Rails-Applikationen kann das Plugin Kaminari für diesen Zweck eingesetzt werden. Es arbeitet sehr gut mit ActiveRecord zusammen und kann auch in Verbindung mit MongoID eingesetzt werden.d

 

Download:

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

 

Resourcen:

 

Quellcode:

[bash]
bundle
rails g kaminari:views default
[/bash]

[ruby]
# Gemfile
gem ‚kaminari‘

# products_controller.rb
@products = Product.order("name").page(params[:page]).per(5)
[/ruby]

[text]
en:
hello: "Hello world"
views:
pagination:
previous: "&lt; Previous"
next: "Next &gt;"
truncate: "…"
[/text]

[html]
# products/index.html.erb
<%= paginate @products %>

# app/views/kaminari/_prev_span.html.erb
<span class="prev disabled"><%= raw(t ‚views.pagination.previous‘) %></span>
[/html]

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: ActiveRecord::Relation Grundlagen

Das neue ActiveRecord bringt einige Erleichterungen für Entwickler mit. Ryan beschreibt diese Woche wie es im Hintergrund funktioniert und gibt somit einen Einblick in die richtigen Stellen von ActiveRecord.

 

Download:

Download(35.4 MB, 11:45)
Alternativer Download für iPod & Apple TV(29.7 MB, 11:45)

 

Resourcen:

 

Quellcode:

[bash]
git clone git://github.com/rails/rails.git
cd rails
git checkout v3.0.1
mate activerecord/lib/active_record
[/bash]

Screencast: Rails 3 Active Model

In Rails 3 wurden viele Funktionen von ActiveRecord nach ActiveModel ausgelagert. Dadurch ist es nun möglich die Validierungen und mehr auch in anderen Datenbanken oder anderen Formen der Datenhaltung zu implementieren.

 

Download:

Download(11.7 MB, 6:47)
Alternativer Download für iPod & Apple TV(10.9 MB, 6:47)

 

Resourcen:

 

Quellcode:

[ruby]
# models/message.rb
class Message
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming

attr_accessor :name, :email, :content

validates_presence_of :name
validates_format_of :email, :with => /^[-a-z0-9_+.]+@([-a-z0-9]+.)+[a-z0-9]{2,4}$/i
validates_length_of :content, :maximum => 500

def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end

def persisted?
false
end
end

# messages_controller.rb
def create
@message = Message.new(params[:message])
if @message.valid?
# TODO send message here
flash[:notice] = "Message sent! Thank you for conacting us."
redirect_to root_url
else
render :action => ’new‘
end
end
[/ruby]

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: Fortgeschrittene Abfragen in Rails 3

In dieser Woche behandelt Ryan fortgeschrittene Abfragemöglichkeiten in Rails 3. Er zeigt wie „named scopes“ eingesetzt werden können und es gibt dabei auch eine Einführung in Arel. Arel wird mit Rails 3 eingeführt und wird die Erstellung von DB-Queries vereinfachen und verbessern.

 

Download:

Download(33.9 MB, 9:26)
Alternativer Downloadfür iPod & Apple TV(47 MB, 9:26)

 

Resourcen:

 

Quellcode:

[ruby]
# rails console
Product.cheap.to_sql
(Category.joins(:products) & Product.cheap).to_sql
Category.with_cheap_products.to_sql
p = Product.discontinued.build
p.discontinued
t = Product.arel_table
t[:price].eq(2.99)
t[:name].matches("%catan").to_sql
Product.where(t[:price].eq(2.99).or(t[:name].matches("%catan")))

# models/product.rb
scope :discontinued, where(:discontinued => true)

def self.cheaper_than(price)
where("products.price < ?", price)
end

scope :cheap, cheaper_than(5)

# models/category.rb
scope :with_cheap_products, joins(:products) & Product.cheap
[/ruby]

Screencast: Refactoring und Dynamic Delegator

Code Refactoring ist der Prozess bei dem ein Programm modifiziert (optimiert, verbessert) wird ohne sein Verhalten nach aussen zu verändern. Diese Woche zeigt Ryan wie Refactoring eingesetzt werden kann und wie man Dynamic Delegator in Active Recors nutzt.

 

Download:

Download (11.2 MB, 7:58)
Alternativer Download für iPod & Apple TV (10.9 MB, 7:58)

 

Resourcen:

 

Quellcode:

[ruby]
# rails console
Product.search({}).class
Object.instance_methods
BasicObject.instance_methods

# products_controller.rb
@products = Product.search(params)

# models/product.rb
def self.search(params)
products = scope_builder
products.where("name like ?", "%" + params[:name] + "%") if params[:name]
products.where("price >= ?", params[:price_gt]) if params[:price_gt]
products.where("price <= ?", params[:price_lt]) if params[:price_lt]
products
end

def self.scope_builder
DynamicDelegator.new(scoped)
end

# lib/dynamic_delegator.rb
class DynamicDelegator < BasicObject
def initialize(target)
@target = target
end

def method_missing(*args, &block)
result = @target.send(*args, &block)
@target = result if result.kind_of? @target.class
result
end
end
[/ruby]