from_json 不适用于录制宏?

from_json doesn't work with record macro?

为什么 from_json 对使用 record 宏创建的 struct 不起作用?

require "json"

record Stock,
  symbol :           String,
  name :             String

p Stock.from_json %({ "symbol": "MSFT", "name": "Some" })

错误

 13 | new parser
      ^--
Error: wrong number of arguments for 'Stock.new' (given 1, expected 2)

Overloads are:
 - Stock.new(symbol : String, name : String)

问题二:

堆栈跟踪没有行号,我尝试使用--error-trace但它没有效果,我如何使用--error-trace

> crystal api/crystal/play.cr --error-trace
Showing last frame. Use --error-trace for full trace.

In /crystal-1.1.1-1/src/json/from_json.cr:13:3

 13 | new parser
      ^--
Error: wrong number of arguments for 'Stock.new' (given 1, expected 2)

Overloads are:
 - Stock.new(symbol : String, name : String)

P.S.

我找到了解决方案,不过如果它能正常工作会更好,而不需要包含 JSON::Serializable

record Stock,
  symbol :           String,
  name :             String,
do
  include JSON::Serializable
end

你已经想通了。关于是否应始终包含 JSON::Serialize 的问题,这意味着 JSON 支持可用(require "json")。否则,它不会编译。我认为这就是默认情况下它没有被烘焙的原因。

关于原题,有调试宏的方法。如果在宏的末尾包含 {% debug %},它将打印生成的代码。我在您的示例中通过复制 record macro (sources are here) 的源代码进行了尝试,仅通过 my_record:

重命名 record
macro my_record(name, *properties)
  struct {{name.id}}
    {% for property in properties %}
      {% if property.is_a?(Assign) %}
        getter {{property.target.id}}
      {% elsif property.is_a?(TypeDeclaration) %}
        getter {{property}}
      {% else %}
        getter :{{property.id}}
      {% end %}
    {% end %}

    def initialize({{
                     *properties.map do |field|
                       "@#{field.id}".id
                     end
                   }})
    end

    {{yield}}

    def copy_with({{
                    *properties.map do |property|
                      if property.is_a?(Assign)
                        "#{property.target.id} _#{property.target.id} = @#{property.target.id}".id
                      elsif property.is_a?(TypeDeclaration)
                        "#{property.var.id} _#{property.var.id} = @#{property.var.id}".id
                      else
                        "#{property.id} _#{property.id} = @#{property.id}".id
                      end
                    end
                  }})
      self.class.new({{
                       *properties.map do |property|
                         if property.is_a?(Assign)
                           "_#{property.target.id}".id
                         elsif property.is_a?(TypeDeclaration)
                           "_#{property.var.id}".id
                         else
                           "_#{property.id}".id
                         end
                       end
                     }})
    end

    def clone
      self.class.new({{
                       *properties.map do |property|
                         if property.is_a?(Assign)
                           "@#{property.target.id}.clone".id
                         elsif property.is_a?(TypeDeclaration)
                           "@#{property.var.id}.clone".id
                         else
                           "@#{property.id}.clone".id
                         end
                       end
                     }})
    end
  end
  {% debug %}
end

注意最后的{% debug %}。现在当运行你的例子...

my_record Stock,
  symbol :           String,
  name :             String

... 它扩展为:

struct Stock
  getter symbol : String

  getter name : String

  def initialize(@symbol : String, @name : String)
  end

  def copy_with(symbol _symbol = @symbol, name _name = @name)
    self.class.new(_symbol, _name)
  end

  def clone
    self.class.new(@symbol.clone, @name.clone)
  end
end

此外,--error-trace 参数应该有效。我没有用 crystal play 试过,但你可以用 crystal run。该选项必须位于文件名之前:

$ crystal run --error-trace example.cr
struct Stock
  getter symbol : String

  getter name : String

  def initialize(@symbol : String, @name : String)
  end

  def copy_with(symbol _symbol = @symbol, name _name = @name)
    self.class.new(_symbol, _name)
  end

  def clone
    self.class.new(@symbol.clone, @name.clone)
  end
end
In example.cr:70:9

 70 | p Stock.from_json %({ "symbol": "MSFT", "name": "Some" })
              ^--------
Error: instantiating 'Stock.class#from_json(String)'


In /usr/lib/crystal/json/from_json.cr:13:3

 13 | new parser
      ^--
Error: wrong number of arguments for 'Stock.new' (given 1, expected 2)

Overloads are:
 - Stock.new(symbol : String, name : String)