Rails Rumble '08

1 Comment

Rails Rumble was an amazing experience.  I think I slept about 6 hours over a 56 hour period and while I became grouchy I don’t think I ever stopped having fun.  The fun stopped after the first day of voting.  Starting during the second day of voting I <3 Games plummeted in the rankings.  I am not really sure what happened to cause this fall.  I am not the most objective person but in testing some of these other apps I’ve run into some real problems that I don’t understand how something that doesn’t quite work could be ranked higher than our app which I think is about 90% done.

Problems with voting

  • With any voting system there are always ways to game the system.  The OpenID requirement for Rails Rumble voting would make gaming rather easy I would think.  Without a captcha or email validation it would be fairly easy for a machine to sign up for voting as many times as it wanted.
  • Voting with most people for Rails Rumble is only based off of a glance, there is very little actual signing up to use the app going on.
  • No way to explain what your feelings about the app were.

I think if some of these issues were addressed I would feel a little better about the voting system.  I think the 8 random apps a day idea was a good one that helps prevent the event from becoming an popularity contest.

Gaming the system is always going to be a problem.  Bots can be created that get around almost every system to stop them, but when the systems are few and far between to stop them they can have a field day and can dramatically skewer results.  I do know a few people who aren’t bots that just said “Oh, well I’ll just give every other app 1 star across the board” and it shows.  Rather than 3 stars being the “average” value for an app it is the high value.  I know there won’t ever be a 5.0 over all app, but we’re talking about 2.9 being the high value and 1.6 being the low value.  That is a lot of 1 and 2 stars over the 4 or 5 stars.  On a scale of 1-5 then 3 should be the average, that should be “this app is unremarkable, there is nothing wrong, but nothing right either” instead if we keep going along the way we are currently going then 2.9 is going to be the winning score, which using the normal averages would mean that it is slightly below average, and while I’ll be the first to admit none of these apps are above average against an application where you have an unlimited time frame and tons of QA testers.  If you keep in mind that every single one of the apps was designed in 48 hours by 1-4 people I would have to say a great many of them are above average.

One of the problems I have with the voting system is that the results tend to be skewed towards pretty on the front page apps.  Powered By Geek didn’t exactly cater to this, we hid a lot of functionality after sign up, but we did put a little video up explaining about the app.  The real problem I have is the number of people who aren’t even signing up who are voting on the app.  I know for a fact that our login/signup process works because we had a guy dedicated to testing that procedure during the actual competition and I’ve created about 10 accounts as well with my numerous Open IDs and as regular users.  Looking at our numbers we’ve had 79 users sign up, and when you take into account all my test accounts that is about 70 users total who have signed up and yet we have 240 votes on our site.  That is a 1:4 ratio of people who actually looked at our site vs people who just voted either after looking at our home page or without looking at the site at all.

I’m wary to point my finger at people and point out flaws as to why they shouldn’t be at their position, however some of the apps I tested had main functionality broken and yet they are sitting in the top 30 spots.  Testing is almost nonexistent, a friend was testing other sites and uploaded a picture that he figured would disappear after a while but instead it was on there for over 24 hours.  The site only had slots to show 5 pictures that means that it took over a day to upload 5 pictures on the site.

My final real problem is that there is no way to tell why people voted one way or another.  I think a short description on why they are voting one way or another would help everyone involved to understand what people liked and what people disliked about the app, not to mention it would make it easier to tell when someone was gaming the system.

Overall I think the Rails Rumble brings an awesome service to the community.  48 hours with the right motivation (competition) is really useful as it can really get an interesting idea or product built.  Not to mention it helps bring you together with friends towards a common goal.  I really just wish everyone could come away from it feeling like the voting wasn’t just a game.

Tech Demo

1 Comment

So in preparation for Rails Rumble I’ve been researching a lot of sexy little plugins.  We have also had a need to test this plugins to make sure they’ll work not just locally but in a shared hosting environment (the first time you get burned and spend an entire weekend, 16 hours, trying to figure out why something that works perfectly on your local box dies horribly on shared hosts you learn to test everything).  To accomplish this testing we’ve needed a small little app so I can get the bare minimum for the plugins in there.  I’d like to post everything I did while I wait for Lynn to push everything up to test. Mostly this is just a quick run down because someone else has already done a far better job than I could explaining in depth how to install and configure all this stuff. This is also by no means how you should do any of this, this is solely for testing out various plugins and packages.

The plugins, gems, and kitchen sink

What I’ve got up and working on my local box so far is:

  1. Simple Email Notification with ActionMailer
  2. SMS Fu
  3. Twitter4r
  4. Gravatar Profile Image
  5. Juggernaut
  6. BackgrounDRB
  7. GoogleCalendar/ICal

Simple Email Notification

Won’t really go into this, it’s obvious and everyone has done it a million times

SMS Fu

  • Clone it
  • Include it
  • Put this code in there:

Controller:

def index
@carriers = %w(alltell ameritech at&t bellsouthmobility blueskyfrog boost cellularsouth helio kajeet metropcs powertel pscwireless qwest southernlink suncom t-mobile virgin verizon)
end

def text_message
deliver_sms(params[:pages][:phone_number],params[:pages][:carrier],"Test Text Message")
redirect_to :action => 'index'
end


View:

<% form_for :pages, :url => {:action => :text_message} do |f| -%>
Phone Number: <%= f.text_field :phone_number %>
<br/>
Carrier: <%= f.select :carrier, @carriers %>
<br/>
<%= submit_tag "Send Text" %>
<% end -%>

Twitter4r

  • Install the gem
  • require the gem
  • Use the following code

Controller

def twitter_message
client = Twitter::Client.new(:login => params[:pages][:user_name], :password => params[:pages][:password])
if client.authenticate?(params[:pages][:user_name], params[:pages][:password])
new_message = client.status(:post, params[:pages][:text_message])
return redirect_to :action => 'index'
else
flash[:notice] = "Failed to authenticate"
return redirect_to :action => 'index'
end
end


View

<% form_for :pages, :url => {:action => :twitter_message} do |f| -%>
Twitter User Name: <%= f.text_field :user_name %>
<br/>
Twitter Password: <%= f.password_field :password %>
<br/>
Text To Post: <%= f.text_field :text_message, :limit => 140 %>
<br/>
<%= submit_tag "Send Twitter" %>
<% end -%>

Gravatar

Just use the code in your application helper

Juggernaut

  • Make sure you have json and eventmachine installed
  • install the gem
  • install the plugin
  • juggernaut -g juggernaut.yml (to make the config file)
  • Use the following code
  • juggernaut -c juggernaut.yml (to start the push server)

Layout (or anywhere you want to use Juggernaut)

<%= javascript_include_tag 'juggernaut/swfobject' %>
<%= javascript_include_tag 'juggernaut/juggernaut' %>
<%= juggernaut(:channels => ['chat', 'notifications']) %>

View

<fieldset><legend>Chat</lengend>
<ul id="chat_data" style="list-style:none">
</ul>
</fieldset>
<%= form_remote_tag(
:url => { :action => :send_data },
:complete => "$('chat_input').value = '#{session.id}'" ) %>
<%= text_field_tag( 'chat_input', session.id, { :size => 20, :id => 'chat_input'} ) %>
<%= submit_tag "Chat" %>
</form>
<br/><br/>
<fieldset><legend>Notifications</legend>
<ul id="notification_data" style="list-style:none">
</ul>
</fieldset>

juggernaut_hosts.yml

:hosts:
- :port: 5001
:host: 127.0.0.1
:public_host: how_the_public_accesses_your_site.com
:public_port: 5001

BackgrounDRB

  • install chronic and packet requirements
  • Clone the code
  • rake backgroundrb:setup
  • ruby script/generate worker notifications
  • use the following code
  • ruby script/background start (to start the backgrounDRB)

backgroundrb.yml

:backgroundrb:
:ip: 0.0.0.0
:port: 11006
:schedules:
:notifications_worker:
:check_notifications:
:trigger_args:
:start: <%= Time.now + 5.seconds %>
:end: <%= Time.now + 1.year %>
:repeat_interval: <%= 5.minutes %>

notifications_worker.rb

class NotificationsWorker < BackgrounDRb::MetaWorker
set_worker_name :notifications_worker
def create(args = nil)
logger.info 'created worker'
end

def check_notifications
if true
logger.info 'sending notification'
Juggernaut.send_to_channel("new Insertion.Top(\"notification_data\", \"<li>Sending Notification at #{Time.now}<\/li>\");", "notifications")
end
end
end

View

<fieldset><legend>Notifications</legend>
<ul id="notification_data" style="list-style:none">
</ul>
</fieldset>

ICalendar

  • Install the gem
  • require the gem
  • Use the following code
  • Add to Google Calendar to check your work

View

def calendar
@cal = Icalendar::Calendar.new

[{:name => 'Meeting', :start => Time.now.beginning_of_day, :end => Time.now},
{:name => 'Greeting', :start => Time.now.beginning_of_day-1.day, :end => Time.now-1.day}].each do |comp|
event = Icalendar::Event.new
event.start = comp[:start].strftime("%Y%m%dT%H%M%S")
event.end = comp[:end].strftime("%Y%m%dT%H%M%S")
event.summary = comp[:name]
@cal.add event
end
@cal.publish
headers['Content-Type'] = "text/calendar; charset=UTF-8"
render :text => @cal.to_ical, :layout => false
end

What you will end up with is a very ugly little app that will look like this:

Rails Rumble

1 Comment

Powered By Geek will be participating in Rails Rumble 08.

I’ll be working along side Lynn Wallenstein, Nathan Ostgard and William Harris to create a Rails application in 48 hours.  We’re super excited, and I’m going to try and keep a log of what is going on to post it (we’ll see how well that goes).

Database Explanation

2 Comments

What is a database?

You’ve probably used or seen an Excel spreadsheet.  Well, a database looks similar, but it’s a lot more powerful, a database can do things that Excel only dreamed of doing.  A “database” is made up of “tables” which are made up of a bunch of “columns” and “rows”.

Excel Terminology | Database Terminology
-----------------------------------------
Excel File        |  Database
Sheets            |  Tables
Letter Columns    |  Columns
Number Rows       |  Rows

Each table holds a specific type of records or rows.  So the tables of a simple database “schema” (or design) would look like this:

+-----------------------------+
| Tables_in_truth_development |
+-----------------------------+
| answers                     |
| choices                     |
| polls                       |
| users                       |
+-----------------------------+

Notice how it is broken up, there is a table for users in there.  That will hold the information for all the users.  We make tables logically for the different items we would like to keep track of.

Databases that are used most commonly are called “Relational Databases”.  What that means is that each of the tables can link to another table.  The most common way to imagine databases is to draw webs how they relate.  Truth Database
As you can see in the picture: A User is related to polls and answers.  A Poll is related to answers, choices and users.  A Choice is related to polls and answers.  Finally, an Answer is related to choices, polls, and users.

This lets us do stuff like to find a User record and then see all his answers and polls.

Now that we know a little bit more about tables, lets look and what a table consists of.  It is made up of columns (or fields) that we want to track. If we were to look at it it would look like this:

+-------------+
| Field       |
+-------------+
| id          |
| facebook_id |
| name        |
| created_at  |
| updated_at  |
+-------------+

Now if you want to picture it in your mind each field would be a letter in Excel.

A table is also made up of rows (or records) which is one full set of filled out columns.  If you were to look at it it would look like this:

+----+-------------+-----------+---------------------+---------------------+
| id | facebook_id | name      | created_at          | updated_at          |
+----+-------------+-----------+---------------------+---------------------+
|  2 |   678674096 | Dan Ahern | 2008-08-08 23:47:36 | 2008-08-08 23:47:36 |
+----+-------------+-----------+---------------------+---------------------+

Rows are the numbers in excel.

The way we link the tables together is to put information from one table into another.  Lets look at a Poll:

+----+---------+---------------------+---------------------+--------------------+
| id | user_id | question            | created_at          | updated_at         |
+----+---------+---------------------+---------------------+--------------------+
| 11 |       2 | What should I wear? | 2008-08-08 23:38:43 | 2008-08-08 23:38:43|
+----+---------+---------------------+---------------------+--------------------+

Notice how the poll has a column called “user_id” and that column has the same number as the “id” field in user?  This is how we know who the poll belongs to.

And then if we wanted to see the choices for the poll we can use the “id” column from poll and find those numbers in choices in the column called “poll_id”, and we get back this:

+----+---------+-----------------+---------------------+---------------------+
| id | poll_id | name            | created_at          | updated_at          |
+----+---------+-----------------+---------------------+---------------------+
| 33 |      11 | Nothing         | 2008-08-08 23:38:43 | 2008-08-08 23:38:43 |
| 34 |      11 | A Monkey        | 2008-08-08 23:38:43 | 2008-08-08 23:38:43 |
| 35 |      11 | T-Shirt & Jeans | 2008-08-08 23:38:43 | 2008-08-08 23:38:43 |
+----+---------+-----------------+---------------------+---------------------+

Now to actually look through a database ourselves would be time consuming and wouldn’t be very helpful (we would have to look at all the users, get the id of the user that we want, then look the polls and find the user_id that matches our user’s id, then look at all the choices and find the ones that match our poll’s id) so there is a way to interact with databases called SQL (Structured Query Language).  So we can basically ask the database questions and it will return answers.  To get the first result above (the tables) I typed:

show tables;

Notice how my SQL statement or query ends in a semicolon “;”?  From the command line this is usually how you tell your database that you are done typing the commands and it should go out and get you the answers.  The answer that query returns are the list of all the tables in the database.  In order the first three queries I performed were:

(Get the list of tables)
show tables;

(Get the columns in users)
describe users;

(Get all the user rows)
select * from users;

Now that last one was a little different.  In the last one I’m saying
“select” (go get)
“*” (all values of all the columns)
“from users” (from the users table)
So it reads “go get all the values of all the columns from the users table” and the database goes out and fetches it.

The next query I did after I saw the user was to do:

select * from polls where user_id = 2;

The “select * from polls” is the same as what we did for the users but we added a “condition” (something that the database should look for)
“where user_id = 2” (find only records where user_id is equal to 2)
So it reads “go get all the values of all the columns from the polls table and find only records where the user_id is equal to 2” and the database went out and fetched those results for me.

The final query I performed was:

select * from choices where poll_id = 11;
This does the exact same thing as the query above only it looks in the choices table and finds only records where the poll_id is equal to 11.

Multiple Databases

All the examples up until now are assuming you only have one database and you have already selected it.  However that isn’t normally the case. Lets move back into our Excel analogy and apply it to the database and look at some of the SQL used as well.

Starting from the beginning

GOAL: Find the database we want.
EXCEL EQUIVALENT:  Finding the Excel file we want in a folder full of excel files we have to look at the names for all the excel files.
SQL: show databases;
RESULT:

+------------------------------+
| Database                     |
+------------------------------+
| information_schema           |
| classifieds_development      |
| dotcom_development           |
| environs_development         |
| fiveruns_blog                |
| hcl                          |
| inever_development           |
| kiobo_development            |
| mysql                        |
| playericious                 |
| test                         |
| truth_development            |
| upillar                      |
| upillar_database_development |
+------------------------------+

NEXT STEP: We want to get into the database.

GOAL: Get into the database we want to use.
EXCEL EQUIVALENT: Double click on the file
SQL: use truth_development;
RESULT:
Database changed
NEXT STEP: We want to look at all the tables.

GOAL: Look at all the tables
EXCEL EQUIVALENT: Look through the sheets tabs.
SQL: show tables;
RESULT:

+-----------------------------+
| Tables_in_truth_development |
+-----------------------------+
| answers                     |
| choices                     |

| polls                       |
| users                       |
+-----------------------------+

NEXT STEP: We want to look at all the users.

GOAL: Find all the users.
EXCEL EQUIVALENT: Click on the sheet labeled “Users”
SQL: select * from users;
RESULTS:

+----+-------------+------------------+---------------------+---------------------+
| id | facebook_id | name             | created_at          | updated_at          |
+----+-------------+------------------+---------------------+---------------------+
|  2 |   678674096 | Dan Ahern        | 2008-08-08 23:47:36 | 2008-08-08 23:47:36 |
|  4 |   569397655 | William Harris   | 2008-08-08 23:52:50 | 2008-08-08 23:52:50 |
|  5 |   500092293 | Lynn Wallenstein | 2008-08-24 01:16:52 | 2008-08-24 01:16:52 |
|  8 |  1424543768 | Benjamin Leiter  | 2008-09-11 08:58:08 | 2008-09-11 08:58:08 |
+----+-------------+------------------+---------------------+---------------------+

Hopefully this helps someone somewhere. I wrote it as a beginning tutorial for a friend on database design and administration.