Lisp Webtales 第 5 章 - 如何让它工作

mwa*_*wal 3 web-frameworks common-lisp

继我的解决方案使第 4 章中的代码像这里一样工作之后,我需要一些帮助来调试lisp webtales 的第 5 章中的应用程序“linkdemo” 。

我已经仔细地输入了所有代码并消除了书中包含的一个错字。

CCL如果我发出(ql:quickload "linkdemo")我得到这个:

CCL is free software.  It is distributed under the terms of the Apache
Licence, Version 2.0.
? (ql:quickload "linkdemo")
To load "linkdemo":
  Load 1 ASDF system:
    linkdemo
; Loading "linkdemo"
.
;;; Checking for wide character support... yes, using code points.
;;; Checking for wide character support... yes, using code points.
;;; Building Closure with CHARACTER RUNES
.
> Error: The value NIL is not of the expected type STRING.
> While executing: ENSURE-SIMPLE-STRING, in process listener(1).
> Type :POP to abort, :R for a list of available restarts.
> Type :? for other options.
1 > 
Run Code Online (Sandbox Code Playgroud)

如果我输入:R我得到这个:

1 > :R
>   Type (:C <n>) to invoke one of the following restarts:
0. Return to break level 1.
1. #<RESTART ABORT-BREAK #x133AEDD>
2. Retry compiling #P"/Users/m/quicklisp/local-projects/linkdemo/defmodule.lisp"
3. Skip compiling #P"/Users/m/quicklisp/local-projects/linkdemo/defmodule.lisp"
4. Retry compiling #<CL-SOURCE-FILE "linkdemo" "defmodule">.
5. Continue, treating compiling #<CL-SOURCE-FILE "linkdemo" "defmodule"> as having been successful.
6. Retry ASDF operation.
7. Retry ASDF operation after resetting the configuration.
8. Give up on "linkdemo"
9. Return to toplevel.
10. #<RESTART ABORT-BREAK #x133C53D>
11. Reset this thread
12. Kill this thread
1 > 
Run Code Online (Sandbox Code Playgroud)

从中我推断出错误在defmodule.lisp.

然后,我发现如果我注释掉以下整个块defmodule.lisp

(restas:define-policy datastore
  (:interface-package #:linkdemo.policy.datastore)
  (:interface-method-template "DATASTORE-~A")
  (:internal-package #:linkdemo.datastore)

  (define-method init ()                                    
    "Initiate the datastore")

  (define-method find-user (username)                       
    "Find the user by username")

  (define-method auth-user (username password)
    "Check if a user exists and has the supplied password")

  (define-method register-user (username password)
    "Register a new user")

  (define-method upvoted-p (link-id username)
    "Check if a user has upvoted a link")

  (define-method upvote (link-id user)
    "Upvote a link")

  (define-method post-link (url title user)
    "Post a new link")

  (define-method get-all-links (&optional user)
    "Get all of the links in the datastore")

  (define-method upvote-count (link-id)
    "Get the number of upvotes for a given link"))
Run Code Online (Sandbox Code Playgroud)

然后重新发布(ql:quickload "linkdemo"),我没有收到任何错误。

(另外,如果我再发表评论,它再次,与quickload重装,我也得到任何错误。我不知道发生了什么或有针对的原因。)

无论我是否快速加载上面有问题的部分,我都可以在 中探索一个函数pg-datastore.lisp,如下所示:

1 > (linkdemo.pg-datastore::hash-password "42")
(:PASSWORD-HASH "71a8c8f54475acb5afee9eae061fa5f7ba838215f280259855e59a7c0ac768f8" :SALT "a283c5328f67c36595dd7277c54342f3")
Run Code Online (Sandbox Code Playgroud)

似乎还不可能尝试启动服务器,因为在它完全运行之前需要第 6 章中的代码(我们还没有路由,只有带有 的 DAO 层postmodern)。

所以不确定如何继续测试 Ch.5 中的代码/调试这个。错误似乎是restas本身的问题 - 也许。

寻求有关如何进行的任何有根据的猜测。

可以通过简单地执行(ql:quickload "restas")然后评估上面给出的块来重现错误。这让Error: The value NIL is not of the expected type STRING.我对在 LISP 中使用其他人的代码很陌生,所以我对如何继续感到有些困惑。

更多信息:这是调用导致define-method此错误的原因,来源是policy.lisp 此处的文件,目前我有点超出了我的范围。

我也试过sbcl

* (ql:quickload "linkdemo")
To load "linkdemo":
  Load 1 ASDF system:
    linkdemo
; Loading "linkdemo"
.
;;; Checking for wide character support... WARNING: Lisp implementation doesn't use UTF-16, but accepts surrogate code points.
 yes, using code points.
;;; Building Closure with CHARACTER RUNES
.........
debugger invoked on a SB-KERNEL:CASE-FAILURE in thread
#<THREAD "main thread" RUNNING {10004F04C3}>:
  NIL fell through ETYPECASE expression.
  Wanted one of (SIMPLE-STRING STRING SB-FORMAT::FMT-CONTROL).

...

(SB-FORMAT::%FORMAT #<SB-IMPL::CHARACTER-STRING-OSTREAM {1003BD1673}> NIL ("UPVOTE-COUNT") ("UPVOTE-COUNT"))
0] 
Run Code Online (Sandbox Code Playgroud)

cor*_*ump 5

仅定义一种方法时,我可以重现测试用例:

(restas:define-policy datastore
  (:interface-package #:linkdemo.policy.datastore)
  (:interface-method-template "DATASTORE-~A")
  (:internal-package #:linkdemo.datastore)
  (define-method init ()                                    
    "Initiate the datastore"))
Run Code Online (Sandbox Code Playgroud)

这里的问题是,当您展开上述宏时,您会获得对RESTAS::%DEFINE-POLICYwhere :INTERNAL-FUNCTION-TEMPLATEis given的函数调用NIL,这不是一个合适的格式字符串。这是因为define-policy宏解析选项的方式。

这是一个简化的示例:

(defun my-function (&key (x 0))
  (print x))
Run Code Online (Sandbox Code Playgroud)

MY-FUNCTION的默认值为 0 :x。如果您调用(my-function),它会打印 0。但是宏执行如下操作:

(defmacro my-macro (args)
  (let ((x-arg))
    (loop for (k v) in args 
       do (case k (:x (setf x-arg v))))
    `(my-function :x ,x-arg)))
Run Code Online (Sandbox Code Playgroud)

它遍历参数列表以检查是否:x存在参数的某个关键字,然后将局部变量(初始化为 nil)设置为相应的值。在所有情况下,它都会扩展为对 的调用my-function并显式绑定x的值x-arg,即使它为 nil 也是如此。事实上,如果我们宏展开(my-macro ()),如果给出(my-function :x nil)

由于函数已经定义了默认值,我们希望避免在其他地方重复这个默认值,因此宏应该注意不要:x在其对应的值为 nil 时给出 an 。或者更准确地说,当关键字参数不存在时(这里我不区分这两种情况)。通常,这是按如下方式完成的:

(defmacro my-macro (args)
  (flet ((maybe (k v) (and v (list k v))))
    (let ((x-arg))
      (loop for (k v) in args do (case k (:x (setf x-arg v))))
      `(my-function ,@(maybe :x x-arg)))))
Run Code Online (Sandbox Code Playgroud)

,@拼接运算符用于关键字参数注入通话的基础上,该值是零或没有。为nil时,拼接空列表。否则,属性列表(list k v)被拼接。因此,对(my-macro ())now的调用扩展为(my-function),因此x将采用其默认值。

这没有经过很好的测试,但我想你可以重新定义define-policy如下:

(defmacro define-policy (name &body body)
  (let (methods
        internal-package  internal-function-template
        interface-package interface-method-template)
    (iter (for item in body)
          (case (car item)
            (:internal-package
             (setf internal-package (second item)))
            (:internal-function-template
             (setf internal-function-template (second item)))
            (:interface-package
             (setf interface-package (second item)))
            (:interface-method-template
             (setf interface-method-template (second item)))
            (otherwise
             (cond
               ((string= (car item) "DEFINE-METHOD")
                (push (cdr item) methods))
               (t
                (error "Unknown DEFINE-POLICY option: ~A" item))))))
    (flet ((maybe (key value) (and value (list key value)))
           (maybe/quote (key value) (and value (list key `(quote ,value)))))
      `(eval-when (:compile-toplevel :load-toplevel :execute)
         (%define-policy ',name ',methods
                         ,@(maybe/quote :interface-package interface-package)
                         ,@(maybe :interface-method-template interface-method-template)
                         ,@(maybe/quote :internal-package internal-package)
                         ,@(maybe :internal-function-template internal-function-template))))))
Run Code Online (Sandbox Code Playgroud)

或者,只需将:internal-function-template参数(例如"~A")添加到您的宏中。


已修补:https : //github.com/archimag/restas/commit/81bbbab6b36f81f846f78e71232e9d3d15f6d952

  • 出色的!这完全解决了它。因此,为了未来的读者总结一下,在网络故事的第 33 页上,他说他“选择跳过定义这样的规则”,但我们在这里发现*我们需要添加它*...所以我添加了 `(:internal-function-template " ~A")` 如此处建议。然后,我刚刚清除了自己的几个拼写错误,并且必须以与第 4 章的解决方案中所述类似的方式删除`:render-method 'html-frame`(请参阅OP顶部的链接),现在***一切正常..***(包括第 6 章中的代码)。短暂休息后,我们将进入第 7 章中的 redis 后端:) (2认同)