Linksammlung KW 06+07 2011

posted: February 18th, 2011

Und schon wieder Freitag… keine Zeit… gespeicherte Links der letzten 2 Wochen raussuchen, Blogeintrag schreiben und schnell weiter an sovido arbeiten.

Ruby + Rails

Webdev

Sonstiges

Code Schnipsel

toggle details in 2nd table row
%table#videos
    %thead
        %tr
            %th Heading 1
            %th Heading 2
    %tbody
        - @videos.each do |video|
            %tr.video{:id => "video-#{video.id}"}
                %td Info 1
                %td Info 2
            %tr.video-details{:id => "video-details-#{video.id}"}
                %td{:colspan => 2}
                    %p= video.to_yaml

:css
    .video-details { display:none; }
    .video-details td { padding: 0 1em; }
    .hover { background: #ddd; }

:javascript
    $(document).ready(function(){
        $(".video").hover(function () {
            $(this).css("cursor", "pointer");
            $(this).addClass("hover");
        }, function () {
            $(this).removeClass("hover");
        });
        $(".video").click(function(event){
            $(this).next("tr").toggle();
        });
    });    

Awesome Feature Suggest 1

posted: February 15th, 2011

Ich habe mich am Sonntag mit einem Artikel von der Seite Tutorialzine befasstet. Das Tutorial beschreibt den Aufbau einer simplen Feature Request App mit PHP, MySQL und jQuery.

HIER geht´s direkt zur app

Da ich noch nicht direkt ein Projekt mit Rails 3 entwickeln konnte, habe ich mich entschlossen den Ansatz von Tutorialzine aufzunehmen und eine kleine Rails 3 app zu schreiben, nur BESSER!

Du brauchst für mein Tutorial RVM!

    rails new suggest
    echo "rvm ruby-1.9.2@suggest --create" > suggest/.rvmrc
    cd suggest

    ================================================================
    = Trusting an .rvmrc file means that whenever you cd into the  =
    = directory RVM will excecute this .rvmrc script in your shell =
    =                                                              =
    = Now that you have examined the contents of the file, do you  =
    = wish to trust this .rvmrc from now on?                       =
    ================================================================

    (yes or no) > yes

Wir werden aufgefordert die angelegte .rvmrc zu vertrauen, was natürlich der Fall ist, denn wir wollen ein seperates gemset für unsere neue app anlegen.

Für unser SCM nutzen wir natürlich git. Ich persönlich verfolge den Ansatz von git-flow und initialisiere somit meinen branches mit:

    git flow init

    No branches exist yet. Base branches must be created now.
    Branch name for production releases: [master] 
    Branch name for "next release" development: [develop] 

    How to name your supporting branch prefixes?
    Feature branches? [feature/] 
    Release branches? [release/] 
    Hotfix branches? [hotfix/] 
    Support branches? [support/] 
    Version tag prefix? []

Sofern nicht anders benötigt, einfach ENTER drücken ;) Jetzt checken in welchen branch wir uns befinden:

    git branch

    * develop
      master

Wir sehen, dass wir uns jetzt im develop branch befinden.

    git add .
    git commit -a -m "initial commit"
    bundle

Zunächst den initial commit ausführen und als nächstes bundeler laufen lassen, damit wir in unserem frischen gemset auch alle benötigten gems haben.

Okay, das Projekt ist soweit um Modelle anzulegen. Fangen wir mit den suggestions an.

    rails generate model suggestion suggestion:string votes_up:integer votes_down:integer rating:integer
    rake db:migrate
    git add .
    git commit -a -m "adding suggestion model"

Füllen wir das model app/models/suggestion.rb mit Leben:

class Suggestion < ActiveRecord::Base
  before_create :set_rating
  
  validates :suggestion, :presence => true
  validates :suggestion, :uniqueness => true
  
  def vote_up
    self.update_attributes({
     :votes_up => self.votes_up.to_i+1,
     :rating => self.rating.to_i+1
    })
  end
  
  def vote_down
    self.update_attributes({
     :votes_down => self.votes_down.to_i+1,
     :rating => self.rating.to_i-1
    })
  end
  
  private
    def set_rating
      self.rating, self.votes_up, self.votes_down = 0, 0, 0
    end
end

Die beiden Methoden vote_up und vote_down erleichtern später die Auswertung. Mit der Methode set_rating initialisiere ich explizit vor dem Erstellen des Models die Wertungsattribute. Zusätzlich spendiere ich dem model die Validierung auf Vorhandensein und Einmaligkeit des Attributs suggestion.

Das sind schon viele Kleinigkeiten, die wir dem Model auftragen zu beherzigen, aber passt das auch? Um z.B. die Validierungen zu testen, könnten wir folgenden Unit Test schreiben:

require 'test_helper'

class SuggestionTest < ActiveSupport::TestCase
  fixtures :suggestions
  
  test "suggestion attributes must not be empty" do
    suggestion = Suggestion.new
    assert suggestion.invalid?
    assert suggestion.errors[:suggestion].any?
  end
  
  test "suggestion is not valid without a unique suggestion" do
    suggestion = Suggestion.new(:suggestion  => suggestions(:rails).suggestion)
    assert !suggestion.save
    assert_equal "has already been taken", suggestion.errors[:suggestion].join('; ')
  end
  
end
rails:
  suggestion: Create a Ruby on Rails Tutorial
  votes_up: 0
  votes_down: 0
  rating: 0

blog:
  suggestion: Add new blog entry
  votes_up: 0
  votes_down: 0
  rating: 0

Und was sagt unser Unit Test dazu?

    rake test:units

    Started
    ..
    Finished in 0.194499 seconds.

    2 tests, 4 assertions, 0 failures, 0 errors, 0 skips

Sauber, 2 tests, 4 assertions und keine Fehler. So kann das weitergehen. Also, Quellcode commiten und einen Controller für unser Model mit einer index action anlegen. Außerdem arbeite ich gerne mit haml und jquery, daher packe ich sie in das gemfile und starte bundler. Danach hole ich mir jQuery in das Projekt (rails.js muss überschrieben werden) und “verschiebe” das application .erb layout nach .haml.

    git commit -a -m "validating unique not empty suggestion"
    rails g controller suggestions index
    echo "gem 'haml'" >> Gemfile
    echo "gem 'jquery-rails', '>= 0.2.6'" >> Gemfile
    bundle
    rails generate jquery:install --ui
    mv app/views/layouts/application.html.erb app/views/layouts/application.html.haml

Hier nun der Inhalt für das application haml layout:

!!! 5
%html
    %head
        %meta{'http-equiv' => 'Content-Type', :content => 'text/html; charset=utf-8'}/
        %title Suggest
        = stylesheet_link_tag :all
        = javascript_include_tag :defaults
        = csrf_meta_tag
    
    %body
        #page
            #heading.rounded
                %h1
                    Feature Suggest
                    %i for Tutorialzine.com
                
            = yield

Der Inhalt vom Controller app/controllers/suggestions_controller.rb :

class SuggestionsController < ApplicationController
  before_filter :load_suggestions
  
  def index
    @suggestion = Suggestion.new
  end
  
  def create
    @suggestion = Suggestion.new(params[:suggestion])

    respond_to do |wants|
      if @suggestion.save
        wants.html { redirect_to root_path }
      else
        wants.html { render :action => "index" }
      end
    end
  end
  
  private
  def load_suggestions
    @suggestions = Suggestion.order("rating DESC")
  end
  
end
    rm public/index.html
    mv app/views/suggestions/index.html.erb app/views/suggestions/index.html.haml
    touch public/stylesheets/styles.css

Das CSS und die Bilder habe ich mir bei dem Artikel von tutorialzine.com ausgeliehen. Das Css kommt in die public/stylesheets/styles.css, die Bilder in den Ordner public/images/

Der Inhalt der routes.rb :

Suggest::Application.routes.draw do
  resources :suggestions
  root :to => "suggestions#index"
end

Der Inhalt vom index View app/views/suggestions/index.haml :

%ul.suggestions
    - @suggestions.each do |suggestion|
        = render :partial => 'suggestion', :locals => {:suggestion => suggestion}
        
= form_for @suggestion, :as => :suggestion, :url => suggestions_path, :html => { :id => "suggest" } do |f|
    %p
        = f.text_field :suggestion, :id => "suggestionText", :class => "rounded"
        = f.submit "Submit", :disable_with => 'Submiting...', :id => "submitSuggestion"

Die Aktivität/Inaktivität der suggestion definiere ich vorerst aus Der Inhalt vom Partial suggestion app/views/suggestions/_suggestion.haml :

%li{:id => "s_#{suggestion.id}"}
    %div{:class => "vote #{inactive ? 'inactive' : 'active'}"}
        %span.up
        %span.down
    .text= suggestion.suggestion
    .rating= suggestion.rating

Als nächstes können wir funktionalen Test erstellen um den Controller zu überprüfen:

require 'test_helper'

class SuggestionsControllerTest < ActionController::TestCase
  test "should get index" do
    get :index
    assert_response :success
    assert_select "#page ul.suggestions li", 2
    assert_tag "form", :attributes => {:action => "/suggestions", :method => "post" }
  end
  
  test "should create suggestion" do 
    assert_difference('Suggestion.count', 1) do
      post :create, :suggestion => {:suggestion => "Create functional tests"}
    end
    assert_redirected_to root_path
  end
  
  test "should not create suggestion" do
    assert_difference('Suggestion.count', 0) do
      post :create, :suggestion => {:suggestion => "Create a Ruby on Rails Tutorial"}
    end
    assert_template :index
  end
  
end
    rake test:functionals

    Started
    ...
    Finished in 0.316923 seconds.

    3 tests, 7 assertions, 0 failures, 0 errors, 0 skips

Okay, wir können sicher sein, dass unser Controller macht, was er tun soll.

Bevor ich hier die 300 LOC Grenze für einen Blogeintrag überschreite, werde ich den suggestion_votes und der meiner awsome Variante des Feature Requests jeweils einen eigenen Blogeintrag widmen.

Linksammlung KW05 2011

posted: February 7th, 2011

Rails

google reader rails posts Seit einigen Wochen habe ich mich nicht getraut den folder “rubyonrails” in meinem google reader abzuarbeiten. Das hat dazu geführt, dass sich dort so Einges angesammelt hat. Nun der Versuch dem Haufen Herr zu werden. Dies sind die Ergebnisse meiner Aufräumaktion:

jQuery/JavaScript

Linux

  • PirateBox
    Das Prinzip der PirateBox ist es überall und jederzeit einen freien und offenen Dateiaustausch zu ermöglichen. Würde mich nicht wundern, wenn auf den nächsten Camps solche Piratenboxen rumstehen ;)
  • ROCKIGER OS 10.04
    Ich finde die Idee gut und werde demnächst meinen alten Laptop mit Rockiger OS ausstatten, mal gucken, ob es hält, was es verspricht…

Mac

  • AppCleaner
    Kennt bestimmt schon jeder. War mir neu und ist eine ganz klar Empfehlung für jeden Mac User.
  • LiveView for iPhone & iPad
    Wie sieht eigentlich mein Design auf dem iPad oder iPhone aus? Mit dieser Kombination aus iPhone/iPad App und OSX Programm kann der aktuelle Bildschirmausschnitt auf das iDevice angeschaut werden.
  • Syncing Socialite across multiple Macs (using Dropbox)
    Cooles Tutorial von Dennis um seine Programme zwischen mehreren Rechnern zu syncen, wenn die Apps dieses von Haus aus nicht können/wollen.

Freelancer Tips

Hier einige Tipps wie man sein Leben führen sollte ;) Einige coole Ideen kann man schon mitnehmen und für sich anwenden.

Sonstiges

Diese beiden Sachen haben keine Kategorie gefunden, sollten aber erwähnt werden

Linksammlung KW03 2011

posted: January 21st, 2011

Linksammlung KW02 2011

posted: January 14th, 2011

Rails

Hier einige Projekte die interessant sein könnten.

RoR E - rails for ecommerce

Leider hab ich das Projekt noch nicht testen können. Klingt aber Vielversprechend, hat das schon jemand Erfahrung mit?

Gmail

Einige Tipps zu Gmail sofern noch nicht bekannt.

Sonstiges

WikiLeaks – Rebellen im Netz from netzpolitik on Vimeo.

Magento Ubuntu Nginx Sample Installation

posted: January 11th, 2011

PHP und nginx

PHP5 und nginx installieren:
sudo aptitude install php5-cgi nginx

Erestelle ein PHP5 FastCGI start-up script mit
sudo nano /etc/init.d/php-fastcgi

Folgenden Inhalt einfügen:

#!/bin/bash
BIND=127.0.0.1:9000
USER=www-data
PHP_FCGI_CHILDREN=15
PHP_FCGI_MAX_REQUESTS=1000

PHP_CGI=/usr/bin/php-cgi
PHP_CGI_NAME=`basename $PHP_CGI`
PHP_CGI_ARGS="- USER=$USER PATH=/usr/bin PHP_FCGI_CHILDREN=$PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS=$PHP_FCGI_MAX_REQUESTS $PHP_CGI -b $BIND"
RETVAL=0

start() {
      echo -n "Starting PHP FastCGI: "
      start-stop-daemon --quiet --start --background --chuid "$USER" --exec /usr/bin/env -- $PHP_CGI_ARGS
      RETVAL=$?
      echo "$PHP_CGI_NAME."
}
stop() {
      echo -n "Stopping PHP FastCGI: "
      killall -q -w -u $USER $PHP_CGI
      RETVAL=$?
      echo "$PHP_CGI_NAME."
}

case "$1" in
    start)
      start
  ;;
    stop)
      stop
  ;;
    restart)
      stop
      start
  ;;
    *)
      echo "Usage: php-fastcgi {start|stop|restart}"
      exit 1
  ;;
esac
exit $RETVAL

Das Startscript ausführbar machen:
sudo chmod +x /etc/init.d/php-fastcgi

PHP starten:
sudo /etc/init.d/php-fastcgi start

(Optional) Beim Booten ausführen:
sudo update-rc.d php-fastcgi defaults

Testen

Trage in der nginx server config folgendes ein:

    location ~ \.php$ {
        fastcgi_pass    127.0.0.1:9000;
        fastcgi_index   index.php;
        fastcgi_param   SCRIPT_FILENAME /var/www/nginx-default$fastcgi_script_name;
        include         fastcgi_params;
    }

nginx neu starten:
sudo /etc/init.d/nginx restart

Erstelle eine Datei in deinem Web-Verzeichnis:

        <?php
          phpinfo();
        ?>

Die erstellte Seite im Browser aufrufen. Nun sollte die PHP Info Seite erscheinen.

Magento installieren:

PHP Pakete für Magento installieren:
sudo apt-get install php5-mcrypt php5-curl php5-gd

Limitierung von Ressourcen. Maximalwert des Speichers erhöhen.
sudo nano /etc/php5/cgi/php.ini
memory_limit = 64M;

Magento aus dem SVN importieren:

        sudo apt-get install subversion 
        cd /var/www
        svn checkout http://svn.magentocommerce.com/source/branches/1.4
        sudo mv 1.4 magento 
        sudo chown -R www-data magento

Magento Datenbank anlegen und Beispieldaten importieren:

        wget http://www.magentocommerce.com/downloads/assets/1.2.0/magento-sample-data-1.2.0.tar.gz
        tar xvfz magento-sample-data-1.2.0.tar.gz
        cd magento-sample-data-1.2.0
        
        mysqladmin -u root -p create magento
        mysql -u root -p magento < magento_sample_data_for_1.2.0.sql
        mv media/* /var/www/magento/media/

Server Config anpassen:

user              nginx;
worker_processes  1;
error_log         /var/log/nginx/error.log;
pid               /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request "'
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    autoindex off;
    map $scheme $fastcgi_https { ## Detect when HTTPS is used
        default off;
        https on;
    }

    keepalive_timeout  10;

    gzip  on;
    gzip_comp_level 2;
    gzip_proxied any;
    gzip_types      text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;

    # Load config files from the /etc/nginx/conf.d directory
    include /etc/nginx/conf.d/*.conf;

}

Magento für nginx einrichten:

server {
    listen 80 default;
    server_name www.DOMAIN.com *.DOMAIN.com; ## Domain is here twice so server_name_in_redirect will favour the www
    root /var/www/vhosts/DOMAIN.com;
    
    location / {
        index index.html index.php; ## Allow a static html file to be shown first
        try_files $uri $uri/ @handler; ## If missing pass the URI to Magento's front handler
        expires 30d; ## Assume all files are cachable
    }
    location /minify/ { ## Needed for Fooman Speedster
        rewrite ^/minify/([0-9]+)(/.*\.(js|css))$ /lib/minify/m.php?f=$2&d;=$1 last;
    }
    
    ## These locations would be hidden by .htaccess normally
    location /app/                { deny all; }
    location /includes/           { deny all; }
    location /lib/                { deny all; }
    location /lib/minify/         { allow all; }  ## Deny is applied after rewrites so must specifically allow minify
    location /media/downloadable/ { deny all; }
    location /pkginfo/            { deny all; }
    location /report/config.xml   { deny all; }
    location /var/                { deny all; }
    
    location /var/export/ { ## Allow admins only to view export folder
        auth_basic           "Restricted"; ## Message shown in login window
        auth_basic_user_file htpasswd; ## See /etc/nginx/htpassword
        autoindex            on;
    }
    
    location  /. { ## Disable .htaccess and other hidden files
        return 404;
    }
    
    location @handler { ## Magento uses a common front handler
        rewrite / /index.php;
    }
    
    location ~ \.php/ { ## Forward paths like /js/index.php/x.js to relevant handler
        rewrite ^(.*\.php)/ $1 last;
    }
    
    location ~ \.php$ { ## Execute PHP scripts
        expires        off; ## Do not cache dynamic content
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_param  HTTPS $fastcgi_https;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params; ## See /etc/nginx/fastcgi_params
    }
}

Quellen:

Linksammlung KW50 2010

posted: December 17th, 2010

Die letzen 2 Wochen musste meine Linksammlung ausfallen, da ich mich an türkisblaue Lagunen mit weißen Sand entspannt habe, siehe letze Posts. Ich werde meine Tagestouren in Bangkok und die Erlebnisse auf Phuket und Ko Phi Phi in den demnächst nachholen, aber nun die Linksammlung dieser Woche mit kleinen Einschränkungen, da viel zutun war.

  • Turnkey administration interface for your Rails apps RailsAdmin bietet ein funktionales Admin-Backend für deine Rails 3 Applikation. Der Blog-Post beschreibt die Nutzung von RailsAdmin mit CanCan von Ryan Bates und einem simplen Rollensystem (is_admin im User-Model). Ich werde RailsAdmin mal in einem kleinen Projekt testen und meine Erfahrungen hier posten.

  • Another one nginx optimal configuration to use with passenger 3.0 Das gist beschreibt die Installation und Einrichtung von Nginx mit Passanger + SSL Redirect + http headers + passenger settings. Wer mal Nginx mit SSL eingerichtet hat und sich durch die z.T. russische Doku gekämpft hat, findet hier recht übersichtliche Konfigurationsdateien. Das ein oder andere kann man sicherlich übernehmen. Wer dem Russischen mächtig ist, darf mir gerne die Details erklären. Google geht aber auch schon recht gut…

  • RubyDrop Nachdem DropBox nun endlich 1.0 geworden ist, finde ich diese Projekt auch spannend. RubyDrop ist ein Dropbox Clone mit git als Backend zum Tracken und Synchronisieren. Nachdem ein Remote Git Repository eingerichtet ist, wird in einem definierten Abstand der ausgewählte Ordner mit dem Remote Repository synchronisiert. Weitere Infos entnehmt ihr bitte aus diesem Blog-Post

  • Ruby on Rails and jQuery: multiselect with checkbox Dieser Post beschreibt die Nutzung von jQuery für eine has_many Relation mit Multi-Select Checkboxen. Kann man immer mal gebrauchen…

  • Titan kann genutzt werden um Hintergrundprozesse zu erzeugen und zu verwalten.

  • Virtualize This – Instant Rails in a Virtual Box Wie ich bereites in Linksammlung KW46 2010 erwähnt habe, wird das Vagrant Projekt von EngineYard supported. In dem Post wird die Installation und Nutzung von Vagrant vorgestellt. Ziel ist es dem Nutzer einen Ubuntu Server mit komplette RoR Stack zu liefern.

Phuket

posted: December 5th, 2010
  • Patong
  • Kamala Beach
  • Ko Phi Phi

Bangkok Tag 7

posted: December 4th, 2010
  • Fische füttern für Charma
  • TukTuk fahren
  • Dusit-Palast
  • Chatuchak Markt
  • MBK/Siam Central
  • Chatuchak Markt Feuerwerk

Bangkok Tag 6

posted: December 3rd, 2010
  • Ko Kret
  • Chatuchak Markt
  • Cabadges & Condoms
Fork me on GitHub