1. 程式人生 > >為專案編寫Readme.MD檔案

為專案編寫Readme.MD檔案

瞭解一個專案,恐怕首先都是通過其Readme檔案瞭解資訊。如果你以為Readme檔案都是隨便寫寫的那你就錯了。github,oschina git gitcafe的程式碼託管平臺上的專案的Readme.MD檔案都是有其特有的語法的。稱之為Markdown語法。基本規則如下:

Markdown 語法速查表
1 標題與文字格式
標題
# 這是 H1 <一級標題>
## 這是 H2 <二級標題>
###### 這是 H6 <六級標題>
文字格式
**這是文字粗體格式**
*這是文字斜體格式*
~~在文字上新增刪除線~~
2 列表
無序列表
* 專案1
* 專案2
* 專案3
有序列表
1. 專案1
2. 專案2
3. 專案3
   * 專案1
   * 專案2
3 其它
圖片
![圖片名稱](http://gitcafe.com/image.png)
連結
[連結名稱](http://gitcafe.com)
引用
> 第一行引用文字
> 第二行引用文字
水平線
***
程式碼
`<hello world>`
程式碼塊高亮
```ruby
  def add(a, b)
    return a + b
  end
```
表格
  表頭  | 表頭
  ------------- | -------------
 單元格內容  | 單元格內容
 單元格內容l  | 單元格內容

如果直接記語法,那似乎困難了些。這裡OneCoder推薦兩個Markdown的編輯器。


OneCoder這裡使用的是後者為自己的shurnim-storage專案寫Readme。至於這個專案是什麼,見Readme文件,OneCoder也會在另外的博文做一些補充說明。成品Readme如下:

# shurnim-storage

![Shurnim icon](http://onecoder.qiniudn.com/8wuliao/DLPii2Jx/rEBO.jpg)

## 目錄
* [背景介紹](#背景介紹)
* [專案介紹](#專案介紹)
* [使用說明](#使用說明)
  * [獲取程式碼](#獲取程式碼)
  * [開發外掛](#開發外掛)
  * [使用ShurnimStorage介面](#使用ShurnimStorage介面)
       * [介面介紹](#介面介紹)
       * [使用樣例](#使用樣例)
* [其他](#其他)

<a name="背景介紹"></a>
## 背景介紹

*Shurnim*,是我和我老婆曾經養過的一隻倉鼠的名字。<br/>
*shurnim-storage*,是一個外掛式雲端儲存/網盤同步管理工具。是在參加又拍雲開發大賽的過程中設計並開發。

<a name="專案介紹"></a>
## 專案介紹

*shurnim-storage* 的設計初衷是給大家提供一個可方便擴充套件的雲端儲存/網盤同步工具。分後端介面和前端UI介面兩部分。<br>

由於目前各種雲端儲存和網盤系統層出不窮,單一工具往往支援支援某幾個特定儲存之間的同步,如**又拍雲**到**七牛雲端儲存**的同步工具,此時如若想同步到其他存則可能需要新的工具,給使用者帶來不便。*shurnim-storage*  正是為了解決此問題而設計的。

在*shurnim-storage*中,使用者使用的固定的統一的後端介面。而所有云儲存/網盤API的支援則是以外掛的形式部署到系統中的。如此,如果使用者想要一個從**又拍雲**到**Dropbox**的同步工具,則只需要在原有基礎上,增加**Dropbox**的外掛,即可實現互通,方便快捷。<br/>

同時,後端統一介面的設計也考慮到介面開發的需求,可直接通過後端提供的介面開發具有上述擴充套件功能的雲端儲存UI工具。<br>

目前,後端整體框架的核心部分已經基本開發完成。只需逐步補充後端介面和外掛開發介面的定義即可。但由於個人時間和能力所限,UI部分沒有開發,有興趣的同學可以一試。

<a name="使用說明"></a>
## 使用說明

<a name="獲取程式碼"></a>
### 獲取程式碼

* gitcafe專案主頁: <https://gitcafe.com/onecoder/shurnim-storage-for-UPYUN>
* OSChina專案主頁: <http://git.oschina.net/onecoder/shurnim-storage><br>
OSChina上的會持續更新。

另外你也可以通過OSChina的Maven庫獲取依賴,或者自己編譯jar包。

* maven

     1. 加入OSC倉庫
   
                    <repositories>
                      <repository>
                           <id>nexus</id>
                           <name>local private nexus</name>
                           <url>http://maven.oschina.net/content/groups/public/</url>
                           <releases>
                                <enabled>true</enabled>
                           </releases>
                           <snapshots>
                                <enabled>false</enabled>
                           </snapshots>
                      </repository>
                 </repositories>

     2. 加入依賴
   
               <dependency>
                 <groupId>com.coderli</groupId>
                 <artifactId>shurnim-storage</artifactId>
                  <version>0.1-alpha</version>
               </dependency>
* Gradle 編譯Jar

在專案目錄執行
   
     gradle jar
   
<a name="開發外掛"></a>
### 開發外掛

在*shurnim-storage*中,外掛就像一塊一塊的積木,不但支撐著框架的功能,也是框架可擴充套件性的基石。開發一個外掛,僅需兩步:

1. 實現PluginAPI介面

```
package com.coderli.shurnim.storage.plugin;

import java.io.File;
import java.util.List;

import com.coderli.shurnim.storage.plugin.model.Resource;

/**
* 各種雲端儲存外掛需要實現的通用介面
*
* @author OneCoder
* @date 2014年4月22日 下午9:43:41
* @website http://www.coderli.com
*/
public interface PluginAPI {

     /**
      * 初始化介面
      *
      * @author OneCoder
      * @date 2014年5月19日 下午10:47:40
      */
     void init();

     /**
      * 獲取子資源列表
      *
      * @param parentPath
      * @return
      * @author OneCoder
      * @date 2014年4月24日 下午11:29:14
      */
     List<Resource> getChildResources(String parentPath);

     /**
      * 下載特定的資源
      *
      * @param parentPath
      *            目錄路徑
      * @param name
      *            資源名稱
      * @param storePath
      *            下載資源儲存路徑
      * @return
      * @author OneCoder
      * @date 2014年4月24日 下午11:30:19
      */
     Resource downloadResource(String parentPath, String name, String storePath);

     /**
      * 建立資料夾
      *
      * @param path
      *            資料夾路徑
      * @param auto
      *            是否自動建立父目錄
      * @return
      * @author OneCoder
      * @date 2014年5月15日 下午10:10:04
      */
     boolean mkdir(String path, boolean auto);

     /**
      * 上傳資源
      *
      * @param parentPath
      *            父目錄路徑
      * @param name
      *            資源名稱
      * @param uploadFile
      *            待上傳的本地檔案
      * @return
      * @author OneCoder
      * @date 2014年5月15日 下午10:40:13
      */
     boolean uploadResource(String parentPath, String name, File uploadFile);
}
```

目前外掛的介面列表僅為同步資源設計,如果想要支援更多操作(如刪除,查詢等),可擴充套件該介面定義。<br/><br/>
介面中,所有的引數和返回值均為*shurnim-storage*框架中定義的通用模型。因此,您在開發外掛過程中需要將特定SDK中的模型轉換成介面中提供的模型。<br/><br/>
外掛實現類只要與*shurnim-storage*工程在同一個classpath即可使用。您既可以直接在原始碼工程中開發外掛,就如工程裡提供的*upyun*和*qiniu*外掛一樣,也可以作為獨立工程開發,打成jar,放置在同一個classpath下。<br/><br/>
*upyun*外掛樣例(功能不完整):

```  
package com.coderli.shurnim.storage.upyun.plugin;

import java.io.File;
import java.util.List;

import com.coderli.shurnim.storage.plugin.AbstractPluginAPI;
import com.coderli.shurnim.storage.plugin.model.Resource;
import com.coderli.shurnim.storage.plugin.model.Resource.Type;
import com.coderli.shurnim.storage.upyun.api.UpYun;

public class UpYunPlugin extends AbstractPluginAPI {

     private UpYun upyun;
     private String username;
     private String password;
     private String bucketName;

     public UpYun getUpyun() {
          return upyun;
     }

     public void setUpyun(UpYun upyun) {
          this.upyun = upyun;
     }

     public String getUsername() {
          return username;
     }

     public void setUsername(String username) {
          this.username = username;
     }

     public String getPassword() {
          return password;
     }

     public void setPassword(String password) {
          this.password = password;
     }

     public String getBucketName() {
          return bucketName;
     }

     public void setBucketName(String bucketName) {
          this.bucketName = bucketName;
     }

     /*
      * (non-Javadoc)
      *
      * @see
      * com.coderli.shurnim.storage.plugin.PluginAPI#getChildResources(java.lang
      * .String)
      */
     @Override
     public List<Resource> getChildResources(String parentPath) {
          return null;
     }

     /*
      * (non-Javadoc)
      *
      * @see
      * com.coderli.shurnim.storage.plugin.PluginAPI#downloadResource(java.lang
      * .String, java.lang.String, java.lang.String)
      */
     @Override
     public Resource downloadResource(String parentPath, String name,
               String storePath) {
          File storeFile = new File(storePath);
//          if (!storeFile.exists()) {
//               try {
//                    storeFile.createNewFile();
//               } catch (IOException e) {
//                    e.printStackTrace();
//               }
//          }
          String filePath = getFullPath(parentPath, name);
          upyun.readDir("/api");
          if (upyun.readFile(filePath, storeFile)) {
               Resource result = new Resource();
               result.setName(name);
               result.setPath(parentPath);
               result.setType(Type.FILE);
               result.setLocalFile(storeFile);
               return result;
          }
          return null;
     }

     String getFullPath(String parentPath, String name) {
          if (!parentPath.endsWith(File.separator)) {
               parentPath = parentPath + File.separator;
          }
          return parentPath + name;
     }

     /*
      * (non-Javadoc)
      *
      * @see com.coderli.shurnim.storage.plugin.PluginAPI#mkdir(java.lang.String,
      * boolean)
      */
     @Override
     public boolean mkdir(String path, boolean auto) {
          // TODO Auto-generated method stub
          return false;
     }

     /*
      * (non-Javadoc)
      *
      * @see
      * com.coderli.shurnim.storage.plugin.PluginAPI#uploadResource(java.lang
      * .String, java.lang.String, java.io.File)
      */
     @Override
     public boolean uploadResource(String parentPath, String name,
               File uploadFile) {
          // TODO Auto-generated method stub
          return false;
     }

     /*
      * (non-Javadoc)
      *
      * @see com.coderli.shurnim.storage.plugin.AbstractPluginAPI#init()
      */
     @Override
     public void init() {
          upyun = new UpYun(bucketName, username, password);
     }

}
```


2. 編寫外掛配置檔案

```
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
     <id>qiniu</id>
     <name>七牛雲端儲存</name>
     <api>
          <className>com.coderli.shurnim.storage.qiniu.QiniuPlugin</className>
          <params>
               <param name="access_key" displayName="ACCESS_KEY">EjREKHI_GFXbQzyrKdVhhXrIRyj3fRC1s9UmZPZO
               </param>
               <param name="secret_key" displayName="SECRET_KEY">88NofFWUvkfJ6T6rGRxlDSZOQxWkIxY2IsFIXJLX
               </param>
               <param name="bucketName" displayName="空間名">onecoder
               </param>
          </params>
     </api>
</plugin>
```
   * **id** 為該外掛在*shurnim-storage*框架下的唯一標識,不可重複,必填。
    * **name** 為顯示值,為UI開發提供可供顯示的有語義的值。
    * **className** 為外掛介面實現類的完整路徑。必填
    * **params/param** 為外掛需要使用者配置的引數列表。其中
         * *name* 代表引數名,需要與介面實現類中的引數名嚴格一致,且必須有相應的set方法的格式要求嚴格,即set+首字母大寫的引數名。例如:setAccess_key(String arg); 目前只支援*String*型別的引數。
         * *displayName* 為引數顯示名,同樣是為了UI開發的考慮,方便使用者開發出可根據引數列表動態顯示的UI介面。
         * 引數的值可以直接配置在配置檔案中,也可以在執行期動態賦值。直接配置值,對於直接使用後端介面來說較為方便。對於UI開發來說,執行期動態賦值更為合理。<br/></br>

     在使用原始碼工程時,外掛配置檔案統一放置在工程的*plugins*目錄下。你也可以統一放置在任何位置。此時,在構造後端介面例項時,需要告知介面該位置。
   
<a name="使用ShurnimStorage介面"></a>
### 使用*ShurnimStorage*介面

<a name="介面介紹"></a>
#### 介面介紹

**ShurnimStorage**介面是*shurinm-storage*框架全域性的也是唯一的介面,目前定義如

```
package com.coderli.shurnim.storage;

import java.util.List;
import java.util.Map;

import com.coderli.shurnim.storage.plugin.model.Plugin;
import com.coderli.shurnim.storage.plugin.model.Resource;

/**
* 後臺模組的全域性介面<br>
* 通過該介面使用後臺的全部功能。<br>
* 使用方式:<br>
* <li>
* 1.先通過{@link #getSupportedPlugins()}方法獲取所有支援的平臺/外掛列表。 <li>
* 2.將列表中返回的ID傳入對應的介面引數中,進行對應的平臺的相關操作。<br>
* 需要注意的是,不同平臺的外掛需要給不同的引數賦值,該值可以直接配置在配置檔案中。<br>
* 也可以在執行期動態賦值。(會覆蓋配置檔案中的值。)<br>
*
* 引數列表的設計,方便UI開發人員動態的根據引數列表生成可填寫的控制元件。並給引數賦值。增強了可擴充套件性。
*
* @author OneCoder
* @date 2014年4月22日 下午9:21:58
* @website http://www.coderli.com
*/
public interface ShurnimStorage {

     /**
      * 獲取當前支援的外掛列表<br>
      * 沒有支援的外掛的時候可能返回null
      *
      * @return
      * @author OneCoder
      * @date 2014年5月7日 下午8:53:25
      */
     List<Plugin> getSupportedPlugins();

     /**
      * 給指定的外掛的對應引數賦值<br>
      * 此處賦值會覆蓋配置檔案中的預設值
      *
      * @param pluginId
      *            外掛ID
      * @param paramsKV
      *            引數鍵值對
      * @author OneCoder
      * @date 2014年5月9日 上午12:41:53
      */
     void setParamValues(String pluginId, Map<String, String> paramsKV);

     /**
      * 獲取外掛對應目錄下的資源列表
      *
      * @param pluginId
      *            外掛ID
      * @param path
      *            指定路徑
      * @return
      * @author OneCoder
      * @date 2014年5月11日 上午8:52:00
      */
     List<Resource> getResources(String pluginId, String path);

     /**
      * 同步資源
      *
      * @param fromPluginId
      *            待同步的外掛Id
      * @param toPluginIds
      *            目標外掛Id
      * @param resource
      *            待同步的資源
      * @return 同步結果
      * @author OneCoder
      * @date 2014年5月11日 上午11:41:24
      */
     boolean sycnResource(String fromPluginId, String toPluginId,
                    Resource resource) throws Exception;
}
```    

當前介面實際僅包含了獲取資源列表*getResources*和同步資源*sycnResource*功能,*getSupportedPlugins*和*setParamValues*實際為輔助介面,在UI開發時較為有用。<br/><br/>
同樣,您也可以擴充套件開發該介面增加更多的您喜歡的特性。例如,同時刪除給定儲存上的檔案。當然,這需要外掛介面的配合支援。<br/><br/>

這裡,*sycnResource*設計成外掛間一對一的形式,是考慮到獲取同步是否成功的結果的需求。如果您想開發一次同步到多個儲存的功能,建議您重新開發您自己的介面實現類,因為預設實現會多次下次資源(每次同步後刪除),造成網路資源的浪費。

介面的預設實現類是: **DefaultShurnimStorageImpl**

<a name="使用樣例"></a>
#### 使用樣例
```      
package com.coderli.shurnim.test.shurnimstorage;

import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

import com.coderli.shurnim.storage.DefaultShurnimStorageImpl;
import com.coderli.shurnim.storage.ShurnimStorage;
import com.coderli.shurnim.storage.plugin.model.Resource;
import com.coderli.shurnim.storage.plugin.model.Resource.Type;

/**
* 全域性介面測試類<br>
* 時間有限,目前僅作整體介面測試。細粒度的單元測試,隨開發補充。
*
* @author OneCoder
* @date 2014年5月19日 下午10:50:27
* @website http://www.coderli.com
*/
public class ShurnimStorageTest {

     private static ShurnimStorage shurnim;

     @BeforeClass
     public static void init() {
          shurnim = new DefaultShurnimStorageImpl(
                    "/Users/apple/git/shurnim-storage-for-UPYUN/plugins");
     }

     @Test
     public void testSycnResource() {
          Resource syncResource = new Resource();
          syncResource.setPath("/api");
          syncResource.setName("api.html");
          syncResource.setType(Type.FILE);
          try {
               Assert.assertTrue(shurnim.sycnResource("upyun", "qiniu",
                         syncResource));
          } catch (Exception e) {
               e.printStackTrace();
          }
     }
}
```
<a name="其他"></a>
## 其他

時間倉促,功能簡陋,望您包涵。OneCoder(Blog:[http://www.coderli.com](http://www.coderli.com))特別希望看到該專案對您哪怕一點點的幫助。任意的意見和建議,歡迎隨意與我溝通,聯絡方式:

* Email: <[email protected]>
* QQ:57959968
* Blog:[OneCoder](http://www.coderli.com)

專案的Bug和改進點,可在OSChina上以issue的方式直接提交給我。

效果預覽: