Embed image in ActionMailer and Recent interviews

Action Mailer embed image

如果按照普通写view的方法直接然后 <%= image_tag some_model.image_url %>
会因为渲染的是相对路径而在用户收到邮件的时候无法显示。
当然我们也可以硬编码host到相对路径前面,不过在staging, test环境下就会有些不自然。

解决方法是将图片打包成attachments 嵌入到邮件中。
具体做法

###

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# in action mailer
class SampleMailer < ApplicationMailer
def send_when_update(user)
# set attachment file
attachments["user_image"] =
File.read(Rails.root.join("public#{user.image.url}"))

# set mail header
mail to: user.email,
subject: 'new email!'
end
end

#in mail template

<%= image_tag attachments[@avatar_file].url %>

三次失败的面试经历

  • pwc合同会社 creative developer

有webtest + 面试。面试很水,很简单的自我介绍,特长,经历,没了。等人事通知下一次面试吧。前后不到20分钟。很卧槽的是面完之后跟我讲了句 我们这里很多人辞职去了你们公司。 我一得瑟忘了webtest没做完的事情,挂。

  • Line 什么j8职位忘了

因为之前说好了求人内推,然后朋友给我发了职位介绍,发现其中没有任何适合我的职位。随便扔了简历让人事帮我挑一个吧。

面试内容 笔试 30分钟,面试 1小时,面试内容是根据笔试内容来的。笔试内容的话其实刷点题,好好准备一下的话应该非常轻松。但是本人秉着考察一下这家公司气氛到底如何心态去面试,何况平时就对面试考算法深恶痛绝所以基本就是裸着上场了。

笔试题首先是问卷,你用过那哪些框架啊,哪些语言啊,哪些特么CI,CD啊,哪些特么package管理工具啊。不过候选项目全是Java栈的东西,特么gg。

其他题目,进程线程区别,排序算法复杂度,两个栈实现一个队列,blocking和non-blocking, 还有个啥j8算法题太长描述不清楚,反正我也是跳过,最后问一个预计有10k qps的echo server如何构建的问题。

综合来说除了算法题实在是年代太久没去仔细准备其他关于构架和OS的问题基本都是得心应手,所以看看面试怎么说吧。

面试官两个人,没自我介绍就开始了以至于我后来也不知道特么这两人是谁。基本就是让我解释笔试的题目。老实说让我上黑板写算法那是算了。这种东西一半工程很少用到,不过凭着我特么大学时代专业前三的的成绩我觉得我至少还记得一点,可惜这两位面试官貌似不是很愿意听我的解释或者觉得我说的比较难懂,反正就是脸一歪表示我听不懂你在说什么的表情。。。

OS和构架的问题,好像说的太多了,其实这两个人也不是很懂。所以还是一脸表示听不懂我在说什么的表情。这个时候其实心里已经卧槽了。

最后面完之前问我我的服务主要就是Java,你没有java经验你觉得有问题吗? 废话问题当然很大,但是语言这种东西特么学的多了自然上手快,经验? 不好意思我没有,但是快速上手的能力我是有的。

不多说了反正也是挂了。综合来讲面试题目一般,面试官略傻逼。没有适合我的职位。

  • 迅销 全栈工程师

猎头介绍的公司,特么也不知道猎头能拿多少钱说了超多这个公司的好话。不过戏剧性的是在我等待这个公司终面结果的时候这个猎头辞职跑路了。
比较伤感的是我都做好了七点钟跑去上班的准备结果最后一面落了。真是以后特么我要往死里黑这家公司。另外这家公司的屌丝产品我以后是绝对不会买的。

1面技术面,英语。JS的问题偏多,问题类型偏广但深度不深。比如Node的Event loop, server side rendering,node的callback hell如何解除。当前流行的Web框架,现在学习中的Elixir的特点,当场写个test case这种。

2面IT部长面,忘了问了啥了。台湾年末吃喝玩乐Skype面的试。就问了些你当前有哪些想做的service之类。

3面CIO面试。问问家常,问问情怀。最后切换成英语问我你觉得现在你26,那么30岁你在干什么。我觉得我就是死在这个问题上了,不假思索说了自己应该是在一个start up当CTO这种屌话。

等了一个星期结果反正就是挂了。

以下是总结,这三个公司其实都没有适合我的职位。为了吃个饭,为了看看人们传说中的优良公司,因为被猎头洗脑过度所以总共浪费了半天带薪休假的事情去经历了这些面试。
总而言之还是应该抱着去找自己想做的事情的心态,而不是觉得我能做,所以让我来吧这种。求职和同异性交往一样,很多时候你可能千方百计想要改变自己的初衷去迎合对方,到头来还是暴露了彼此的不合适。我们真正需要去寻找的,还是那个适合自己的人,适合自己的岗位,而不是勉强自己去证明我才是最合适的。

以上。

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

Swift Singleton

有个config需要从api读取,打算把Config设计成Singleton。

1
2
3
4
5
6
7
8
final class Conig {

static let sharedInstance = Config()

private init() {

}
}

由于api异步读取,我们需要在渲染页面之前就能够使用Config,这就需要类似eager load的功能。很遗憾swift3没有对eager load的支持,这个singleton只能在第一次被调用的时候初始化。解决方法是在渲染之前的一些hook里面引用一下这个singleton。

sqlite3 migrate to postgresql && swift parse response time string from api

Rails migrate sqlite3 into postgresql

reference
尝试了pgloader, 遇到的问题很多。一筹莫展了半个下午。
尝试sequel,意外地顺利。

1
sequel -C sqlite://db/production.sqlite3 postgres://user@localhost/db

得到的教训是从刚一开始就应该使用postgresql,其次sqlite3中并不严格检查表项的类型,
导致以前定义成timestamps的表项直接被当string存储下来,给时间的转换统一带来了不少麻烦。

将API传来的关于时间的string转换成Swift的Date

1
2
3
4
5
6
7
8
9
10
// str =  "2016-05-31T08:18:37.969Z"
// string.asDate(with: "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
extension String {
func asDate(with dateFormat: String) -> Date? {
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(identifier: "UTC")
dateFormatter.dateFormat = dateFormat
return dateFormatter.date(from: self)
}
}

基于Alamofire 和 Carrierwave 的图片上传 && Ecto的insert conflict

基于Alamofire 和 Carrierwave 的图片上传

此文备忘如何使用Alamofire与Rails后台的图片上传API交互。

思路

谈不上思路。注意前端后端传递的内容要一致。

前端

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
static func upload(image: UIImage, progressHandler:((Double) -> Void)?, responseHandler: (() -> Void)?, errorHandler: (() -> Void)?) {
if !User.isLogin() { return }
let headers: HTTPHeaders = ["Authorization": "Bearer \(User.token)"]

print("POST: \(imagesUploadPath)")
print("HEADER: \(headers)")

Alamofire.upload(multipartFormData: { multipartFormData in
if let data = UIImagePNGRepresentation(image) {
multipartFormData.append(
data,
withName: "images[]",
fileName: "image.png",
mimeType: "image/png"
)

} else if let data = UIImageJPEGRepresentation(image, 0.5) {
multipartFormData.append(
data,
withName: "images[]",
fileName: "image.jpg",
mimeType: "image/jpeg"
)
}

},
to: imagesUploadPath,
method: .post,
headers: headers) { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.uploadProgress { progress in
print("progress: \(progress.fractionCompleted)")
progressHandler?(progress.fractionCompleted)
}.responseJSON { response in
debugPrint(response)
responseHandler?()
}
case .failure(let encodingError):
print(encodingError)
errorHandler?()
}
}
}

注意一般后端会parse文件名之类的meta信息,所以前端传递数据的时候需要指定一个文件名。

后端

按照Carrierwaver的流程建立uploader。
但是由于前端传递的是base64的string,所以后端需要把base64 string还原成文件。
很幸运我们可以直接使用carrierwave-base64这个gem轻松完成。

ecto insert_all on conflict

https://dev.classmethod.jp/server-side/db/postgresql-9-5-new-function-upsert-use/

https://hexdocs.pm/ecto/Ecto.Repo.html#c:insert_all/3

Ajax with Phoenix API

备忘一下如何在Ajax Post的用法。
注意点是以前Ajax相当于在浏览器中调用API,所以发起请求时候会带上Cookie。
另外同时需要带上csrf token。

1
2
3
4
5
6
7
8
9
10
11
$.ajax({
method: "POST",
url: "/tags",
data: JSON.stringify({tag: {name: "John"},
_csrf_token: "IA19DUIPbQcJFURhdTo5fD4EOW49NgAAKJ7Y+H9cOb1QFWU+Xss/Uw=="}),
contentType:"application/json; charset=utf-8",
dataType:"json"
})
.done(function( msg ) {
alert( "Data Saved: " + msg );
});

Api for both promise or callback

如果没有callback参数就直接resolve或者reject,如果有callback参数就执行callback,然后resove或者reject。 于是我们看到当我们带上callback,然后又用then去chain promise的时候就返回了两次结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

// divid.js
module.exports = function asyncDivision (dividend, divisor, cb) {
return new Promise((resolve, reject) => {
process.nextTick(() => {
const result = dividend / divisor;
if (isNaN(result) || !Number.isFinite(result)) {
const error = new Error('Invalid operands');
if (cb) { cb(error); }
return reject(error);
}
if (cb) { cb(null, result); }
resolve(result);
});
});
};

1
2
3
4
5
6
7
8
9
10
11
12

let asyncDivision = require('./divid')

asyncDivision(10, 2, (error, result) => {
if (error) {
return console.error(error);
}
console.log(result);
})
.then(result => console.log(result)) // [1]
.catch(error => console.error(error)); // [2]
;
1
2
3
4
//result
☁ 5:54PM node_design_pattern node test.js
5
5