Hao's Thoughts

Ruby, Rails, Objective-C and everything else

Rails 4 Gotcha: Nested Form Created New Child Object Every Time Parent Object Is Updated

I’ve been playing with Rails 4 recently, the strong parameter, as you all know, is a new feature just appeared in Rails 4. It will get really tricky when you are posting a nested form.

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
# question.rb
class Question < ActiveRecord::Base
  has_one :answer, dependent: :destroy
  accepts_nested_attributes_for :answer, reject_if: Proc.new {|a| a["body"].blank?}
end

# answer.rb
class Answer < ActiveRecord::Base
  belongs_to :question
end



# questions_controller.rb
class QuestionsController < ApplicationController
  def update
      @question = Question.find(params[:id])
      
      if @question.update_attributes(question_params)
        redirect_to questions_path, notice: "Question updated."
      else
        render :edit
      end
  end

  protected
  def question_params
      params.require(:question).permit(:content, answer_attributes: [:body])
  end
end

# questions/edit.html.haml

= form_for @question do |f|
  = f.text_field :content

  = f.fields_for :answer, @question.answer || @question.build_answer do |builder|
    = builder.text_field :body

  = f.submit "Update", disable_with: "Updating ..."

The above code are really straightforward Rails 4 code for updating a given question with its answer, the problem right now is every time I updated the question, no matter if updated the associated answer or not, it will destroy the old answer object, and create a new answer object for me, that’s really annoying, the gotcha here is you need have the answer’s id field go through for strong parameter, otherwise, it will be filtered out, and the old answer will be destroyed, and a new answer object will be created silently. The revision is something like:

1
2
3
4
5
6
7
8
9
# questions_controller.rb
class QuestionsController < ApplicationController
  ...

  protected
  def question_params
      params.require(:question).permit(:content, answer_attributes: [:id, :body])
  end
end

Happy coding.

How to Capture Changes in After_commit

@flyerhzm has done a great job on when you should use after_commmit instead of after_save in this post. One thing is sometimes you may want to check if there’s any changes to a specific column before executing the code within the callback. Sadly, since it is in after_commit block, all the changes have been persisted into the database, you will never got a chance to check if there’s any changes to any columns, that’s the problem we are going to attack.

1
2
3
4
5
6
7
8
9
10
class Person < ActiveRecord::Base
  after_commit :notify_police, only: [:create, :update]

  private
  def notify_police
    if self.name_changed? or self.dob_changed?
      PoliceNotification.notify(self)
    end
  end
end

Obviously, you don’t want to bother the police every time you change your hair style is changed, believe me, you don’t want to do that. The fact is we need to let the police know once you change your name or your DOB. You will find a tricky bug in the above code, the PoliceNotification.notify(self) will never get triggered because in the after_commit block, you will never see any changes to any columns. So, the way I fix this problem is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person < ActiveRecord::Base
  after_save :prepare_to_notify_police
  after_commit :notify_police, only: [:create, :update]

  private
  def prepare_to_notify_police
    @name_or_dob_changed = self.name_changed? or self.dob_changed?
  end

  def notify_police
    if @name_or_dob_changed
      PoliceNotification.notify(self)
    end
  end
end

As you can see, I set a flag @name_or_dob_changed within the after_save block, as long as you do not reload the object, you can do the check based on the flag in the after_commit even after all the changes are persisted into database.

I Am Happy I Have You in My Life

It’s been 12 years we know each other since 2002, I can never forget that summary vacation when I saw you, I didn’t know that was love.

2009, we got married, for the past 12 years, we take care of each other, we fight, sometimes we went too far to say the word “divorce”, we were too young.

2013, our angel baby girl came to this world, that’s the best thing I’ve had in my life, I can not forget the night when you are going to deliver, you woke me up gently and calmed me down. The moment you were sent into the delivery room, I had a lot in my mind, it was a rainy night, a peaceful night. When our baby was out, and you were sent to the ward, I can still remember the blood all over you, I can not help myself, you are a hero mom, to me and to our baby.

I am the most happy man in world I have you in my life, let’s expore this wonderful world together, never separated.

Customize the Polymorphic URL for STI

One thing I love Rails is it provides really handy URL helpers to the developers. Today, I am going to present you guys how to use polymorphic_url like a ninja.

Say, I’ve got the following models:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# app/models/vehicle.rb
class Vehicle < ActiveRecord::Base
  ...
end

# app/models/bicycle.rb
class Bicycle < Vehicle
  ...
end

# app/models/motercycle.rb
class Motorcycle < Vehicle
  ...
end

As you can see, we’ve have a STI table named vehicles in the database, and it stores all the bicycles, motorcycles and the other new vehicles we may have in the future, like unicycle.

We have a collection of vehicles, @vehicles, it contains bicycles and motercycles. What we are going to do is iterating the @vehicles, and display a link to the vehicle detail page. With polymorphic_url, we can write down the following code:

1
2
3
<% @vechicles.each do |v| %>
  <%= link_to "Vehicle Detail", polymorphic_url(v) %>
<% end %>

instead of:

1
2
3
4
5
6
7
<% @vehicles.each do |v| %>
  <% if v.is_a? Bicycle %>
      <%= link_to "Vehicle Detail", bicycle_page(v) %>
  <% elsif v.is_a? Motercycle %>
     <%= link_to "Vehicle Detail", motorcycle_page(v) %>
  <% end %>
<% end %>

Once we add a new kind of vehicle, we don’t need to open the above view file and add another elsif, it complies to the Open Closed Principle perfectly!

The request to bicycle detail will go to:

1
2
3
4
5
6
7
8
9
# app/controllers/bicycles_controller.rb
class BicyclesController < ApplicationController
  def show
    @bicycle = Bicycle.find(params[:id])
  end
end

# app/views/bicycles/show.html.erb
<h1>Here comes the bicycle details</h1>

And the request to motorcycle detail will go to:

1
2
3
4
5
6
7
8
9
# app/controllers/motorcycles_controller.rb
class MotorcyclesController < ApplicationController
  def show
    @motorcycle = Motorcycle.find(params[:id])
  end
end

# app/views/motorcycles/show.html.erb
<h1>Here comes the motorcycle details</h1>

What if we want to have the same controller rendering the same view for all the vehicles? Say, we only want one VehiclesController#show for all the vehicles’s detail, here we go:

1
2
3
4
5
6
7
8
9
# app/controllers/vehicles_controller.rb
class VechiclesController < ApplicationController
  def show
    @vehicle = Vehicle.find(params[:id])
  end
end

# app/views/vechicles/show.html.erb
<h1>Here comes the motorcycle details</h1>

You may say we can just use vehicle_url(v) to generate the URL, I can not agree more, but we are exploring something deep inside of Ruby on Rails, so, bare with me, :)

To have the polymorphic_url, generate the URL like vechicle_url, we need to overwrite self.model_name for the Vehicle, here’s how I found it out:

  1. the polymorphic_url will call build_named_route_call to generate the URL
  2. the build_named_route_call will call RecordIdentifier.__send__("plural_class_name", record) to find out reource name, say, a record of Bicycle will generate bicycles
  3. the plural_class_name will call model_name_from_record_or_class to determine the model name of the record
  4. model_name_from_record_or_class will call the record’s class model_name method to find out the model name of the class

We need to override the self.model_name to let polymorphic_url pick up the right model name:

1
2
3
4
5
6
7
8
# app/models/vehicle.rb
class Vehicle < ActiveRecord::Base
  ...

  def self.model_name
    ActiveModel::Name.new(self, nil, "Vehicle")
  end
end

If we want a custom URL and view template for specific vehicle, we can also write something like:

1
2
3
4
5
6
# app/models/unicycle.rb
class Unicycle < Vehicle
  def self.model_name
    ActiveModel::Name.new(self, nil, "Unicycle")
  end
end

With the following customization, polymorphic_url(@unicycle) will generate the URL unicycle_url(@unicycle).

Sortable Bootstrap Table With STI

You are working on a Rails 3 project and your client asks you to sort a list of items and display the items in order, the first thing come to your mind might be acts_as_list and jQuery sortable, you are not alone. I found that acts_as_list requires the items to be sorted belong to a parent, sadly, that’s not the case I am facing, the items I am trying to sort belongs to nothing, RankedModel come to rescue.

ranked-model is a modern row sorting library built for Rails 3 & 4. It uses ARel aggressively and is better optimized than most other libraries.

Lucky me, i found this great post introducing how to get RankdedModel work with Bootstrap table and jQuery sortable.

The requirement I am facing is bit more complex than the one explained in the above article, I have a list of items, which is an array of items of different types stored in one single tables, say, I have the following object layout:

1
2
3
4
5
6
7
8
9
10
class Employee < ActiveRecord::Base
  include RankedModel
  ranks :title
end

class Manager < Employee
end

class Salesman < Employee
end

Say, I have a list of employees, including managers, salesman shown in order, I can now drag and drop to sort the items in the list, great, ready to charge the customer? Not yet, you will find it will only sort the managers and salesmen separately, it will not sort them as a whole entity, dig into the source of RankedModel, i found something really interesting,

1
2
3
4
#lib/ranked-model/ranker.rb
def instance_class
  ranker.class_name.nil? ? instance.class : ranker.class_name.constantize
end

This where the RankedModel decides which class to use as the finder, the problem it could not sort managers and salesmen as a whole entity is because it will use Manager and Salesman separately to do the sorting, RankedModel found this problem before I do, and they provide an option class_name to unify the finder lookup, the solution is:

1
2
3
4
class Employee < ActiveRecord::Base
  include RankedModel
  ranks :title, :class_name => "Employee"
end

Back to the items listing page, viola, it sorts the Manager and Salesman as a whole entity.

Happy hacking!

爷爷,走好

“时光时光慢些吧,不要让你再老了,我愿用我一切换你岁月长留……”,不知不觉泪水模糊了双眼,心中隐隐作痛,呼吸变得困难,闭上眼睛,爷爷清瘦的脸庞缓缓浮现,微笑着,看着我。

爷爷,一个严厉的老头,严厉的有些怪,童年的回忆里,都是他冷峻的面庞和不留情面的训斥。他是新中国第一批飞行员,听人说,爷爷是因为坚持跟奶奶的婚姻,离开部队,回到地方。文革中,因为仗义执言,被打成右派,十几年没有工资,被送到崂山修水库,文革结束后,爷爷得到平反,回到国棉三厂任养鸡场场长,一直干到离休。

不知道是不是那十几年的磨难,让他变得阴森古怪,印象中,爷爷没有对我笑过,可能是他对我的要求太高了吧,我是你最亲最爱的孙子啊,爷爷!

慢慢的,我长大了,离开了青岛,去外地上学,每次从家离开,爷爷都要坚持送我到大门口,帮我整理肩上的背包,我装作轻松,摆摆手让他回去,回头已是泪眼模糊,从未想过有一天会与他阴阳两隔。

2005年,我寒假回家,爷爷提前在电话里跟我说搬家了,为了腿脚不太方便的奶奶,爷爷特地买了一楼,我记下了地址。晚上10点多,我下了火车,急急忙忙赶回家,在小区门口看到寒风中等我的爷爷,嘴上却怪他多虑。

2008年,我毕业了,留在了北京,每个星期都会给他打电话,问问家里的情况,爷爷闭口不谈家里的困难,他和奶奶行动都已经比较困难了,去医院拿药已经是比较困难的事情了,爷爷没有跟我提一个“难”字,怕我担心,怕耽误我的“大好前程”,那时的我也是没心没肺,心思全都放在工作上了,压根儿就没想到家里的情况已经不像从前了。

2010年春节,在青岛陪爷爷奶奶一起过年三十儿,爷爷很为难的跟我说:“我和你奶奶年纪都大了,你看你能不能回来青岛工作?”,看得出来,他是斟酌了很久,才把这句话说出来的,爷爷奶奶一辈子要强,从不会轻易求人。我没有多想,一口答应下来。

2011年初,我回到了青岛,干的还是老本行,因为都是给外国客户做活,经常需要熬夜,爷爷看了不忍,多次劝我说,“林林,你能不能换个工作啊?开个小店什么的,或者包个出租车也行啊。”,我总是一笑而过,但我知道,爷爷是心疼我,怕我熬坏了身子。平常没事儿的时候,他会出去转悠转悠,其实是帮我寻摸生意去了,看看哪家店干的挺好,哪家店准备出兑,回来跟我说,让我考虑考虑。你心疼我,我都知道,只是我跟你一样,不善于表达,有些话,我不说,我很后悔。

随着年龄的增长,我也慢慢的跟爷爷从爷孙变成了朋友,跟他开玩笑,跟他犟嘴,跟他讲我在twitter上看到小道消息,他像个孩子一样,问这问那。

爷爷驾鹤西去,我无处寻找,只能从记忆中挤出片段,拼凑成断续的影像,愿他在天国一切安好。

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.