Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ruby 2 Methodology

Ruby 2 Methodology

Slides for RubyConf 2015 talk "Ruby 2 Methodology" http://rubyconf.org/program#prop_1641

76a777ff80f30bd3b390e275cce625bc?s=128

Akira Matsuda

November 17, 2015
Tweet

More Decks by Akira Matsuda

Other Decks in Programming

Transcript

  1. Ruby 2 Methodology Akira Matsuda RUBYCONF 2015

  2. This Talk Focuses on “Method” in Ruby

  3. I'm Sorry but this Talk Is Going to Be a

    Serious Ruby Talk
  4. RubyConf 2015

  5. None
  6. RubyConf 2015 Tracks Domain Patterns Less Code Wetware Ruby In

    Depth Beyond Ruby Play
  7. RubyConf 2015 Tracks Very well balanced Diverse topics

  8. So Glad that RubyConf Still Has the ”Ruby In Depth”

    Track This Year Makes me feel like I came to a Ruby conference
  9. RubyKaigi 2015 Next month In Japan "

  10. None
  11. 2 Tracks * 3 Days

  12. RubyKaigi Tracks Ruby Ruby Ruby Ruby Ruby Ruby

  13. Everyone There Talks About Ruby Because it's a Ruby conference

  14. If You Want to See More Ruby Talks RubyKaigi is

    the conference you should go!
  15. The RubyKaigi Team

  16. “Chief Organizer” Of RubyKaigi 2015

  17. Akira Matsuda GitHub: amatsuda Twitter: @a_matsuda

  18. GitHub: amatsuda

  19. GitHub: amatsuda Ruby

  20. GitHub: amatsuda Rails

  21. I Work for Ruby, Rails, Several Other OSS Products, And

    Several Companies
  22. begin

  23. This Talk Focuses on “Method” in Ruby

  24. I'm Going to Talk About Modern Usage of Ruby's Method

    Not just introducing the features I’d like to tell my stories With lots of Ruby code in the slides
  25. Method Definition

  26. def class Person def hello p 'hello' end end

  27. Sometimes We Want to Define a Method with Weird Name

    Which character can/ cannot be a (part of) Method name?
  28. Can a Method Name Contain Emoji?

  29. def see: sferik/active_emoji

  30. active_emoji/core_ext/ kernel.rb

  31. Defining and Calling def () '' * 5; end puts

    #=>
  32. Then Which Character Cannot Be a Method Name?

  33. Some ASCII Chars Such as 1 ? ) @ "

    : etc.
  34. def @ def @ end #=> Syntax Error

  35. How Can We Define an @ Method? (Without using C-exts)

  36. amatsuda/rfd I do this a lot in my app It

    might be interesting to see a real use case for methods with such weird names
  37. rfd/commands.rb # Change current directory (cd). define_method(:'@') do process_command_line preset_command:

    'cd' end # Execute a shell command in an external shell. define_method(:!) do process_shell_command end # Execute a command in the controller context. define_method(:':') do process_command_line end
  38. Listing up ASCII Chars that Can/ Cannot Be Used for

    Method Name h = {def: [], define_method: []} (' '..'~').each do |c| begin eval "def #{c}() puts '#{c}'; end" h[:def] << c rescue SyntaxError define_method(c) { puts c } h[:define_method] << c end end puts h
  39. Result :def => "!", "%", "&", "*", "+", "-", "/",

    "<", ">", "A"-"Z", "^", "_", "`", "a"-"z", "|", "~" :define_method => " ", "\"", "#", "$", "'", "(", ")", ",", ".", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "=", "?", "@", "[", "\\", "]", "{", "}"
  40. Then How Can We Call this Method? foo = @()

    #=> syntax error
  41. Then How Can We Call this Method? Kernel#send
 (or public_send)

  42. send @ define_method(:'@') do p '@' end send :'@' #=>

    "@"
  43. When to Use Kernel#send When calling a method with abnormal

    name When dynamically changing a Method to call When calling a Method from Outside of scope
  44. When Dynamically Changing a Method to Call # actionpack/lib/abstract_controller/base.rb module

    AbstractController class Base ... private ... def process_action(method_name, *args) send_action(method_name, *args) end alias send_action send ... ennd
  45. When Calling a Method from Outside of Scope Class.new {

    private def priv() p:hello end }.new.send :priv #=> :hello
  46. Method Scopes

  47. Method Scopes 3 Types of Method Scopes public, protected, and

    private
  48. public Open to Everyone The Default Method Scope

  49. private class C private def hello() p:hello end end C.new.hello

    #=> private method `hello' called for #<C:0x007ff143cb2d70> (NoMethodError)
  50. private class C private def hello() p:hello end def greeting()

    hello end end C.new.greeting
  51. private class C private def hello() p:hello end def greeting()

    hello end end C.new.greeting #=> :hello
  52. If There's a lvar with the Same Name, the private

    Method Would Never Be Called class C private def hello() 'hello' end def greeting(hello = 'hola') p hello end end C.new.greeting #=> "hola"
  53. How to Avoid This?

  54. Put Parens()? class C private def hello() 'hello' end def

    greeting(hello = 'hola') p hello() end end C.new.greeting #=> "hello"
  55. Put Parens Sure it works, but we don't want to

    add parens to all method calls... Ruby is not JavaScript!
  56. Prepend “self.” to private Method Calls class C private def

    hello() 'hello' end def greeting(hello = 'hola') p self.hello end end C.new.greeting
  57. Prepend self. to private Method Calls class C private def

    hello() 'hello' end def greeting(hello = 'hola') p self.hello end end C.new.greeting #=> private method `hello' called for #<C:0x007fe7564799e8> (NoMethodError)
  58. WTF

  59. Our Real App Problem at Ubiregi Inc. We changed a

    public method in a Rails controller to be private, then we saw a NoMethodError because of “self.” But we thought this should not be an error
  60. So, We Wrote a Patch Feature #11297 Proposed and patched

    by @soutaro, the CTO at Ubiregi Inc.
  61. me @soutaro

  62. Matz Accepted this “It changes the concept of private methods

    a little. It's OK to merge the patch if the document is updated at the same time..”
  63. But, I Forgot to Merge this in and Do the

    Documentation
  64. This Change Won't Be Included in 2.3, but Maybe in

    2.4...
  65. Another Solution to Call it with “self.” class C -

    private def hello() 'hello' end + protected def hello() 'hello' end def greeting(hello = 'hola') p self.hello end end C.new.greeting #=> "hello"
  66. protected? What exactly is the protected scope? Who can explain?

  67. protected Different from Java, C++’s protected Proposed and implemented by

    @shugomaeda
  68. protected Method Can't Be Called from Outside class C protected

    def hello() 'hello' end end C.new.greeting #=> undefined method `greeting' for #<C:0x007fc5b42b3a28> (NoMethodError)
  69. protected Method Can't Be Called from Outside class C protected

    def hello() 'hello' end end class D def greeting C.new.hello end end D.new.greeting #=> protected method `hello' called for #<C: 0x007fc040b0de90> (NoMethodError)
  70. protected Method Can Be Called from Other Instance of the

    C Class class C protected def hello() 'hello' end def greeting() p C.new.hello end end C.new.greeting #=> "hello"
  71. Difference Between private and protected class C private def hello()

    'hello' end def greeting() p C.new.hello end end C.new.greeting #=> private method `hello' called for #<C:0x007f879e443ae0> (NoMethodError)
  72. Who Uses this Feature?

  73. Let's Find a Real-world Use Case...

  74. Let's Find a Real-world Use Case... in Rails

  75. git grep "^ *protected" % git grep "^ *protected" |

    wc -l #=> 203
  76. The Rails People Abuse protected Why are there that many

    “protected”s?
  77. The Rails People Abuse And Misuse protected Do people really

    know what protected means?
  78. So, I Wrote a Patch 150 occurrences of protected could

    actually be replaced to private https://github.com/amatsuda/ rails/commit/6832343
  79. Only a Few Proper Use Cases of protected in Rails

    More than 90% of protected in Rails code actually means private
  80. An Example of a Proper Use Case of protected in

    Rails # AP/lib/action_controller/metal/strong_parameters.rb module ActionController class Parameters < ActiveSupport::HashWithIndifferentAccess def dup super.tap do |duplicate| duplicate.permitted = @permitted end end protected def permitted=(new_permitted) @permitted = new_permitted ennnd
  81. Don't Use protected Unless you're sure what you're doing

  82. However,

  83. The Patch Is Still not Merged

  84. The RDoc Problem RDoc mutes private method documentation

  85. The RDoc Problem class C # This is a public

    method def pub() end protected # This is a protected method def prot() end private # This is a private method def priv() end end
  86. The Generated RDoc

  87. RDoc Does not Generate Documentations for private Methods

  88. My Rails Patch I want to change the method scope

    accurately, but that spoils so much documentations!
  89. This Is How We Abuse protected, and Why We Can't

    Stop Abusing protected in Rails
  90. Another RDoc Problem

  91. ✨(2.1) def Returns the Method Name In combination with `private

    :foo` syntax, now we can do Java like
 `private def foo() end` Proposed and implemented by usa (@unak)
  92. Another RDoc Problem class C # This is a public

    method public def pub() end # This is a protected method protected def prot() end # This is a private method private def priv() end end
  93. The Generated RDoc ɹEmpty!

  94. And Even Worse… class C # This is a private

    method private def priv() end # This should be documented def pub1() end # This should also be documented def pub2() end end
  95. The Generated RDoc Empty again!

  96. rdoc/rdoc/issues/355

  97. This Is Why We Still Don't Use “private def” Style

    Definition in Rails We need to patch RDoc first Patches are welcome!
  98. The Method Object

  99. Another Way to Invoke a Method Call `Method#call` via Ruby

  100. Class.instance_methods class Person end p Person.instance_methods(false) #=> []

  101. Inspecting Defined Methods Via Class.instance_methods class Person def hello p

    'hello' end end p Person.instance_methods(false) #=> [:hello]
  102. Getting an Instance of Method from an Instance of the

    Person Class class Person def hello p 'hello' end end p Person.new.method(:hello) #=> #<Method: Person#hello>
  103. Calling the Method class Person def hello p 'hello' end

    end Person.new.method(:hello).call #=> "hello"
  104. Passing in a Parameter class Person def say(something) p something

    end end Person.new.method(:say).call('hello') #=> "hello"
  105. Getting an Instance of Method from the Person Class class

    Person def hello p 'hello' end end p Person.instance_method(:hello) #=> #<UnboundMethod: Person#hello>
  106. UnboundMethod? This must be a Method so let's just call

    it
  107. Calling The UnboundMethod class Person def hello p 'hello' end

    end Person.instance_method(:hello).call #=> undefined method `call' for #<UnboundMethod: Person#hello> (NoMethodError)
  108. So, What's Defined on the UnboundMethod? class Person def hello

    p 'hello' end end p Person.instance_methods(false).sort #=> [:==, :arity, :bind, :clone, :eql?, :hash, :inspect,
 :name, :original_name, :owner, :parameters,
 :source_location, :super_method, :to_s]
  109. Difference Between Method and UnboundMethod p m = Method.instance_methods(false).sort #=>

    [:==, :[], :arity, :call, :clone, :curry, :eql?,
 :hash, :inspect, :name, :original_name, :owner,
 :parameters, :receiver, :source_location, :super_method,
 :to_proc, :to_s, :unbind] p u = UnboundMethod.instance_methods(false).sort #=> [:==, :arity, :bind, :clone, :eql?, :hash, :inspect,
 :name, :original_name, :owner, :parameters,
 :source_location, :super_method, :to_s] p m - u #=> [:[], :call, :curry, :receiver, :to_proc, :unbind] p u - m #=> [:bind]
  110. Binding an UnboundMethod to an Instance of Person class Person

    def hello p 'hello' end end p um = Person.instance_method(:hello) #=> #<UnboundMethod: Person#hello> person = Person.new p meth = um.bind(person) #=> #<Method: Person#hello> meth.call #=> "hello"
  111. Can a Cat Say Hello? class Person def hello() p

    'hello' end end class Cat def hello() p 'Meow' end end um = Person.instance_method(:hello) cat = Cat.new meth = um.bind(cat) meth.call
  112. No, A Cat Is not a Person class Person def

    hello() p 'hello' end end class Cat def hello() p 'Meow' end end um = Person.instance_method(:hello) cat = Cat.new meth = um.bind(cat) meth.call #=> bind argument must be an instance of Person (TypeError)
  113. ✨ (2.0) Method Transplanting Feature #4254 Proposed by @zimbatm Implemented

    by @nobu
  114. Transplanting a Method From a Module module Greeter def hello

    p 'hello' end end class Cat define_method :hello, Greeter.instance_method(:hello) end Cat.new.hello #=> "hello"
  115. Method Transplanting Seems like there’s nothing different from including the

    whole Module in this case You can cherry-pick a Method without including the whole Module
  116. ✨ (2.2, 2.3) Transplanting a Method From a Class module

    Greeter def hello() p 'hello' end end class Person include Greeter end class Cat define_method :hello, Person.instance_method(:hello) end Cat.new.hello #=> "hello"
  117. Method Transplanting Methods can now be transplanted from a Class

    to another Class If the Method originally comes from a Module So many Module-based Rails Methods became portable now!
  118. Parameter

  119. Passing in a Parameter to Method#call class Person def say(something)

    p something end end Person.new.method(:say).call('hello') #=> "hello"
  120. Parameter?

  121. Methods Take Parameters, Then Parameters Can Be Accessed as Local

    Variables (lvar) in the Method def say(something) ... end def say something = 'hello' ... end # same kind of variable
  122. How Can We Inspect a Method’s Parameters? Use Method#parameters

  123. ✨(1.9) Method#parameters Implemented by ko1

  124. ko1 (@_ko1)

  125. ko1 The author of Ruby 1.9 VM (YARV) Full-time Ruby

    Committer Employed by Heroku
  126. Inspecting a Method's Parameter class Person def say(something) p something

    end end p Person.new.method(:say).parameters #=> “hello” #=> [[:req, :something]]
  127. An Optional Parameter class Person def say(something, options = {})

    p something end end p Person.new.method(:say).parameters #=> [[:req, :something], [:opt, :options]]
  128. rest, block class Person def say(something, options = {}, *args,

    &blk) p something end end p Person.new.method(:say).parameters #=> [[:req, :something], [:opt, :options], [:rest, :args], [:block, :blk]]
  129. Real-world Example of Method#parameters asakusarb/action_args

  130. action_args A Rails plugin Placed under Asakusa.rb organization respecting Ko1's

    work on Method#parameters The plugin code is 99% written by me Something that makes your Rails app's controller code like Merb's controller Initially named ”Merbish” I ALWAYS use this when working on Rails apps
  131. What action_args Does # Before class UsersController < ApplicationController def

    show @user = User.find params[:id] end end # After class UsersController < ApplicationController def show(id) @user = User.find id end end
  132. It Used not to Be Able to Handle filters' Parameters

    It's supported in recent versions!
  133. action_args Supports Keyword Arguments As Well

  134. ✨(2.0) Keyword Arguments Proposed and implemented by mame

  135. mame (@mametter) Ruby 2.0 release manager Known by his crazy

    quine works, such as 
 mame/quine-relay
  136. The quine-relay

  137. Method#parameters for kwargs class Person def say(word:, volume: :loud, **args)

    p word end end p Person.new.method(:say).parameters #=> [[:keyreq, :word], [:key, :volume], [:keyrest, :args]]
  138. This Apparently Makes the API Clearer We should rewrite Rails’

    methods to conform this style!
  139. kwargs and Rails My First Trial January 2013

  140. My First Trial Replaced some methods in ActiveSupport, ActionPack, ActiveModel

  141. Looks Good, Isn’t it? - def truncate(truncate_at, options = {})


    + def truncate(truncate_at, omission: '...', separator: nil) - def number_to_currency(number, options = {}) + def number_to_currency(number, locale: nil, format: nil, unit: nil, **options) - def cattr_accessor(*syms, &blk) + def cattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, &blk) - def truncate(text, options = {}, &block)
 + def truncate(text, length: 30, escape: true, **options, &block)
  142. However,

  143. I Found a Specification Problem Reserved words like if, unless,

    end can be a keyword But we can’t touch the variable
  144. Reserved Words in Ruby (lvar) if = 1 #=> syntax

    error, unexpected '='
  145. Reserved Words in Ruby (Method Parameter) def a(if) end #=>

    syntax error, unexpected keyword_if, expecting ')'
  146. Reserved Words Can Be kwargs Keys! def say(something, if:) end

    say 'Hello', if: weather.good?
  147. I Said, Method Parameters Are Essentially Local Variables

  148. Which Means, You Can Create a lvar with a Reserved

    Name!
  149. But Wait, How Can We Access the Variable? def say(something,

    if:) p something if if end say 'Hello', if: true #=> syntax error, unexpected keyword_end #=> syntax error, unexpected end-of-input, expecting keyword_end
  150. This Is What Happened in Ruby 2.0

  151. So I Reported this Problem Maybe via Twitter or at

    the meeting or Asakusa.rb or something (couldn’t find the issue) Then ko1 and nobu made a fix
  152. Nobu the Patch Monster

  153. nobu “Pachimon” Another Heroku employee

  154. How Can We Access a lvar with a Reserved Name?

  155. ✨ (2.1) binding.local_variables and binding.local_variable_get/set/defined? a = 'foo' p binding.local_variables

    #=> [:a] p binding.local_variable_get(:a) #=> "foo"
  156. kwargs + Reserved Word + binding.local_variable_get def say(something, if:) p

    something if binding.local_variable_get(:if) end say 'Hello', if: true #=> "Hello"
  157. The Performance Problem of kwargs Because it used to create

    a Hash instance internally
  158. ✨(2.2) Performance Improvement of kwargs By ko1 Refactored not to

    create a Hash per method call
  159. Rails 5 Coming soon (?) Supports only Ruby >= 2.2

    Major version bump is a good chance to introduce an API incompatibility Now we have fast kwargs and binding.local_variable_get I think we're ready to revise the *args API and move to kwargs
  160. If I Had Enough Time to Work on This and

    Finish My Patch...
  161. AMC and Module#prepend

  162. Rails 5 alias_method_chain is deprecated

  163. alias_method_chain (a.k.a. AMC) A monkey-patching idiom Very simple, but super

    useful Genius work
  164. The Very First Implementation of AMC

  165. Usage of AMC class Record def save() p 'Saved.' end

    def save_with_validation p 'Validated.' save_without_validation end alias_method_chain :save, :validation end Record.new.save #=> "Validated." #=> "Saved."
  166. You Still Can Call the Un- monkeypatched Version class Record

    def save() p 'Saved.' end def save_with_validation p 'Validated.' save_without_validation end alias_method_chain :save, :validation end Record.new.save_without_validation #=> "Saved."
  167. *_without_* Methods Very handy Very intuitive method name Isn't is?

  168. The Dark Side of AMC save_without_validation doesn’t always act as

    what it literally means
  169. What If We Had Another AMC Chain? class Record def

    save() p 'Saving...' end def save_with_validation p 'Validating...' save_without_validation end alias_method_chain :save, :validation def save_with_callback save_without_callback p 'Calling back...' end alias_method_chain :save, :callback end Record.new.save #=> "Calling back..." #=> "Validating..." #=> "Saving..."
  170. Now Let's save Without validation class Record def save() p

    'Saving...' end def save_with_validation p 'Validating...' save_without_validation end alias_method_chain :save, :validation def save_with_callback save_without_callback p 'Calling back...' end alias_method_chain :save, :callback end Record.new.save_without_validation #=> "Saving..."
  171. *_without_* Methods Are Harmful Unpredictable behaviour The method name (probably)

    lies! *_without_* shouldn't be callable *_without_* shouldn’t even be defined
  172. We Needed a Nicer Language Level Monkey-patching Support

  173. ✨ (2.0) Module#prepend Proposed by @wycats Implemented by nobu

  174. How It Works Basically it's like a reverse-ordered Module#include

  175. Module#include class Record def save super p 'Saved.' ennd module

    Validation def save p 'Validated.' ennd Record.send :include, Validation Record.new.save #=> "Validated." #=> "Saved."
  176. Common Idiom of Module#prepend in 2.0 class Record def save()

    p 'Saved.' end end module Validation def save p 'Validated.' super ennd Record.send :prepend, Validation Record.new.save #=> "Validated." #=> "Saved."
  177. How it Makes Your Code Clean amatsuda/ html5_validators

  178. No Other Method Definition Than render # html5_validators/action_view/form_helpers.rb module Html5Validators

    module ActionViewExtension module PresenceValidator def render ... super end end module LengthValidator def render ... super end end module NumericalityValidator def render ... super ennnnd module ActionView::Helpers::Tags::TextField prepend Html5Validators::ActionViewExtension::NumericalityValidator prepend Html5Validators::ActionViewExtension::LengthValidator prepend Html5Validators::ActionViewExtension::PresenceValidator end
  179. Module#prepend Always Comes with send Because it initially was a

    private method in 2.0 In conformity to Module#include
  180. Isn't It Natural to Make it public? The main purpose

    of this method is monkey-patching Usually done from outside, rather than from inside Also Module#include should be public (Object#extend is already public)
  181. So, I Proposed and Patched

  182. ✨(2.1) Module#include and Module#prepend Became public Feature #8846 Proposed and

    implemented by a_matsuda
  183. @a_matsuda

  184. super and super_method

  185. Numbers of `super` calls Rails 2.3: about 221 Rails master:

    about 499
  186. `super` Is Heavily Used @carlhuda style “Micro Kernel Architecture”

  187. Code Complexity So many method definitions with the same name

    e.g. `def save`, `def render` Hard to read through the code Hard to debug
  188. ActiveRecord.has_many `super` def save(*args) create_or_update(*args) rescue ActiveRecord::RecordInvalid false end def

    save(*) if status = super changes_applied end status end def save(*) #:nodoc: rollback_active_record_state! do with_transaction_returning_status { super } end end def save(options={}) perform_validations(options) ? super : false end
  189. Which save Method Will Actually Get Called Via Each `super`?

    Who can tell?
  190. ✨(2.2) Method#super_method Feature #9781 Returns a Method object that will

    be called via `super` Proposed by @schneems Implemented by nobu
  191. For debugging purpose “I believe adding Method#super_method, or exposing this

    same information somewhere else, could greatly help developers to debug large systems easily.” - @schneems
  192. But I Found that,

  193. super_method Can Be Used not Only for Debugging

  194. Let’s Get Back to AMC vs. Module#prepend Module#prepend does not

    define *_without_* method So, There's No Way Calling save_without_validation for Prepended Method?
  195. No. You Can Call the super_method! class Record def save()

    p 'Saved.' end end module Validation def save p 'Validated.' and super end end Record.prepend Validation record = Record.new record.method(:save).super_method.call #=> "Saved."
  196. Method#super_method Provides a Way to Call save_without_validation This technique can

    be used for include extend prepend refine
  197. What if We Have 2 Modules Prepended?

  198. You Can Call super_method’s super_method class Record def save() p

    'Saved.' end end module Validation def save() p 'Validated.' and super ennd module Callback def save() super and p 'Calling back...' ennd Record.prepend Callback, Validation method = Record.new.method(:save) method.super_method.super_method.call #=> "Saved."
  199. What if We Have n Modules Prepended?

  200. Use Method#owner

  201. Method#owner class Record def save() p 'Saved.' end end module

    Validation def save() p 'Validated.' and super ennd module Callback def save super and p 'Calling back...' ennd module Transaction def save p 'Transaction start.' and super and p 'Transaction end.' ennd Record.prepend Callback, Transaction, Validation method = Record.new.method(:save) p method.owner p method.super_method.owner #=> Callback #=> Transaction
  202. What if We Have n Modules Prepended? ... Record.prepend Callback,

    Transaction, Validation method = Record.new.method(:save) until method.owner == Record method = method.super_method end method.call #=> "Saved."
  203. Generalizing a Little Bit More class Method def call_without(mod, *args,

    &block) if mod == owner super_method.call(*args, &block) else super_method.call_without(mod, *args, &block) end end end ... Record.prepend Callback, Transaction, Validation method = Record.new.method(:save) method.call_without(Validation) #=> "Saved."
  204. Method#super_method I’m sure nobody uses this feature Because it’s still

    so buggy
  205. Module#prepend + UnboundMethod#super_method class C def a() end end module

    M def a() end end C.prepend M p C.instance_method(:a).super_method #=> nil # This should be #<UnboundMethod: C#a>
  206. Module#prepend + Method#super_method’s super_method class C def a() end end

    module M def a() end end C.prepend M p C.new.method(:a).super_method #=> #<Method: Object(C)#a> p C.new.method(:a).super_method.super_method #=> #<Method: Object(M)#a> p C.new.method(:a).super_method.super_method.super_method #=> #<Method: Object(C)#a> (and this loops...) # This should be nil
  207. I’m Hoping These Bugs to Be Fixed Hopefully before 2.3.0

    stable
  208. Refinements

  209. Module#prepend Is a Great Monkey-patching Tool Less polluting Only When

    Overriding an Existing Method But that's not Always the Case
  210. Consider this Case What if we want to include a

    Module that has internal methods, and we don't want to expose them to the users?
  211. Some Methods from
 the End Users class Framework::Base end module

    MyMonkeypatch # We want to reveal this to the end users public def foo ... end # We want to hide this from the end users private def bar ... end end Framework::Base.include MyMonkeypatch
  212. Refinements For this purpose, we can use Refinements!

  213. ✨ (2.0) Refinements Proposed and implemented by shugo (@shugomaeda) The

    initial implementation was more powerful and useful See my talk “Ruby 2.0 on Rails” at RubyConf 2012 https://speakerdeck.com/a_matsuda/ruby-2-dot-0-on-rails Then diminished to the current specification
  214. Current Refinements’ Spec File scoped monkey-patch “Lexical scope”

  215. Basic Usage of Refinements # amplifier.rb module Amplifier refine String

    do def shout() p self + '!' end end end # another_file.rb require_relative 'amplifier' using Amplifier 'hello'.shout #=> "hello!"
  216. Real-world Use Case of Refinements asakusarb/action_args

  217. refining Rails’ Core Class # lib/action_args/params_handler.rb module ActionArgs module ParamsHandler

    refine AbstractController::Base do def extract_method_arguments_from_params(method_name) ... end def strengthen_params!(method_name) ... ennnnd
  218. Then using it from Another File # lib/action_args/abstract_controller.rb require_relative 'params_handler'

    using ActionArgs::ParamsHandler module ActionArgs module AbstractControllerMethods def send_action(method_name, *args) ... strengthen_params! method_name values = extract_method_arguments_from_params method_name super method_name, *values ennd ... end
  219. I Would Call this “super private” The methods defined inside

    the library are visible only inside the library Never exposed to the end-users (unless they explicitly call `using`) Thanks @_ksss_ for giving me an inspiration for this implementation!
  220. A Pitfall of Refinements Refined methods can’t be called via

    Kernel#send Because Kernel#send is not lexically scoped? But we often want to dynamically change the method to call, for instance in Rails
  221. I Think this Restriction Should Be Loosen So I raised

    a feature request Feature #11476 If this is allowed, we could implement something useful, maybe?
  222. Ruby’s Method is still evolving! ⤴⤴ Let’s play with Method!

    You can make it better! Let’s make it more fun! Summary
  223. end