Hao's Thoughts

Ruby, Rails, Objective-C and everything else

As a Rails Dev, You May Want to Think in the iOS Way

I am not sure this is a good idea, but I think it worths a post to share the idea to the people who’re suffering/fighting messy dependencies within their applications.

Anyone who’s been programming in Objective-C should be familiar with the delegate/protocol, you can barely make any practical iOS application without knowing the delegate/protocol. For those of you who have never been in the iOS world, this is the quick definition from apple official document

In the world of object-oriented programming, it’s important to be able to define a set of behavior that is expected of an object in a given situation. As an example, a table view expects to be able to communicate with a data source object in order to find out what it is required to display. This means that the data source must respond to a specific set of messages that the table view might send.

I am not a language expert, I have no idea who invented this, but this is definitely a brilliant idea in my opinion, you just fire a method on your collaborator, and you will be notified what to do next. You don’t ask, you tell, sounds familiar? Yeah, people in Hollywood are always saying:

You don’t call us, we will call you

It’s also well known as Tell, don’t ask.

You know what, you don’t have to be an iOS developer to benefit from this, this is generic you can apply it to your Rails applications.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class PostsController < ApplicationController
  def new
    @post = Post.new
  end

  def create
    @post = Post.new(params[:post])

    if @post.save
      redirect_to posts_path, :notice => "Post created succesfully!"
    else
      render :new
    end
  end
end

The above snippet is a classical PostsController that does nothing wrong, the coming part is the same logic implemented with delegate/protocol.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class PostsController < ApplicationController
  def new
    @post = Post.new
  end

  def create
      service = PostService.new
      service.delegate = self
      service.create_new_post(params[:post])
  end

  # PostServiceProtocol begins
  def new_post_created_successfully(post)
      redirect_to posts_path, :notice => "Post created succesfully!"
  end

  def new_post_not_created_because_of(errors)
    render :new
  end
  # PostServiceProtocol ends
end

class PostService
  attr_writer :delegate

  def create_new_post(params)
      post = Post.new(params)
      
      if post.save
        self.delegate.new_post_created_successfully(post)
      else
        self.delegate.new_post_not_created_because_of(post.errors)
      end
  end

  protected
  def delegate
    @delegate ||= NullPostServiceDelegate.new
  end
end

class NullPostServiceDelegate
  def new_post_created_successfully(post)
  # no-op
  end

  def new_post_not_created_because_of(errors)
    # no-op
  end
end

If you are still with me, you may ask: “why bother?”, why we want to do this as long as the first version is working perfectly, my explanation is you never know who’s going to call PostService#create_new_post. For now, it’s just PostsController doing that. Imagine the Rails application is providing API backend for a mobile app, someone is going to create new post via API, are we going to duplicate the logic as well as the tests? I don’t think so.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Api::PostsController < Api::Base
  def create
      service = PostService.new
      service.delegate = self
      service.create_new_post(params[:post])
  end

  # PostServiceProtocol begins
  def new_post_created_successfully(post)
    render :json => post.to_json
  end

  def new_post_not_created_because_of(errors)
    render :json => {
      :errors => errors
    }.to_json
  end
  # PostServiceProtocol ends 
end

You can see from the above example, I am having the same logic for creating post for the web endpoint and API endpoint, it’s consitent.

For now, there’s no way in plain Ruby to define protocol for a class, there’s also no way to have a class corform to specific protocol, I am considering have a gem for that, if you are interested pair with me, ping me.

Polymorphic Image Styles With Paperclip

You may have ran into the following scenario that your rails app having 1+ domain models having image with them, say Album has one cover, User has one avatar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Image < ActiveRecord::Base
  belongs_to :viewable, :polymorphic => true

  validate :viewable, :presence => true

  has_attached_file :attachment
end

class User < ActiveRecord::Base
  has_one :avatar, :class_name => "Image", :as => :viewable, :dependent => :destroy
end

class Album < ActiveRecord::Base
  has_one :cover, :class_name => "Image", :as => :viewable, :dependent => :destroy
end

As you can see, an image belongs to a viewable object, the viweable object could be an instance of User or Album or anything else.

The presumption here is that the cover is having a set of styles different from with the avatar, but we only have one Image class, what are we supposed to do? We dispatch. We let the viewable object decide the styles of the image.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Image < ActiveRecord::Base
  belongs_to :viewable, :polymorphic => true

  validate :viewable, :presence => true

  has_attached_file :attachment,
    :styles => Proc.new {|a| a.instance.viewable.image_styles}
end

class User < ActiveRecord::Base
  has_one :avatar, :class_name => "Image", :as => :viewable, :dependent => :destroy

  def image_styles
    {
      :thumb => "30x30>",
      :medium => "100x100>"
    }
  end
end

class Album < ActiveRecord::Base
  has_one :cover, :class_name => "Image", :as => :viewable, :dependent => :destroy

  def image_styles
    {
      :cart => "50x50>"
      :large => "300x300>"
    }
  end
end

We now have one Image class having different styles for different viewable objects, we can also do the same thing to default_url and path. Leave me a comment if you have better solution, thanks a lot.

Gotcha: Get DateTime.now Work With ActiveRecord::Base Scope

1
2
3
class Invitation < ActiveRecord::Base
  scope :expired, where("invitations.expired_at < ?", DateTime.now)
end

It looks nothing wrong with the above code at the first glance. Let’s have an interesting experiment,

1
2
3
4
1.9.3p327 :001 > Invitation.expired.to_sql
 => "SELECT `invitations`.* FROM `invitations`  WHERE (invitations.created_at < '2013-06-29 12:00:16')"
1.9.3p327 :002 > DateTime.now.utc
 => Sat, 29 Jun 2013 12:35:50 +0000

As you can see, the DateTime.now.utc returns “12:35:50”, while the SQL statement in the first line says “12:00:16”, there’s a mysterious gap between two of them. Actually, the “12:00:16” is the time I started the rails console, to be more precise, it’s the time the app/models/invitation.rb is loaded, it’s the time the DateTime.now in the expired scope was evaluated. This could be a serious problem for some of you, espeically under production environment, since once you deployed your application, the DateTime.now will keep what it was when the application was fully loaded by the application server, and it will be a constant value instead of a variable value as you imagined, the only chance it will get changed is the next time you deploy the application.

The problem seems to be mysterious, but the solution to the problem is pretty straightforwarding, lambda, have lambda work with the scope will make the statements in the closure evaluated dynamically every time the scope is called.

1
2
3
4
5
class Invitation < ActiveRecord::Base
  scope :expired, lambda {
    where("invitations.expired_at < ?", DateTime.now)
  }
end
1
2
3
4
1.9.3p327 :001 > Invitation.expired.to_sql
 => "SELECT `invitations`.* FROM `invitations`  WHERE (invitations.created_at < '2013-06-29 12:40:11')"
1.9.3p327 :002 > DateTime.now.utc
 => Sat, 29 Jun 2013 12:40:12 +0000

It seems to be working this time. It’s time to add more spec to cover this.

1
2
3
4
5
6
7
8
9
10
describe Invitation do
  descirbe '.expired' do
  it "should find all the expired invitations" do
    invitation_1 = Factory(:invitation, :expired_at => DateTime.now - 1.seconds)
    invitation_2 = Factory(:invitation, :expired_at => DateTime.now + 1.seconds)
  
    Invitation.expired.to_a.should == [invitation_1]
  end
  end
end

The above test will fail if you not using lambda, just keep in mind, test will save your ass every time.

Gotcha: ActiveRecord::Base.transaction Stops Working Within Callbacks

If you are not familiar with ActiveRecord::Base.transaction, i highly recommend you go through the document.

As you can see,

Exceptions will force a ROLLBACK that returns the database to the state before the transaction began.

that means the following code should rollback the transaction if any of the deposit! fails

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# transaction.rb
class Transaction < ActiveRecord::Base
  after_create :transfer_credits

  attr_accessible :credits

  belongs_to :send_account, :class_name => "Account"
  belongs_to :recv_account, :class_name => "Account"

  private
  def transfer_credits
    begin
      ActiveRecord::Base.transaction do
          send_account.deposit!(-credits)
          recv_account.deposit!(credits)
      end
    rescue => e
      Rails.logger.info e.message
    end
  end
end

# transaction_spec.rb
context "exception raised" do
  before do
    @send_account = create :account, :credits => 1000

    @transaction = Transaction.new
    @transaction.send_account = @send_account
    @transaction.credits = 1

    recv_account = double(:account).as_null_object
    @transaction.stub(:recv_account) {recv_account}
    recv_account.should_receive(:deposit!).and_raise(StandardError.new("Transfer failed!"))
  end

  it "should not transfer the credits" do
    @transaction.save

    @send_account.reload

    @send_account.credits.should == 1000
  end
end

But the result is the spec fails and says

1
2
3
4
5
6
7
8
9
10
Failures:

  1) Transaction status exception raised should not transfer the credits
     Failure/Error: @send_account.credits.should == 1000
       expected: 1000
            got: 999 (using ==)
     # ./spec/models/transaction_spec.rb:185:in `block (4 levels) in <top (required)>'

Finished in 1.73 seconds
1 example, 1 failure

It did deduct the credits from the send_account by 1 while i was expecting it should do nothing to it. What’s going wrong, the doc says it will rollback the transaction, then I tried to test if calling transfer_credits explicitly is going to work. And yes, it works. Here’s the revised version:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# transaction.rb
class Transaction < ActiveRecord::Base
  attr_accessible :credits

  belongs_to :send_account, :class_name => "Account"
  belongs_to :recv_account, :class_name => "Account"

  def transfer_credits
    begin
      ActiveRecord::Base.transaction do
          send_account.deposit!(-credits)
          recv_account.deposit!(credits)
      end
    rescue => e
      Rails.logger.info e.message
    end
  end
end

# transaction_spec.rb
describe "#transfer_credits" do
  before do
    @send_account = create :account, :credits => 1000

    @transaction = Transaction.new
    @transaction.send_account = @send_account
    @transaction.credits = 1

    recv_account = double(:account).as_null_object
    @transaction.stub(:recv_account) {recv_account}
    recv_account.should_receive(:deposit!).and_raise(StandardError.new("Transfer failed!"))
  end

  it "should not transfer the credits" do
    @transaction.transfer_credits

    @send_account.reload

    @send_account.credits.should == 1000
  end
end

GOTCHA: DO NOT place ATOMIC operations within callbacks, like before_*, after_*

Fancy Login With Red Light

I am not saying you are wrong if you are still redirecting the visitor to http://your_killer_app/login to input his/her credentials before letting him/her access the authentication required resource. I am just introducing another way of showing login page to you, the fancy way.

It’s fancy because it detects the links and forms requiring authencation in a fancy way. I am going to use Devise for authentication in the following example app, and you will learn how to have fancy login within your app.

Generate the app

1
$ rails new fancy_login_app

Generate the home controller

1
$ rails g controller home index

Generate the controller requiring authentications

1
$ rails g controller posts new create

Add Devise and red_light to the Gemfile

1
2
gem 'devise'
gem 'red_light'

Install the gems

1
2
3
$ bundle install
$ rails g devise:install
$ rails g red_light:install

Configure Devise views

1
$ rails g devise:views

The config/routes.rb should be something like

1
2
3
4
5
6
7
8
9
devise_scope :user do
  devise_for :users, :controllers => { :sessions => "sessions" }

  get "/login", :to => "sessions#new"
end

resources :posts, :only => [:new, :create]

root :to => "home#index"

Change app/controllers/posts_controller.rb to something like

1
2
3
4
5
6
7
8
9
class PostsController < ApplicationController
  before_filter authenticate_user!

  def new
  end

  def create
  end
end

Change app/views/home/index.html.erb to something like

1
<%= link_to "New Post", new_post_path %>

Fire the webrick

1
$ rails s

Visit localhost:3000, inspect the “New Post” link, you fill find the interesting rel

red_light add that rel to the link requiring authentication auto-magically. With the rel, you can play some javascript tricks to present a modal view when the link is clicked, here’s a demo.

You can also customize the config/initializers/red_light.rb to tell the red_light which before_filter should be auto blocked, for now, it’s only authenticate_user! there.

My Thoughts on Sandi Metz’s Talk on Testing Magic

Sandi Metz (@sandimetz) never lets the audiences down, this time, she brought another great talk on testing. I highly recommend you at least walk through the slides, here is the video if you are blessed.

I learned a lot from the talk that I don’t have to have 100% coverage for the test suite. Here’s some pickups you can take away:

  • DO NOT test private method, since they are the most unstable part in the whole object
  • DO NOT test out-going query message, it has no side effect to the message receiver
  • DO test the out-going command message, ensure you have told the message receiver something it should execute on its part. If you heard about “Tell, don’t ask”, you know that one object should tell another object to do something instead of asking the state of another object and make the decision based on the returned result. Since the out-going command message has side effect to the message receiver, you need to make sure the message is sent out
  • DO test the incoming query message, just ensure the return result is expected
  • DO test the incoming command message, just ensure the changes is made as expected

That’s all, happy testing. BTW, keep two things in mind for good OO and testing:

  • Dependency injection
  • Tell, don’t ask

New Gem Released: Red Light for Fancy Login

I am really happy to announce that the red_light is released, you can have fancy login effortlessly incorporated into your Rails application. Check it out, and tell me what you think about it. Tutorial will come soon.

Take a REST

REST is a buzzword in the modern web development world. “Be RESTful”, “Keep up to the RESTful rules” is usually thought as one of the best practices we need to keep in mind.

We are all blessed as a Ruby & Rails developer, since Rails has already had REST baked within its heart, for example,

1
2
3
# config/routes.rb

resources :photos

will generate 7 routes for us:

HTTP Verb Path action used for
GET /photos index display a list of all photos
GET /photos/new new return an HTML form for creating a new photo
POST /photos create create a new photo
GET /photos/:id show display a specific photo
GET /photos/:id/edit edit return an HTML form for editing a photo
PUT /photos/:id update update a specific photo
DELETE /photos/:id delete delete a specific photo

Awesome! we can also adjust which actions we want via editing the resources defined in the config/routes.rb:

1
2
3
# config/routes.rb

resources :photos, :only => [:index, :show]

Now we have only two actions left for the PhotosController, and they are GET /photos and GET /photos/:id, any other requests except these two will result in an error. So far so, good, until one day, the boss came over and told you to add comment to the photos, we want the user to comment the photos, who doesn’t? OK, roll up the sleeves, and change the config/routes.rb to:

1
2
3
4
5
6
7
# config/routes.rb

resources :photos, :only => [:new, :create] do
  member do
    post :comment
  end
end

that will give us a new routes POST /photos/:id/comment, and we add the corresponding action to the PhotosController:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class PhotosController < ApplicationController
  before_filter :find_the_current_user
  before_filter :find_the_current_photo

  def comment
    comment = current_user.comments.new(params[:comment])
    comment.commentable = current_photo

    if comment.save
      redirect_to current_photo, notice: "new comment added!"
    else
      redirect_to current_photo, notice: "oops, xxit happens!"
    end
  end
end

After done with spec, and all green, we are happy to close the ticket and move on to next issue. Wait a minute, someone is whispering “Be RESTful”, sounds like we are doing something wrong, we are not RESTful enough! We all know the above code will work and do the job, the QA will happily accept your work and everything seems to be working flawlessly. Just one thing, we are not keeping up to RESTful rules, we are adding new action besides the 7 actions given to us, that’s not good enough. Let’s fix it:

  • remove the action comment defined in the PhotosController
  • edit the config/routes.rb and change it to:
1
2
  resources :photos, :only => [:new, :create]
  resources :comments, :only => [:create]
  • add a new CommentsController and it should have action create within it
  • move the photos_controller#comment spec to comments_controller#create

that’s all, the refactor is all about moving things around, nothing more. But it’s important for two reasons:

  • we keep the PhotosController only concerned photos, that’s SRP
  • we make it clear that the comments related action should go to its own controller, we are telling the developer reading this code later after us that we have two resources defined in our app, one is photo, another is comment.

Yes, RESTful is all about resource.

Open Source the BPDUKPT

I am happy to announce I just released the v0.1 of BPDUKPT.

We’ve been processing credit card payments from the iOS device more and more, and we are facing a common problem, security. The genius invented the DUKPT to ensure the credit card data will never be hijacked by some middleman between our client app and our server, that’s really great for the card holders. But for the developer, we’ve got no luck to have a ready-to-go solution to decrypt the magnetic data captured from the card reader.

Here comes the BPDUKPT, you can decrypt magnetic strips like a boss.

1
2
3
4
5
6
7
8
9
10
#include "BPDUKPTParser.h"

NSString *magData = @"foobarbaz"; // this is the magnetic strips data captured from the card reader
BPDUKPTIDTechParser *parser = [[BPDUKPTIDTechParser alloc] initWithHID:magData];
BPDUKPTParsingResult *result = [parser parse];

NSLog(@"the encrypted track 1 is: %@", result.track1);
NSLog(@"the encrypted track 2 is: %@", result.track2);
NSLog(@"the encrypted track 3 is: %@", result.track3);
NSLog(@"the KSN is: %@", result.ksn);

Tell me what you think about it, and please help to make it better.