Ruby方法可选参数和&block参数

4
  1. Hey there

    Is it possible to have optional attributes and a block as parameters for a method call?

    Example: I have to call

    method(foo, foo: bar, -> { do_something }
    

    and tried it with

    def method(foo, *bar, &block)
    end
    

    As for my understanding the block always has to be at last position?

    After a bit of research I found out the unary(?) * seems to be for arrays. Since I try to pass a Hash I changed the code to

    def method(foo, bar={}, &block)
    end
    

    But this doesn't do the trick either. I guess its because he cant figure out where the bar ends and the block starts.

    Any ideas or suggestions? Thank you in advance

    Append: Just for the curious why I need this. We have a big json schema running and have a small DSL that builds the json from the model definitation. Without going to much into detail we wanted to implement exportable_scopes.

    class FooBar
      exportable_scope :some_scope, title: 'Some Scope', -> { rewhere archived: true }
    end
    

    On some initializer this is supposed to happens:

    def exportable_scope scope, attributes, &block
      scope scope block
      if attributes.any?
        attributes.each do |attribute|
          exportable_schema.scopes[scope] = attribute
        end
      else
        exportable_schema.scopes[scope] = {title: scope}
      end
    end
    

    So this is working fine, I just need a hint for the method parameters.


我认为,您可以使用exportable_scope(:some_scope, title: 'Some Scope'){ rewhere archived: true }。您不需要像参数一样发送块。 - Lukas Baliak
1
我认为你混淆了传递块和将可调用对象(lambda)作为参数传递,这是ActiveRecord .scope期望接收的。范围的主体是必需的,因此您可以将方法签名更改为exportable_scope(name, body, attributes = {})并调用scope name, body。在这里查看.scope定义:https://github.com/rails/rails/blob/dc925119a3912ecfe0df400007163f33b99d6385/activerecord/lib/active_record/scoping/named.rb#L143。`exportable_scope :some_scope, -> { rewhere archived: true }, title: 'Some Scope'`。 - Alexandre Angelim
1个回答

8

是的,这是可能的。

当混合不同类型的参数时,它们必须按照以下特定顺序包含在方法定义中:

  1. 位置参数(必需和可选)和单个splat参数,任意顺序;
  2. 关键字参数(必需和可选),任意顺序;
  3. 双星号参数;
  4. 块参数(前缀为&);

上面的顺序有一定的灵活性。我们可以定义一个方法并以一个单独的splat参数开始参数列表,然后是几个可选位置参数,等等。虽然Ruby允许这样做,但通常这是一个非常糟糕的实践,因为代码会很难读懂,甚至更难调试。通常最好使用以下顺序:

  1. 必填位置参数;
  2. 可选位置参数(具有默认值);
  3. 单个splat参数;
  4. 关键字参数(必需和可选,其顺序无关紧要);
  5. 双星号参数;
  6. 显式块参数(前缀为&)。

例如:

def meditate cushion, meditation="kinhin", *room_items, time: , posture: "kekkafuza", **periods, &b
    puts "We are practicing #{meditation}, for #{time} minutes, in the #{posture} posture (ouch, my knees!)."
    puts "Room items: #{room_items}"
    puts "Periods: #{periods}"
    b.call # Run the proc received through the &b parameter
end

meditate("zafu", "zazen", "zabuton", "incense", time: 40, period1: "morning", period2: "afternoon" ) { puts "Hello from inside the block" }

# Output:
We are practicing zazen, for 40 minutes, in the kekkafuza posture (ouch, my knees!).
Room items: ["zabuton", "incense"]
Periods: {:period1=>"morning", :period2=>"afternoon"}
Hello from inside the block

请注意,在调用该方法时,我们做了以下几个操作:
  • 提供了必需的位置参数cushion;
  • 覆盖了可选的位置参数meditation的默认值;
  • 通过*room_items参数传递了额外的位置参数(zabuton和incense);
  • 提供了必需的关键字参数time;
  • 省略了可选的关键字参数posture;
  • 通过**periods参数传递了额外的关键字参数(period1: "morning", period2: "afternoon");
  • 通过&b参数传递了代码块{ puts "Hello from inside the block" }。
请注意,上面的示例仅用于说明可以混合使用不同类型的参数。在实际代码中构建这样的方法是不好的实践。如果一个方法需要那么多参数,最好将其拆分为更小的方法。如果绝对需要向单个方法传递这么多数据,我们应该考虑创建一个类来更有组织地存储数据,然后将该类的实例作为单个参数传递给该方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接