Mark Gandolfo's Blog
Node.Js Knockout Retrospective & Dns Bot App
It was a crazy 48 hours! My team members @coenhyde and @nathanhoad and myself had little knowledge of node.js and really didn't know how to even start a project. But we investigated a bunch of frameworks and decided on:
ExpressJS as our main framework.
Jade as our template language (which was integrated into express)
Less for our stylesheets
MongoDB for our database server
It all started with a mad rush to understand what the hell was happening within node, and more importantly how we could implement our idea! The event based architecture that node is built around made for an interesting adventure since our whole team is mainly PHP and Ruby on Rails developers.
The first day was spent finding our feet, given the massive amount of nodeJs plugins and frameworks, but by the end of the day we had a good idea of the scope of the technology and more importantly a good idea of how much code we could cut within the remaining 24 hours.
We decided to ditch our original idea and move more towards a simple tool that every technologist has been craving! We called it DNS Bot App and its primary purpose is to assist you in the propagation of a domain.
It works by pinging a variety of root servers all around the globe and building a picture of where your domain is resolving. for this first iteration it will show you A records, but stay tuned for more to come!
All in all, Node Knockout was in my opinion a complete success, the goal of the competition was to get a few more people involved in Node and more importantly build some buzz around it. Personally my goals going into the competition was simply to learn a new language, and although I'm nowhere near an expert, I really feel I've crossed that first hurdle that exists when learning a new language!
So if you think that DNS Bot App may be useful for you now or in the future, please show some support and Vote for us!
So a big shoutout to the node.js team, node knockout crew and of course my team members. It was a cool beer filled and sleep deprived ride, but one I'm looking forward to doing again!
Word Countr
I just wrote a simple rails app to count words in a block of text!
You can find it at http://wordcountr.com/. I built it mainly to play around with rails3 but also I hated the fact there wasn't an easy way to count words in a document.
So I've created this. I hope to implement some Natural Language Processing in the future to give more details about the block of text a user submits!
Rails3 With Cucumber, Rspec
Initially I had a few problems getting rspec and cucumber (especially gherkin) working with rails3 beta and ruby 1.9.2! Mainly these problems I ran into seemed to deal with bundler.
So easy enough, to get cucumber and rspec working, add the following to your Gemfile
group :test do
gem 'webrat'
gem 'rspec'
gem 'capybara'
gem 'database_cleaner'
gem 'cucumber-rails'
gem 'cucumber'
gem 'rspec-rails'
gem 'spork'
gem 'launchy'
end
then run
bundle install
chances are the first time you try to run your tests (especially if you're using rvm) you'll have segfaults displayed. To fix this manually remove the natively compiled gets and reinstall them using the gem command.
i.e.
gem uninstall gherkin nokogiri
gem install gherkin -v=2.1.5
gem install nokogiri -v=1.4.3.1
Do this for each of your segfaulting gems and things should start to work!
Mysql Gem With Rails 3 And Ruby 1.9.2
The traditional mysql gem that was used with ruby 1.8.7 and rails 2.* no longer works with rails3. The new mysql gem for rails 3 and ruby 1.9.2 is now called ruby-mysql, it takes the same params as the original mysql gem so to install it on a mac, try something like
env ARCHFLAGS="-arch i386" gem install ruby-mysql -- --with-mysql-dir=/usr/local/mysql --with-mysql-lib=/usr/local/mysql/lib --with-mysql-include=/usr/local/mysql/include
and add the following line in your Gemfile
gem 'ruby-mysql'
Hudson Ci Server Running Selenium/Webdriver Cucumber In Headless Mode Xvfb
I was determined to get our CI server running cucumber features in headless mode. I ran into a few webdriver problems which resulting in a small monkey patch (capybara patch being written and submitted) for capybara.
This is how I got it running on a fedora 12 server (and some tips on getting it working on an ubuntu server).
Install Xvfb
On fedora
sudo yum install xorg-x11-server-Xvfb
on ubuntu
sudo apt-get install xvfb
Running Xvfb
To run Xvfb, in console simply type, then set a environment variable to bind all X requests from the command line to go to the new virtual deskspace
Xvfb -ac :99
export DISPLAY=:99
This will launch a new virtual display bound to display 99.
Install X11VNC to see whats happening!!
This is very cool. You can actually set up a vnc server and bind it to the virtual display to see what is happening!!!
On Fedora
sudo yum install x11vnc
On ubuntu
sudo apt-get install x11vnc
To run
Simple run the following command! And point your VNC client to the installation server!
x11vnc -display :99
Test it by going to the command line and typing something like xterm or firefox!
Hudson
Install the hudson gem
The hudson gem is in the gems repo, but for docmentation go http://github.com/cowboyd/hudson.rb. I generally do this as the user "hudson".
gem install term-ansicolor
gem install yajl-ruby
gem install httparty -v=0.5.2
gem install builder -v=2.1.2
gem install thor -v=0.13.6
gem install hpricot
gem install hudson --pre
Install hudson
On the command line just type
hudson server
this will install hudson, and save configuration to the ~/.hudson directory
Checkout your application to this box
First thing is to ensure you have set up a git user and email in the global config
git config --global user.name "hudson"
git config --global user.email "hudson@myserver.com"
Then clone your app down, install all associated gems and create your testing database
git clone git@github.com:username/mygitrepo.git
rake gems:install
rake db:create:all
RAILS_ENV=test rake db:schema:load
Test cucumber/webdriver runs
Type cucumber in your apps directory
Create a new Hudson Project
Thanks to the hudson gem, you can set up the inital project skeleton via the command line (isn't technology great) Here we can use the hudson create command and tell hudson to use the current directory, and that hudson is installed on localhost:3001
cd #{to_your_rails_app}
hudson create . --host localhost --port 3001
Set up the hudson project
Log into the project, if its on your local computer, point your browser to http://localhost:3001 and click on your project, also skip this next section.
If you're installing hudson on a remote server you'll need to ssh port forward since hudson at this point is only listening on localhost.
ssh -L 8123:localhost:3001 hudson@mytestserver.com
Then open a browser and point it to http://localhost:8123 and click on your project
Set up the build
- Remove all defined build sets.
- Add a new build step "Execute Shell" with the command "cp config/database.tests.yml config/database.yml"
- Add a new build step "Exceute Shell" with the command "RAILS_ENV=test rake gems:install"
- Add a new build step "Execute Shell" with the command "RAILS_ENV=test rake db:migrate"
- Add a new build step "Execute shell" with the command "spec spec" # if you have specs
- Add a new build step "Execute shell" with the command "DISPLAY=:99 cucumber" # with cucumber
Press the save button.
If webdriver doesn't append to your virtual display
If webdriver doesn't append to your Xvfb then you can put this script in /features/support/capybara_webdriver_patch.rb
# Added profile support for capybara, so we can run our tests in headless mode
class Capybara::Driver::Selenium < Capybara::Driver::Base
def self.driver
unless @driver
profile = Selenium::WebDriver::Firefox::Profile.new
profile.load_no_focus_lib = false
@driver = Selenium::WebDriver.for :firefox, :profile => profile
at_exit do
@driver.quit
end
end
@driver
end
end
Some after thoughts
Some extras you should set up before you think your finished. They're outside of the scope of this document but there are plenty of docs out there on the interwebs who can give you all the information you need to rock and roll.
- At the moment its open to everyone, go to global configuration and set up ACL's
- Your builds aren't very continuous yet, you can schedule builds periodically by going into the project configuration area, or I believe you can tie it into githubs callback mechanisms
Observers In Rails
In an effort to reduce the clutter of before and after callbacks in your Rails models an Observer can be used. In a simple use case, you may want to give the user 500 credits in your online store when they signup.
Without an observer your User model would look something similar to
class User < ActiveRecord::Base
def before_create
self.credits = 500
end
end
Which is fine, if thats all that is in your model. But once your model starts to grow its generally a good idea to keep the callbacks in a different location! Observers to the rescue! With a Rails Observer your code would look similar to
# models/user.rb
class User < ActiveRecord::Base
end
# models/user_observer.rb
class UserObserver < ActiveRecord::Observer
def before_create(user)
user.credits = 500
end
end
Now tell Rails that you want your observer to be included
# config/environment.rb
Rails::Initializer.run do |config|
config.active_record.observers = :user_observer
end
Rails will automatically understand that this observer is for the user class based on the name (convention over configuration to the rescue). Although you can also have an observer where the model can't be inferred by the name, in this case you can explicitly define the models the observer should be observing.
For instance, lets say we have an Audit observer, for a number of models.
class AuditObserver < ActiveRecord::Observer
observe :user, :item
def after_update(record)
AuditTrail.new(record, "UPDATED")
end
end
This will observe the user model and on an observed update will create a new audit trail record. Sexy huh?
So no excuses, clean your code up, use observers! Its a sexy design pattern which I feel is completely underused!
How To Debug Rails Apps
Coming from PHP I used to rely heavily on the die() command to print out debugging information. In rails nobody could really tell me if there was an equivalent, I was told to "raise", which I'm sure everyone feels is a bit dirty to use.
So I thought I'd quickly put up a few ways I debug in Rails.
Ruby-Debug
Its a sexy little gem that lets you easily step through your code and even drop into an IRB session. To use it, make sure you have the gem or plugin installed and then.
require 'ruby-debug'; debugger
Ruby debug shines when running a script/server (mongrel, webrick, etc) but can also be used with passenger, but with limitations.
Abort
This is equivelent to the PHP die() command. It takes an argument, evals and prints the statement to the screen. Note that it does halt execution.
abort('hello world')
This is a nice hacky way to print something out to the screen very fast, no matter where you are in your application.
Debug
Debug lets you easily eval something to a screen in your views.
debug('hello world')
Now that you have a bunch of debugging commands with rails, please stop using raise() to debug!
Send Keys Released!
Send keys is a capybara extension that lets you send keystrokes to an element in the browser. It uses webdriver so must be used using the @javascript tag in your features.
Check it out at http://github.com/markgandolfo/send-keys and read about it at http://markgandolfo.com/pages/send-keys
How to use it
First make sure you use the @javascript tag, to force capybara to use the webdriver driver.
Then in your features you can send characters or modifier keys to an element, or an array of modifier keys and keys. You'll need to use the css selectors to select an element.
For Example
# Send the 'a' character to the input who's id is search (#search)
And I send a to "input#search"
# Send the 'a', 'b' and 'c' characters to the input who's id is search (#search)
And I send abc to "input#search"
# You can put them in quotes if you feel more comfortable
And I send 'abc' to "input#search"
# You can also send modifier/special key strokes to an element
And I send arrow_left to "input#search"
# You can even send a combination of modifier and characters
# This will result in a the charcater 'A' being sent to the input
And I send [shift, a] to "input#search"
# Or maybe you just want to press enter
And I send enter to "input#search"
# How cool would it be to test the counter in a text area (say for a twitter app)
And I send hello to "#message"
And I should see "135" in "characters_left"
And I send backspace to "#message"
And I should see "135" in "characters_left"
# We used it to test completion suggestions
# The first suggested name was highlighted and responded to an enter keypress
And I send "bo" to "input#username"
And I should see "bob" within "username_suggestions"
And I send enter to "input#username"
Curl From Within Text Mate
I have created a small command for textmate that lets you curl a file from within textmate.
The source is on github
Installation
git clone git://github.com/markgandolfo/tmbundle.curl.git
Navigate to the download directory and double click on the curl.tmbundle file
How to use
^ ⌥ ⌘W will pop a dialog box up, place the url in text field and press enter.
The file will download and your project drawer (or project+ drawer) will refresh!
Possible Todo
- Add progress bar for larger files
- Possibly download to the current open file path (maybe add a checkbox or something)
- Maybe add some sort of output if the url can't download for some reason (404, 500, etc.)
Sqlite3 Uninitialized Constant Encoding
I was getting a crazy error while trying to migrate/create a sqlite3 database using rails 2.3.5 today. After trolling the interwebs, and the source code I finally found a hint in the stack trace that sqlite3 gem was being called half way through the filter chain.
So to fix.
gem uninstall sqlite3
And make sure you only use
gem install sqlite3-ruby
Happy hacking!
Git Completion On A Mac
To enable bash for git you need to include the git bash completion script in your profile
cd GIT_SRC/contrib/completion
cp git-completion.bash ~/bin
echo "source ~/bin/git-completion.bash" >> ~/.profile
. ~/.profile
Or simply enough if you have the latest git version from macports, follow this one command to automagically include the bash completion file in your profile shell script
echo "source /opt/local/var/macports/software/git-core/1.6.5.3_0+doc/opt/local/share/doc/git-core/contrib/completion/bash_completion.bash" >> ~/.profile
And then reload your profile
. ~/.profile
And hey presto!
How To Alias A Class Method In Ruby
To alias a class method, you can't just use the alias/alias_method methods.
Lets say we have a simple System Event class that has a log() method.
class SystemEvent
def self.log
// Do things in here (maybe database / file log depending on log levels)
end
end
If for some reason we would want to alias log to be log_me, Then I would have to lay out my class as follows
class SystemEvent
def self.log
// Do things in here (maybe database / file log depending on log levels)
end
class << self
alias :log_me :log
end
end
Changing The Cookie Domain Path In Rails
In rails 2.3.4+ if you want to modify your cooking information set by your system, in any of your environment files you can simple overwrite them as follows.
config.action_controller.session = {
:session_key => '_my_session_id',
:secret => '..your_secret_code_here..',
:domain => ".domain.com"
}
Obviously if you only want to change your domain for instance you can simple do
config.action_controller.session[:domain] = ".domain.com"
Git Tips
I'll just post a few quick little command line git commands.
Basic Commands
Sync the meta data (e.g. branch names, tag names, etc) with remote.. This will not update the branches, just the names, etc.
git fetch
To see a list of remote branches
git branch -r
To see a list of local branches
git branch
# or
git branch -l
To see both local and remote
git branch -a
Branching
To create a new branch from the branch you are currently on.
git checkout -b new_branch
To delete a branch
git branch -d new_branch
To delete a remote branch
git push origin :new_branch
To checkout into a new branch, and merge uncommitted changes
git checkout -mb new_branch
To checkout into an existing branch, merging uncommitted changes
git checkout -m existing_branch
Checkout a remote branch (one that you haven't checked out before)
git checkout --track -b branch_name origin/branch_name
Tags
To tag at the current point in time on the current branch
git tag new_tag
To push tag to a remote
git push --tags
To delete a tag (locally)
git tag -d new_tag
To delete a tag from a remote
git push origin :tags/new_tag
Rails Plugin: Associated With?
I've just released a new plugin called associated_with, that allows you to check if two objects are associated.
Check out the write up: http://markgandolfo.com/pages/associated-with
Or the GitHub Repo: http://github.com/markgandolfo/associated_with