【问题标题】:Define method type for meta dynamically created method为元动态创建的方法定义方法类型
【发布时间】:2019-07-22 23:32:16
【问题描述】:

我正在使用graphql-ruby,我真的希望能够键入为arguments 之类的东西创建的动态方法。

小例子:

class Test

  argument :first_argument, String
  argument :secondArgument, String, as: second_argument, required: false

  def method
    puts first_argument.length # this is okay
    puts second_argument.length # this is a problem, because it can be nil
  end
end

我试图通过以下方式定义这些:

  # ...
  first_argument = T.let(nil, String)
  second_argument = T.let(nil, T.nilable(String))

这似乎不起作用。我也做过

  #...
  sig { returns(String) }
  def first_argument; ""; end
  sig { returns(T.nilable(String)) }
  def second_argument; end

工作,但并不过分漂亮。有没有更好的方法来解决这个问题?

【问题讨论】:

    标签: sorbet


    【解决方案1】:

    对元编程声明的类型方法有一些新生的实验性支持,如下所示:https://sorbet.org/docs/metaprogramming-plugins

    在这种情况下,您可以定义如下插件文件:

    # argument_plugin.rb
    
    # Sorbet calls this plugin with command line arguments similar to the following:
    # ruby --class Test --method argument --source "argument :first_argument, String"
    # we only care about the source here, so we use ARGV[5]
    source = ARGV[5]
    /argument[( ]:([^,]*?), ([^,]*?)[) ]/.match(source) do |match_data|
      puts "sig {return(#{match_data[2]})}" # writes a sig that returns the type
      puts "def #{match_data[1]}; end"      # writes an empty method with the right name
    end
    

    我在这里只包含了参数的“getter”,但是继续写出 setter 方法的 sig 应该很简单。您还想处理argument 方法的所有变体,因为我只处理了带有Symbol, Type 参数的变体。对于它的价值,我不确定传递给你的插件的“源”是否会用括号标准化,所以我也做了正则表达式匹配。我还怀疑如果您将符号名称作为变量而不是文字传递,这将不起作用。

    然后我们使用 YAML 文件告诉 Sorbet 这个插件。

    # triggers.yaml
    
    ruby_extra_args:
      # These options are forwarded to Ruby
      - '--disable-gems' # This option speeds up Ruby boot time. Use it if you don't need gems
    triggers:
      argument: argument_plugin.rb # This tells Sorbet to run argument.rb when it sees a call to `argument`
    

    运行 Sorbet 并传入 yaml 配置文件作为 --dsl-plugins 的参数:

    ❯ srb tc --dsl-plugins triggers.yaml ... files to type check ...
    

    【讨论】:

      【解决方案2】:

      我真的很希望能够键入为参数等创建的动态方法

      Sorbet 不支持键入这样的动态方法。但它们确实提供了具有类似功能的T::Struct 类。上周我为我的项目做了类似的事情,我将在下面描述我做了什么。如果T::Struct 不适合您,另一种方法是编写一些代码来生成您需要手动编写的 Sig。

      我的方法是使用T::Struct 作为“参数”类的包装器。您可以将 args 定义为 T::Struct 中的道具,如下所示:

      • const 用于不变的参数
      • prop 可能会更改的参数
      • 在未指定值时使用default 提供默认值
      • 对可以为 nil 的参数使用 T.nilable 类型

      在原版 T::Struct 的基础上,我还添加了对“maybe”的支持,这是对真正可选且可以为 nil 的 args 的支持。 IE:当一个值没有传入时,根本不应该使用它。它与使用nil 作为默认值不同,因为当传入一个值时,它可以是nil。如果您对这个“可能”组件感兴趣,请随时 DM 我。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-19
        相关资源
        最近更新 更多