Browsing Posts published by Robert Heim

TL;DR:
Generate Getters and Setters in 2 Seconds:
Think: “generate getters and setters” et voilà here they are!
Do: Shift + Alt + S -> R -> TAB -> ENTER -> Shift+TAB -> ENTER

Hi, I hate using the mouse while thinking and programming in Eclipse. It’s a common task to implement a simple POJO with Getters and Setters for all fields and you all probably now the possibility to do it like that:

Right-Click -> Source -> Generate Getters and Setters… Than you need to select all fields or hit the “Select All” button and click OK.

This is nice but you need to move your hand to the mouse, then it takes at least 4 Clicks and you need to navigate through 2 levels of the context-menu. This takes me around 4-5 Seconds (unless I mess up some mouse-movement..) and it disrups my thinking-process. I need getters and setters as fast as possible without thinking!

Here is what I do – without touching the mouse!

By default the short-cut Shift + Alt + S in Windows shows the source quick menu. Then pressing R opens the Generate Getters and Setters menu. You can rebind it in Windw->Propertiers > General > Keys. Many people use Shift + Alt + G as a short cut, but I will go with the default one.

After opening the Generator you can select all fields by hitting: TAB -> ENTER.

Then to close and actually generate the methods you need to press OK. However the focus is still on the “Select All” Button. But by hitting Shift +TAB the focus is back to the list, and hitting ENTER on the list actually is the same as pressing the OK button. So here you go:

Shift + Alt + S -> R -> TAB -> ENTER -> Shift+TAB -> ENTER

Just do it like 5-10 times for training right away.

Now, this takes me less than 2 seconds which does not disrupt my thinking process: It’s like thinking “generate getters and setters” et voilà here they are!

Hi!

Do you know the Transition3d + Rotate3dAnimation from the APIDemos?

I wondered how one could use this animation effect for finishing and starting Activities. Well, this is my solution to it.

*UPDATE 05.12.2011* Working example: ActivitySwitcher

  1. Create a package com.yourapp.animation and copy the Rotate3dAnimation.java in it.
  2. Next, create a new Class yourapp.animation.ActivitySwitcher and implement it like this:

    com.yourapp.animation.ActivitySwitcher.java

    package com.yourapp.animation;
     
    import android.view.Display;
    import android.view.View;
    import android.view.WindowManager;
    import android.view.animation.AccelerateInterpolator;
    import android.view.animation.Animation;
     
    public class ActivitySwitcher {
     
    	private final static int DURATION = 300;
    	private final static float DEPTH = 400.0f;
     
    	/* ----------------------------------------------- */
     
    	public interface AnimationFinishedListener {
    		/**
    		 * Called when the animation is finished.
    		 */
    		public void onAnimationFinished();
    	}
     
    	/* ----------------------------------------------- */
     
    	public static void animationIn(View container, WindowManager windowManager) {
    		animationIn(container, windowManager, null);
    	}
     
    	public static void animationIn(View container, WindowManager windowManager, AnimationFinishedListener listener) {
    		apply3DRotation(90, 0, false, container, windowManager, listener);
    	}
     
    	public static void animationOut(View container, WindowManager windowManager) {
    		animationOut(container, windowManager, null);
    	}
     
    	public static void animationOut(View container, WindowManager windowManager, AnimationFinishedListener listener) {
    		apply3DRotation(0, -90, true, container, windowManager, listener);
    	}
     
    	/* ----------------------------------------------- */
     
    	private static void apply3DRotation(float fromDegree, float toDegree, boolean reverse, View container, WindowManager windowManager, final AnimationFinishedListener listener) {
    		Display display = windowManager.getDefaultDisplay();
    		final float centerX = display.getWidth() / 2.0f;
    		final float centerY = display.getHeight() / 2.0f;
     
    		final Rotate3dAnimation a = new Rotate3dAnimation(fromDegree, toDegree, centerX, centerY, DEPTH, reverse);
    		a.reset();
    		a.setDuration(DURATION);
    		a.setFillAfter(true);
    		a.setInterpolator(new AccelerateInterpolator());
    		if (listener != null) {
    			a.setAnimationListener(new Animation.AnimationListener() {
    				@Override
    				public void onAnimationStart(Animation animation) {
    				}
     
    				@Override
    				public void onAnimationRepeat(Animation animation) {
    				}
     
    				@Override
    				public void onAnimationEnd(Animation animation) {
    					listener.onAnimationFinished();
    				}
    			});
    		}
    		container.clearAnimation();
    		container.startAnimation(a);
    	}
    }
  3. That’s it! Now you can use it like this:

    Activity1.java

    package com.yourapp.activity
     
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import com.yourapp.R;
    import com.yourapp.animation.ActivitySwitcher;
     
    public class Activity1 extends Activity {
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity1);
     
    		Button switchActivityBtn = (Button) findViewById(R.id.bSwitchActivity);
    		switchActivityBtn.setOnClickListener(new View.OnClickListener() {
    			@Override
    			public void onClick(View v) {
    				animatedStartActivity();
    			}
    		});
    	}
     
    	@Override
    	protected void onResume() {
    		// animateIn this activity
    		ActivitySwitcher.animationIn(findViewById(R.id.container), getWindowManager());
    		super.onResume();
    	}
     
    	private void animatedStartActivity() {
    		// we only animateOut this activity here.
    		// The new activity will animateIn from its onResume() - be sure to implement it.
    		final Intent intent = new Intent(getApplicationContext(), Activity2.class);
    		// disable default animation for new intent
    		intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
    		ActivitySwitcher.animationOut(findViewById(R.id.container), getWindowManager(), new ActivitySwitcher.AnimationFinishedListener() {
    			@Override
    			public void onAnimationFinished() {
    				startActivity(intent);
    			}
    		});
    	}
    }

    Implement Activity2.java like Activity1.java.

    The layouts (R.layout.activity1 and R.layout.activity1) must jave a container-id like this:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    	android:id="@+id/container"
    	android:orientation="vertical"
    	android:layout_width="fill_parent"
    	android:layout_height="fill_parent">
    	<!-- ... -->
    </LinearLayout>
  4. If you want to animate an activity out when it is finished, you could override the finish() method:
    // ...
    	@Override
    	public void finish() {
    		// we need to override this to performe the animtationOut on each
    		// finish.
    		ActivitySwitcher.animationOut(findViewById(R.id.container), getWindowManager(), new ActivitySwitcher.AnimationFinishedListener() {
    			@Override
    			public void onAnimationFinished() {
    				Activity1.super.finish();
    				// disable default animation
    				overridePendingTransition(0, 0);
    			}
    		});
    	}
    // ...

That’s it! 🙂

Hi,

here is a rough implementation of the Annotated Time Line from the Google Visualization API for the sdInteractiveChartPlugin for symfony:

sdAnnotatedTimeLineGraph.class.php

<?php
/**
 * sdAnnotatedTimeLineGraph - Creates AnnotatedTimeLine chart!
 * Google API URL: http://code.google.com/apis/visualization/documentation/gallery/annotatedtimeline.html
 *
 * @package    plugins
 * @subpackage sdInteractiveChart
 * @author     Robert Heim
 * @version    0.1
 */
 
 
class AnnotatedTimeLineGraph extends BaseChart {
    protected $chartType = 'AnnotatedTimeLine';
 
    public function  __construct() {
        $this->chartPackage = 'annotatedtimeline';
    }
 
    protected function formatData($data, $labels = null) {
        $addedColumns = false;
        $this->html .= 'var data = new google.visualization.DataTable();' . "\n";
        $this->html .= "data.addColumn('date','Second');" . "\n";
 
        $rowCountAdded = false;
        $row = 1;
        $latestRow = 0;
        foreach($data as $dataText=>$dataSet) {
            if ($latestRow < $row) {
                if (is_string($dataText)) {
                    $this->html .= "data.addColumn('number', '".$dataText."');" . "\n";
                } else
                    $this->html .= "data.addColumn('number','total');" . "\n";
                $latestRow = $row;
            }
 
 
            if (!$rowCountAdded) {
                if ($dataSet instanceof sfOutputEscaperArrayDecorator) {
                    $total = $dataSet->count();
                } else {
                    $total = (is_array($dataSet)) ? count($dataSet) : count($data);
                }
                $this->html .= 'data.addRows('.$total.');' . "\n";
                // Draw labels
                foreach($labels as $key=>$label) {
                	// javascript uses milliseconds as base
                	$xLable = 'new Date('.($label*1000).')';
                    $this->html .= "data.setValue($key, 0, {$xLable});" . "\n";
                }
 
                $rowCountAdded = true;
            }
 
            if ((!is_array($dataSet)) && !($dataSet instanceof sfOutputEscaperArrayDecorator)) {
                 $this->html .= "data.setValue($dataText, 1, $dataSet);" . "\n";
            } else {
                foreach($dataSet as $key=>$nextRow) {
                   $this->html .= "data.setValue($key, $row, $nextRow);" . "\n";
                }
                $row++;
            }
        }
    }
 
}
 
?>

Usage:

<?php
$chart = InteractiveChart::newAnnotatedTimeLineChart();
$chart->setWidthAndHeight('700', '250');
// timestamps = unix timestamps (seconds since 1970, like php function time(), mktime() etc.)
$timestamps = array(1291935600,129383640,1295218800,1295823600);
$chart->inlineGraph(array('hits' => array(100,131,150,127)), $timestamps, 'chart_div');
$chart->render();
?>
<!-- IMPORTANT: As required by the google api for annotated time lines you need to explicity specify the size of the div! -->
<div id="chart_div" style="width:700px; height:250px;"></div>

IMPORTANT: As required by the google api for annotated time lines you need to explicity specify the size of the div!

DAU

No comments

Ok, JavaEE ist vielleicht nicht gerade die einfachste Technologie und der ein oder andere verzweifelt daran. Aber es ist schon beachtlich, was manch ein – sagen wir mal unerfahrener – Nutzer alles ausprobiert. Gefunden in einem Forum:
// ...
EntityManager em = factory.createEntityManager();
arg0.getSession().setAttribute("em", em);
// ...

Sehr schön! 😉 Begleitet wurde das von dem wehleidigen Versuch Annotationen in einer JSP zu nutzen… . 🙂

Deploying symfony 1.4 projects to the production  server may be horrible if you’re on windows…
(For older versions of Symfony it’s quite the same.. but the symfony file you need to edit is a different one… check out this thread on the symfony-forums: Setting up rsync with a Windows computer). For symfony 1.3/1.4 this works:

1) Use Linux if you can!

2) On Windows you can do it with some workarounds …

  1. Fabian Potencier posted everything you need to know for ssh-configs: http://fabien.potencier.org/article/19/quick-ssh-tip
  2. you need to install cwrsync
  3. Many users seems to get an error when they now try to deploy (dry-run) :
    php symfony project:deploy production
    this will probably fail. This seems to be an symfony-issue on the way that symfony passes the rsync-command. Workaround is to generate the command by the symfony-task, but then execute it by hand.
  4. Open ../lib/vendor/symfony/lib/task/project/sfProjectDeployTask.class.php
    on line 162 it says
    $command = "rsync $dryRun $parameters -e $ssh ./ $user$host:$dir";
    in the next line add
    echo $command
    and comment the line $this->getFilesystem()->execute($command, ...); to prevent symfony executing the command.

    Finally it should look like this:

    // ...
    $dryRun = $options['go'] ? '' : '--dry-run';
    $command = "rsync $dryRun $parameters -e $ssh ./ $user$host:$dir";
    echo $command;
    //$this->getFilesystem()->execute($command, $options['trace'] ? array($this, 'logOutput') : null, array($this, 'logErrors'));
     
    $this->clearBuffers();
    //...
  5. Now execute
    php symfony project:deploy production > deploy
    This will write the (dry-run) command in a new file called “deploy”. Open it, copy&paste the command to your command line and execute it!
  6. Now do the same for the real command:
    php symfony project:deploy production --go > deploy_go
    Again copy&paste the command from the file to your command line and execute it.
  7. If you get permission errors check that the folder you specified in your properties.ini is writeable by the user you specified for the ssh connection.

Da die meisten Blog Einträge zu “Putty Konfiguration sichern” ziemlich alt sind:
Unter Windows 7 Start > Ausführen:
regedit /e puttynew.reg HKEY_CURRENT_USER\Software\SimonTatham\PuTTY

Die Datei findet sich dann unter C:\Windows\System32\puttynew.reg
Von dort aus kann sie dann auf z.B. einen USB Stick kopiert werden und auf einem anderen Rechner per Doppel-Klick installiert werden. Ein anschließender Start von Putty auf dem letzteren Rechner, sollte dann die alte Konfiguration aufweisen.

TIPP: Darüber hinaus sollte man nicht vergessen bei BackUps auch den eigenen .ssh Ordner extra zu sichern. Bei mir liegt der unter C:\Users\BenutzerName\.ssh

This is how I managed to connect my symfony-users with the PhpBB3-sessions. Goal is to check in a symfony-action or -template if a user is authenticated in the corresponding forum.

From here you could easily add: If there is no valid session, you could create a new one. And on each interaction with the website you could update the existing session. But this is not a MUST for me. If you need it, you might want to implement it by yourself (I marked the parts in the code with TODO) and post it in the comments or there: http://forum.symfony-project.org/index.php?t=msg&goto=98360&#msg_98360

<?php
/**
 * Integration with phpBB v3 forums
 * (c)Amir Wald 2008 - http://colnect.com
 * (c) modified by Robert Heim 2010 - http://blog.robert-heim.de
 *
 * see: http://forum.symfony-project.org/index.php/m/29883/
 */
class PhpbbIntegration
{
	// adjust these constants to your needs..
	const FORUM_URL = 'http://localhost:8046/';
	const FORUM_PATH = '/../../../../forum/';
	const DB_SERVER = 'localhost';
	const DB_USER = 'root';
	const DB_PASSWORD = '';
	const DB_DATABASE = 'phpbb3';
	const DB_PREFIX = 'phpbb';
	const ANONYMOUS = 1;
 
	private static $link = null;
	private static $cookies = null;
 
	/**
	 * Gets the link to phpBB database
	 *
	 * @return resource - a MySQL link identifier on success, throws Exception on error
	 * @author (c) Robert Heim 2010 - http://blog.robert-heim.de
	 */
	public static function getDbConnection()
	{
		if (!is_null(self::$link))
		{
			return self::$link;
		}
 
		self::$link = @mysql_connect(self::DB_SERVER, self::DB_USER, self::DB_PASSWORD);
		if (self::$link && @mysql_select_db(self::DB_DATABASE, self::$link))
		{
			return self::$link;
		}
 
		throw new Exception("getDbConnection failed. MESSAGE [[file:".__FILE__.' line:'.__LINE__.": ".mysql_error()."]]");
		return self::$link;
	}
 
	/**
	 * Execute SQL query on phpBB database
	 *
	 * @param string $sql
	 * @return resource
	 */
	public static function sqlExec($sql)
	{
		if (!($result = @mysql_query($sql, self::getDbConnection()))) {
			throw new Exception("SqlExec failed. QUERY [[$sql]] MESSAGE [[".mysql_error()."]]");
		}
		return $result;
	}
 
	/**
	 * Checks if the user is logged in to the Phpbb
	 *
	 * @param integer $user_id
	 * @return boolean - true if the user is logged in to Phpbb, false otherwise
	 * @author (c) Robert Heim 2010 - http://blog.robert-heim.de
	 */
	public static function isUserLoggedIn($user_id)
	{
		return (self::checkSession() > 0);
	}
 
	/**
	 * Validates a users session from phpBB cookies against the db.
	 *
	 * @return integer - user_id if the user is logged in to Phpbb, 0 otherwise
	 * @author (c) Robert Heim 2010 - http://blog.robert-heim.de
	 */
	public static function checkSession()
	{
		$cookies = self::readCookies(self::getConfigVal('cookie_name'));
 
		if ($cookies['user_id'] != null && $cookies['sessionId'] != null )
		{
			// check session
			$user_id = self::checkDbSession((int)$cookies['user_id'], $cookies['sessionId']);
			if ($user_id > 0)
			{
				// user is logged in and has a valid session
				return $user_id;
			}
		}
 
		// no valid session yet.
 
		// check session-key ("remember me"-feature)
		if ($cookies['sessionKey'] == null)
		{
			// key is missing
			return 0;
		}
 
		// get/create and update session within checkDbSessionKey
		$user_id = self::checkDbSessionKey($cookies['sessionKey']);
		if ($user_id <= 0)
		{
			// the session key was not found in the db
			return 0;
		}
 
		$sessionId = self::getSessionId($cookies['user_id']);
		if ($sessionId == '')
		{
			// there was no valid session and a new one was not created during the getSessionId(...)
			// so we don't have a session and the user is not logged in.
			return 0;
		}
 
		return $user_id;
	}
 
 
	/**
	 * Get a configuration value from phpBB's database
	 *
	 * @param string $name - value name
	 * @return string - the value
	 */
	private static function getConfigVal($name)
	{
		$sql = "SELECT `config_value` FROM `".self::DB_PREFIX."_config` WHERE `config_name` LIKE '$name'";
		$result = self::sqlExec($sql);
		$ar = mysql_fetch_array($result);
		return $ar['config_value'];
	}
 
	/**
	 * Gets the session_id for a user_id from the sessions-table of te phpbb3.
	 * TODO: If there is none a new session is created. Existing sessions will be updated.
	 *
	 * @param integer $user_id
	 * @return string - session_id if the user is logged in to Phpbb, '' otherwise
	 * @author (c) Robert Heim 2010 - http://blog.robert-heim.de
	 */
	private static function getSessionId($user_id)
	{
		if (!is_int($user_id) || $user_id < 1 || $user_id === self::ANONYMOUS) {
			return '';
		}
		$user_id = mysql_real_escape_string($user_id, self::getDbConnection());
 
		$sql = "SELECT * FROM `".self::DB_PREFIX."_sessions` WHERE `session_user_id`=$user_id";
		$result = self::sqlExec($sql);
 
		if (mysql_num_rows($result) <= 0) 
		{
			// no session found
			// TODO insert a new phpbb session  - see phpbb3/includes/session.php for how this should be done
			// TODO set $session_ar;
			return '';
		}
		else
		{
			$session_ar = mysql_fetch_array($result);
			// TODO update current phpbb session - see phpbb3/includes/session.php for how this should be done
			return $session_ar['session_id'];
		}
		return '';
	}
 
	/**
	 * Check user's session(s) from phpBB's database
	 *
	 * @param integer $user_id
	 * @param integer $sid session_id
	 * @return integer - user_id if the user is logged in to Phpbb, 0 otherwise
	 * @author (c) Robert Heim 2010 - http://blog.robert-heim.de
	 */
	private static function checkDbSession($user_id, $sid)
	{
		if (!is_int($user_id) || $user_id < 1 || $user_id === self::ANONYMOUS) {
			return 0;
		}
		$user_id = mysql_real_escape_string($user_id, self::getDbConnection());
		$sid = mysql_real_escape_string($sid, self::getDbConnection());
 
		$sql = "SELECT * FROM `".self::DB_PREFIX."_sessions` WHERE `session_user_id`=$user_id AND `session_id` = '".$sid."'";
		$result = self::sqlExec($sql);
		if (mysql_num_rows($result) <= 0) 
		{
			// no session found
			return 0;
		}
		#$session_ar = mysql_fetch_array($result);

		return $user_id;
	}
 
	/**
	 * Validates a users session_key with the phpBB's database
	 *
	 * @param string $sessoin_key
	 * @return integer - user_id if the key is in the db, 0 otherwise
	 * @author (c) Robert Heim 2010 - http://blog.robert-heim.de
	 */
	private static function checkDbSessionKey($session_key)
	{
		$session_key = mysql_real_escape_string($session_key, self::getDbConnection());
 
		$sql = "SELECT * FROM `".self::DB_PREFIX."_sessions_keys` WHERE `key_id`='$session_key'";
		$result = self::sqlExec($sql);
		if (mysql_num_rows($result) <= 0) 
		{
			// no session_key found
			return 0;
		}
		$session_key_ar = mysql_fetch_array($result);
 
		return (int)$session_ar['user_id'];
	}
 
	/**
	 * reads client cookies of the phpBB3
	 *
	 * @param string $phpbbCookieName
	 * @return array - $phpbbCookieName
	 * @author (c) Robert Heim 2010 - http://blog.robert-heim.de
	 */
	private static function readCookies($phpbbCookieName)
	{
		if(is_null(self::$cookies))
		{
			$sessionKey = sfContext::getInstance()->getRequest()->getCookie($phpbbCookieName.'_k');
			$user_id = sfContext::getInstance()->getRequest()->getCookie($phpbbCookieName.'_u');
			$sessionId = sfContext::getInstance()->getRequest()->getCookie($phpbbCookieName.'_sid');
			$cookies = array('sessionKey' => $sessionKey,
							'user_id' => $user_id,
							'sessionId' => $sessionId,
			);
			self::$cookies = $cookies;
		}
		return self::$cookies;
	}
 
}

Wow! Finally I got it to work… that took me about 6 hours…. in hope to save someones time, I will write down step-by-step, what I did.

We will set up Retrospectiva on xampp under http://localhost/retrospectiva .

You will need to do several steps including installation of XAMPP, Rails, many missing *.gem’s and of course Retrospectiva. Then you need to set up a mongrel-service and Apache-proxy configuration. After that, you should be able to enjoy Retrospectiva! 🙂

This is how I got it to work:

STEP 1 – Install XAMPP

http://www.apachefriends.org/xampp.html
I installed it in D:\xampp.

STEP 2 – Install Rails

We’re going to use the RubyInstaller from http://rubyforge.org/.
I first tried it with Rails 1.9.1 but it won’t work for me. I found that I might use 1.8.7:
http://rubyforge.org/frs/?group_id=167&release_id=42563
So, you might install 1.8.7: rubyinstaller-1.8.7-p249-rc2.exe
Be sure to add the ruby/bin to your PATH (you’re able to check that option during the installation).

I installed it in D:\xampp\ruby.

cmd:
cd D:\xampp\ruby\bin
gem update
gem install rails --include-dependencies

That takes some time…

STEP 3 – Install Mongrel

I used mongrel 1.1.5.

cmd:
gem install mongrel

STEP 4 – Install mysql gem

I used mysql gem 2.8.1. :

cmd:
gem install mysql

(their might be some definition-errors, but that should be ok)

You need to download an older MySQL client library. The suggested one from the Retrospectiva-Wiki, which I used, can be found there: http://instantrails.rubyforge.org/svn/trunk/InstantRails-win/InstantRails/mysql/bin/libmySQL.dll.

Copy the file to D:\xampp\ruby\bin.

STEP 5 – Install mongrel-service + win32-service gems

  1. Download “mongrel_service-0.3.4-i386-mswin32.gem” from http://rubyforge.org/frs/?group_id=1306
  2. Download “win32-service-0.5.2-mswin32.gem” from http://files.ruby.inoack.com/win32utils/
    Be sure to use 0.5.2 as newer ones may not work with ruby 1.8.7!
  3. Create a directory in htdocs like “gems”: D:\xampp\htdocs\gems and move the two *.gem files to this folder.
  4. Install the gemscmd:
    cd D:\xampp\htdocs\gems
    gem install mongrel_service

STEP 6 – Install Retrospectiva

(You can follow the official Installation-Guide for more details)

Download the latest release: http://github.com/dim/retrospectiva/tarball/master
(I used dim-retrospectiva-b179453.tar.gz)

Unpack the downloaded file and move it to: D:\xampp\htdocs\retrospectiva

Create Database

start the xampp-control and start apache+mysql.
Go to: http://localhost/phpmyadmin and perform the sql-query (change the x’s 😉 )

sql:
CREATE DATABASE IF NOT EXISTS retrospectiva;
GRANT ALL PRIVILEGES ON retrospectiva.*
TO "retrospectiva"@"localhost" IDENTIFIED BY "xxxxxxxxxxx";

Configure Database

Rename D:\xampp\htdocs\retrospectiva\config\templates\database.mysql.yml
to D:\xampp\htdocs\retrospectiva\config\database.yml

Edit config/database.yml with a text editor and adjust the configuration details in the production section. That will mainly be the password.

Load initial content and install missing gems

Try to setup Retrospectiva…

cmd:
cd D:\xampp\htdocs\retrospectiva
rake RAILS_ENV=production db:setup

That probably fails with errors about missing gems.
Go through the list of the missing gems and install each BUT NOT RedCloth:

cmd:
gem install MISSING_GEM

We need to download the RedCloth*.gem manually because on windows the installation will fail caused by missing compilers/make.exe/etc.. don’t try to fix that.. that’s not worth the time.
Download RedCloth-4.2.2-x86-mswin32-60 from http://rubyforge.org/frs/?group_id=216&release_id=36337

Put the downloaded file in the folder you already used in Step 5 D:\xampp\htdocs\gems and install the gem:

cmd:
gem install RedCloth-4.2.2-x86-mswin32-60.gem

Testing Retrospectiva installation

After installing all that gem’s you might want to test the installation with the built-in webrick-server:

cmd:
cd D:\xampp\htdocs\retrospectiva
ruby script/server -e production -p3000

That takes some time… and their might be some errors, but after a while you should see Retrospectiva at http://localhost:3000

Well, fine! But we want it to work with the apache from XAMPP! This needs some more steps…
Hit ctrl+c to stop the webrick server and read on! 🙂

STEP 7 – Configure Apache and customize Retrospectiva

First create a file D:\xampp\htdocs\retrospectiva\config\runtime\custom.rb with this content to inform retrospectiva that we use an other root-url than “/”:

[code]
ActionController::Base.relative_url_root = "/retrospectiva"

Now we’ll configure the apache to proxy the retrospectiva calls to mongrel (we will install mongrel services in the next step).

Open D:\xampp\apache\conf\httpd.conf and add at the bottom:

[code]
Include conf/httpd-proxy-mongrel.conf

Create the file we just included: D:\xampp\apache\conf\httpd-proxy-mongrel.conf
with the content:

[code]
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
ProxyPass /retrospectiva balancer://retrospectiva_cluster
ProxyPassReverse /retrospectiva balancer://retrospectiva_cluster
<Proxy balancer://retrospectiva_cluster>
BalancerMember http://127.0.0.1:3002
</Proxy>

STEP 8 – Install Mongrel

Open a comand-window as Administrator (Windows->cmd, but instead of hitting ENTER hit CTRL+SHIFT+ENTER, or just right-click cmd.exe and choose Run as Administrator).

Go to the retrospectica folder and install services (it’s important that you perform the install in that folder!):

cmd:
cd D:\xampp\htdocs\retrospectiva
mongrel_rails service::install -N mongrel_retrospectiva_3001 -D "Mongrel serving Retrospectiva on 3001" -p 3001 -e production -c D:\xampp\htdocs\retrospectiva

now start the service:

cmd:
net start mongrel_retrospectiva_3001

cmd:
services.msc /s

-> find “Mongrel serving Retrospectiva on 3001” service and right click, select Properties and set Startup type to “Automatic”. Press OK.

STEP 9 – Start! 🙂

Wow that was a long way to go… but if everything works fine, you only need to restart apache in the xampp-control (hit stop then start button).

Now go to http://localhost/retrospectiva and login (admin/password)!

DONE! 🙂

I hope you could save some time and that it work’s for you!

References

As I did not reinvent the wheel, here are the pages, that I used to find out how this works (or at least some of them.. don’t remember everything..):

Hallo,

letztes Wochenende habe ich mit meiner Band Purveyor den Rockförderpreis gewonnen.

Bilder gibt es bald auf unserer Band-Website: Purveyor.de

Robert

PMViewer

Ich kämpfe in meinem Penspinnig Forum ständig mit zu vollen “Inboxes” an privaten Nachrichten. Deswegen habe ich ein Programm geschrieben, welches dabei hilft, die alten Nachrichten weiterhin leicht aufzufinden.

Screenshots, Download usw gibt es dort: http://robert-heim.de/PMViewer

Key-Features

  • MVC-Design-Pattern
  • Portabilität
  • OOP
  • UML / Klassendiagramm