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

Ruby 2 in Ruby on Rails

Ruby 2 in Ruby on Rails

Slides for my keynote "Ruby 2 in Ruby on Rails" at RedDot RubyConf 2017 in Singapore https://www.reddotrubyconf.com/#amatsuda #rdrc2017


Akira Matsuda

June 23, 2017


  1. Ruby 2 in
 Ruby on Rails Akira Matsuda

  2. pp self name: Akira Matsuda GitHub: amatsuda Twitter: @a_matsuda From:

    Japan "
  3. A Ruby Committer Who lives in a "Ruby Village" in

    Tokyo "Many of the core contributors like Koichi Sasada, Shyouhei Urabe, Yui Naruse, Zachary Scott and Akira Matsuda live within 10-15 minutes of each other" https:/ /appfolio-engineering.squarespace.com/appfolio- engineering/2017/5/24/how-is-ruby-different-in-japan
  4. A Rails Committer

  5. A Gem Author kaminari action_args active_decorator stateful_enum gem-src rfd etcetc.

  6. Chief Organizer of RubyKaigi

  7. RubyKaigi 2017 September 18..20 In Hiroshima Tickets are on sale!!

    http:/ /rubykaigi.org/2017
  8. RubyKaigi 2017

  9. pp self name: Akira Matsuda GitHub: amatsuda Twitter: @a_matsuda From:

    Japan " Ruby: committer Rails: committer Gems: many RubyKaigi: the organizer
  10. Today I'm Going to
 Show You The new features that

    the Ruby team recently introduced to the language How the Rails framework uses these new features Some of my works as a "committer of both"
  11. begin

  12. Versions

  13. Ruby & Rails Timeline Rails Ruby 2.0.0 - Feb. 24,

    2013 4.0.0 - Jun. 25, 2013 2.1.0 - Christmas, 2013 4.1.0 - Apr. 08, 2014 4.2.0 - Dec. 20, 2014 2.2.0 - Christmas, 2014 2.3.0 - Christmas, 2015 5.0.0 - Jun. 30, 2016 2.4.0 - Christmas, 2016 5.1.0 - Apr. 27, 2017
  14. Ruby & Rails Timeline Rails Ruby 2.0.0 - Feb. 24,

    2013 4.0.0 - Jun. 25, 2013 2.1.0 - Christmas, 2013 4.1.0 - Apr. 08, 2014 4.2.0 - Dec. 20, 2014 2.2.0 - Christmas, 2014 2.3.0 - Christmas, 2015 5.0.0 - Jun. 30, 2016 2.4.0 - Christmas, 2016 5.1.0 - Apr. 27, 2017
  15. Ruby & Rails Timeline Ruby 2 and Rails 4 came

    together Rails 5 supports only Ruby 2.2+
  16. Supported versions Rails 4.x: 1.9.3+ Rails 5.x: 2.2.2+

  17. This Means... Rails 5.x (master) codebase can use Ruby <=

    2.2 features Rails 5.x (master) codebase still cannot use Ruby 2.3, 2.4, 2.5 features
  18. What Are New Features in Ruby 2?

  19. New Things in Ruby 2 Core features Module#prepend, public include,

    Refinements, keyword arguments, frozen Strings, Symbol GC Syntax changes private def, %i, &., <<~, String#-@
  20. Module#prepend

  21. This is Probably My Favorite Feature in Ruby 2

  22. Pre-prepend Era

  23. `alias_method_chain` in Active Support A great trick for extending an

    existing method
  24. Initial Implementation of `alias_method_chain` https:/ /github.com/ rails/rails/commit/ 794d93f7a5e class Module

    def alias_method_chain(target, feature) alias_method "#{target}_without_#{feature}", target alias_method target, "#{target}_with_#{feature}" end end
  25. Initial Implementation of `alias_method_chain` Back in 2006 https:/ /github.com/rails/rails/commit/ 794d93f7a5e

  26. Beauty of `alias_method_chain` Amazingly simple implementation Straightforward convention

  27. Usage of AMC gem 'activesupport', '4.2.8' require 'active_support/core_ext/module/aliasing' class C

    def foo() p:foo; end end class C def foo_with_bar() foo_without_bar; p:bar; end alias_method_chain :foo, :bar end C.new.foo #=> :foo, :bar
  28. Downside of AMC This feature is basically just a workaround

    Creates some publicly callable intermediate methods `foo_with_bar` and `foo_without_bar` in the example above Behavior of these methods are unpredictable
  29. public Methods gem 'activesupport', '4.2.8' require 'active_support/core_ext/module/aliasing' class C def

    foo() p:foo; end def foo_with_bar() foo_without_bar; p:bar; end alias_method_chain :foo, :bar end p C.instance_methods(false) #=> [:foo, :foo_with_bar, :foo_without_bar]
  30. What's Wrong with These public Methods? gem 'activesupport', '4.2.8' require

    'active_support/core_ext/module/aliasing' class C def foo() p:foo; end def foo_with_bar() foo_without_bar; p:bar; end alias_method_chain :foo, :bar # defining bar first, then baz def foo_with_baz() foo_without_baz; p:baz; end alias_method_chain :foo, :baz end C.new.foo_without_bar #=> :foo
  31. What's Wrong with These public Methods? (2) gem 'activesupport', '4.2.8'

    require 'active_support/core_ext/module/aliasing' class C def foo() p:foo; end def foo_with_baz() foo_without_baz; p:baz; end alias_method_chain :foo, :baz # swapping the definition order of bar and baz def foo_with_bar() foo_without_bar; p:bar; end alias_method_chain :foo, :bar end C.new.foo_without_bar #=> :foo, :baz
  32. Can't Tell What's Gonna Happen with `foo_without_bar` But it's publicly

  33. Think About This Case You bundled two plugins that extend

    `foo()` method with AMC The "bar" plugin defines a public method `foo_without_bar` But the behavior is unpredictable The behavior depends on the order of plugin loading order So, if you change the sort order in Gemfile, the behavior would change
  34. We Needed a More Robust Monkey-patching Mechanism And so @wycats

    from the Rails team proposed `Module#prepend` as a language core feature
  35. Module#prepend as a Ruby Core Feature (2.0) class C def

    foo() p:foo; end end module M def foo() super; p:bar; end end C.send :prepend, M C.new.foo #=> :foo, :bar
  36. Very Clean Way of Monkey-patching No ugly `foo_with_bar`, `foo_without_bar` methods

    But you still can do that via `super_method` if you really want to The module name appears in `ancestors` Easier to debug
  37. There's A Pitfall Though You should never mix AMC and

  38. What If We Mix AMC and `prepend`? gem 'activesupport', '4.2.8'

    require 'active_support/core_ext/module/aliasing' class C def foo() p:foo; end end # AMC first class C def foo_with_bar() foo_without_bar; p:bar; end alias_method_chain :foo, :bar end # Then prepend module M def foo() super; p:baz; end end C.send :prepend, M C.new.foo #=> :foo, :bar, :baz
  39. Looks Like It works!

  40. …Is It Working? gem 'activesupport', '4.2.8' require 'active_support/core_ext/module/aliasing' class C

    def foo() p:foo; end end # prepend first module M def foo() super; p:baz; end end C.send :prepend, M # Then AMC class C def foo_with_bar() foo_without_bar; p:bar; end alias_method_chain :foo, :bar end C.new.foo #=> stack level too deep
  41. AMC and `prepend` Cannot Co-exist It's dangerous to use both

    AMC and `prepend` against a single method The result is not only unpredictable Sometimes critical ☠
  42. AMC is Dead Rails terminated the use of AMC in

    Rails 5 Plugin authors must abandon AMC and switch to `prepend` App developers should better refrain from bundling any plugin that still uses AMC
  43. As a Plugin Author We're forced to support `prepend` It's

    hard to maintain a codebase that supports both AMD and `prepend`
  44. Ruby 1's End Many gems stopped supporting Ruby 1.x due

    to this This brought the extinction of Ruby 1.x to the community This is another reason that I like `Module#prepend`
  45. Module#include and Module#prepend as public Methods (2.1)

  46. The `prepend` Code I Showed You Before Has a Small

    Room for Improvement
  47. The `send :include` Idiom C.send :include, M C.send :prepend, M

  48. `send :include` This had been the basic idiom for extending

    an existing Module for long time
  49. Rails Plugin Authors Use This Idiom Too Often I wanted

    to do this simpler So I proposed to make them public
  50. My Proposal https:/ /bugs.ruby-lang.org/ issues/8846 And Matz accepted the proposal

    (although he didn't actually like it)
  51. public `include` Is Available in All Supported Versions of Ruby

    And it's everywhere in Rails' codebase and plugins already You may forget the `send :include` idiom and just use the public `include`
  52. Refinements (2.0)

  53. Who Have Ever Actually Used This Feature in Production

  54. Real World Usage in Rails Plugins action_args database_rewinder etc.

  55. database_rewinder's case (refine) # lib/database_rewinder/multiple_statements_executor.rb module DatabaseRewinder module MultipleStatementsExecutor refine

    ActiveRecord::ConnectionAdapters::AbstractAdapte do def execute_multiple(sql) ... ennnnd
  56. database_rewinder's case (using) # lib/database_rewinder/cleaner.rb using DatabaseRewinder::MultipleStatementsExecutor ... private def

    delete_all(ar_conn, tables, multiple: true) ... if multiple ar_conn.execute_multiple sql ...
  57. A Gem Internal Public Method The `execute_multiple` method in the

    example is - A public method - Visible only inside the gem - Invisible from the application code
  58. Super private Monkey- patching Very useful feature for plugin authors

    / monkey-patchers Since it's "file scoped", we can define any method that is only available inside the plugin
  59. Refinements in Rails I introduced refinements in Active Support 5.1

    codebase To extend `Array#sum` https:/ /github.com/rails/rails/pull/27363
  60. The `Array#sum` Patch - class Array - alias :orig_sum :sum

    - end + # Using Refinements here in order not to expose our internal method + using Module.new { + refine Array do + alias :orig_sum :sum + end + }
  61. In This Case The previous code was defining a public

    `Array#orig_sum` method Although it was an internal method for calculating `Array#sum` The method used to be visible to all Active Support users Refinements made it perfectly "file scoped"
  62. File Scoped!
 File Scoped? While implementing `Array#sum`, I realized that

    it's not actually file scoped! And so I reported what I found https:/ /bugs.ruby-lang.org/issues/13109
  63. I Told You That Refinements is "File Scoped"

  64. But I'm Sorry That Was a Lie

  65. The Scope Problem This happens when you're putting everything in

    one file
  66. This Works # Put a refinement first using Module.new {

    refine Object do def foo() p 'hello'; end end } # Then calling the method defined in the refinement class Object def bar() foo; end end Object.new.bar #=> "hello"
  67. But This Doesn't Work! # Put a method call first

    class Object def bar() foo; end end # Then define the actual method definition in a refinement using Module.new { refine Object do def foo() p 'hello'; end end } Object.new.bar #=> doesnot_work.rb:2:in `bar': undefined local variable or method `foo' for #<Object:0x007f8f2a0251c8> (NameError)
  68. So The Scope Is Not Actually "File Scope" It's "file

    scope + physical position of using" scope I thought it's a bug, but Matz said "it's an intended behavior"
  69. This Behavior Is Sometimes Annoying I said refinements is useful

    for creating a library-internal "super private" method But when you define a private method, you usually put it below the public methods, right? However, refinements definition has to be put at the top of the file
  70. Such a Weird Feature! Probably the weirdest feature in Ruby

    that I know
  71. And… This Complex Behavior Is Still Undocumented for Now Maybe

    this behavior is very hard to explain Or maybe because nobody was aware of this beavior Anyway this is the "intended behavior"
  72. This Funny Feature Was Proposed And Implemented by Shugo Matz's

    boss at NaCl
  73. The protected Scope

  74. Another Funny Feature Created by Shugo

  75. Example (Taken from Shugo's RubyKaigi Slides) class BinaryCodedDecimal def +(other)

    figure1 = self.figure figure2 = other.figure ... end protected attr_accessor :figure end
  76. What Matz Thinks About protected "(If I would re-design Ruby)

    now? I'll reject protected" https:/ /twitter.com/yukihiro_matz/status/ 180090910451834881
  77. Usage of protected in Rails Almost everyone misuses `protected` In

    most cases, you can just rewrite your `protected` to `private`, and it should still work
  78. Removal of protected in Rails I tried doing this for

    the Rails' codebase
  79. The Huge Patch 3.years.ago https:/ /github.com/amatsuda/rails/commit/ 6832343c

  80. I Made This But I Didn't Push This to The

    Upstream Although all tests were passing Because it caused some unintentional update on the API document
  81. Finally I Pushed Them Last Year Made a separate commit

    per each component Making sure that tests didn't break per each component Adding `:doc:` to all previously `protected` methods Last year, for Rails 5.1
  82. protected Dead in Rails master Don't use it

  83. BTW

  84. Shugo, The Creator of Refinements And protected His most recent

  85. A Text Editor Written in Ruby Based on curses Ruby

    binding That he created and he still maintains He's going to do a presentation about this at an international Ruby conference
  86. RubyKaigi 2017

  87. private def (2.1)

  88. Defining a private Method (1) class Klass ... # Changes

    the visibility of all methods defined below private def private_method; end end
  89. Defining a private Method (2) class Klass ... def private_method;

    end # Changes the visibility of this single method private :private_method end
  90. The private def Syntax class Klass ... # Now we

    can do this in one line.
 # Don't have to repeat the method name! private def private_method; end end
  91. usa's Patch `def` statement used to return nil, but he

    changed it to return the defined method name in Symbol https:/ /github.com/ruby/ruby/commit/0f0b60ea86 `private` (`protected`, `public`) takes a Symbol and changes the method's visibility The patch was written in August 2013
  92. This Feature Is a Very Important Feature Especially for The

    Rails Team
  93. The Rails Style class Klass ... private def private_method; end

  94. VS. Traditional Style class Klass ... private def private_method; end

  95. Everybody Is Happy with This, Right? class Klass ... private

    def private_method; end end
  96. That's Why I Said This Feature Is Very Important for

    Rails Team
  97. But,

  98. `private def` in Rails Very rarely used

  99. Still Only 17 Occurrencies in Rails! (As of Today) %

    git grep -c "private def" actioncable/test/channel/stream_test.rb:1 actionmailer/test/abstract_unit.rb:2 actionpack/test/abstract_unit.rb:2 actionpack/test/controller/new_base/render_context_test.rb: actionview/test/abstract_unit.rb:2 actionview/test/template/form_helper/form_with_test.rb:1 activemodel/test/cases/helper.rb:2 activerecord/lib/active_record/connection_adapters/ abstract_mysql_adapter.rb:1 activerecord/test/cases/query_cache_test.rb:1 activesupport/test/abstract_unit.rb:2 railties/test/abstract_unit.rb:2
  100. Why?

  101. Because There Was a Documentation Problem class Klass def public_method_1;

    end private def private_method; end def public_method_2; end end
  102. The RDoc Problem RDoc couldn't properly generate the document The

    `private` (`protected`, `public`) prefix was changing the scope of all following methods It breaks the Rails API document https:/ /github.com/rdoc/rdoc/issues/355
  103. The RDoc Problem class Klass def public_method_1; end private def

    private_method; end # This method is public and has to appear in the doc, but RDoc was treating this as a private method def public_method_2; end end
  104. Why Did RDoc Have
 Such Bug? Because RDoc has its

    own Ruby parser Nobody made a change to that parser along with the change in Ruby 2.1
  105. The Parser (Lexer) in RDoc lib/rdoc/ruby_lex.rb

  106. lib/rdoc/ruby_lex.rb # coding: US-ASCII # frozen_string_literal: false #-- # irb/ruby-lex.rb

    - ruby lexcal analyzer # $Release Version: 0.9.5$ # $Revision: 17979 $ # $Date: 2008-07-09 10:17:05 -0700 (Wed, 09 Jul 2008) $ # by Keiju ISHITSUKA(keiju@ruby-lang.org) # #++ require "e2mmap" require "irb/slex" require "stringio" ## # Ruby lexer adapted from irb. # # The internals are not documented because they are scary. class RDoc::RubyLex ...
  107. lib/rdoc/ruby_lex.rb It's a "ruby lexcal analyzer" Written by keiju "adapted

    from irb" OMG! RDoc requires IRB!
  108. lib/rdoc/ruby_lex.rb # coding: US-ASCII # frozen_string_literal: false #-- # irb/ruby-lex.rb

    - ruby lexcal analyzer # $Release Version: 0.9.5$ # $Revision: 17979 $ # $Date: 2008-07-09 10:17:05 -0700 (Wed, 09 Jul 2008) $ # by Keiju ISHITSUKA(keiju@ruby-lang.org) # #++ require "e2mmap" require "irb/slex" require "stringio" ## # Ruby lexer adapted from irb. # # The internals are not documented because they are scary. class RDoc::RubyLex ... WTF
  109. Parsers in Ruby parse.y (+ yacc, bison) Ripper ruby-lex.rb in

  110. Ruby has Ripper in stdlib Written by Minero Aoki, the

    author of "Ruby Hacking Guide"
  111. But Why Didn't RDoc Use Ripper? Because Ripper was not

    yet created when Dave Thomas created RDoc
  112. Parsers History IRB: 1997 RDoc: 2003 Ripper: 2004

  113. I Tried, But... Shouldn't we rewrite the RDoc Ruby parser

    from the IRB parser to Ripper? I tried to do this before, but gave up Because the RDoc parser is a super chaotic legacy code
  114. Future Work But I brought this to Asakusa.rb, and someone

    is actually working on this The result is supposed to be presented soon at
  115. RubyKaigi 2017

  116. Parsers History (Reprise) IRB: 1997 RDoc: 2003 Ripper: 2004

  117. IRB Created in 1997 It's IRB's 20th anniversary this year!

  118. Let's Celebrate! There'll be an IRB 20th anniversary speech by

    the original author
  119. RubyKaigi 2017

  120. Anyway, Let's go back to the `private def` problem

  121. I Gave Up Replacing The Whole RDoc Parser But I

    could make a tiny patch for RDoc to add `private def` support last year
  122. My Patch https:/ /github.com/rdoc/ rdoc/pull/435

  123. It Took 3 And Half Years Since `private def` syntax

    has been introduced to Ruby But it's finally unleashed in Rails RDoc >= 5.1 can properly parse `private def` So it won't break the document any longer
  124. Feel Free to Use
 `private def` Now!

  125. Keyword Arguments (2.0)

  126. Pseudo Keyward Arguments in Rails (As a Hash) def deliver_later(options

    = {}) def form_for(record, options = {}, &block) def link_to_if(condition, name, options = {}, html_options = {}, &block)
  127. Pseudo Keyward Arguments in Rails (Through Varargs) def accepts_nested_attributes_for(*attr_names) def

    cycle(first_value, *values) def redirect(*args, &block)
  128. Pseudo Keyward Arguments in Rails Uses a Ruby Hash to

    pass in arguments with names Method calls apparently look better than something like
 `form_for(@user, nil, false, :post, false, nil, false)`
  129. Pseudo Keyward Arguments in Rails But OTOH the method definition

    looks miserable The argument list becomes like "options as any Hash", or even "*args" It tells nothing unless we write a thorough documentation comment So hard for this non-statically typed language
  130. Rails Includes Some Tools To handle this psuedo keyword arguments

  131. Extracting the Options Hash from Varargs # actionview/lib/action_view/helpers/text_helper.rb def cycle(first_value,

    *values) options = values.extract_options! ... end
  132. Giving a Default Option Value # actionpack/lib/action_dispatch/routing/redirection.rb def redirect(*args, &block)

    options = args.extract_options! status = options.delete(:status) || 301 ...
  133. Giving a Default Option Value The defaults have to be

    manipulated inside the method And they have to be properly documented outside of the method
  134. Verifying Option Keys # activerecord/lib/active_record/nested_attributes.rb def accepts_nested_attributes_for(*attr_names) options = {

    allow_destroy: false, update_only: false } options.update(attr_names.extract_options!) options.assert_valid_keys(:allow_destroy, :reject_if, :l imit, :update_only)
  135. assert_valid_keys Raises when an unexpected key was given Again, have

    to be declared inside the method, and be documented outside We very often forget to do this Then unhandled keys would just be ignored
  136. Ruby 2 Has The Built-in Keyword Arguments! Since 2.0 Proposed

    and implemented by mame, the insane genius
  137. Rewriting with Ruby 2 Kwargs (before) # activesupport/lib/active_support/message_verifier.rb def initialize(secret,

    options = {}) raise ArgumentError, "Secret should not be nil." unless secret @secret = secret @digest = options[:digest] || "SHA1" @serializer = options[:serializer] || Marshal end
  138. Rewriting with Ruby 2 Kwargs (patch) -def initialize(secret, options =

    {}) +def initialize(secret, digest: 'SHA1', serializer: Marshal) raise ArgumentError, "Secret should not be nil." unless secret @secret = secret - @digest = options[:digest] || 'SHA1' - @serializer = options[:serializer] || Marshal + @digest = digest + @serializer = serializer end
  139. Merits of Ruby 2 Kwargs The logic becomes simple (obviously)

    The behavior becomes more strict (invalid keys, missing keys, etc.) Runs faster (in most cases, I guess) Creates less garbage objects (maybe) The method definition becomes a great documentation (most important IMO)
  140. A Branch that I Created for Experimenting Kwargs in Rails

    https:/ /github.com/amatsuda/rails/tree/ kwargs Back in Jan. 2013 Before Ruby 2.0.0 stable release In order to experiment the new feature in a real codebase
  141. Then I Found A Problem While Experimenting This Some methods

    in Rails take Ruby keyword as an option name
 (e.g. if, unless, end)
  142. This Method Can Be Defined, And Can Be Called def

    x(if: nil) end x if: true
  143. But We Can Never Access The Given Parameter def x(if:

    nil) p if end x if: true #=> syntax error, unexpected keyword_end
  144. Kwargs Introduced A New Way to Define An Unaccessible Lvar

    `if = 1` is not a valid Ruby code, so this was never possible Kwargs opened a new possibility!
  145. I Broght This Problem to ruby-core (Couldn't find a ticket.

    Maybe at the developers' meeting, or maybe I discussed with ko1 or mame in person?) Then we introduced a new feature in 2.1
  146. Binding#local_variable_ get (2.1) def x(if: nil) p binding.local_variable_get :if end

    x if: true #=> true
  147. You Can Also Set def x(nil: 1) binding.local_variable_set :nil, 2

    p binding.local_variables p nil p binding.local_variable_get :nil end x #=> [:nil], nil, 2
  148. My Proposal for Rails

  149. Kwags as a 1st Citizen API in Rails

  150. Kwags as a 1st Citizen API in Rails Should run

    faster Makes the code more readable And generates a better API document Rails master now supports only >= 2.1 (actually 2.2.2) that does have `local_variable_get`
  151. require_relative (1.9)

  152. require_relative and Rails This feature was originally introduced to Ruby

    because Ruby excluded '.' from `$LOAD_PATH` Because '.' in `$LOAD_PATH` may be a potential security risk A "Rails effect" to the Ruby language
  153. require_relative May Run Faster Than require Because it's a very

    simple implementation It directly requires a file from `rb_current_realfilepath` Instead of scanning the file through all `$LOAD_PATH` entries
  154. Occurrences of `require` and `require_relative` in Rails
 (As of Today)

    '^require_relative': 73 '^require ': 4055
  155. So, There's a Huge Room for an Improvement? I thought

    so before So I created a patch
  156. require_relative Patch for Rails Another huge patch from me that

    was never pushed to the upsteram https:/ /github.com/amatsuda/ rails/compare/ master...require_relative
  157. Why Didn't I Push This Because I couldn't see any

    significant performance improvement
  158. Why No Perf Improvement? I haven't investigated deeper, but ko1

    said maybe because Bundler is doing a good work(?) Or maybe I had better use a slow HDD when benchmarking?
  159. Anyway, `require_relative` is basically a good thing Although it's not

    popular in Rails
  160. ISeq#to_binary, ISeq.load_from_binary (2.3)

  161. Public APIs to Dump & Restore Compiled Instruction Sequences

  162. Yomikomu by ko1 (2015) An experimental gem https:/ /github.com/ko1/yomikomu No

    significant perf improvement on real-world apps (ko1@RailsConf 2016)
  163. bootsnap (2017) Shopify's refined version of yomikomu With actual perf

    improvement! Proposed to be bundled in Rails' default Gemfile https:/ /github.com/rails/rails/pull/29313 Still misterious
  164. But, I heard that more about this is going to

    be presented soon
  165. RubyKaigi 2017

  166. Encoding (1.9-)

  167. Encoding in Ruby Every String object has its encoding Ruby

    can handle various encodings `Encoding.list.size` #=> 101 Even matz had been using non-utf8 Encoding until several years ago
  168. Encoding in Rails Rails has to face with various encodings

    Such as server OS encoding, DB encoding, program file encoding, HTTP request encoding, URL encoding, Cookie encoding Rails is a web framework, and the recent web defacto encoding is UTF-8 Rails deals with YAML and JSON files, both of which supports only UTF (usualy UTF-8)
  169. Basic Strategy Keep every String in the Rails world be

    UTF-8 Convert all non-UTF-8 Strings to UTF-8 at the boundary between the outside and Rails
  170. Rails Needs to Be Defensive Against Inputs from Outside For

    example we're validating URLs not to include any broken (possibly maliscious) UTF-8 chars
  171. View Templates Template file encoding Magic comment at the top

    of the template
  172. Rails Seems to Be Supporting Multiple Encodings But nobody actually

    uses any other encoding than UTF-8, even in Japan I'm willing to drop multiencodings support from Action View I told this last year, but still I haven't published a patch I'm sorry, I'm so lazy...
  173. What I Only Did _.html.erb https:/ /github.com/rails/rails/ commit/da9038eaa5d

  174. ActiveSupport::Multibyt e Active Support includes an original multibyte chars handling

    utilities Since Ruby 1.8 era Includes a huge set of Unicode data in Active Support for this feature AS/values/unicode_tables.dat
  175. Improvements in Ruby 1.9: Code Set Independent Encoding system 2.0:

    UTF-8 script encoding by default 2.2: Unicode normalization 2.4: Non-ASCII case conversion 2.4: Unicode 9.0 support in Regexp \X
  176. We're Ready to Remove Active Support Original Multibyte Library We

    can do everything using the language core features We can remove unicode_tables.dat from Active Support Smaller gem size Faster boot time Less memory consumption Multibyte chars handling will become faster Because it's implemented in C in Ruby No longer need to maintain that misterious code in Active Support
  177. Actually, We Have to Drop It As Soon As Possible

    Because @norman the long time maintainer has "completely retired from open source" And so I became the main maintainer of Haml after him
  178. Removing ActiveSupport::Multibyte Rails 5 still supports Ruby < 2.4 It

    can't be completely removed in Rails 5 But we can use Ruby native implementation if RUBY_VERSION >= '2.4' We made a PR for this already https:/ /github.com/rails/rails/pull/28067
  179. Frozen Strings (2.3)

  180. String#freeze PRs Got so many "I froze some String literals"

    PRs in the past few years Because freezing Strings resulted in good micro- benchmark results
  181. String#freeze Everywhere ❄ Pro: Some kind of performance improvement Con:

    Ugly code
  182. No Thank You, This Looks Too Ugly... # inflector/methods.rb def

    underscore(camel_cased_word) return camel_cased_word unless /[A-Z-]|::/.match? (camel_cased_word) word = camel_cased_word.to_s.gsub("::".freeze, "/".freeze) word.gsub!(/(?:(?<=([A-Za-z\d]))|\b) (#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1 && '_'.freeze }#{$2.downcase}" } word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze) word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze) word.tr!("-".freeze, "_".freeze) word.downcase! word end
  183. So, I Proposed to Introduce a New "Magic Comment" in

    2015 To be more accurate, I proposed to reconsider akr's proposal once rejected in 2013 https:/ /bugs.ruby-lang.org/issues/ 8976#note-30 The proposal was accepted, and included in Ruby 2.3
  184. The Magic Comment # frozen_string_literal: true

  185. The "fstring" Literal p 'a'.frozen? p 'a'.freeze.frozen? p (-'a').frozen?

  186. The "fstring" Literal Maybe you've never heard of this before

    Actually a little bit faster than calling `String#freeze` method Very rarely used, but introduced in ERB recently by @k0kubun
  187. I Would Like to Put The Magic Comment to All

    Files in Rails But not now, because Rails 5.x still supports Ruby 2.2 This can be done when Rails drops Ruby 2.2 support in the next major
  188. Symbol GC (2.2)

  189. Rails 5 Was Decided to Support Ruby 2.2+ Because of

    This Feature GC collects dynamically created Symbols Disables Symbol fixation attack
  190. Another Thing We Might Be Able to Do Reduce redundunt

    Stringifications May cause some compatibility issues but worth trying I haven't done anything yet though
  191. Integer Unification (2.4)

  192. Fixnum & Bignum => Integer Use of Fixnum and Bignum

    has been deprecated We saw massive number of warnings in the gems Almost fixed in major libraries
  193. If you're Still Seeing the Warning Update bundled gems Patch

    it and PR if master is still not fixed Unbundle it and find a maintained alternative if your PR is left unmerged
  194. Active Support as an Experimental Lab for Ruby

  195. Object#try! (2.3) `&.` `try!` can be deprecated in favor of

    `&.` when we dropped 2.2 support
  196. String#strip_heredoc (2.3) `<<~` `strip_heredoc` can be deprecated in favor of

    `<<~` when we dropped 2.2 support
  197. Numeric#positive?, negative? (2.3) Another trivial Active Support to Ruby pair

    of methods
  198. Array#append, Array#prepend (2.5) "Active Support can serve as an experimental

    lab for future core features in Ruby ❤" https:/ /twitter.com/dhh/status/ 871034291786002433
  199. Array#sum, Enumerable#sum Ruby version is slightly different from the original

    Active Support version The Ruby version ignores non- numeric value It's OK because Ruby is a general purpose language, while Active Support is "web specific" language extension
  200. But The Situation Is So Complicated!! Rails 5 supports both

    Rubies that have and don't have `sum` `sum` is defined on both Enumerable and Array, where `Enumerable < Array` Native `sum` is faster, so it's preferrable because it's written in C Even if there's native `sum`, it has to fall back to `super` on non-numeric value It takes an argument It takes a block
  201. Somehow Done Before Releasing 5.1.0 Stable Not perfect, but works

    Can be made simpler when we dropped Ruby < 2.3 support
  202. Ruby 2.3, 2.4, and 2.5 Features These Features Cannot Yet

    Be Used In The Rails Codebase Because Rails master still supports 2.2 But you can use them in your apps! Update your production Ruby version to the newest stable, and start using these features now! There's no reason to stay on old version of Ruby
  203. Wrap Up Ruby is evolving day by day Rails is

    also getting better Ruby and Rails are in a very good relationship I'm having fun working on both projects There're always so many things to do because both Ruby and Rails are always changing I hope you to join us if you're interested!
  204. That's It!

  205. Thank You for Having Me And see you at another

    conference! Hopefully,
  206. RubyKaigi 2017

  207. end

  208. pp self name: Akira Matsuda GitHub: amatsuda Twitter: @a_matsuda