Christian Neukirchen – Ruby on Rails Podcast

Christian Neukirchen, Ruby veteran and inventor of the tumblelog.

Christian Neukirchen – Ruby on Rails Podcast

Christian Neukirchen, Ruby veteran and inventor of the tumblelog.

#72 Adding an Environment

Rails comes with three environments: development, test, and production. But, you aren’t restricted to just these. You can add your own! See how in this episode.

#72 Adding an Environment

Rails comes with three environments: development, test, and production. But, you aren’t restricted to just these. You can add your own! See how in this episode.

Total ADD!

As you may have noticed I really like Flex and Ruby On Rails. So my days job is to provide consulting services for Flex and Ruby On Rails. Currently I am working on a cool Ruby On Rails project for a customer in the online food ordering world. Man I get hungry when testing the application we write. I also work for another customer to create a Flex front-end for an online application in the insurance arena (First Notice of Injury, Certificate of Insurance, Payroll report and Web Payment).

As if that wasn’t enough, I agreed to write a book together with a couple of other cool guys in the Flex and Ruby On Rails arena. That’s still far away, but we will provide some more information on this in the coming weeks. If that’s not enough I am creating two startups.

So besides my consulting engagements my professional life looks this:

20070921_add.gif

Now if that doesn’t qualify as Attention Deficit Disorder.

I am writing this more as a reflection to myself. At the beginning of the year Pascal commented on my blog that execution is everything to turn an idea into a business. We have been working hard on myspyder.net and it’s start to be looking really good. Digital-seed has the potential to become a really cool platform for eLearning and has the potential to turn into a big business. The “Book”, is more of a personal challenge and interest.

So besides execution, focus and determination are required to turn an idea into a real business.

So something will need to give. I still need to continue my consulting engagements in order to bring in money for the food. The “Book” will be a challenge from a writing point of view, but it will be a great learning and self improvement experience. The material for the book is what I have been “geeking” around for the past several years so it’s going to be a pleasure to write about it. Phase one for MySpyder is close to completion (still a couple of month). And phase two is really where the beef will be. So right now it looks like DigitalSeed is on the line for me. The contracts are not yet signed, so the door is open for me to pull out. This will hurt the people I started the endeavor with, and I really don’t like this. But I cannot stay on board some project unless I can dedicate enough resource to complete it. Let me chew on these thoughts over the week-end.

In Spirit,
Daniel.

View Testing 2.0

I’ve come to realize how anti-web 2.0 this blog really is. Why are we, the authors, doing all the work?! Where is the user generated content? How can we pretend to be Rails developers when we waste our time writing all these blog posts!

No longer. Today all that changes. It’s time you guys start pulling your weight.

Let’s Talk About Testing Views

There seem to be about a bajillion different solutions to the ‘problem’ of testing views. If you’re into testing and also into Rails, my guess is you’ve tried a few different styles before settling on your current method. (hey, me too)

What I would love is for you to post your current, favorite, flavor-of-the-month style of view testing. What library do you use, where can we download it, and how about some sample code?

When commenting, wrap your code in <code>code tags</code> and use "textile":http://textism.com/tools/textile/ for links.

Everyone has different taste, but the hope is we’ll have enough options for people to find something they like. Something tasty.

Hit me!

#71 Testing Controllers with RSpec

Controllers are tricky to test, and there’s no perfect way to do it. In this episode you will see how I test controllers, and my reasoning behind it.

#71 Testing Controllers with RSpec

Controllers are tricky to test, and there’s no perfect way to do it. In this episode you will see how I test controllers, and my reasoning behind it.

#70 Custom Routes

In this episode you will learn how to add custom routes, make some parameters optional, and add requirements for other parameters.

#70 Custom Routes

In this episode you will learn how to add custom routes, make some parameters optional, and add requirements for other parameters.

Exceptional Slicehost support

Our server that hosts onrails.org and time.onrails.org died on Sunday. Before we could react Slicehost migrated us to new hardware and everything was up and running. Thanks guys, awesome support!

The events where the following:

  • [Slicehost] Sep 1, 2007 3:04 PM, Emergency Server Reboot and HW Migration – onrails
  • [Montastic] Sep 1, 2007 3:20 PM, Website status: unreachable
  • [Slicehost] Sep 1, 2007 3:44 PM, Emergency HW Migration #2 – onrails.
  • [Montastic] Sep 1, 2007 4:12 PM, Website status: OK

[Slicehost] indicates emails we received from Slicehost, and [Montastic] emails from the monitoring system we use. Slicehost warned us of the situation and action they are taking before our monitoring system found out that the service was down. Well, apparently they migrated us to new hardware that had another issue (bad memory) and they moved us a second time to different hardware. Well no data was lost, all our service are up and running and I didn’t have to cut short my BBQ. Thanks!

Update1: Well something is wrong with the template of our blogs. I am not sure it’s related to the update of hardware as it was working last night. More to come.

#69 Markaby in Helper

Do you ever need to generate HTML code in a helper method? Placing it directly in Ruby strings is not very pretty. Learn a great way to generate HTML through Markaby in this episode.

#69 Markaby in Helper

Do you ever need to generate HTML code in a helper method? Placing it directly in Ruby strings is not very pretty. Learn a great way to generate HTML through Markaby in this episode.

Using ActiveResource to consume web-services

        Today I’m reviewing Joe Van Dyk’s <a href="http://code.google.com/p/monkeycharger/">monkeycharger</a> application, which is a web-service for storing and charging credit cards. I loved looking at this app, because its only interface is a RESTful web service: there is no <span class="caps">HTML</span> involved. (If you’ve never written an app that only exposes a web-service UI, you ought to. It’s a blast.)


In general, Joe has done a fantastic job with keeping the controllers slim and moving logic to models. The only significant gripe I had with the application is that it is not ActiveResource compatible.


For those of you that are late to the party, ActiveResource is the newest addition to the Rails family. It lets you declare and consume web-services using an ActiveRecord-like interface…BUT. It is opinionated software, just like the rest of Rails, and makes certain assumptions about the web-services being consumed.


<ol>
<li>The service must understand Rails-style <span class="caps">REST</span> URLs. (e.g. “POST <!--more-->.xml” to create a credit card, etc.)</li>
    <li>The service must respond with a single <span class="caps">XML</span>-serialized object (Rails-style).</li>
    <li>The service must make appropriate use of <span class="caps">HTTP</span> status codes (404 if the requested record cannot be found, 422 if any validations fail, etc.).</li>
</ol>


It’s really not much to ask, and working with ActiveResource (or “ares” as we affectively call it) is a real joy.


However, monkeycharger tends to do things like the following:
1
2
3
4
5
6
7
8
9
10
class AuthorizationsController < ApplicationController
  def create
    @credit_card   = Authorizer.prepare_credit_card_for_authorization(params)
    transaction_id = Authorizer::authorize!(:amount => params[:amount], :credit_card => @credit_card)
    response.headers['X-AuthorizationSuccess'] = true
    render :text => transaction_id
  rescue AuthorizationError => e
    render :text => e.message
  end
end
Three things: the request is not representing an “authorization” object, the response is not <span class="caps">XML</span>, and errors are not employing <span class="caps">HTTP</span> status codes to indicate failure.


Fortunately, this is all really, really easy to fix. First, you need (for this specific example) an Authorization model (to encapsulate both the the <span class="caps">XML</span> serialization and the actual authorization).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Authorization
  attr_reader :attributes

  def initialize(attributes)
    @attributes = attributes
  end

  def credit_card
    @credit_card ||= Authorizer.prepare_credit_card_for_authorization(attributes)
  end

  def authorize!
    @transaction_id = Authorizer.authorize!(:amount => attributes[:amount],
      :credit_card => credit_card)
  end

  def to_xml
    { :transaction_id => @transaction_id }.to_xml(:root => "authorization")
  end
end
Then, we rework the AuthorizationsController to use the model:
1
2
3
4
5
6
7
8
class AuthorizationsController < ApplicationController
  def create
    authorization = Authorization.new(params[:authorization])
    authorization.authorize!
    render :xml => authorization.to_xml, :status => :created
  rescue AuthorizationError => e
    render :xml => "<errors><error>#{e.message}</error></errors>", :status => :unprocessable_entity
  end
(Note the use of the “created” status, which is <span class="caps">HTTP</span> status code 201. Other verbs just use “ok”, status code 200, to indicate success. Also, with an error, we return an “unprocessable_entity” status, which is <span class="caps">HTTP</span> status code 422. ActiveResource will treat that as a failed validation.)


With that change, you could now use ActiveResource to authorize a credit card transaction:
1
2
3
4
5
6
7
8
9
10
11
12
class Authorization < ActiveResource::Base
  self.site = "http://my.monkeycharger.site"
end

auth = Authorization.new(:amount => 15, :credit_card_id => 1234,
  :remote_key => remote_key_for_card)

if auth.save
  puts "success: #{auth.transaction_id}"
else
  puts "error: #{auth.errors.full_messages.to_sentence}"
end
It should be mentioned, too, that making an app ActiveResource-compatible does nothing to harm compatibility with non-ActiveResource clients. Everything is <span class="caps">XML</span>, both ways, with <span class="caps">HTTP</span> status codes being used to report whether a request succeeded or not. Win-win!


Obviously, real, working code trumps theoretical whiteboard sketches every time, and Joe is to be congratulated on what’s done. Even though ActiveResource-compatibility can buy you a lot, you should always evaluate whether you really need it and implement accordingly.

Using ActiveResource to consume web-services

Today I’m reviewing Joe Van Dyk’s monkeycharger application, which is a web-service for storing and charging credit cards. I loved looking at this app, because its only interface is a RESTful web service: there is no HTML involved. (If you’ve never written an app that only exposes a web-service UI, you ought to. It’s a blast.)

In general, Joe has done a fantastic job with keeping the controllers slim and moving logic to models. The only significant gripe I had with the application is that it is not ActiveResource compatible.

For those of you that are late to the party, ActiveResource is the newest addition to the Rails family. It lets you declare and consume web-services using an ActiveRecord-like interface…BUT. It is opinionated software, just like the rest of Rails, and makes certain assumptions about the web-services being consumed.

  1. The service must understand Rails-style REST URLs. (e.g. “POST /credit_cards.xml” to create a credit card, etc.)
  2. The service must respond with a single XML-serialized object (Rails-style).
  3. The service must make appropriate use of HTTP status codes (404 if the requested record cannot be found, 422 if any validations fail, etc.).

It’s really not much to ask, and working with ActiveResource (or “ares” as we affectively call it) is a real joy.

However, monkeycharger tends to do things like the following:

1
2
3
4
5
6
7
8
9
10
class AuthorizationsController < ApplicationController
  def create
    @credit_card   = Authorizer.prepare_credit_card_for_authorization(params)
    transaction_id = Authorizer::authorize!(:amount => params[:amount], :credit_card => @credit_card)
    response.headers['X-AuthorizationSuccess'] = true
    render :text => transaction_id
  rescue AuthorizationError => e
    render :text => e.message
  end
end

Three things: the request is not representing an “authorization” object, the response is not XML, and errors are not employing HTTP status codes to indicate failure.

Fortunately, this is all really, really easy to fix. First, you need (for this specific example) an Authorization model (to encapsulate both the the XML serialization and the actual authorization).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Authorization
  attr_reader :attributes

  def initialize(attributes)
    @attributes = attributes
  end

  def credit_card
    @credit_card ||= Authorizer.prepare_credit_card_for_authorization(attributes)
  end

  def authorize!
    @transaction_id = Authorizer.authorize!(:amount => attributes[:amount],
      :credit_card => credit_card)
  end

  def to_xml
    { :transaction_id => @transaction_id }.to_xml(:root => "authorization")
  end
end

Then, we rework the AuthorizationsController to use the model:

1
2
3
4
5
6
7
8
class AuthorizationsController < ApplicationController
  def create
    authorization = Authorization.new(params[:authorization])
    authorization.authorize!
    render :xml => authorization.to_xml, :status => :created
  rescue AuthorizationError => e
    render :xml => "<errors><error>#{e.message}</error></errors>", :status => :unprocessable_entity
  end

(Note the use of the “created” status, which is HTTP status code 201. Other verbs just use “ok”, status code 200, to indicate success. Also, with an error, we return an “unprocessable_entity” status, which is HTTP status code 422. ActiveResource will treat that as a failed validation.)

With that change, you could now use ActiveResource to authorize a credit card transaction:

1
2
3
4
5
6
7
8
9
10
11
12
class Authorization < ActiveResource::Base
  self.site = "http://my.monkeycharger.site"
end

auth = Authorization.new(:amount => 15, :credit_card_id => 1234,
  :remote_key => remote_key_for_card)

if auth.save
  puts "success: #{auth.transaction_id}"
else
  puts "error: #{auth.errors.full_messages.to_sentence}"
end

It should be mentioned, too, that making an app ActiveResource-compatible does nothing to harm compatibility with non-ActiveResource clients. Everything is XML, both ways, with HTTP status codes being used to report whether a request succeeded or not. Win-win!

Obviously, real, working code trumps theoretical whiteboard sketches every time, and Joe is to be congratulated on what’s done. Even though ActiveResource-compatibility can buy you a lot, you should always evaluate whether you really need it and implement accordingly.