No! 2.0!!
1
1.2
1.4
1.6
1.8
2
1995/121996/121997/081998/121999/082000/092003/082007/122010/082011/102013/02
Title
Slide 14
Slide 14 text
Why 2.0?
'1.9'.succ
Features
Birthday
Slide 15
Slide 15 text
'1.9'.succ
Slide 16
Slide 16 text
'1.9'.succ
'1.9'.succ #=> "2.0"
Slide 17
Slide 17 text
Features
Slide 18
Slide 18 text
Matz's slide for RubyConf 2003
http://www.rubyist.net/~matz/slides/rc2003/
mgp00009.html
Slide 19
Slide 19 text
Optional Type?
Optional Type!
Slide 20
Slide 20 text
Happy 20th birthday!
Ruby was "born" in
1993/2/24
Ruby will become 20 yrs
old in 2013/2/24
Ruby is no more a teenager!
Let's call it Ruby 2.0
Slide 21
Slide 21 text
New Features
Slide 22
Slide 22 text
New Features
Refinements
Module#prepend
Enumerable#lazy
Keyword Arguments
Slide 23
Slide 23 text
Refinements
Slide 24
Slide 24 text
@shugomaeda
Slide 25
Slide 25 text
No content
Slide 26
Slide 26 text
@shugomaeda
Lives in "the Ruby City"
Matz-e
Matz's boss at NaCl
http://www.netlab.jp/
Ruby Association
http://www.ruby.or.jp/
Slide 27
Slide 27 text
What are Refinements?
Somewhat limited
monkey-patching
Slide 28
Slide 28 text
background
"Monkey-patching" is
sometimes too much
powerful
Slide 29
Slide 29 text
Monkey Battles
God vs ActiveSupport
Mail vs ActiveSupport
Slide 30
Slide 30 text
God vs ActiveSupport
require 'active_support/all'
p 3.days.since(Date.today)
#=> Mon, 10 Dec 2012
gem 'god', '0.13.0'
require 'god'
p 3.days.since(Date.today)
#=> Tue, 08 Aug 2722
Slide 31
Slide 31 text
The new methods
Module#refine
Kernel#using
Slide 32
Slide 32 text
Module#refine
Slide 33
Slide 33 text
Module#refine
module Foo
refine String do
def say
puts "#{self}!!"
end
end
'hello'.say
end
#=> hello!!
Slide 34
Slide 34 text
Module#refine
module Foo
refine String do
def say; puts "#{self}!!"; end
end
'hello'.say
end
#=> hello!!
'world'.say
#=> NoMethodError: undefined method `say'
for "world":String
Slide 35
Slide 35 text
Kernel#using
Slide 36
Slide 36 text
using Kernel#using
module Foo
refine String do
def say; puts "#{self}!!"; end
end
end
class Bar
using Foo
def hello
'hello'.say
end
end
Bar.new.hello
#=> hello!!
Slide 37
Slide 37 text
using using in Module
module Foo
refine String do
def say; puts "#{self}!!"; end
end
end
module Bar
using Foo
end
Bar.module_eval {
'hello'.say
}
#=> hello!!
Slide 38
Slide 38 text
using anonymous Module
module Foo
refine String do
def say; puts "#{self}!!"; end
end
end
Module.new { using Foo }.module_eval {
'hello'.say
}
#=> hello!!
'world'.say
#=> NoMethodError: undefined method `say' for
"world":String
Slide 39
Slide 39 text
using lambda / Proc?
module Foo
refine String do
def say; puts "#{self}!!"; end
end; end
-> {
using Foo
'hello'.say
}.call
#=> hello!!
'world'.say
#=> world!!
Slide 40
Slide 40 text
using lambda + Module
module Foo
refine String do
def say; puts "#{self}!!"; end
end; end
Module.new { using Foo }.module_eval {
-> { 'hello'.say }
}.call
#=> hello!!
'world'.say
#=> undefined method `say' for
"world":String
activerecord-refinements
https://github.com/
amatsuda/activerecord-
refinements
% gem i activerecord-
refinements
Slide 46
Slide 46 text
activerecord-refinements/lib/
active_record/refinements.rb (1)
module ActiveRecord::Refinements
module WhereBlockSyntax
refine Symbol do
%i[== != =~ > >= < <=].each
do |op|
define_method(op) {|val|
[self, op, val] }
ennnnd
Slide 47
Slide 47 text
activerecord-refinements/lib/
active_record/refinements.rb (2)
module ActiveRecord::Refinements
module QueryMethods
def where(opts = nil, *rest, &block)
if block
col, op, val = Module.new { using
ActiveRecord::Refinements::WhereBlockSyntax }.module_eval &block
arel_node = case op
when :!=
table[col].not_eq val
when :=~
table[col].matches val
when :>
table[col].gt val
when ... # (snip)
end
clone.tap do |relation|
relation.where_values += build_where(arel_node)
end
else
super
ennnnd
Slide 48
Slide 48 text
activerecord-refinements/lib/
activerecord-refinements.rb
module ActiveRecord::QueryMethods
prepend ActiveRecord::Refinements::QueryMethods
end
Slide 49
Slide 49 text
activerecord-refinements/
spec/where_spec.rb
describe 'Symbol enhancements' do
describe '#!=' do
subject { User.where { :name != 'nobu' }.to_sql }
it { should =~ /WHERE \("users"."name" != 'nobu'\)/ }
end
describe '#>=' do
subject { User.where { :age >= 18 }.to_sql }
it { should =~ /WHERE \("users"."age" >= 18\)/ }
end
describe '#=~' do
subject { User.where { :name =~ 'tender%' }.to_sql }
it { should =~ /WHERE \("users"."name" LIKE 'tender%'\)/ }
end
context 'outside of where block' do
it { expect { :omg > 1 }.to raise_error ArgumentError }
end
end
Slide 50
Slide 50 text
No content
Slide 51
Slide 51 text
"Refinements and nested
methods"
ruby-core:50336
https://bugs.ruby-
lang.org/issues/4085
Charlie's worry
module WeirdPlus
refine String do
def +(other)
"#{self} plus #{other}"
end
end
end
class MyArray
def initialize
@ary = ['foo', 'bar', 'baz']
end
def inject(accum, &block)
@ary.each do |str|
accum = WeirdPlus.module_exec(str, accum, &block)
end
accum
end
end
def add_all(str_ary)
str_ary.inject('') do |str, accum|
accum + str
end
end
add_all(MyArray.new)
#=> " plus foo plus bar plus baz"
Slide 54
Slide 54 text
Matz's new plan
(as of today)
"using" is only allowed at top level.
refined methods are called only after "using".
or within blocks given to "refine".
if you pass the proc to "refine" e.g.
refine(C,&b) refined methods may not be
called from b. It's implementation dependent.
refinements are not available in subclasses,
nor in reopened classes/modules.
refinements are not available from
module_eval/class_eval.
Slide 55
Slide 55 text
Restrictions
only allowed at top level
not available in subclasses
not available in reopened
classes/modules
not available from
module_eval
Slide 56
Slide 56 text
Matz's intension
let `refine` + `using` be
lexical (file scoped)
not for library /
framework authors, but
for script writers (?)
Slide 57
Slide 57 text
Moved to ext/refinement
https://github.com/ruby/ruby/commit/328e0ff
Slide 58
Slide 58 text
OMG
Slide 59
Slide 59 text
Module#prepend
Slide 60
Slide 60 text
background
Slide 61
Slide 61 text
Ruby < 2, Rails < 3
Slide 62
Slide 62 text
alias_method_chain
def alias_method_chain(target, feature)
alias_method "#{target}_without_#{feature}", target
alias_method target, "#{target}_with_#{feature}"
end
https://github.com/rails/rails/commit/794d93f by @jamis
Slide 63
Slide 63 text
AMC
class A
def foo; puts 'foo'; end
end
class A
def foo_with_bar
foo_without_bar
puts 'bar'
end
alias_method_chain :foo, :bar
end
A.new.foo
A.new.foo_without_bar
Slide 64
Slide 64 text
simple pagination
ActiveRecord::FinderMethods.module_eval do
def all_with_page(*args)
if args.any? && (page =
args.first.delete(:page))
limit(10).
offset(10 * (page - 1)).
all_without_page(*args)
else
all_without_page(*args)
end
end
alias_method_chain :all, :page
end
Slide 65
Slide 65 text
Problem
Slide 66
Slide 66 text
adding `baz` and
calling foo "without_bar"
class A
def foo; puts 'foo'; end
def foo_with_bar
foo_without_bar
puts 'bar'
end
alias_method_chain :foo, :bar
def foo_with_baz
foo_without_baz
puts 'baz'
end
alias_method_chain :foo, :baz
end
Slide 67
Slide 67 text
AMC defines tons of messy
public methods
class A
def foo; puts 'foo'; end
def foo_with_bar
foo_without_bar
puts 'bar'
end
alias_method_chain :foo, :bar
def foo_with_baz
foo_without_baz
puts 'baz'
end
alias_method_chain :foo, :baz
end
A.instance_methods.grep(/foo/)
#=> [:foo,
:foo_with_bar, :foo_without_bar, :foo_with_baz, :foo_without_baz]
Slide 68
Slide 68 text
"without_bar" skips "baz"
class A
def foo; puts 'foo'; end
def foo_with_bar
foo_without_bar
puts 'bar'
end
alias_method_chain :foo, :bar
def foo_with_baz
foo_without_baz
puts 'baz'
end
alias_method_chain :foo, :baz
end
A.new.foo_without_bar
#=> foo
Slide 69
Slide 69 text
save_without_*
gem 'activerecord', '<2.3'
require 'active_record'
ActiveRecord::Base.configurations = {'test' => {:adapter =>
'sqlite3', :database => ':memory:'}}
ActiveRecord::Base.establish_connection('test')
class User < ActiveRecord::Base
validates_presence_of :name
end
class CreateAllTables < ActiveRecord::Migration
def self.up
create_table(:users) {|t| t.column :name, :string}
end
end
CreateAllTables.up
# p User.new.save_without_validation
p User.new.save_without_dirty
p User.new.save
Slide 70
Slide 70 text
Rails 3’s solution
use the power of Ruby
Module
super
Slide 71
Slide 71 text
https://github.com/rails/
rails/commit/d916c62
Slide 72
Slide 72 text
excerpt from d916c62
included do
- alias_method_chain :save, :dirty
...
end
- def save_with_dirty(*args)
- if status =
save_without_dirty(*args)
+ def save(*) #:nodoc:
+ if status = super
...
Slide 73
Slide 73 text
@wycats style
module Bar
def foo
puts 'bar'
super
end; end
module Baz
def foo
puts 'baz'
end; end
class A
include Baz
include Bar
def foo
puts 'foo'
super
end
end
A.new.foo
#=> foo
bar
baz
Slide 74
Slide 74 text
the original foo method
class A
def foo
puts 'foo'
super
end
end
Slide 75
Slide 75 text
how can we extend methods
that are not calling super?
class A
def foo
puts 'foo'
end
end
(do something...)
A.new.foo
#=> foo
bar
AMC
class A
def foo
puts 'foo'
end
end
class A
def foo_with_bar
foo_without_bar
puts ‘bar’
end
alias_method_chain :foo, :bar
end
A.new.foo
#=> foo
bar
Slide 84
Slide 84 text
Module#prepend
class A
def foo; puts 'foo'; end
end
module Bar
def foo
super
puts 'bar'
end
end
class A
prepend Bar
end
A.new.foo
#=> foo
bar
Slide 85
Slide 85 text
prepending multiple Modules
class A; def foo; puts 'foo'; end; end
module Bar
def foo
super
puts 'bar'
end
end
module Baz
def foo
super
puts 'baz'
end
end
class A
prepend Bar
prepend Baz
end
A.new.foo
#=> foo
bar
baz
Slide 86
Slide 86 text
simple pagination with
Module#prepend
module PrependPaginator
def all(*args)
if args.any? && (page =
args.first.delete(:page))
self.limit_value = 10
self.offset_value = 10 * (page - 1)
end
super
end
end
ActiveRecord::FinderMethods.send :prepend,
PrependPaginator
Slide 87
Slide 87 text
activerecord-refinements/lib/
activerecord-refinements.rb
module ActiveRecord::Refinements
module QueryMethods
def where(opts = nil, *rest, &block)
if block
... # (snip)
else
super
ennnnd
module ActiveRecord::QueryMethods
prepend ActiveRecord::Refinements::QueryMethods
end
Slide 88
Slide 88 text
Enumerable#lazy
Slide 89
Slide 89 text
@yhara
Slide 90
Slide 90 text
@yhara
Lives in "the Ruby City"
Matz-e
Matz's co-worker at NaCl
http://www.netlab.jp/
Slide 91
Slide 91 text
Ruby committers in NaCl
@yukihiro_matz
(since 15.years.ago)
@shugomaeda
@gotoyuzo
@nari3
@takaokouji
@shyouhei (until October 2012)
Slide 92
Slide 92 text
The proposal
http://bugs.ruby-
lang.org/issues/4890
Hash#merge
def foo(args = {})
values = {a: 1, b: 2}.merge args
puts "a is #{values[:a]}, b is #{values[:b]}"
end
foo b: 4
#=> a is 1, b is 4
Slide 109
Slide 109 text
Hash#reverse_merge
(ActiveSupport)
def foo(args = {})
values = args.reverse_merge a: 1, b: 2
puts "a is #{values[:a]}, b is #{values[:b]}"
end
foo b: 4
#=> a is 1, b is 4
extract_options!
class Array
def extract_options!
if last.is_a?(Hash) &&
last.extractable_options?
pop
else
{}
end
end
end
AS/core_ext/array/extract_options.rb
Implementation of
assert_valid_keys
def assert_valid_keys(*valid_keys)
valid_keys.flatten!
each_key do |k|
raise ArgumentError.new("Unknown key:
#{k}") unless valid_keys.include?(k)
end
end
AS/core_ext/hash/keys.rb
No need to merge or
reverse_merge anymore!
def foo(a: 1, b: 2)
puts "a is #{a}, b is #{b}"
end
foo()
#=> a is 1, b is 2
foo a: 3, b: 2
#=> a is 3, b is 2