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

Ruby - Can your language do this?

Ruby - Can your language do this?

Georgy Angelov

June 24, 2016
Tweet

More Decks by Georgy Angelov

Other Decks in Programming

Transcript

  1. А вашият език може ли това?

    View Slide

  2. View Slide

  3. View Slide

  4. Защо точно ?

    View Slide

  5. View Slide

  6. user.save if email.valid?

    View Slide

  7. allow_any_instance_of(Validator).to receive(:validate)
    .and_return(true)

    View Slide

  8. Often people, especially computer engineers, focus on the machines.
    They think, "By doing this, the machine will run faster. By doing this,
    the machine will run more effectively. By doing this, the machine will
    something something something." They are focusing on machines.
    But in fact we need to focus on humans, on how humans care about
    doing programming or operating the application of the machines.
    We are the masters. They are the slaves.
    -- Matz

    View Slide

  9. Добър код

    View Slide

  10. View Slide

  11. The Rails Doctrine
    By David Heinemeier Hansson (DHH) in January, 2016

    View Slide

  12. 1. Optimize for programmer happiness

    View Slide

  13. 2. Convention over Configuration

    View Slide

  14. 3. The menu is omakase

    View Slide

  15. 4. No one paradigm

    View Slide

  16. 5. Exalt beautiful code

    View Slide

  17. 6. Value integrated systems

    View Slide

  18. 7. Push up a big tent

    View Slide

  19. The community

    View Slide

  20. View Slide

  21. Конференции (2015)
    Garden City RubyConf
    Ruby devroom at
    FOSDEM
    Ruby ConfAustralia
    Rubyfuza
    Ruby on Ales
    Tropical Ruby
    MountainWest
    RubyConf
    Bath Ruby Conference
    wroc_love.rb
    RubyConfLT
    Ancient City Ruby
    RubyConfPhilippines
    Ruby ConfIndia
    RailsConf
    ROSSConf Vienna
    RubyConfKenya
    Ruby Conference Kiev
    RedDotRubyConf
    RubyNation
    Gotham Ruby Conference
    Brighton Ruby
    JRubyConfEU
    Burlington Ruby
    Conference
    eurucamp
    DeccanRubyConf
    Madison+ Ruby
    Ruby Midwest
    Barcelona Ruby Conf
    RubyConfTaiwan
    RubyConfPortugal
    WindyCityRails
    RubyConfBrazil
    Rocky Mountain Ruby
    Conference
    ROSSConf Berlin
    ArrrrCamp
    Los Angeles Ruby Conference
    RubyConfColombia
    EuRuKo
    Keep Ruby Weird
    RubyWorld Conference
    RubyDay
    RubyConf
    RailsIsrael
    RubyKaigi

    View Slide

  22. require 'open-uri'
    require 'nokogiri'
    CONFERENCES_SITE = 'http://rubyconferences.org/past/'
    doc = Nokogiri::HTML(open(CONFERENCES_SITE))
    headings = doc.css('article#past dt').map { |heading| heading.content.strip }
    dates = doc.css('article#past dd').map { |desc| desc.css('li').first.content }
    conferences = headings.zip(dates)
    .select { |_, description| description.include? '2015' }
    .map(&:first)
    puts conferences

    View Slide

  23. EuRuKo Sofia
    23-24 септември 2016

    View Slide

  24. Rails Girls
    Шесто поредно издание (в България)
    25-26 март 2016

    View Slide

  25. View Slide

  26. View Slide

  27. fmi. .bg

    View Slide

  28. if talk.is? :cheap
    show me, the_code
    end

    View Slide

  29. View Slide

  30. В всичко е израз

    View Slide

  31. classification = if age < 13
    'младок'
    elsif age < 20
    'тийн'
    else
    'старец'
    end
    puts classification

    View Slide

  32. def foo(a, b = a, c = b)
    c
    end
    puts foo('bar')
    # => 'bar'

    View Slide

  33. def doge(a, b = def doge(a); 'W'; end)
    a
    end
    puts doge('W', 1) + doge('O') + doge('whatever')
    # => WOW

    View Slide

  34. В има числа

    View Slide

  35. a = 5 * 2 #=> 10
    b = 10 ** 2 #=> 100
    a.class #=> Fixnum

    View Slide

  36. c = 2 ** 1_024 #=> 17976931348623159077293051907890247…
    c.to_s.size #=> 309
    c.class #=> Bignum

    View Slide

  37. (2 ** 100_000).to_s.size #=> 30103

    View Slide

  38. View Slide

  39. View Slide

  40. В всичко е обект

    View Slide

  41. class A
    def foo
    'bar'
    end
    end
    class_a = A
    class_a.new.foo #=> 'bar'

    View Slide

  42. В може да се дефинират методи

    View Slide

  43. class String
    def such; "such #{self}"; end
    def wow; "#{self}. wow!"; end
    end
    'cool'.such.wow #=> 'such cool. wow!'

    View Slide

  44. 2.mm #=> 0.002
    10.m #=> 10.0
    10.5.km #=> 10500.0
    10.5.km + 15.cm – 3.mm #=> 10500.147

    View Slide

  45. class Numeric
    MM_SIZE = 0.001
    UNITS = [:mm, :cm, :dm, :m, :dam, :hm, :km]
    UNITS.reduce(MM_SIZE) do |size, unit|
    define_method(unit) { self * size }
    size * 10
    end
    end

    View Slide

  46. liar = [1, 2, 3]
    liar.size #=> 3
    def liar.size
    42
    end
    liar.size #=> 42
    [1, 2, 3].size #=> 3

    View Slide

  47. В има дати

    View Slide

  48. 2.days.ago
    1.hour.from_now
    (1.hour + 3.minutes).ago

    View Slide

  49. class Numeric
    def day
    self * 24.hours
    end
    def hour
    self * 60.minutes
    end
    def minute
    self * 60.seconds
    end
    def second
    self
    end
    end

    View Slide

  50. class Numeric
    alias_method :days, :day
    alias_method :hours, :hour
    alias_method :minutes, :minute
    alias_method :seconds, :second
    end

    View Slide

  51. class Numeric
    def ago
    Time.now - self
    end
    def from_now
    Time.now + self
    end
    end

    View Slide

  52. В се пише на английски

    View Slide

  53. 5.пъти { покажи 'въй' } #=> въй въй въй въй въй

    View Slide

  54. module Kernel
    alias_method :покажи, :puts
    end
    module Enumerable
    alias_method :пъти, :times
    end

    View Slide

  55. В false е обратното на true

    View Slide

  56. true #=> true
    !true #=> false

    View Slide

  57. def [email protected]!
    true
    end
    !true #=> true
    !!false #=> true

    View Slide

  58. В има if-ове...

    View Slide

  59. true .if_true { 'yes' }.if_false { 'no' } #=> 'yes'
    false.if_true { 'yes' }.if_false { 'no' } #=> 'no'
    'wtf'.if_true { 42 }.if_false { 666 } #=> 42

    View Slide

  60. class BasicObject
    def if_true; yield; end
    def if_false; self; end
    end
    def false.if_true; self; end
    def nil.if_true; self; end
    def false.if_false; yield; end
    def nil.if_false; yield; end

    View Slide

  61. В клас се инстанцира с `.new`...

    View Slide

  62. class Class
    alias_method :[email protected], :new
    end
    !Array #=> []
    !String #=> ''
    !A #=> #

    View Slide

  63. class Class
    alias_method :[], :new
    end
    Array[1, 2, 3] #=> [1, 2, 3]
    String[] #=> ''
    A[] #=> #

    View Slide

  64. В няма абстрактни класове...

    View Slide

  65. class A
    class << self
    private :new
    end
    end
    A.new #=> NoMethodError: private method `new' called

    View Slide

  66. Singleton?

    View Slide

  67. class A
    def self.new
    @instance ||= super
    end
    end
    A.new #=> #
    A.new #=> #
    A.new #=> #

    View Slide

  68. В няма декоратори...

    View Slide

  69. class Fibonacci
    memoized def calculate(n)
    return 1 if n <= 2
    puts "Calculating fib(#{n})"
    calculate(n – 1) + calculate(n – 2)
    end
    end

    View Slide

  70. f = Fibonacci.new
    f.calculate(8)
    # Calculating fib(8)
    # Calculating fib(6)
    # Calculating fib(4)
    # Calculating fib(3)
    # Calculating fib(5)
    # Calculating fib(7)
    #=> 21
    f.calculate(8)
    #=> 21

    View Slide

  71. class Class
    def memoized(method_name)
    original = "#{method_name}_original"
    alias_method original, method_name
    define_method method_name do |*args|
    cache_key = [method_name, args]
    @_method_cache ||= {}
    @_method_cache[cache_key] ||= send(original, *args)
    end
    end
    end

    View Slide

  72. class Class
    def memoized(method_name)
    decorate method_name do |method, *args|
    cache_key = [method_name, args]
    @_method_cache ||= {}
    @_method_cache[cache_key] ||= method.call(*args)
    end
    end
    end

    View Slide

  73. class Class
    def decorate(method_name)
    method = instance_method(method_name)
    define_method method_name do |*args|
    yield method.bind(self), *args
    end
    end
    end

    View Slide

  74. не е Java...

    View Slide

  75. module A
    public static void def lol()
    'I am a Java! Love me!'
    end
    end
    A.lol #=> "I am a Java! Love me!"

    View Slide

  76. class Module
    def static(name)
    module_function name
    end
    end

    View Slide

  77. class Module
    def void(method)
    decorate method do |method, *args|
    method.call(*args)
    nil
    end
    end
    end

    View Slide

  78. не е Haskell...

    View Slide

  79. plus = proc { |a, b| a + b }
    plus_one = plus.curry[1]
    [1, 2, 3, 4].map &plus_one #=> [2, 3, 4, 5]

    View Slide

  80. naturals = 1..Float::INFINITY
    naturals.lazy
    .map { |x| x ** 2 }
    .take_while { |x| x <= 100 }
    .reduce(:+)
    #=> 385

    View Slide

  81. В може да си направим
    placeholder обект

    View Slide

  82. naturals = 1..Float::INFINITY
    naturals.lazy
    .map { |x| x ** 2 }
    .take_while { |x| x <= 100 }
    .reduce(:+)
    #=> 385

    View Slide

  83. naturals = 1..Float::INFINITY
    naturals.lazy
    .map(&P ** 2)
    .take_while(&P <= 100)
    .reduce(:+)
    #=> 385

    View Slide

  84. [[1, 2, 3, 8], [4, 5], [6]].map(&P.select(&:even?))
    #=> [[2, 8], [4], [6]]

    View Slide

  85. class P < BasicObject
    class << self
    def method_missing(method, *args, &block)
    ::Proc.new { |object| object.send method, *args, &block }
    end
    def to_proc
    ::Proc.new { |object| object }
    end
    end
    end

    View Slide

  86. В инстанционните променливи
    са private...

    View Slide

  87. f = Fibonacci.new
    f.calculate(4) #=> 3
    f.instance_variable_get('@_method_cache')
    #=> {
    # [:fib, [2]] => 1,
    # [:fib, [1]] => 1,
    # [:fib, [3]] => 2,
    # [:fib, [4]] => 3
    # }
    f.instance_variable_set('@_method_cache', {[:fib, [4]] => 42})
    f.calculate(4) #=> 42

    View Slide

  88. В има private методи...

    View Slide

  89. class A
    private
    def call_me_if_you_can; 'gj'; end
    end
    a = A.new
    a.call_me_if_you_can #=> NoMethodError: private method
    # `call_me_if_you_can' called

    View Slide

  90. a.call :call_me_if_you_can #=> "gj"
    a.instance_eval { call_me_if_you_can } #=> "gj"
    A.class_eval { public :call_me_if_you_can }
    a.call_me_if_you_can #=> "gj"

    View Slide

  91. В има константи...

    View Slide

  92. A_CONSTANT = 1234
    A_CONSTANT = 42 #=> warning: already initialized
    # constant A_CONSTANT
    A_CONSTANT #=> 42

    View Slide

  93. A_CONSTANT = 1234
    Object.send :remove_const, 'A_CONSTANT'
    A_CONSTANT = 42
    A_CONSTANT #=> 42

    View Slide

  94. В всеки обект може да се
    направи immutable...

    View Slide

  95. array = [1]
    array << 2
    array #=> [1, 2]
    array.freeze
    array << 3 #=> RuntimeError: can't modify frozen Array

    View Slide

  96. В няма начин да се un-freeze-не
    обект

    View Slide

  97. require 'fiddle'
    class Object
    def unfreeze
    Fiddle::Pointer.new(object_id * 2)[1] &= ~(1 << 3)
    end
    end

    View Slide

  98. array << 3 #=> RuntimeError: can't modify frozen Array
    array.unfreeze
    array.frozen? #=> false
    array << 3
    array #=> [1, 2, 3]

    View Slide

  99. В дори методите са обекти

    View Slide

  100. class Person
    def initialize(name)
    @name = name
    end
    def likes(*items)
    "I am #{@name}. I like #{items.join(', ')}."
    end
    end

    View Slide

  101. ivan = Person.new('Ivan')
    ivan.likes('cake', 'cats')
    #=> 'I am Ivan. I like cake, cats.'
    m = ivan.method(:likes)
    #=> #
    m.call('pancakes')
    #=> 'I am Ivan. I like pancakes.'

    View Slide

  102. patrick = Person.new('Patrick')
    def patrick.likes
    '[censored]'
    end
    patrick.likes('coffee')
    #=> '[censored]'
    m.unbind.bind(patrick).call('coffee')
    #=> 'I am Patrick. I like coffee.'

    View Slide

  103. class Test
    def f(a, b=2, *args)
    end
    end
    m = Test.new.method(:f)
    m.source_location
    #=> ["/Users/gangelov/ruby-test.rb", 2]
    m.parameters
    #=> [[:req, :a], [:opt, :b], [:rest, :args]]

    View Slide

  104. може да разопакова аргументи

    View Slide

  105. [[1, [2, 3]], [4, [5, 6]]].each do |a, (b, c)|
    puts a, b, c
    end
    #=> 1 2 3
    #=> 4 5 6

    View Slide

  106. person = {
    first_name: "Иван",
    last_name: "Петров",
    age: 22,
    # ...
    }
    person.using do |first_name, last_name|
    puts "Здравей, #{first_name} #{last_name}!"
    end

    View Slide

  107. person = {
    first_name: "Иван",
    last_name: "Петров",
    age: 22,
    # ...
    }
    person.using do |first_name, age|
    puts "#{first_name} е на #{age}!"
    end

    View Slide

  108. class Hash
    def using(&block)
    values = block.parameters.map do |(type, name)|
    self[name]
    end
    yield *values
    end
    end

    View Slide

  109. triangles = [
    {a: 3, b: 4, c: 5 },
    {a: 5, b: 12, c: 13},
    ...
    ]
    triangles.select.using do |a, b, c|
    a**2 + b**2 == c**2
    end

    View Slide

  110. module Enumerable
    def using(&block)
    each do |hash|
    hash.using(&block)
    end
    end
    end

    View Slide

  111. В всеки обект си има клас

    View Slide

  112. 'foo'.class #=> String
    1234.class #=> Fixnum
    String.class #=> Class
    Class.class #=> Class
    Class.class.class.….class #=> Class

    View Slide

  113. Кажете
    "Класът на класа Class е Class"

    View Slide

  114. В всеки клас си има родител

    View Slide

  115. String.superclass #=> Object
    Object.superclass #=> BasicObject
    BasicObject.superclass #=> nil

    View Slide

  116. Class.superclass #=> Module
    Module.class #=> Class

    View Slide

  117. Хм, класът е клас, значи има new…

    View Slide

  118. Class.new #=> #
    doge_class = Class.new do
    def such
    'much'
    end
    end
    doge_class.new.such #=> 'much'

    View Slide

  119. В има пет типа променливи

    View Slide

  120. $global = 'глобална променлива'
    local = 'локална променлива'
    Constant = 'константа'
    @instance = 'инстанционна променлива'
    @@class = 'класова променлива'

    View Slide

  121. В неанонимните класове са
    анонимни класове с имена

    View Slide

  122. doge_class = Class.new do
    def such
    'much'
    end
    end
    doge_class.name #=> nil
    Doge = doge_class
    doge_class.name #=> 'Doge'

    View Slide

  123. В използването на
    недефинирана променлива
    предизвиква грешка

    View Slide

  124. bla #=> NameError: undefined local variable or method

    View Slide

  125. def method_missing(*) end
    bla #=> nil
    hello_undefined_var #=> nil

    View Slide

  126. В низовете се ограждат с
    кавички

    View Slide

  127. 'hello world!' #=> 'hello world!'
    str { hello world! } #=> 'hello world!'

    View Slide

  128. class Str
    def method_missing(*args)
    args.join(' ')
    end
    end
    def str(&block)
    Str.new.instance_eval(&block)
    end

    View Slide

  129. В може да дефинираме
    безкраен хеш

    View Slide

  130. h = {}
    h['a'] #=> nil
    h['a']['b'] #=> undefined method `[]` for nil:NilClass

    View Slide

  131. h = Hash.new do |hash, key|
    hash[key] = Hash.new(&hash.default_proc)
    end
    h['a'] #=> {}
    h['a']['b']['c']['d'] = 42
    h #=> {"a" => {"b" => {"c" => {"d" => 42}}}}

    View Slide

  132. ви има пълно доверие

    View Slide

  133. ObjectSpace.each_object(String) do |object|
    p object
    end
    #=> всички низове, които са в паметта в момента

    View Slide

  134. GC.disable
    loop { '1' }
    #=> NoMemoryError
    GC.enable
    GC.start

    View Slide

  135. С може да възкресите мъртвите

    View Slide

  136. john = Person.new('John')
    john_id = john.object_id
    john = nil
    ObjectSpace._id2ref(john_id) #=> #

    View Slide

  137. В дори binding-ът е обект

    View Slide

  138. puts answer #=> NameError: undefined local variable
    binding.local_variable_set('answer', 42)
    puts answer #=> 42

    View Slide

  139. a_counter = 1
    b_counter = 3
    c_counter = 9
    increment_counters
    a_counter #=> 2
    b_counter #=> 4
    c_counter #=> 10

    View Slide

  140. require 'binding_of_caller'
    def increment_counters
    b = binding.of_caller(1)
    b.local_variables
    .select { |name| name =~ /_counter$/ }.each do |name|
    value = b.local_variable_get(name)
    b.local_variable_set(name, value + 1)
    end
    end

    View Slide

  141. С може да бъдете ДАНС

    View Slide

  142. set_trace_func proc do |event, file, line, id, binding, cn|
    printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, cn
    end
    t = Test.new
    t.test
    #=> line prog.rb:11 false
    #=> c-call prog.rb:11 new Class
    #=> c-call prog.rb:11 initialize Object
    #=> c-return prog.rb:11 initialize Object
    #=> c-return prog.rb:11 new Class
    #=> line prog.rb:12 false
    #=> call prog.rb:2 test Test
    #=> line prog.rb:3 test Test
    #=> line prog.rb:4 test Test
    #=> return prog.rb:4 test Test

    View Slide

  143. С може да декомпилирате Ruby

    View Slide

  144. i = RubyVM::InstructionSequence.compile("Test.new.method")
    puts i.disassemble
    #=> 0000 trace 1 ( 1)
    #=> 0002 getinlinecache 9,
    #=> 0005 getconstant :Test
    #=> 0007 setinlinecache
    #=> 0009 opt_send_without_block ARGS_SIMPLE>,
    #=> 0012 opt_send_without_block ARGS_SIMPLE>,
    #=> 0015 leave

    View Slide

  145. С може да сте шпиони

    View Slide

  146. class Person
    def say(message)
    "#{message}!"
    end
    end
    person = Spy.new(Person.new)
    person.class
    #=> Person
    person.say('hello')
    #=> They called say with hello
    #=> 'hello!'

    View Slide

  147. class Spy < BasicObject
    def initialize(obj)
    @instance = obj
    end
    def method_missing(name, *args, &block)
    $stdout.puts "They called #{name} with (#{args.join(', ')})"
    @instance.send(name, *args)
    end
    end

    View Slide

  148. С може да пътувате във времето

    View Slide

  149. require 'timecop'
    new_time = Time.local(2008, 9, 1, 12, 0, 0)
    Timecop.freeze(new_time)
    sleep 10
    new_time == Time.now #=> true

    View Slide

  150. # Seconds will now seem like hours
    Timecop.scale(3600)
    Time.now
    # => 2016-02-28 21:23:25 +0200
    # A few seconds later...
    Time.now
    # => 2016-02-29 06:22:59 +0200

    View Slide

  151. ви дава достатъчно дълго въже,
    че да се застреляте в крака

    View Slide

  152. Бонус: Знаете ли какво е Quine?

    View Slide

  153. View Slide