Debug手记:VIM在mac OS High Sierra上出现“Image not found”问题

问题描述

将macOS更新至High Sierra之后,VIM出现故障,启动时缺少必要动态链接库(Image not found)。

解决方案

第一时间想到的解决方案是,使用brew upgrade更新。然而却出现了找不到Python.h的问题。
接下来使用brew doctor命令,检查报出的所有Warning。发现有一条提示至关重要,大意是如果环境变量$PATH包含了Anaconda的路径,有可能导致某些包编译时break。那么解决问题的方案就变得非常明朗:将$PATH中Anaconda相关的内容暂时移除。再一次使用brew upgrade后,所有问题成功解决。

LANG="C"?

不知道为什么,安装好neovim之后,UI显示的都是日文。。。
nvim_error.png
经过一番查找,在看到了github上贴的一个issue
打开自己系统查看了一下:

$ locale
LANG=
LC_COLLATE="C"
LC_CTYPE="UTF-8"
LC_MESSAGES="C"
LC_MONETARY="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_ALL=

试着加了一句:

$ export LANG=C
$ locale
LANG="C"
LC_COLLATE="C"
LC_CTYPE="UTF-8"
LC_MESSAGES="C"
LC_MONETARY="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_ALL=

再次启动neovim
nvim.png
居然真的好了。。。
所以LANG=C究竟是个啥。。。

LANG=C是指定系统编码,C的意思是C语言,也就是最基本的ASCII编码。

Ruby中一个很有意思的语法糖

Ruby中定义一个Hash可以用这样的语法创建:

hash = { user: 'username', pwd: 'password' }

看起来很像JSON是不是?但其实,Ruby的“正经”语法是:

hash = { :user => 'username', :pwd => 'password' }

也就是说,开篇的写法实际上只是当key为Symbol类型时的语法糖——把:移动到后面,同时去掉=>。如果key不是Symbol类型,比如是一个String类型,那么就只能老老实实的用下面的写法了。

理解Spring Data Rest的协议:HATEOAS与HAL

Spring Data Rest是运行在Spring Data之上的一套框架,能够非常方便的将Spring Data中的Repository映射成REST接口。其中有一些基本概念看起来有些陌生,在这里做出一些整理。

协议规范HATEOAS与数据格式HAL

Spring Data Rest采用了HATEOAS(Hypermedia as the Engine of Application State)协议规范。HATEOAS是一套REST接口的约束准则,它规定了访问数据的接口命名规范和返回数据应该包含的内容。
HAL(Hypertext Application Language)则规定了数据交换的格式,它规定了诸如General Resource(对应数据库一个单一实体对象),Embedded Resource(可以用于嵌入数据库外键关联的实体对象)和Collection(对应数据库的一个实体集合)等形式,为编写通用库提供了标准。
HATEOAS与HAL相辅相成,HATEOAS规定了程序应该如何“动态”转换,HAL规定了程序如何“静态”显示。

样例说明

HAL非常重要的两个概念是Resource(资源)和Link(链接),Resource就是我们想要获得的数据的内容,而Link则是获取特定数据的索引。
对于初次接触类似REST体系的人来说,可能最大的一道障碍就来自于对这两个概念的陌生。来自于Wikipedia的这段样例,可以帮助进行理解。

{
  "_links": {
    "self": {
      "href": "http://example.com/api/book/hal-cookbook"
    }
  },
  "id": "hal-cookbook",
  "name": "HAL Cookbook"
}

这段代码展示了获取一个实体对象的返回数据。先看第一层,有id name_links三个键。在HAL中,所有以_开头的键都可以理解为这个协议规定的必须存在的内容。在具体的框架上,这些内容是由框架生成的。所以,_links键是这个协议的要求字段。从里层对象的键名不难猜出,_links.self.href存储的,就是这个实体对象本身的link。通过访问这个link,可以得到现在呈现的这个数据,也就是resource。idname是用户自己的数据,按照惯例,id往往对应着数据库的主键。当然,主键可以是任意名字。

{
  "_links": {
    "self": {
      "href": "http://example.com/api/book/hal-cookbook"
    }
  },
  "_embedded": {
    "author": {
      "_links": {
        "self": {
          "href": "http://author-example.com"
        }
      }, 
      "id": "shahadat",
      "name": "Shahadat Hossain Khan"
    }
  },
  "id": "hal-cookbook",
  "name": "HAL Cookbook"
}

第二段代码展示了Embedded Resource的格式。与第一组不同的是,多了一个_embedded字段,里面负载了author这个数据。书和作者往往是一个外键的关系,这个代码就展示了外键会怎样被显示。事实上,在Spring Data Rest里面,外键默认只会展示对应对象的link,而不会显示完整的数据,这种情况下只会在_links中多出一个author字段处在与_links.self平级的位置。如果想要一同返回完整的数据,数据的内容就会嵌入在_embedded里面。

{
  "_links": {
    "self": {
      "href": "http://example.com/api/book/hal-cookbook"
    },
    "next": {
      "href": "http://example.com/api/book/hal-case-study"
    },
    "prev": {
      "href": "http://example.com/api/book/json-and-beyond"
    },
    "first": {
      "href": "http://example.com/api/book/catalog"
    },
    "last": {
      "href": "http://example.com/api/book/upcoming-books"
    }
  },
  "_embedded": {
    "author": {
      "_links": {
        "self": {
          "href": "http://author-example.com"
        }
      },
      "id": "shahadat",
      "name": "Shahadat Hossain Khan"
    }
  },
  "id": "hal-cookbook",
  "name": "HAL Cookbook"
}

第三段代码展示了带导航链接的集合资源。所谓导航链接,可以想象成数据库分页时候,前一页、后一页与第一页、最后一页的功能。对于Spring Data Rest,还会有包含页码页号每页记录数的page信息,帮助实现更加完备的分页功能。

  1. HATEOAS
  2. HAL

Spring Data Rest中关联对象获取与批量对象获取

Spring Data Rest是非常快捷的实现Rest API的一个方案。但是,其中也有很多的坑需要处理。下面就记录下遇到的坑和处理方案:

有外键的对象如何添加/关联对象的POST数据格式是怎样的

假如有如下两个实体类,一个代表班级,一个代表学生,存在学生到班级的多对一单向映射:

@Entity
public class Clazz {
    private String id;
    // ......
}
@Entity
public class Student {
    
    private String id;
    private Clazz clazz;
    // ......
    
    @ManyToOne
    @JoinColumn(name = "clazzId")
    public Clazz getClazz() {
        return this.clazz;
    }

    // ......
}

使用Spring Data Rest添加student中class的正确格式是:

{
    // ......
    "clazz": "http://localhost:8080/api/clazzes/8a818fee5667b764015667ba27520002"
    // ......
}

也就是说,应该提交对应clazz的link。

如何选择性的展开外键对应的对象

Spring Data Rest的外键全部都是以link的方式给出,这样的方式在某些情况下很难以使用。比如,依然采用上一节的学生和班级实体,如果每个学生对应的班级都用link给出,那就意味着显示一个学生列表时每显示一条记录就要发一次请求以获取学生对应的班级。这样编程显然是一种灾难。那么,在返回的数据中直接嵌入clazz对象是合理的。Spring Data Rest提供了这样的机制来完成这种需求,那就是投影(projection)和摘录(excerpt)。
为了让学生列表直接携带班级信息,可以采用如下实现:

@Projection(name = "inlineClazz", types = { Clazz.class }) 
interface InlineClazz {

  // 这里写你要暴露的class字段的getter方法

  Clazz getClazz();    // 这句话会让clazz信息以内嵌(inline)的方式出现在学生的信息中

}

@RepositoryRestResource(excerptProjection = InlineClazz.class)
interface StudentRepository extends CrudRepository<Student, String> {}

一次性获取多个特定对象

例如,用一组ID获取一组题目的ID用以生成作业,可以用下面的方式实现:

@Query("from Problem p where (p.id in (:ids))")
List<Problem> fetchByIds(@Param("ids") List<String> ids);

用下面的方式获取数据

http://localhost:8080/rest/problems/search/fetchByIds?ids=62183,72111

这样就可以一次性取得ID为62183和72111的题目数据。

参考文献:

  1. 8.3. Excerpting commonly accessed data
  2. Selectively expand associations in Spring Data Rest response
  3. Spring Data REST: How to retrieve many items using list of Ids in one single call?