Screencast: ActiveRecord Anfragen in Rails 3

Rails 3 führt ein paar Neuerungen in der Abfrage-Schnittstelle ein. Durch diese Neuerungen wird ActiveRecord schneller und einfacher zu benutzen sein. Ryan zeigt diese Woche wie die neuen Funktionen (incl. named scopes) genutzt werden können.

 

Download:

Download (17.8 MB, 10:43)
Alternativer Download für iPod & Apple TV (13.7 MB, 10:43)

 

Resourcen:

 

Quellcode:

[ruby]
# Article.find( :all, :order => "published_at desc", :limit => 10)
Article.order("published_at desc").limit(10)

# Article.find(:all, :conditions => ["published_at <= ?", Time.now], :include => :comments)
Article.where("published_at <= ?", Time.now).includes(:comments)

# Article.find(:first, :order => "published_at desc")
Article.order("published_at").last

# rails console
Article.all
articles = Article.order("name")
articles.all
articles.first
Article.recent.all
puts Article.recent.to_sql

# articles_controller.rb
@articles = Article.order("name")

if params[:hidden]
@articles = @articles.where(:hidden => (params[:hidden] == "1"))
end

# models/active_record.rb
scope :visible, where("hidden != ?", true)
scope :published, lambda { where("published_at <= ?", Time.zone.now) }
scope :recent, visible.published.order("published_at desc")
[/ruby]

Artikel zu Rails 3

Rails 3 steht vor der Tür und wird jede Menge Neuerungen mitbringen. Maxim C. hat eine Liste mit Links von interessanten Artikeln und Anleitungen zusammengestellt um sich mit dem kommenden Release anzufreunden. Weitere Vorschläge und Empfehlungen sind willkommen.

Die Liste ist in die verschiedenen Bereiche des Frameworks aufgeteilt und chronologisch sortiert.

Router

ActiveRecord

Controller

Generatoren

Rails 3 Applikationen laufen lassen

Auf Rails 3 aktualisieren

Plugin/Gem Kompatiblität

Architektur

Screencast: Formulare mit verschachtelten Modellen

Fomulare mit verschachtelten Modellen lassen sich viel einfacher mit der Methode accepts_nested_attributes erstellen. Wie diese Methode benutzt werden kann, zeigt Ryan in seinem dieswöchigen Screencast.

Download:

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

 

Resourcen:

Quellcode:

[bash]
rails surveysays
script/generate nifty_layout
script/generate nifty_scaffold survey name:string
script/generate model question survey_id:integer content:text
script/generate model answer question_id:integer content:string
rake db:migrate
[/bash]

[ruby]
# models/survey.rb
class Survey < ActiveRecord::Base
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end

# models/question.rb
class Question < ActiveRecord::Base
belongs_to :survey
has_many :answers, :dependent => :destroy
accepts_nested_attributes_for :answers, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end

# models/answer.rb
class Answer < ActiveRecord::Base
belongs_to :question
end

# surveys_controller.rb
def new
@survey = Survey.new
3.times do
question = @survey.questions.build
4.times { question.answers.build }
end
end
[/ruby]

[html]
<!– views/surveys/_form.html.erb –>
<% form_for @survey do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<% f.fields_for :questions do |builder| %>
<%= render "question_fields", :f => builder %>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>

<!– views/surveys/_question_fields.html.erb –>
<p>
<%= f.label :content, "Question" %><br />
<%= f.text_area :content, :rows => 3 %><br />
<%= f.check_box :_destroy %>
<%= f.label :_destroy, "Remove Question" %>
</p>
<% f.fields_for :answers do |builder| %>
<%= render ‚answer_fields‘, :f => builder %>
<% end %>

<!– views/surveys/_answer_fields.html.erb –>
<p>
<%= f.label :content, "Answer" %>
<%= f.text_field :content %>
<%= f.check_box :_destroy %>
<%= f.label :_destroy, "Remove" %>
</p>
[/html]

Screencast: Model ohne DB-Tabelle

Ein Model muss nicht zwingend eine Datenbank Tabelle referenzieren. Um dies umzusetzen müssen lediglich ein paar Methoden angelegt werden. Welche das sind zeigt Ryan Bates in seinem dieswöchigen Screencast.

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

Resourcen:

Quellcode:

[bash]
script/generate nifty_scaffold recommendation from_email:string to_email:string article_id:integer message:text new create
rake db:migrate
rake db:rollback
rm db/migrate/*_recommendations.rb
[/bash]

[ruby]
# models/recommendation.rb
class Recommendation < ActiveRecord::Base
def self.columns() @columns ||= []; end

def self.column(name, sql_type = nil, default = nil, null = true)
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
end

column :from_email, :string
column :to_email, :string
column :article_id, :integer
column :message, :text

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

belongs_to :article
end

# recommendations_controller.rb
def new
@recommendation = Recommendation.new(:article_id => params[:article_id])
end

def create
@recommendation = Recommendation.new(params[:recommendation])
if @recommendation.valid?
# send email
flash[:notice] = "Successfully created recommendation."
redirect_to root_url
else
render :action => ’new‘
end
end
[/ruby]

Screencast: Embedded Association

Diese Woche zeigt Ryan Bates in seinem Screencast wie selbstreferenzierende DB-Beziehungen (one-to-many, many-to-many) umgesetzt werden können.

Download (18.7 MB, 14:03)
Alternativer Download für iPod & Apple TV (15.4 MB, 14:03)

Ressourcen:

Quellcode:

One to many:

[bash]
script/generate migration add_role_to_users role:string
rake db:migrate
[/bash]

[ruby]
# models/user.rb
class User < ActiveRecord::Base
acts_as_authentic
has_many :articles
has_many :comments

ROLES = %w[admin moderator author]

def role_symbols
[role.to_sym]
end
end
[/ruby]

[ror]
<!– users/new.html.erb –>
<p>
<%= f.label :role %><br />
<%= f.collection_select :role, User::ROLES, :to_s, :titleize %>
</p>
[/ror]

Many to many

[bash]
script/generate migration add_roles_mask_to_users roles_mask:integer
rake db:migrate
[/bash]

[ruby]
# models/user.rb
class User < ActiveRecord::Base
acts_as_authentic
has_many :articles
has_many :comments

named_scope :with_role, lambda { |role| {:conditions => "roles_mask & #{2**ROLES.index(role.to_s)} > 0"} }

ROLES = %w[admin moderator author]

def roles=(roles)
self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.sum
end

def roles
ROLES.reject { |r| ((roles_mask || 0) & 2**ROLES.index(r)).zero? }
end

def role_symbols
roles.map(&:to_sym)
end
end
[/ruby]

[ror]
<!– users/new.html.erb –>
<p>
<%= f.label :roles %><br />
<% for role in User::ROLES %>
<%= check_box_tag "user[roles][]", role, @user.roles.include?(role) %>
<%=h role.humanize %><br />
<% end %>
<%= hidden_field_tag "user[roles][]", "" %>
</p>
[/ror]

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: 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: Model Versioning

Ryan Bates hat diese Woche einen weiteren Screencast veröffentlicht. In dieser Version behandelt er das Thema Model Versionierung. Für die Versionierung der Models setzt er das gem vistal_versions ein und zeigt wie es verwendet wird.

Downloadlinks:

Download (14.4 MB, 9:28)

Alternativer Download für iPod & Apple TV (10.4 MB, 9:28)

Weitere Resourcen zu diesem Screencast:

Verwendung in der Konsole

[bash]
sudo rake gems:install
script/generate vestal_versions_migration
script/generate migration version_existing_pages
rake db:migrate
[/bash]

Quellcode:

[ruby]
# config/environment.rb
config.gem ‚laserlemon-vestal_versions‘, :lib => ‚vestal_versions‘, :source => ‚http://gems.github.com‘

# models/page.rb
class Page < ActiveRecord::Base
versioned
end

# version_existing_pages migration
say_with_time "Setting initial version for pages" do
Page.find_each(&:touch)
end

# pages_controller.rb
def show
@page = Page.find(params[:id])
@page.revert_to(params[:version].to_i) if params[:version]
end

# script/console
p = Page.all
p.versions
p.revert_to(7.minutes.ago)
p.content
p.revert_to(:last)

[/ruby]

[xhtml]
<p>
<%= link_to "Edit", edit_page_path(@page) %>
| Version <%= @page.version %>
<% if @page.version > 1 %>
| <%= link_to "Previous version", :version => @page.version-1 %>
<% end %>
<% if params[:version] %>
| <%= link_to "Latest version", :version => nil %>
<% end %>
</p>
[/xhtml]