Screencast: Daten-Bäume mit Ancestry verwenden

Ancestry ist ein gem über welches man auf einfache Weise Baumstrukturen in Rails (2+3) /Ruby-Applikationen einsetzen kann. Neben allen zu erwartenenden Funktionen wie zum Beispiel für Eltern, Kinder und Nachbar-Elementen, unterstützt Ancestry auch STI (Single Table Inheritance). Auch Funktionen zum Sortieren, Integretäts-Test und Rückwärtskombatibilität sind verfügbar.
Ryan zeigt in dieser Woche wie es in eingesetzt werden kann.

 

Download:

Download(15.7 MB, 9:39)
Alternativer Download für iPod & Apple TV(15.1 MB, 9:39)

 

Resourcen:

 

Quellcode:

[bash]
bundle
rails g migration add_ancestry_to_messages ancestry:string
rake db:migrate
[/bash]

[ruby]
# Gemfile
gem ‚ancestry‘

# migration.rb
class AddAncestryToMessages < ActiveRecord::Migration
def self.up
add_column :messages, :ancestry, :string
add_index :messages, :ancestry
end

def self.down
remove_index :messages, :ancestry
remove_column :messages, :ancestry
end
end

# messages_controller.rb
def new
@message = Message.new(:parent_id => params[:parent_id])
end

# messages_helper.rb
def nested_messages(messages)
messages.map do |message, sub_messages|
render(message) + content_tag(:div, nested_messages(sub_messages), :class => "nested_messages")
end.join.html_safe
end
[/ruby]

[html]
<!– messages/index.html.erb –>
<%= nested_messages @messages.arrange(:order => :created_at) %>

<!– messages/show.html.erb –>
<%= nested_messages @message.subtree.arrange(:order => :created_at) %>

<!– messages/_form.html.erb –>
<%= f.hidden_field :parent_id %>

<!– messages/_message.html.erb –>
<%= link_to message.content, message %>

<%= link_to "Reply", new_message_path(:parent_id => message) %> |
[/html]

[css]
/* application.css */

.nested_messages {
margin-left: 30px;
}

.nested_messages .nested_messages .nested_messages .nested_messages {
margin-left: 0;
}
[/css]

Freies eBook: Ruby on Rails Tutorial

Michael Hartl hat ein freies Ruby on Rails eBook veröffentlicht und stellt es auf seiner Seite zum Download zur Verfügung. Die ersten vier Kapitel sind bereits fertig und weitere sollen in kürze folgen.

Den aktuellen Stand könnt ihr unter http://www.railstutorial.org/book einsehen oder auf der gleichen Seite als PDF runterladen um es offline zu lesen.

Das Buch behandelt momentan Rails 2.3.4 und zusätzlich einen breiten Themenbereich rund um das Framework.

Sobald Rails 3 erscheint soll das Buch aktualisiert werden um den neuesten Stand zu representieren.

Weier Infos:

Hauptseite: http://www.railstutorial.org

Buchseite: http://www.railstutorial.org/book

PDF-Version: http://www.railstutorial.org/book/ruby-on-rails-tutorial-book.pdf

MongoDB: Eine Einführung und Nutzung mit Ruby/Rails

MongoDB ist eine dokumenten-orientierte Datenbank, die als OpenSource-software zur Verfügung steht. Wir haben euch drei Screencasts rausgesucht, in denen ihr diese Datenbank näher kennenlernen und sehen wie ihr es mit Ruby und Rails benutzen könnt.

Die Screencasts stammen von Yoon You und sind auf techmecode.com auch als Downloadversion erhältlich.

Die Download-Versionen sind unter folgenden Links zu finden:


Introduction to MongoDB – Part I

Download(36MB, 13:09)
Alternativer Download für iPod & iPhone (17MB, 13:09)



Introduction to MongoDB – Part II

Download(52.5MB, 20:20)
Alternativer Download für iPod & iPhone (22.3MB, 20:20)



Introduction to MongoDB – Part III, MongoMapper & Rails

Download(21.8MB, 09:29)
Alternativer Download für iPod & iPhone (9.5MB, 09:29)

Script-Tip: Arrays

In unserem heutigen Script-Tip wollen wir euch zeigen wie man Arrays nach zufälliger Reichenfolge mischt, das Element mit dem größten/kleinsten Wert incl Index raussucht und den Durchschnittswert aller Elemente herausfindet.

Als erstes ein Beispiel wie man die Elemente nach zufälliger Reichenfolge mischen kann:

[ruby]
[1,2,3,4,5,6,7,8,9].shuffle
# => [3, 2, 5, 7, 4, 6, 1, 9, 8]

# Bitte beachtet, dass dies erst ab Ruby Version 1.8.7 unterstützt wird.
# Falls ihr noch mit einer älteren Version arbeitet/arbeiten müsst,
# könnt ihr auch auf andere Weise eure Arrays "mischen"

[1,2,3,4,5,6,7,8,9].sort_by{ rand }
# => [4, 7, 9, 6, 1, 5, 3, 8, 2]
[/ruby]

Nun ein Beispiel wie ihr den größten/kleinsten Wert in einem Array inclusive dem Index herausfinden könnt:

[ruby]
# Ruby bringt bereits alles mit um die gesuchten Werte
# zu ermitteln: min und max

# Den größten Wert incl. Index finden…
[7,5,10,9,6,8].each_with_index.max
# => [10, 2]

# Den kleinsten Wert incl. Index finden…
[7,5,10,9,6,8].each_with_index.min
# => [5, 1]
[/ruby]

Als letzter Arry-Script-Tip noch ein paar Beispiele um den Durchschnittswert eines gesamten Arrays herauszufinden:

[ruby]
arr = [9,8,5,2,8,7]
arr.inject(:+).to_f / arr.size # => 6.5

# oder
arr.instance_eval{inject(&:+).to_f / length}

# oder
arr.inject{ |r, x| r + x }.to_f / arr.size

# oder
arr.inject(0.0){ |r, x| r + x } / arr.size

# oder
class Array
def gesamt
inject(0.0) { |erg, el| erg + el }
end

def durchschnitt
gesamt / size
end
end

arr.durchschnitt
[/ruby]

Betriebssystem herausfinden

Hin und wieder ist es erforderlich das Betriebssystem, auf dem die Applikation gerade ausgeführt wird, herauszufinden. Oft wird hierzu die „RUBY_PLATFORM“ dazu benutzt. Wie das folgende Beispiel zeigt, kann dies zu einem „falschen“ Ergenis führen.

RUBY_PLATFORM gibt z.B. bei der Verwendung von JRuby den Wert „java“ zurück. Das spiegelt nicht das Betriebssystem wieder, sondern die verwendete Engine unter der die Applikation läuft.

Ein Beispiel-Modul um das Betriebssystem herauszufinden wäre:

[ruby]
module Betriebssystem
def macintosh?
RUBY_PLATFORM.downcase.include?("darwin")
end

def windows?
RUBY_PLATFORM.downcase.include?("mswin")
end

def linux?
RUBY_PLATFORM.downcase.include?("linux")
end
end
[/ruby]

Um einen verlässlichen Wert zu bekommen, sollte man das Gem „sys-uname“ verwenden. Damit bekommt neben dem Betriebssystem auch viele weitere Informationen über die Umgebung heraus.

Installation von sys-uname:

[bash]
gem install sys-uname
[/bash]

Verwendung von sys-uname:

[ruby]
require ’sys/uname‘
include Sys

puts Uname.nodename
puts Uname.version
puts Uname.sysname
puts Uname.machine
puts Uname.release

p Uname.uname
[/ruby]

Auf diese Weise bekommt alle relevanten Daten die man ggf. benötigt.

Doppelte Einträge filtern

Heute wollen wir euch eine Möglichkeit vorstellen um Datensätze / Arrayelemente von doppelten Einträgen zu befreien.
Um dies zu ermöglichen wird die Array-Klasse um eine weitere Methode erweitert und kann dann in einer beliebigen Ruby/Rails-App benutzt werden.

Dies ist die Erweiterung die benötigt wird.
[ruby]
require ’set‘

class Array
def uniq_by
seen = Set.new
select{ |x| seen.add?( yield( x ) ) }
end
end
[/ruby]

Danach könnt ihr eure Ergebnisse folgendermaßen filtern:

[ruby]
@uniqueposts = Posts.all.uniq_by { |h| h.body }
[/ruby]

Screencast: Pickle mit Cucumber

Neue Woche, neuer Screencast. In dieser Woche behandelt Ryan Pickle, eine Erweiterungs-Gem für Cucumber. Nebenbei gibt es ein bisschen Knowhow zu diffs.

Download(32.5 MB, 16:43)
alternativer download für iPod & Apple TV (21.7 MB, 16:43)

Resourcen:

Quellcode:

[bash]
rails store
sudo rake gems:install RAILS_ENV=test
script/generate cucumber
script/generate pickle
script/generate rspec_model product name:string price:decimal
rake db:migrate
rake db:test:clone
script/generate rspec_controller products show
cucumber features -q
[/bash]

[ruby]
# config/environments/test.rb
config.gem "rspec", :lib => false, :version => ">=1.2.9"
config.gem "rspec-rails", :lib => false, :version => ">=1.2.9"
config.gem "webrat", :lib => false, :version => ">=0.5.3"
config.gem "cucumber", :lib => false, :version => ">=0.4.3"
config.gem "pickle", :lib => false, :version => ">=0.1.21"

# product_steps.rb
Then(/^I should see products table$/) do |expected_table|
html_table = table_at("#products").to_a
html_table.map! { |r| r.map! { |c| c.gsub(/<.+?>/, “) } }
expected_table.diff!(html_table)
end
[/ruby]

[bash]
# display_products.feature
Feature: Display Products
In order to purchase the right product
As a customer
I want to browse products and see detailed information

Scenario: Show product
Given a product exists with name: "Milk", price: "2.99"
When I go to the show page for that product
Then I should see "Milk" within "h1"
And I should see "$2.99"

Scenario: List products
Given the following products exist
| name | price |
| Milk | 2.99 |
| Puzzle | 8.99 |
When I go to path "/products"
Then I should see products table
| Milk | $2.99 |
| Puzzle | $8.99 |
Then show me the page
[/bash]

Screencast: Formtastic Teil 2

In der letzten Woche hat Ryan Bates Formtastic vorgestellt. In seinem neuen Screencasts geht er tiefer in den Funktionaumfang ein und erklärt vorgeschrittene Themen wie: many-to-many Beziehungen, Pflichtfelder und der Gestaltung von Formularen.

Download (19.7 MB, 9:11)
Alternativer Download für iPod & Apple TV(12.6 MB, 9:11)

Resourcen:

Quellcode:

[bash]
script/generate nifty_scaffold problem name:string
rake db:migrate
script/generate nifty_scaffold symptom animal_id:integer problem_id:integer –skip-controller
script/plugin install git://github.com/redinger/validation_reflection.git
[/bash]

[ruby]
# models/animal.rb
class Animal < ActiveRecord::Base
attr_accessible :name, :category_id, :born_on, :female, :problem_ids
belongs_to :category
has_many :symptoms
has_many :problems, :through => :symptoms
validates_presence_of :name, :born_on
end

# models/problem.rb
class Problem < ActiveRecord::Base
attr_accessible :name
has_many :symptoms
has_many :animals, :through => :symptoms
end

# models/symptom.rb
class Symptom < ActiveRecord::Base
attr_accessible :animal_id, :problem_id
belongs_to :animial
belongs_to :problem
end

# config/initializers/formtastic_config.rb
Formtastic::SemanticFormBuilder.inline_errors = :none
[/ruby]

[javascript]
/* formtastic_changes.css */
form.formtastic fieldset ol li p.inline-hints {
font-style: italic;
font-size: 11px;
}
[/javascript]

[html]

<!– views/animals/_form.html.erb –>
<% semantic_form_for @animal do |f| %>
<%= f.error_messages %>
<% f.inputs do %>
<%= f.input :name, :hint => "Use the owner’s name if none is provided" %>
<%= f.input :born_on, :start_year => 1900 %>
<%= f.input :category, :include_blank => false %>
<%= f.input :female, :as => :radio, :label => "Gender", :collection => [["Male", false], ["Female", true]] %>
<%= f.input :problems, :as => :check_boxes, :required => false %>
<% end %>
<%= f.buttons %>
<% end %>
[/html]

Screencast: Formtastic Teil 1

Formtastic, ein Hilfmittel um die Arbeit mit Views zu vereinfachen, ist das heutige Thema in dem Screencst von Ryan Bates.

Download(19.7 MB, 10:41)
Alternativer Download für iPod & Apple TV (12.7 MB, 10:41)

Resourcen:

[bash]
rails vet
sudo rake gems:install
script/generate nifty_layout
script/generate nifty_scaffold category name:string description:text
rake db:migrate
script/generate formtastic_stylesheets
script/generate nifty_scaffold animal name:string category_id:integer born_on:date female:boolean
[/bash]

[html]
<!– application.html.erb –>
<%= stylesheet_link_tag ‚application‘, ‚formtastic‘, ‚formtastic_changes‘, :cache => "base" %>

<!– views/animals/_form.html.erb –>
<% semantic_form_for @category do |f| %>
<%= f.inputs %>
<%= f.buttons %>
<% end %>

<!– views/animals/_form.html.erb –>
<% semantic_form_for @animal do |f| %>
<% f.inputs do %>
<%= f.input :name %>
<%= f.input :born_on, :start_year => 1900 %>
<%= f.input :category, :include_blank => false %>
<%= f.input :female, :as => :radio, :label => "Gender", :collection => [["Male", false], ["Female", true]] %>
<% end %>
<%= f.buttons %>
<% end %>
[/html]

Screencast: Bilder ausschneiden

In seinem heutigen Screencast zeigt Ryan Bates wie man mit Jcrop und Paperclip eine Applikation bauen kann, mit der Benutzern die Möglichkeit gegeben werden kann Bilder zu beschneiden.

Download:
Download(46.3 MB, 14:07)
Alternativer Download für iPod & Apple TV (22.4 MB, 14:07)

Resourcen:

Quellcode:

[ruby]

# models/user.rb
class User < ActiveRecord::Base
has_attached_file :avatar, :styles => { :small => "100×100#", :large => "500×500>" }, :processors => [:cropper]
attr_accessor :crop_x, :crop_y, :crop_w, :crop_h
after_update :reprocess_avatar, :if => :cropping?

def cropping?
!crop_x.blank? && !crop_y.blank? && !crop_w.blank? && !crop_h.blank?
end

def avatar_geometry(style = :original)
@geometry ||= {}
@geometry[style] ||= Paperclip::Geometry.from_file(avatar.path(style))
end

private

def reprocess_avatar
avatar.reprocess!
end
end

# users_controller.rb
def create
@user = User.new(params[:user])
if @user.save
if params[:user][:avatar].blank?
flash[:notice] = "Successfully created user."
redirect_to @user
else
render :action => "crop"
end
else
render :action => ’new‘
end
end

def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
if params[:user][:avatar].blank?
flash[:notice] = "Successfully updated user."
redirect_to @user
else
render :action => "crop"
end
else
render :action => ‚edit‘
end
end

# lib/paperclip_processors/cropper.rb
module Paperclip
class Cropper < Thumbnail
def transformation_command
if crop_command
crop_command + super.sub(/ -crop S+/, “)
else
super
end
end

def crop_command
target = @attachment.instance
if target.cropping?
" -crop ‚#{target.crop_w.to_i}x#{target.crop_h.to_i}+#{target.crop_x.to_i}+#{target.crop_y.to_i}’"
end
end
end
end
[/ruby]

[html]
<!– views/users/crop.html.erb –>
<% title "Crop avatar" %>
<% content_for(:head) do %>
<%= stylesheet_link_tag "jquery.Jcrop" %>
<%= javascript_include_tag "jquery.Jcrop.min" %>
<script type="text/javascript" charset="utf-8">
$(function() {
$(‚#cropbox‘).Jcrop({
onChange: update_crop,
onSelect: update_crop,
setSelect: [0, 0, 500, 500],
aspectRatio: 1
});
});

function update_crop(coords) {
var rx = 100/coords.w;
var ry = 100/coords.h;
$(‚#preview‘).css({
width: Math.round(rx * <%= @user.avatar_geometry(:large).width %>) + ‚px‘,
height: Math.round(ry * <%= @user.avatar_geometry(:large).height %>) + ‚px‘,
marginLeft: ‚-‚ + Math.round(rx * coords.x) + ‚px‘,
marginTop: ‚-‚ + Math.round(ry * coords.y) + ‚px‘
});
var ratio = <%= @user.avatar_geometry(:original).width %> / <%= @user.avatar_geometry(:large).width %>;
$("#crop_x").val(Math.round(coords.x * ratio));
$("#crop_y").val(Math.round(coords.y * ratio));
$("#crop_w").val(Math.round(coords.w * ratio));
$("#crop_h").val(Math.round(coords.h * ratio));
}
</script>
<% end %>

<%= image_tag @user.avatar.url(:large), :id => "cropbox" %>

<h4>Preview:</h4>
<div style="width:100px; height:100px; overflow:hidden">
<%= image_tag @user.avatar.url(:large), :id => "preview" %>
</div>

<% form_for @user do |f| %>
<% for attribute in [:crop_x, :crop_y, :crop_w, :crop_h] %>
<%= f.hidden_field attribute, :id => attribute %>
<% end %>
<p><%= f.submit "Crop" %></p>
<% end %>
[/html]