[Elixir007] on_definition規範函數定義時的各種潛規則

来源:http://www.cnblogs.com/zhongwencool/archive/2016/04/04/elixir_on_definition.html
-Advertisement-
Play Games

1.需求 寫一個基於memcache的cache模塊, 需要在key前面加上特定的首碼, 所以user cache的原始的store函數應該寫成 由於加首碼的操作(key_encode/1)是所有存入cache前必須要做的事, 所以我們可以考慮通過metaprogramming來定義一個行為叫bef ...


1.需求

寫一個基於memcache的cache模塊, 需要在key前面加上特定的首碼, 所以user cache的原始的store函數應該寫成

# user.ex
def
store(user_id, value) do key
= Cache.key_encode(user_id, :user)
...
end

由於加首碼的操作(key_encode/1)是所有存入cache前必須要做的事, 所以我們可以考慮通過metaprogramming來定義一個行為叫before_store/2來做這件事,然後在put前hook before_store,但這會讓代碼非常難以理解。

我覺得更好的方法是在編譯store/2期間去檢查它的開始有沒有執行過這個加首碼的encode函數, 這才能讓讓代碼更容易理解。

所以我們的潛規則是在模塊中的每一個函數的第一行,必須是Cache.key_encode/2

2. 使用@on_definition檢查模塊的每個函數第一行必須調用Cache.key_encode/2

我們接下來要使用@on_definition 在編譯器去檢查指定模塊是不是符合這個自定義的潛規則。

mix new on_definition_play
cd on_definition_play
# lib/user.ex
defmodule User do
  @on_definition {Cache.Enforcement, :on_def}

  def store_user(user_id, user) do
    key = Cache.key_encode(user_id, :user)
    Cache.put(key, user)
  end
  # 這個是沒有做key_encode的例子,應該編譯不過
  def store_comment(user_id, comment) do
    Cache.put(user_id, comment)
  end
end

看上面的我們定義了on_definition屬性,接下來我們就來實現這個on_def/6

defmodule Cache do
  # 這裡只是用到了memcache_client做例子,你可以使用其它backend
  def put(key, value) do
    Memcache.Client.put(key, value)
  end
  def get(key)  do
    Memcache.Client.get(key)
  end

  def key_encode(key, prefix) do
    "#{prefix}:#{inspect key}"
  end

  defmodule Enforcement do
    def on_def(env, _kind, _name, args, _guards, body) do
      check_start_with_key_encode(env, args, body)
    end

    defp check_start_with_key_encode(_env, [{_, meta, _} | _args], body) do
      line = Keyword.get(meta, :line)
# 從body裡面取出第一行,然後再check它的格式 expr
= get_first_line(body) IO.inspect expr case expr do :print_to_see_this_struct-> # 我們現在也不知道這東西是個什麼東西,所以先用IO.inspect/1打出來看看,然後再對格式 :ok _ -> raise Cache.LacksEncodeError, message: "Function line#{line} must begin with a Cache.key_encode/2" end end
# 定義函數里使用的簡略模式 def func, do: defp
get_first_line({:__block__, _, expr_list}) do List.first(expr_list)
end defp get_first_line(expr) do expr end end defmodule LacksEncodeError do defexception [:message] end end

我們也不知道第一行編成AST後會是什麼樣子,所以我們先把正確的格式給IO.inspect看一看。然後再匹配上去 :)

所以根據inspect的結果我們可以最後把check_start_with_key_encode/3寫成:

defp check_start_with_key_encode(_env, [{_, meta, _} | _args], body) do
      line = Keyword.get(meta, :line)
      expr = get_first_line(body)
      case expr do
       {:=, _,
         [{_, _, _},
          {{:., _,
          [{:__aliases__, _, [:Cache]},#就是它!
           :key_encode]}, _,#就是它!
            _}]} ->
          :ok
       _ ->
        raise Cache.LacksEncodeError, message: "Function line#{line} must begin with a Cache.key_encode/2"
      end
    end

這裡再運行mix compile就會得到

> mix compile
== Compilation error on file lib/user.ex ==
** (Cache.LacksEncodeError) Function line9 must begin with a Cache.key_encode/2
    lib/cache.ex:31: Cache.Enforcement.check_start_with_key_encode/3
    (stdlib) erl_eval.erl:669: :erl_eval.do_apply/6

大功告成!

3. 結論:

  @on_definition時會調用on_def/6所以我們可以在編譯期間對每一個函數自定義你所需要的任何潛規則(但是也不要濫用哦:) )

4. Resources

Module docs  這裡面還有其它的compile callback函數和選項,值得好好看看

 



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 作者:[美]Adam Freeman 來源:《精通ASP.NET MVC 4》 本文將繼續構建 SportsStore 示例應用程式。在上一章中,添加了對購物車的基本支持,現在打算改善並完成其功能。 1.使用模型綁定 MVC 框架使用了一個叫作“模型綁定”的系統,以便通過 HTTP 請求來創建一些 ...
  • ASP.NET Core在啟動以及後續針對每個請求的處理過程中的各個環節都需要相應的組件提供相應的服務,為了方便對這些組件進行定製,ASP.NET通過定義介面的方式對它們進行了“標準化”,我們將這些標準化的組件稱為服務,ASP.NET在內部專門維護了一個DI容器來提供所需的服務。要瞭解這個DI容器以... ...
  • ??二元操作符在對first??second求值時,大致會經歷以下步驟: 1)對first進行求值; 2)如果結果非空,則該結果就是整個表達式的結果; 3)否則求second的值,其結果作為整個表達式的結果。 例如: ...
  • 當我們初學Winform的時候被其神奇的事件功能所吸引,當點擊一個按鈕時,便會跳到我們所寫的點擊方法當中去。然而這並不符合我們對方法的理解,究竟.net在後面幫助我們實現了什麼。我們怎樣模擬其事件的實現呢。下麵先從Button的Click方法說起。 1.首先查看設計器自動生成的代碼 EventHan ...
  • 目錄結構: 1. SVN伺服器搭建和使用-VisualSVNServer 2. SVN客戶端安裝和使用-TortoiseSVN 3. TortoiseSVN使用方法 SVN簡介 SVN是Subversion的簡稱,是一個開放源代碼的版本控制系統,相較於RCS、CVS,它採用了分支管理系統,它的設計目 ...
  • 1,.NET Framework:是開發平臺,包含兩大部分: ①龐大的代碼庫(類庫),可以在客戶語言(C#,VB)中來使用這些代碼 ②Common Language Runtime,負責管理應用程式的執行 2,使用.NET Framework編寫應用程式,就是使用.NET 代碼庫編寫程式。 3,C# ...
  • 0x00 前言 關於TDD測試驅動開發的文章已經有很多了,但是在游戲開發尤其是使用Unity3D開發游戲時,卻聽不到特別多關於TDD的聲音。那麼本文就來簡單聊一聊TDD如何在U3D項目中使用以及如何使用U3D 5.3.X之後版本已經集成的單元測試模塊Editor Test Runner。 0x01 ...
  • 為什麼要以對象的方式來訪問xml數據表? 還記得,自己是在一次完成師兄佈置的任務時接觸到了xml,那時候需要用xml來作為數據文件,保存一個簡單的圖書管理系統的數據。於是就知道了,可以用xml文件來保存數據(而且比用簡單的文本文件保存數據規範的多,在訪問與讀取數據上面都十分方便),就這樣使用xml的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...