一直被忽视的tap方法

ruby的任何object,都带有一个奇怪的method,:tap

tap接受一个block,把自身作为参数传递到block中,并执行block中的内容,最后返回自身。

引用文档的说明
:tap 就像轻拍一下,试探以下该对象的反应,做一些操作,然后仍然返回该object。

如果不使用:tap,我们需要输入数组内容时候,有以下做法。

1
2
3
4
arr = "hello".bytes.to_a
p arr
arr = arr.collect {|byte| byte.to_s(16) }
p arr

有了:tap,我们就可以写成长到像一口老痰的一句话形式。

1
2
"hello".bytes.to_a.tap {|arr| p arr }
.collect {|byte| byte.to_s(16) }.tap {|arr| p arr }

算了吧,我还是喜欢不用:tap的形式。至少看到文档中的这个例子我是这么想的。

不过今天总算是看到利用tab可以写的略简单明了的地方了。

我以前常犯这个错误。

1
user = User.new(name: "gyorou", age: 26).save

你们猜user结果是啥? 嗯。结果是 true。 因为:save返回的结果是true或者false。

那我们怎么获取user?

1
2
user = User.new(name: "gyorou", age: 26)
user.save

但是写成两行的话这里就比较难受了。利用:tap,我们可以写成一行。

1
user = User.new(name: "gyorou", age: 26).tap(&:save)

时间瞬间变得清爽。可见,:tap也是有存在的道理,只不过文档没能让人信服而已罢了。

以上。

Polymorphic has_and_belongs_to_many relationship in ActiveRecord

While I was checking this awesome Rails plugin, I was wondering how it can use only one model for different has_and_belongs_to_many relations.

Check readme on github to see what it can do and let’s start to see how it can.

Here is the only migration file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CreateActions < ActiveRecord::Migration[5.0]
def change
create_table :actions do |t|
t.string :action_type, null: false
t.string :action_option
t.string :target_type
t.integer :target_id
t.string :user_type
t.integer :user_id

t.timestamps
end

add_index :actions, [:user_type, :user_id, :action_type]
add_index :actions, [:target_type, :target_id, :action_type]
end
end

and its model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Auto generate with action-store gem.
class Action < ActiveRecord::Base
include ActionStore::Model
end

module ActionStore
module Model
extend ActiveSupport::Concern

included do
# puts "Initialize ActionStore::Model"
belongs_to :target, polymorphic: true
belongs_to :user, polymorphic: true
end
end
end

we need to define the belongs_to relationship using polymorphic: true

to see what has many looks like

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# in action-store/lib/action_store/mixin.rb

user_klass.send :has_many, has_many_name, has_many_scope, class_name: 'Action'

user_klass.send :has_many, has_many_through_name,
through: has_many_name,
source: :target,
source_type: target_klass.name


target_klass.send :has_many, has_many_name_for_target, has_many_scope,
foreign_key: :target_id,
class_name: 'Action'
target_klass.send :has_many, has_many_through_name_for_target,
through: has_many_name_for_target,
source_type: user_klass.name,

its ok to ignore those metaprogramming tricks, so to make it more readable
it’s something like

1
2
3
4
5
6
7
8
9
class User < ActiveRecord::Base
has_many :like_topic_actions, -> {where(action_type: "like", target_type: "Topic", user_type: "User")}, class_name: "Action"
has_many :like_topics, through: "like_topic_actions", source: :target, source_type: "Topic"
end

class Topic < ActiveRecord::Base
has_many :like_by_user_actions, -> {where(action_type: "like", target_type: "Topic", user_type: "User")}, class_name: "Action"
has_many :like_by_users, through: "like_by_user_actions", source: :user, source_type: "User"
end

When we call user.like_topics, ActiveRecord knows we first find the relation through like_topic_actions whose class indeed is Action, and when we get a collection of like_topic_actions, we know we are going to find a collection of the target, so we will find the collection by looking for the target_id in the collections, and the source_type tell us the target_id is the id of Topic
so we should query Topic table for the results.

Since we can define different relation type, it’s easy for us to define different relations between those models.

Here provide a better example.
https://stackoverflow.com/questions/9500922/need-help-to-understand-source-type-option-of-has-one-has-many-through-of-rails

Ruby Design Pattern 2

Composite pattern

The GoF called the design pattern for our “the sum acts like one of the parts” situation the Composite pattern.

iterator pattern

very common in normal programming languages.(Enum)

decorator pattern

the most famous usage in ruby is alias_method_chain. There is a very interesting passage about alias_method_chain in “metaprogramming-ruby-2”

The Rise and Fall of alias_method_chain

“Design Pattern in Ruby” 读后感

以前经常去啃一些gem的源代码。比如omniauth,比如warden甚至Rack。没看设计模式之前总是觉得云里雾里,读了设计模式才觉得其实是自己还没有入门。
觉得自己很了不起的时候,还是静下来多读书吧。

Ruby Design Pattern

template pattern

write abstract method in base class.

In ruby raise in abstract method to make sure it’s implemented in subclass.

Hook is the so called empty method in base class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Base
def abstract_method
raise "this method need to be implemented"
end
def hook_method
#just do nothin here but can be implemented to so something in subclass
end
end




class Sub < Base
def abstract_method
# implemented the method
end


def hook_method
# implement the method
end
end

Example of this pattern in ruby: GenerickServer in webrick

Strategy pattern

The GoF call this “pull the algorithm out into a separate object” technique the Strategy pattern

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
# pull the algorithm of renderer outside of the class
class Reporter
def initialize(content, renderer)
@content = content
@renderer = renderer
end
def render
@renderer.render(@content)
end
end


class MarkdownRenderer
def self.render(content)
# markdown render
end
end


class HTMLRenderer
def self.render(content)
# html render
end
end


content = "##title##"
htmlReport = Reporter.new(content, HTMLRenderer)
markdownReport = Reporter.new(content, MarkdownRenderer)

Thanks for Ruby’s duck typing, we don’t need to have a base abstract class for the Renderer. We only need to make sure a class method render is defined

Even we can use a callable proc to remove the class declaration.

Example of this pattern in ruby: the Auth middle-ware Warden

Observer pattern

Take ActiveRecord hooks as an Example.

n a nice example of the Convention Over Configuration pattern (see Chapter 18), ActiveRecord does not require you to register your observer: It just figures out that EmployeeObserver is there to observe Employees, based on the class name

ruby design pattern

template pattern

write abstract method in base class.

In ruby raise in abstract method to make sure it’s implemented in subclass.

Hook is the so called empty method in base class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Base
def abstract_method
raise "this method need to be implemented"
end
def hook_method
#just do nothin here but can be implemented to so something in subclass
end
end




class Sub < Base
def abstract_method
# implemented the method
end


def hook_method
# implement the method
end
end

Example of this pattern in ruby: GenerickServer in webrick

Strategy pattern

The GoF call this “pull the algorithm out into a separate object” technique the Strategy pattern

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
# pull the algorithm of renderer outside of the class
class Reporter
def initialize(content, renderer)
@content = content
@renderer = renderer
end
def render
@renderer.render(@content)
end
end


class MarkdownRenderer
def self.render(content)
# markdown render
end
end


class HTMLRenderer
def self.render(content)
# html render
end
end


content = "##title##"
htmlReport = Reporter.new(content, HTMLRenderer)
markdownReport = Reporter.new(content, MarkdownRenderer)

Thanks for Ruby’s duck typing, we don’t need to have a base abstract class for the Renderer. We only need to make sure a class method render is defined

Even we can use a callable proc to remove the class declaration.

Example of this pattern in ruby: the Auth middle-ware Warden

Observer pattern

Take ActiveRecord hooks as an Example.

n a nice example of the Convention Over Configuration pattern (see Chapter 18), ActiveRecord does not require you to register your observer: It just figures out that EmployeeObserver is there to observe Employees, based on the class name