advantage of tap method in ruby

后端 未结 19 1002
天命终不由人
天命终不由人 2020-12-22 16:50

I was just reading a blog article and noticed that the author used tap in a snippet something like:

user = User.new.tap do |u|
  u.username = \         


        
相关标签:
19条回答
  • 2020-12-22 17:35

    I would say that there is no advantage to using tap. The only potential benefit, as @sawa points out is, and I quote: "A reader would not have to read what is inside the block to know that an instance user is created." However, at that point the argument can be made that if you're doing non-simplistic record creation logic, your intent would be better communicated by extracting that logic into its own method.

    I hold to the opinion that tap is an unnecessary burden on the readability of the code, and could be done without, or substituted with a better technique, like Extract Method.

    While tap is a convenience method, it's also personal preference. Give tap a try. Then write some code without using tap, see if you like one way over another.

    0 讨论(0)
  • 2020-12-22 17:35

    What is the difference?

    The difference in terms of code readability is purely stylistic.

    Code Walk through:

    user = User.new.tap do |u|
      u.username = "foobar"
      u.save!
    end
    

    Key points:

    • Notice how the u variable is now used as block parameter?
    • After the block is done, the user variable should now point to a User ( with a username: ‘foobar’, and who is also saved).
    • It's just pleasant and easier to read.

    API Documentation

    Here’s an easy to read version of the source code:

    class Object
      def tap
        yield self
        self
      end
    end
    

    For more info, see these links:

    https://apidock.com/ruby/Object/tap

    http://ruby-doc.org/core-2.2.3/Object.html#method-i-tap

    0 讨论(0)
  • 2020-12-22 17:40

    There is a tool called flog that measures how difficult it is to read a method. "The higher the score, the more pain the code is in."

    def with_tap
      user = User.new.tap do |u|
        u.username = "foobar"
        u.save!
      end
    end
    
    def without_tap
      user = User.new
      user.username = "foobar"
      user.save!
    end
    
    def using_create
      user = User.create! username: "foobar"
    end
    

    and according on flog's result the method with tap is the most difficult to read (and I agree with it)

     4.5: main#with_tap                    temp.rb:1-4
     2.4:   assignment
     1.3:   save!
     1.3:   new
     1.1:   branch
     1.1:   tap
    
     3.1: main#without_tap                 temp.rb:8-11
     2.2:   assignment
     1.1:   new
     1.1:   save!
    
     1.6: main#using_create                temp.rb:14-16
     1.1:   assignment
     1.1:   create!
    
    0 讨论(0)
  • 2020-12-22 17:42

    You're right: the use of tap in your example is kind of pointless and probably less clean than your alternatives.

    As Rebitzele notes, tap is just a convenience method, often used to create a shorter reference to the current object.

    One good use case for tap is for debugging: you can modify the object, print the current state, then continue modifying the object in the same block. See here for example: http://moonbase.rydia.net/mental/blog/programming/eavesdropping-on-expressions.

    I occasionally like to use tap inside methods to conditionally return early while returning the current object otherwise.

    0 讨论(0)
  • 2020-12-22 17:42

    You can make your codes more modular using tap, and can achieve a better management of local variables. For example, in the following code, you don't need to assign a local variable to the newly created object, in the scope of the method. Note that the block variable, u, is scoped within the block. It is actually one of the beauties of ruby code.

    def a_method
      ...
      name = "foobar"
      ...
      return User.new.tap do |u|
        u.username = name
        u.save!
      end
    end
    
    0 讨论(0)
  • 2020-12-22 17:43

    Another case to use tap is to make manipulation on object before returning it.

    So instead of this:

    def some_method
      ...
      some_object.serialize
      some_object
    end
    

    we can save extra line:

    def some_method
      ...
      some_object.tap{ |o| o.serialize }
    end
    

    In some situation this technique can save more then one line and make code more compact.

    0 讨论(0)
提交回复
热议问题