Character encoding

ASCII

  • encoding in 7-bit.
  • 32 -> 127 representing characters.
  • 0 -> 31 representing control characters.
  • 128 -> 255 was called OEM characters, many company has their own idea about how to use these charaters.

ANSI:

  • lower 127 characters is same with ASCII.
  • higher 127 characters were divided into different “code pages”

Unicode:

  • Code point: In Unicode, a letter maps to something called a code point which is still just a theoretical concept.
  • Encoding: Unicode Byte Order Mark: indicating encoding order is ‘high-endian’ or ‘low-endian’

UTF8: Every code point from 0-127 is stored in a single byte. Only code points 128 and above are stored using 2, 3, in fact, up to 6 bytes.

The Most Important thing:

  It does not make sense to have a string without knowing what encoding it uses. 

怎样写单元测试

最近和同事讨论过几次如何写单元测试之后,突然意识到,是要写点什么了。
什么是单元测试, 维基百科上是这么定义的: unit testing is a method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine if they are fit for use.[1] Intuitively, one can view a unit as the smallest testable part of an application. 简而言之,就是验证系统中最小可测试单元的功能是否正确的自动化测试。因此,单元测试的目地就是“对被测试对象的职责进行验证”, 在写单元测试之前,先识别出被测试对象的职责,就知道该怎么写这个单元测试了。

在我看来,根据被测试对象,单元测试可以分为两大类:“对不依赖于外部资源的组件的单元测试”和“对依赖于外部资源的组件的单元测试”。由于测试的对象的不同,我们需要采用不同的测试方案:

对不依赖于外部资源的组件的单元测试

对不依赖于外部资源的组件进行测试时,我们主要关注被测试对象的状态变化是否和预期的一致,而对于其内部实现,则不用关心,举个例子:

Account account = new Account(20)
assertThat( account.withdraw(10).balance(),  is(10))

开始账户里有20元,扣款10元后余额应该是10元,我们不关心扣款是现金交易还是银行转账,我们只关心扣款功能是正常的, 这就是一种黑盒测试

对依赖于外部资源的组件的单元测试

对依赖于第三方库、组件的接口的组件进行单元测试时,需要对依赖的库进行mock,让依赖库按照约定表现不同的行为,并且,在测试中验证被测试代码是否按照约定正确的调用了第三方接口(是否调用了正确的API, 是否传递了正确的参数), 我们再来看个例子:

//AuthenticateService是一个外部部件通过Web Service暴露的用户鉴权接口,
//在单元测试中,我们使用一个mock的AuthenticateService对象,来验证UserServiceImpl中的接口调用是否正确。
AuthenticationService mockAuthService = createMock(AuthenticateService.class)
UserService userService = new UserServiceImpl(mockAuthService);
User user = new User("nicholasren", "encrypted_password");

assertThat(userService.authenticate(user), is(true));

//期望userService.authenticate中会调用AuthenticateService.authenticate, 并且传递了正确的用户名和加密后的密码
verify(mockAuthService).authenticate(user.getName(), user.getPassword())

上面的例子中,对于依赖于外部资源的组件,我们需要验证的是该组件调用了正确的第三方接口,并且传入了正确的参数,这就是一种白盒测试

对依赖于框架的组件的单元测试

我把这种情形也归类为“依赖于外部资源的组件的单元测试”,但是特殊的是,被测试对象依赖的是框架,而这个框架代码在单元测试运行是不会被执行,我们来看个例子:

  //load.js
  $('.trigger').click(loadItems)

  //...
  loadItems:function() {
    var ajaxLoading = $("<li class='ajax-loading' style='display:block;'>Loading...</li>");
    var itemContainer = $(".container");
    $.ajax({
      url: this.config.itemLoadingUrl + groupID,
      type: 'GET',
      beforeSend: function(){
        itemContainer.append(ajaxLoading);
      },
      complete: function(){
        ajaxLoading.remove();
      },
      success: function (data) {
        itemContainer.append(data);
      },
      error: function () {
        itemContainer.append("<li class='error' style='display:block;'>Oops, connection time out. Please try again later.</li>");
      }
    });
  }

$.ajax 是jquery提过的发送ajax请求的方法,然而,在spec中,我们无法真正地发送ajax请求,然后再进行验证,于是我们stub了这个方法。 这时候,就有个问题了:
真正的$.ajax()方法没有被调用,complete,success,error的这几个function都没有被调用,那么这些function中的逻辑该如何测试呢?

$.ajax是jquery提供的API,出现错误的机率是非常非常低的,因此,我们可以假设$.ajax是正常工作的,然后stub $.ajax, 当被测试代码中调用$.ajax时,让其调用我们stub的一个fake function. 例如,我们想测试ajax request失败时,是否正确地显示了错误信息,那么我们可以写出下面的测试:

  //load_spec.js
  it("should show error message when ajax request failed", function(){
    spyOn($, "ajax").andCallFake(function(params) {
      params.beforeSend();
      params.complete();
      params.error("", "404");
    });
  
    var trigger = $(".itemContainer .trigger");
    trigger.click();
  
    expect($(".itemContainer  .error").length).toBe(1);
    expect($(".ajax-loading").length).toBe(0);
  });

$.ajax 是jquery提过的发送ajax请求的方法,然而,在spec中,我们无法真正地发送ajax请求,然后再进行验证,于是我们stub了这个方法。 这时候,就有个问题了:
真正的$.ajax()方法没有被调用,complete,success,error的这几个function都没有被调用,那么这些function中的逻辑该如何测试呢?

$.ajax是jquery提供的API,出现错误的机率是非常非常低的,因此,我们可以假设$.ajax是正常工作的,我们可以stub $.ajax,传入了一个fake function,在这个fake function中执行我们期望的行为,然后验证这些function的执行效果。

使用jasmine的spyOn机制,传入一个fake function,在此function中依次执行 beforeSend, complete,error 这几个callback,(注意上面andCallFake里的三行,这也是真实情况下ajax 请求失败时,这些callback被执行的顺序),然后验证error这个callback是否显示了error message。

因此,对于依赖于外部API的组件的测试,当此API在单元测试中不能被执行,我们可以假设其API工作正常,通过测试代码模拟其API执行的情况, 验证被测试对象的行为

一句话,写单元测试之前,弄清被测试对象的职责,然后针对被测试对象的职责进行测试,单元测试写起来,真的不难。

How SSH works?

简介:

SSH,全名secure shell,其目的是用来从终端与远程机器交互,SSH设计之处初,遵循了如下原则:

  • 机器之间通讯的内容必须经过加密。
  • 加密过程中,通过 public key加密,private 解密。

密钥:

SSH通讯的双方各自持有一个公钥私钥对,公钥对对方是可见的,私钥仅持有者可见,你可以通过”ssh-keygen”生成自己的公私钥,默认情况下,公私钥的存放路径如下:

  • 公钥:$HOME/.ssh/id_rsa.pub
  • 私钥:$HOME/.ssh/id_rsa

通讯原理:

前提条件:

  1. 两个节点都持有各自的公钥私钥对,分别标记为PUBLIC KEY(client), PRIVATE KEY(client), PUBLIC KEY(server), PRIVATE KEY(server)
  2. 服务器上运行了SSH服务程序

建立通信通道的步骤如下:

  1. 客户端发起请求给服务器,服务器发回自己的public key给客户端
  2. 客户端检查这个public key是否在自己的$HOME/.ssh/known_hosts中,如果没有,客户端会提示是否要把这个public key加入到known_hosts中。
  3. 客户端会提示输入密码,用户输入密码后,客户端会使用PUBLIC KEY(server)对密码加密,然后发送给服务器。
  4. 服务器收到密码后,使用PRIVATE KEY(server) 解密,校验密码正确性. ??? 需要解密吗?
  5. 客户端把PUBLIC KEY(client), 发送给服务器。
  6. 至此,通讯通道建立完毕,当客户端想服务器发送消息时,会使用PUBLIC KEY(server)加密,服务器会使用PRIVATE KEY(server)解密,当服务器向客户端发送消息时,会使用PUBLIC KEY(client)加密,客户端收到数据后,会使用PRIVATE KEY(client)解密。

免密码登录:

我们的目标是: 用户已经在主机A上登录为a用户,现在需要以不输入密码的方式以用户b登录到主机B上。
步骤如下:

  1. 以用户a登录到主机A上,生成一对公私钥。
  2. 把主机A的公钥复制到主机B的authorized_keys中,可能需要输入b@B的密码。

    ssh-copy-id -i ~/.ssh/id_dsa.pub b@B

  3. 在a@A上以免密码方式登录到b@B
ssh b@B

SSH forwarding:

ssh -f user@ssh_host -L 1433:target_server:1433 -N

Getting started with chef

Set up a ubuntu node and run recipe on it

Predication:

  1. a running ubuntu node which accessiable via SSH from your labtop(workstation)
  2. cd to chef-repo directory.

    Steps:


    Bootstrap ububtu:
knife bootstrap IP_ADDRESS -x USERNAME -P PASSWORD --sudo


Verify the installation completed
knife client list

you will see your nodename in the client list

Download cookbooks from community site
knife cookbook site install getting-started

the cookbook named “getting-started” will be downloaded into chef-repo/cookbooks/

Upload the recipe to Hosted Chef so it is available for our nodes
knife cookbook upload getting-started 


Add this new recipe to the new nodes run list
knife node run_list add NODENAME 'recipe[getting-started]'


Run the added recipe remotely via ssh
knife ssh name:NODENAME -x USERNAME -P PASSWORD "sudo chef-client" -a ipaddress


Runnig chef-client as a deamon
knife cookbook site install chef-client
knife cookbook upload chef-client
knife node run_list add NODENAME 'recipe[chef-client]'
knife ssh name:NODENAME -x USERNAME -P PASSWORD "sudo chef-client" -a ipaddress


Advanced tips:

  • add recipe when bootstrap node
knife bootstrap IP_ADDRESS -r 'recipe[chef-client]' -x USERNAME -P PASSWORD --sudo

Polymorphic Association

有这么一个需求,一个在线音乐商店系统,我们暂且叫它’online-store’,需要记录消费者对每首歌的评论,一个有经验的rails developer会很快地写出下面的代码:

class Music
  has_many :comments
end

class Comment
  belongs_to :music
end

对应的表结构如下:

#musics:   
  |  id  | integer|   
  | name | varchar|   
  | ...  |  ...   |   
#comments:   
  | id      | integer|   
  | text    | varchar|   
  | music_id| integer|   

如果需要给music添加,查看评论,可以通过如下代码实现:

  #添加评论
  music.comments.create {:text => 'this is realy nice'}
  #查看评论
  music.coments

风云变幻,斗转星移,apple的app store创造了软件销售的新模式,我们的vendor也坐不住了,决定在现有的音乐商店系统上出售应用程序,电影,游戏等内容,同样,这些内容也需要支持评论,有了前面成功的经验,你信心满满增加了下面几个model:

  class Application
    has_many :comments
  end

  class Movie
    has_many :comments
  end

  class Game
    has_many :comments
  end

再来看看我们之前写的model Comment:

class Comment
  belongs_to :music
end

现在需要支持多种内容,而且这些类内容之间出了都可以被评论外,再无其他关联,那这个belongs_to该怎么写呢?一个最直接的思路是,扩展Comment,让其支持对以上四类内容的评论,代码如下:

class Comment
  belongs_to :music, though => "music_id"
  belongs_to :game, though => "game_id"
  belongs_to :application, though => "application_id"
  belongs_to :movie, though => "movie_id"
end

表结构如下:

#comments:   
  | id             | integer|   
  | text           | varchar|   
  | music_id       | integer|   
  | game_id        | integer|
  | application_id | integer|
  | movie_id       | integer|

有了以上的model,你就可以给应用程序,电影,游戏增加评论了:

  #创建评论
  application.comments.create {:text => "this is a nice app"}
  movie.comments.create {:text => "this is a nice movie"}
  game.comments.create {:text => "this is a nice game"}
  #查看评论
  application.comments
  movie.comments
  game.comments

目前看来,这些代码工作得很好,然而,做为一个有着良好直觉的程序员,你敏锐地觉察到,将来可能有更多的内容出现在这个”onlne-store”中,也会有更多的内容需要支持评论——你成功地识别出一个”易变点“,每当新增一种内容的时候,你就需要打开这个Comment类,新增一个association,同时,还需要增加migration,这个设计明显违背了开闭原则”。那么,这个问题该怎么解决呢? 再来分析下这个问题,上面我们提到,这些model唯一的共性是 “可以被评论”,于是我们可以抽象出一个概念——“commentable”。如果我们让comment对象知道它所对应的”commentable”对象的id以及类型(game/application/movie),我们就可以获得一个“commentable”对象的所有comments,参考下面的代码:

  #查看id为1的music的评论
  Comment.find(:commentable_id => 1, :commentable_type => 'music')

  #查看id为1的application的评论
  Comment.find(:commentable_id => 1, :commentable_type => 'application')

如此,comments的表结构就可以简化为:

#comments:   
  | id              | integer|   
  | text            | varchar|   
  | commentable_id  | integer|   
  | commentable_type| varchar|

model代码简化为:

 #添加评论
 Comment.create({:text => "good staff", :commentable_id => "1", :commentable_type => "music"})
 #查看评论
 Comment.find(:commentable_id => 1, :commentable_type => 'music')
  class Comment
    belongs_to :commentable, :polymorphic => true
  end
  class Application
    has_many :comments, :as => :commentable
  end
  class Movie
    has_many :comments, :as => :commentable
  end
  class Game
    has_many :comments, :as => :commentable
  end
  #添加评论
  movie.comments.create {:text => 'this is realy nice'} 
  #查看评论
  movie.coments

更多关于“polymorphic association”的信息,请参考这里 怎么样,这样一来,再新增多少种内容类型,处理起来都非常容易,扩展下commentable_type的值就可以了。这个思路的根本出发点在于为识别、分离变化点。这类问题可以抽象为这样一个问题:”如何把一个概念应用到一组除了这个概念,没有其他任何关联的对象上?” ,此类问题可以采用上述思路解决。