While trolling through old code, I came across the following specification:
describe Builddo
describe "passed?"do
it "returns true for 1"do
build =Build.new(:status => 1)
build.passed?.should be_true
endendend
The problem with this test is it only tests for success. Realizing that it's equally--if not more--important to test for failure than it is success, I decided to ask the team for opinions on what this test should look like.
After bouncing this around with the team, we decided to refactor it as such:
describe Build
describe "passed?"dobefore(:each) do@build=Build.newend
it "returns false when status has not been set"do@build.passed?.should be_false
end
it "returns false when status is 0"@build.status=0@build.passed?.should be_false
end
it "returns true when status is 1"@build.status=1@build.passed?.should be_true
endendend
As you can see, we were able to find 2 failure conditions and only one success.
This is a very simple example but we think it has a broad application to testing in general.
When in need of executing shell commands, it's easy enough in ruby to use exec(), system(), the ubiquitous backtick or %x{...}, but what's the difference between these?
Ok, so the backticks and %x{} are exactly the same. What about system()?
irb(main):003:0>system('ls')
one three two
=> true
Nice, no '\n'. And now exec():
irb(main):004:0>exec('ls')
one three two
bash$
Ah! it kicked me out of irb, meaning it kills the current Thread. Good to know!
Return Values
Process::Status Objects
irb(main):001:0>system('ls')
one three two
=> trueirb(main):002:0>$?
=> #<Process::Status: pid=79599,exited(0)>irb(main):004:0>
$? accesses the status of the last system executed command, whether you use the backticks, system() or %{}.
Use '.exitstatus' to get the status:
irb(main):001:0>irb(main):001:0>system('ls')
one three two
=> trueirb(main):002:0>$?# a magic ruby method
=> #<Process::Status: pid=79606,exited(0)>irb(main):003:0>$?.exitstatus
=> 0irb(main):004:0>
Contrary to what you might think, 0 means success, 1 means failure. If you want to make the $? more readable, require English:
irb(main):001:0>system('ls')
one three two
=> trueirb(main):002:0>$?
=> #<Process::Status: pid=79615,exited(0)>irb(main):003:0>require'English'
=> trueirb(main):004:0>$CHILD_STATUS
=> #<Process::Status: pid=79615,exited(0)>irb(main):005:0>$CHILD_STATUS.exitstatus
=> 0irb(main):006:0>
Parsing STDERR and STDOUT can only be done by logging and parsing logs. More to come...
Red5 is an Open Source Flash Server written in Java that supports:
Streaming Audio/Video (FLV and MP3),
Recording Client Streams (FLV only),
Shared Objects,
Live Stream Publishing,
Remoting”
Red5 is essentially an open source alternative to Adobe’s Flash Media Server. Both technologies leverage Adobe’s RTMP (Real Time Message Protocol) to stream small binary fragments in real time. The packets are multiplexed and therefore only one persistent connection is needed for each user. Objects are serialized via AMF (Action Message Format) which is what we will be using in this tutorial to create SharedObjects, which as the name suggests, are objects (in this case refrigerator magnets!) that are shared amongst all viewers of the application.
Once installed, navigate the Red5 root directory to get started.
Red5 Structure Rundown
Just the important stuff...
In the root Red5 directory you’ll find red5.jar which holds a pre-compiled jar holding the red5 java classes. You’ll use this later to compile your main application class.
The directory webapps is where you will be creating new Red5 applications. All new applications have a consistent structure which is laid out as a template for you in doc/templates
Red5 New Application Structure Rundown
Creating a new Red5 application is simple. We just create a new folder in webapps/.
We will create an app called elcMagnets so we create a directory for it and now have webapps/elcMagnets. Red5 expects us to also have a directory inside our new application named WEB-INF/ so we create that as well, and now have webapps/elcMagnets/WEB-INF.
There are 4 default configuration files in doc/templates/myapp/WEB-INF that we will modify and use in our app, so we can copy those over to our elcMagnets directory. (log4j.properties, red5-web.properties, red5-web.xml, web.xml)
You can read up about all the properties and handlers specified in these files here.
For now, we only want to customize these files for our current app. We need to change the webapp.contextPath specified in red5-web.properties to reflect our newly created app. Take note of the virtualHosts parameter and remember to deal with it on deployment to a remote host.
The only other configuration file we will modify for now is red5-web.xml. For this we need to specify the handler that is invoked when users connect to the app. You can read up on handlers here.
For the purposes of this demo app we will only be creating a web.handler. We will end up writing Application.java as the web.handler inside the a package called package org.red5.server.webapp.elcMagnets so we will set our web.handler to org.red5.server.webapp.elcMagnets.Application.
To keep things organized, we will create the directories lib/, classes/, src/ inside webapps/. Next we will create Application.java inside of src/.
As you recall, Application.java will serve as our web.handler, and therefore needs to extend ApplicationAdapter. (For more info on handlers, see the ‘HOWTO-NewApplications.txt’) In short, it allows you to stack functionality on top of existing events (connect, disconnect, join, leave, start, stop) or create your own.
Our example handler will be trivial, but illustrate calls to the Red5 server.
packageorg.red5.server.webapp.elcMagnets;
importorg.red5.server.adapter.ApplicationAdapter;
public classApplicationextendsApplicationAdapter {
public Stringhigh5Red5(int how_many)
{
String highFives = “”;
if(how_many==0){ return “I dont like you anyways, bro.”; }
for(int i=0;i<how_many;i++){ highFives += “*Smack*\n”; }
return highFives;
}
}
Later, we can directly call this handler in ActionScript...
nc.call("high5Red5",nr,5);
//Or more generally...
netconnection.call("function",responder,arguments.....)
Compiling Application.java
What we want to end up with is a nicely packaged up elcMagnets.jar located in lib/
So from src/ we compile Application.java to the classes/ directory.
This section documents my trials and tribulations with installing Red5 on an Amazon EC2 instance running a standard FC4 image. This is a nasty (but working) implementation of this.
Some of the rpms are unnecessary, but some of the yum packages were failing on me. Anyways, this might help someone...
$ yum install gcc
$ rpm -ivh ftp://rpmfind.net/linux/fedora/core/updates/4/i386/libstdc++-devel-4.0.2-8.fc4.i386.rpm
$ rpm -ivh ftp://rpmfind.net/linux/fedora/core/updates/4/i386/gcc-c++-4.0.2-8.fc4.i386.rpm
$ yum install fedora-rpmdevtools
$ fedora-buildrpmtree
$ cd /etc/yum.repos.d/
$ wget http://www.jpackage.org/jpackage.repo---->>> download jdk 5.0 update .bin from <a href="http://java.sun.com/javase/downloads/index.jsp" target="_blank">sun</a>, scp it up to server (requires accepting of TOS)
---->>> cp jdk-....bin rpmbuild/SOURCES
$ wget http://mirrors.dotsrc.org/jpackage/1.7/generic/non-free/SRPMS/java-1.5.0-sun-1.5.0.14-1jpp.nosrc.rpm
$ rpmbuild --rebuild java-1.5.0-sun-1.5.0.14-1jpp.nosrc.rpm
$ cd ~/rpmbuild/RPMS/i586/
$ (echo config gpgcheck 0; echo localinstall java-1.5.0-sun*.rpm; echo run) > yum-cmd
$ yum shell yum-cmd
$ rm yum-cmd
$ cd ~/
wget http://archive.apache.org/dist/ant/binaries/apache-ant-1.7.0-bin.tar.gz
$ cd /usr/local/
$ tar -zxf ~/apache-ant-1.7.0-bin.tar.gz
$ mv apache-ant-1.7.0 ant
$ vim /etc/profile
PATH=$PATH:$HOME/bin:/usr/local/ant/bin
export PATH
$ source /etc/profile
$ tar -zxvf red5-0.6.3.tar.gz
$ cd red5-0.6.3
$ make
$ make install
$ /usr/lib/red5/red5.sh
At this point if you can hit http://{ip here}:5080 you are golden.
Windows Server? Yeah right. Redhat? No thanks.
It may not be “supported” by the Flash Media Server, but by installing a few libraries you can get one up and running on CentOS in a few minutes…
I got a weird problem recently, the project runs well with script/server, but when I run a rake task, a 'uninitialized constant ***' error appeared. After spending me about 1h, I finally detected the problem was caused by AWS/S3 gem.
There's a rake file named 'mysql.rake' in my project, it requires 'aws/s3'. So when i running a rake task, AWS/S3 will be loaded before environment.rb, and it will define String#underscore before ActiveSupport, because ActiveSupport use Module to extend String, so AWS/S3 version will be used, but it's outdated:
You should notice the difference, if a plugin doesn't require all its lib files in init.rb and it uses name space (eg acts_as_paranoid), and you just have a rake task need AWS/S3, then you will have problems to run rake tasks. because Rails can't load necessary files for that plugin automatically with the outdated String#underscore.
The solutions are:
modify the weird plugin to require all lib files itself.
update AWS/S3 with the new version String#underscore
But if it's a large plugin, then solution#1 will require lots of time, It's what i encountered, so I chose the solution#2.
I've submit a patch to AWS/S3 ml, but before they merging it into the trunk, if you are unfortunately the man satisfying all above conditions, hope this post can save a little time for you.
It seems such a small thing, really, and it's so easy to take for granted. One of the things that I first came to love about Rails was the fact that it would generate standard views for your models. Sure, the HTML that the framework generates would charitably be described as skeletal; it requires a ton of work in order to look even remotely presentable. Even pushing design concerns to the side, you still have to account for the fact that all of your fields default to textboxes.
And yet, despite the headaches that accompany generated views, not having to type out every single field by hand is a beautiful thing. Of course, if that were all I had to say, I wouldn't have bothered to type this out. I mean, really, who'd want to read that?
So, I guess you're wondering why I called you all together, then. It's simple, really, I love generated views, but I also happen to prefer typing out my migration files rather than using command line arguments in the script/generate command. Unfortunately, there doesn't seem to be a native way to generate views from an existing migration, or, heaven forbid, a model that has been modified by multiple migrations.
In response to this dilemma, I decided to write a Rails Generator that would leverage the core view templates against field parameters that are created from an existing model.
And so, without further ado, I present the Refresh View plugin.
Create your scaffold, modify the migration, then run the following command:
./script/generate refresh_view model_name
If you continue to make changes to the model's structure? Just keep refreshing the view. BUT, and this is important, the generator completely rewrites the view, so don't refresh the view if you've made changes you want to keep.
Are you tired of typing the same commands in Terminal every day just to get your project going? Do you want to save, like *minutes* per day? Did you know that you only get so many keystrokes per lifetime before your hands are permanently crippled? OK, that last bit I might have just read somewhere but I digress.