PaySimple Plugin/Gem
Posted by Jonathan on June 29, 2007 at 12:09 PM
For RoundHaus I needed to find a payment processor for collecting subscription payments and after doing a fair bit of research I found PaySimple. I was very impressed with their service and support. It is absolutely top notch and I highly recommend them.
There wasn’t anything out there in Rubyland for dealing with PaySimple though so I created the PaySimple plugin/gem. The library provides a simple interface to find, create, edit, delete, and query subscriptions using the PaySimple SOAP API.
Installation:
The simple way:
$ sudo gem install paysimple
Directly from repository:
$ svn co svn://svn.roundhaus.com/daikini/plugins/paysimple
Requirements:
- soap4r 1.5.6 or higher
Configuration:
When you signup for a PaySimple account you can setup a source key and optionally a pin and client ip address. These are your credentials when using the PaySimple API.
1 PaySimple.key = "123456" 2 PaySimple.pin = "topsecret" 3 PaySimple.client_ip = "192.168.0.1"
Usage:
1 require 'rubygems' 2 require 'paysimple' 3 4 # Bill Jennifer $12.00 monthly 5 begin 6 customer_number = PaySimple::Subscription.create( 7 :CustomerID => 12345, 8 :BillingAddress => { 9 :FirstName => "Jennifer", 10 :LastName => "Smith" 11 }, 12 :CreditCardData => { 13 :CardNumber => '4444555566667779', 14 :CardExpiration => '0908' 15 }, 16 :Schedule => :monthly, 17 :Next => "2008-09-05", 18 :Amount => 12.00 19 ) 20 21 puts "Subscription created with Customer Number: #{customer_number}" 22 rescue Exception => e 23 puts "An error occurred: #{e.message}" 24 end 25 26 27 # Update subscription to use new credit card 28 begin 29 customer_number = 12345 30 response = PaySimple::Subscription.update( 31 customer_number, 32 :CreditCardData => { 33 :CardNumber => '4444555566667779', 34 :CardExpiration => '0908' 35 } 36 ) 37 38 puts "Subscription updated" 39 rescue Exception => e 40 puts "An error occurred: #{e.message}" 41 end 42 43 44 # Delete subscription 45 begin 46 customer_number = 12345 47 response = PaySimple::Subscription.delete(customer_number) 48 49 puts "Subscription removed from active use." 50 rescue Exception => e 51 puts "An error occurred: #{e.message}" 52 end 53 54 55 # Find an existing subscription 56 begin 57 customer_number = 12345 58 customer = PaySimple::Subscription.find(customer_number) 59 60 puts "Found subscription for #{ [customer["BillingAddress"]["FirstName"], customer["BillingAddress"]["LastName"]].join(" ")}" 61 rescue Exception => e 62 puts "An error occurred: #{e.message}" 63 end 64 65 66 # Process one-time sale against existing subscription 67 begin 68 customer_number = 12345 69 response = PaySimple::Subscription.charge(customer_number, :Amount => 34.56) 70 71 if response['Response'] == "Approved" 72 puts "One-time charge successful." 73 else 74 puts "An error occurred: #{response['Error']}" 75 end 76 rescue Exception => e 77 puts "An error occurred: #{e.message}" 78 end 79 80 81 # Search for transactions 82 begin 83 response = PaySimple::Subscription.query( 84 [ 85 { :Field => 'amount', :Type => 'gt', :Value => '5.0' } 86 ] 87 ) 88 89 response.transactions.each do |transaction| 90 puts "CustomerID = #{transaction['CustomerID']}, Amount = #{transaction['Details']['Amount']}" 91 end 92 rescue Exception => e 93 puts "An error occurred: #{e.message}" 94 end
LICENSE:
paysimple is licensed under the MIT License.
Copyright (c) 2007 [Jonathan Younger], released under the MIT license
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Daikini Plugins
Posted by Jonathan on June 23, 2007 at 09:07 AM
With the release of a several plugins and the announcement that RoundHaus allows for public access to projects, I’ve opened up the Daikini Plugins RoundHaus project so that you can keep up to date on the goings on of the plugins I’ve released.
Enjoy!
RoundHaus: Demo Account
Posted by Jonathan on June 22, 2007 at 10:54 PM
RoundHaus supports giving public access to projects. You can specify on a per project basis whether or not you want to grant read-only privileges to anonymous users.
To demonstrate this capability in RoundHaus check out the Demo Account where you can browse around and check out some of the cool things that RoundHaus provides, like integrated code coverage, continuous integration for changesets and changeset diffs.
And be sure to signup! to be notified when RoundHaus goes live!
EventAttribute Rails Plugin: Boolean datetime attributes for AR models
Posted by Jonathan on June 21, 2007 at 06:05 AM
Jamis Buck posted a tip on using datetime columns in the database to represent boolean values. According to Jamis, this gives you the capability down the road to not only report whether the event occurred, but how frequently over various periods of time. I’ve been using this technique in many different project since I read his post and I must say that it has worked very well.
Jamis’ example was pretty simple and only provided read-only access to the attribute and no searching. I decided to kick this technique up a notch to provide read-write access as well as searching using dynamic finders. I’ve wrapped it all up in a Rails plugin that makes it easy to create boolean attributes from datetime columns.
To install the EventAttribute plugin you can:
./script/plugin install svn://svn.roundhaus.com/daikini/plugins/event_attribute
or if you prefer piston:
piston import svn://svn.roundhaus.com/daikini/plugins/event_attribute vendor/plugins/event_attribute
Here is an example of using the EventAttribute plugin to replicate Jamis’ example from his post:
1 class Referral < ActiveRecord::Base 2 event_attribute :applied_at, :attribute => 'pending', :nil_equals => true 3 event_attribute :subscribed_on 4 end
1 referral = Referral.create(:applied_at => Time.now) 2 referral.pending? # => false 3 referral.subscribed? # => false 4 5 referral.pending = true 6 referral.applied_at # => nil 7 referral.pending? # => true 8 9 referral.subscribed = true 10 referral.subscribed_at # => Time.now 11 referral.subscribed? # => true 12 13 14 # Dynamic finders are also added so that you can search on these boolean attributes. 15 Referral.find_all_by_pending(true) # => [Referral objects] 16 17 # or 18 Referral.find_by_name_and_pending_and_subscribed('John Smith', false, true) # => Referral object 19
SimpleHighlight - Syntax highlighting for SimpleLog
Posted by Jonathan on June 14, 2007 at 10:02 AM
SimpleHighlight uses the ultraviolet library for syntax highlighting of code inside posts. You must have ultraviolet installed and working for SimpleHighlight to work.
Installation
To install SimpleHighlight you can:
./script/plugin install svn://svn.roundhaus.com/daikini/plugins/simple_highlight
or if you prefer piston:
piston import svn://svn.roundhaus.com/daikini/plugins/simple_highlight vendor/plugins/simple_highlight
You will also need to include the css for the particular theme you are using. The css files are included as part of the ultraviolet library.
Usage
To use SimpleHighlight you just add code tags around whatever code you want to have highlighted. You can optionally specify the language to use with by adding a class on to the code tag with the language you want. By default code uses the ruby_on_rails language. SimpleHighlight also defaults to using the “all_hallows_eve” theme. Right now the only way to change the theme is to change it in the plugin code itself.
Here is some RubyOnRails code:
1 class Post 2 has_many :comments 3 end
Here is some Javascript code:
1 function init() 2 // init vars, run some initial functions 3 { 4 body_container = document.getElementById('wrapper'); 5 search_field = document.getElementById('q'); 6 search_div = document.getElementById('search'); 7 results_wrapper_div = document.getElementById('search_results'); 8 loading_msg_span = document.getElementById('loading'); 9 results_span = document.getElementById('results'); 10 tag_block = document.getElementById('tags'); 11 author_block = document.getElementById('authors'); 12 13 default_field_value = 'Enter your terms, hit enter'; 14 message_when_searching = 'Searching...'; 15 message_when_done = ''; 16 results_when_searching = ''; 17 18 passive_search_text_color = '#777'; 19 active_search_text_color = '#000'; 20 21 isIE = false; 22 23 searchInit(search_field); // capture key events 24 clearSearch(); // set everything right with search field / areas 25 }
Here is some CSS code:
1 /** 2 * =Body 3 ***************************/ 4 body { 5 font: normal 14px/22px Helvetica, Arial, Helvetica, Sans-serif; 6 margin: 0; 7 padding: 0; 8 color: #4c4c4c; 9 background-color: #273d4a; 10 text-align: center; 11 }
RoundHaus: Code Browser
Posted by Jonathan on June 13, 2007 at 06:29 AM
This is the fifth and final post in the series of presenting the features in RoundHaus. If you missed them here are the first, second, third and fourth in the series.
Sometimes it’s nice to be able to look at a file without having to check it out from the repository. Perhaps it is to look at a previous version of the file or maybe it is to review the code someone just checked in. RoundHaus displays beautifully highlighted code for whatever reason you are looking at it for.
Code coverage analysis is built right into the code browser so you can see if the code you are looking at has at least been executed by the testing framework.
Covered code

Uncovered code

Be sure to signup! to be notified when RoundHaus goes live.
RoundHaus: People & Permissions
Posted by Jonathan on June 12, 2007 at 05:59 AM
This is the fourth in the series of posts presenting the features in RoundHaus. If you missed them here are the first, second and third in the series.
RoundHaus makes adding new people and assigning permissions to projects quick and simple.
People
The people view shows a list of all the people in the account and from there it is just a click away from adding, editing or removing people.

New Person
Adding a person to the account is equally simple. Granting a person administrator privileges lets them manage people, permissions and signals. The default permission specifies what level of access is granted to existing and new projects for the account.

Permissions
Each project has its own set of permissions for setting who can access the project and what rights they have. There are three permission levels that can be assigned.
- Does not have access to the project
- Has read-only access to the project
- Has full access to the project

Be sure to signup! to be notified when RoundHaus goes live.
RoundHaus: Signals
Posted by Jonathan on June 11, 2007 at 07:03 AM
This is the third in the series of posts presenting the features in RoundHaus. If you missed them here are the first and second in the series.
Signals are external applications that RoundHaus interacts with when a changeset is committed to the repository or a continuous integration process has completed. Currently RoundHaus supports four different types of Signals.

ATOM feeds
Each project in RoundHaus has its own ATOM feed of all of the changesets for that project. Also each account in RoundHaus has a single feed that contain all of the changesets for all of the projects in the account.

Campfire Signal
I use 37Signals’ Campfire application to keep in direct contact with my fellow co-workers. Any time someone checks something in to the repository we’re all notified at once.

Twitter Signal
Twitter has become a very popular way of keeping friends, family and co-workers up-to-date on what is happening right now. RoundHaus can do the same and send tweets whenever someone commits something to the repository.

Lighthouse Signal
Lighthouse is the beautifully simple issue tracking application from those ninjas over at Active Reload. Using Lighthouse Beacons, RoundHaus can send changeset notifications along with the list of files affected by the change.

Be sure to signup! to be notified when RoundHaus goes live.
Rails Validation Test Helpers
Posted by Jonathan on June 10, 2007 at 11:22 AM
I’ve written a couple test helpers that I’ve found to be pretty useful when testing ActiveRecord validations. Just add them to your RAILS_ROOT/test/test_helper.rb file.
The assert_errors_on method takes an object and a hash of attributes and the validation errors that they should have.
The assert_no_errors_on method takes an object and a list of attributes that should not have any validation errors.
1 def assert_errors_on(object, errors = {}) 2 assert !object.valid? 3 errors.each do |attribute, error| 4 assert_equal error.to_a.sort, object.errors.on(attribute).to_a.sort, "Error on #{attribute}" 5 end 6 end 7 8 def assert_no_errors_on(object, *attributes) 9 object.valid? 10 attributes.each { |attribute| assert_nil object.errors.on(attribute), "Error on #{attribute}" } 11 end
You can then use them like this:
1 require File.dirname(__FILE__) + '/../test_helper' 2 3 class PersonTest < Test::Unit::TestCase 4 def test_should_require_unique_email_address 5 # Using assert_errors_on method 6 person = Person.new :email_address => "cosmo@sprockets.com" 7 assert_errors_on person, :email_address => "has already been taken" 8 9 # Doing the same thing but without the assert_errors_on method 10 person = Person.new :email_address => "cosmo@sprockets.com" 11 assert !person.valid? 12 assert_equal "has already been taken", person.errors.on(:email_address) 13 14 # Using assert_no_errors on method 15 person = Person.new :email_address => "john.smith@example.com" 16 assert_no_errors_on person, :email_address 17 18 # Doing the same thing but without the assert_no_errors_on method 19 person = Person.new :email_address => "john.smith@example.com" 20 person.valid? 21 assert_nil person.errors.on(:email_address) 22 end 23 24 # We can test other attributes independently as well 25 26 def test_should_require_a_first_name 27 person = Person.new 28 assert_errors_on person, :first_name => "can't be blank" 29 30 person = Person.new :first_name => "Jonathan" 31 assert_no_errors_on :person, :first_name 32 end 33 34 def test_should_require_a_last_name 35 person = Person.new 36 assert_errors_on person, :last_name => "can't be blank" 37 38 person = Person.new :last_name => "Younger" 39 assert_no_errors_on :person, :last_name 40 end 41 42 def test_should_blah_blah_blah 43 ... 44 end 45 end
Updated 2007-11-28: Added additional attribute tests to show that the attributes can be tested in isolation without regards to ensuring that all of the attributes have to be valid for testing to work.
Updated 2007-09-14: Added code for showing what the tests would look like by not using the test helpers.
Rails Fixtures
Posted by Jonathan on June 10, 2007 at 10:00 AM
Tom Preston-Werner outlined pretty well in his RailsConf 2007 presentation why fixtures suck so I’ll just repeat them here:
Why fixtures suck:
- swampy
- become an unmanageable mess
- hard to keep track of links between data
- things aren’t easy to refactor
- no namespacing – i.e. you need to have lots of users in different states for good testing
- brittleness – one day you add a new column to a table, and half of your tests fail
- no validation – there is no automatic way for ActiveRecord validation for fixture data
- contamination – tests pass independently, but fail when running all at once
- performance – fixtures can be slow
Tom came up with a possible solution to these problems but I’ve been kicking around a different solution. Let me just say that I’m not entirely sure there is a perfect solution to the fixture problem but the one I came up with works really well for me.
I’ve worked on two projects that took two approaches to the fixture problem. Project A used the Rails fixture files as intended. Project A pretty much experienced all of the problems above when dealing with the fixture data. The big kicker was that performance was absolutely horrible. The combined unit and functional tests took over 2 minutes to run. Needless to say less tests were being written as time went on.
Not wanting to repeat the testing hell that Project A suffered from Project B didn’t use a single fixture file. Instead Project B used the totally kick-ass Mocha library for all the tests. Objects were mocked or stubbed as needed. The big problem that Project B experienced was that there was easily twice the amount of test code than there was actual code because the test code was pretty much just duplicating all of the logic inside the regular code anyway. Every ActiveRecord find method was being mocked out and returned manually instead of letting the database do its job. The big benefit of not hitting the database was that the tests were super fast. Project B was tested very well but the tests were very brittle.
After thinking about the fixture problem for awhile it occurred to me that I really liked the Project B approach of specifying what data were being used for a given test. I just didn’t like the fact that I was having to write a lot of test code to do what the database should have been doing. The Project A approach allowed the database do what the database does best but sucked because of the fixture file problem in general.
So when it came time for Project C I needed an entirely new approach to testing. What if I could combine the benefits of each approach, Project A letting the database do what its best at and the Project B approach of specifying the data in the exact context it is needed? So I did just that and for me it solved the fixture problem very well. I converted Project A to use this new approach and the tests went down from over 2 minutes to run to just under 25 seconds. Still not great but way better than it was and it no longer suffered from the other fixture problems it was having. Needless to say, testing is going up and the code is getting better.
Okay, enough talking, let’s see some freakin’ code already. Let’s say that your application has a Person model and each person is required to have a unique email address.
1 class Person < ActiveRecord::Base 2 validates_uniqueness_of :email_address 3 end
Using the normal fixture file in test/fixtures/people.yml you would add a person entry with an email address you could test against.
1 first: 2 id: 1 3 name: Cosmo Spacely 4 email_address: cosmo@sprockets.com
Then in your test/unit/person.rb unit test you would have a test something like this:
1 require File.dirname(__FILE__) + '/../test_helper' 2 3 class PersonTest < Test::Unit::TestCase 4 fixtures :people 5 6 def test_should_require_unique_email_address 7 person = Person.new :email_address => "cosmo@sprockets.com" 8 assert !person.valid? 9 assert_equal "has already been taken", person.errors.on(:email_address) 10 11 person = Person.new :email_address => "john.smith@example.com" 12 person.valid? 13 assert_nil person.errors.on(:email_address) 14 end 15 end
To change this example to use the fixture method you would simply remove the call to fixtures :people that loaded the yaml fixture file. Then in the test method you would add a call to the new fixture method and pass in the data that should be loaded.
1 require File.dirname(__FILE__) + '/../test_helper' 2 3 class PersonTest < Test::Unit::TestCase 4 # We don't need to load the people fixtures anymore 5 # fixtures :people 6 7 def test_should_require_unique_email_address 8 # The fixture method will load in the data we need for this test 9 fixture :person, :email_address => "cosmo@sprockets.com" 10 11 person = Person.new :email_address => "cosmo@sprockets.com" 12 assert !person.valid? 13 assert_equal "has already been taken", person.errors.on(:email_address) 14 15 person = Person.new :email_address => "john.smith@example.com" 16 person.valid? 17 assert_nil person.errors.on(:email_address) 18 end 19 end
If you think taking this approach would work for you then you can install the fixture plugin with:
script/plugin install svn://svn.roundhaus.com/daikini/plugins/fixture
RoundHaus: Project Overview
Posted by Jonathan on June 10, 2007 at 07:20 AM
This is the second post in the series of presenting RoundHaus features. The first post went over the RoundHaus Dashboard.
The project overview page is a dashboard type view for a single project. This page explains how to checkout the project from subversion, what the last three changesets are and the top 10 files in the project in order of least covered to most covered code.
For project administrators this page is where changeset notifications, called Signals, are accessed. The next post in the series will go over exactly what Signals are and what they can do.

Be sure to signup! to be notified when RoundHaus goes live.
RoundHaus: Dashboard
Posted by Jonathan on June 09, 2007 at 04:40 PM
This is the first post in a series detailing the different features of RoundHaus. So let’s get started then.
First up is the dashboard. The dashboard displays the ten most recent changesets for each of the projects a person has access to. For paying accounts each changeset displayed shows the result of the continuous integration and for successful builds the amount of code covered as reported by the excellent rcov application.

Be sure to signup! to be notified when RoundHaus goes live.
RoundHaus
Posted by Jonathan on June 09, 2007 at 04:23 PM
Nathaniel Talbott’s RailsConf 2006 keynote presentation of “Homesteading: A Thriver’s Guide” just ended and I left wanting to buy that ticket. I knew I wanted to go, but where? It had to be someplace that I was interested in. Some place I was passionate about. With two children and my wife competing for my limited time it had to be something that I had a personal stake in. Doing it for someone else according to their whims wasn’t going to cut it for me. Unless it’s something really great it’s hard to get passionate about someone else’s dream. I needed to make something for me. Something that I wanted and would use.
Every project I’ve worked on has had the same basic requirements of getting started. Get the source control repository up and access granted to it. Get the continuous integration working so that tests are run on the checked in code. RSS feeds and Campfire notifications so we can keep updated on what is happening. Know at a basic level what code has been executed by the testing suite. It’s not that any of this is difficult to setup. It’s just error prone, repetitive and cumbersome. Three adjectives that typically strike a nerve with programmers. It certainly bothers me.
When I get started on a project I want to jump right in. Get that problem solved and that code out there. Release early, release often and all that. Having to go through the somewhat painful task of going through the setup motions every project is one itch that I decided to scratch.
RoundHaus is my attempt to scratch that itch. Make it easy to get started and easy to stay on top of it. I don’t think I’m the only one with this particular itch so hopefully it will be a great help to others as well. I’m buying that ticket. Only time will tell if the destination pans out.
Be sure to signup! to be notified when RoundHaus goes live.
Starting 'er up again
Posted by Jonathan on June 09, 2007 at 04:08 PM
This time running on SimpleLog by Garrett Murray.
I'll be updating more often. Really, I'm serious this time.
