Кой (и как) може да вика private
методи?
class Something
private
def foo
end
end
self.foo
)Какви са конвенциите за имена на методи, променливи, константи и имена на класове?
UpperCamelCase
- константи (включва имена на класове)
normal_snake_case
- променливи, методи
SCREAMING_SNAKE_CASE
- константи, които не са имена на класове или модулиНа какво могат да завършват методите в Ruby?
?
, ако са предикати
!
, ако имат две версии
=
, ако са setter-и
%
Как можем да направим наш клас-колекция, която да има всички методи на Enumerable
?
В класа трябва:
include Enumerable
.each
.Избройте поне две разлики в поведението на lambda
и Proc.new
.
Proc.new
обектите не са
return
в тяло на lambda
прекратява работата на lambda
-та, а в Proc.new
– на обгръщащата функция
Array
, Hash
, Range
, Set
и други са все Enumerable
#all?
/#any?
връщат истина, ако всички/един елемент(и) от
колекцията отговарят на някакво условие.
[1, 2, 3, 4].all? { |x| x.even? } # => false
[1, 2, 3, 4].any? { |x| x.even? } # => true
[2, 4, 6, 8].all? { |x| x.even? } # => true
[2, 4, 6, 8].any? { |x| x.odd? } # => false
# И разбира се:
[1, 2, 3, 4].any?(&:even?) # => true
#all?
и #any?
могат да работят и без блок:
[1, 2, 3, nil].all? # => false
[1, 2, 3, :nil].all? # => true
[1, 2, 3, false].any? # => true
Аналогични на #all?
и #any?
. Също могат да работят без блок.
%w(foo bar larodi).one? { |word| word.length == 6 } # => true
%w(foo bar larodi).one? { |word| word.length == 3 } # => false
[1, 5, 3].none? { |number| number.even? } # => true
[1, 2, 3].none? { |number| number.even? } # => false
[1, 2, 3].one? # => false
[1, nil, nil].one? # => true
class Integer
def prime?
return false if self == 1
2.upto(self ** 0.5).none? { |n| self % n == 0 }
end
end
7.prime? # => true
8.prime? # => false
#each_with_index
yield-ва всеки елемент с индекса му в масива:
%w[foo bar baz].each_with_index do |word, index|
puts "#{index}. #{word}"
end
Горното извежда:
0. foo 1. bar 2. baz
Няма #map_with_index
.
Затова пък има нещо по-общо:
%w[foo bar baz].map.with_index do |word, index|
[index, word]
end
# => [[0, "foo"], [1, "bar"], [2, "baz"]]
Името казва всичко, което ви е нужно да знаете.
words = %w(foo bar plugh larodi)
groups = words.group_by { |word| word.length }
groups # => {3=>["foo", "bar"], 5=>["plugh"], 6=>["larodi"]}
#each_slice(n)
yield
-ва елементите на части по n
:
%w(a b c d e f g h).each_slice(3) do |slice|
p slice
end
Извежда
["a", "b", "c"] ["d", "e", "f"] ["g", "h"]
#each_cons(n)
yield
"подмасиви" с n
елемента:
[1, 2, 3, 4, 5].each_cons(3) do |cons|
p cons
end
Извежда
[1, 2, 3] [2, 3, 4] [3, 4, 5]
Вече знаете какво прави:
[1, 2, 3, 4].include? 3 # => true
[1, 2, 3, 4].member? 5 # => false
Двете са синоними.
(0...1.0 / 0.0).include? -1 # => false
1 / 0 # => error: ZeroDivisionError
1.0 / 0.0 # => Infinity
[1, 2, 3].zip([4, 5, 6]) # => [[1, 4], [2, 5], [3, 6]]
[1, 2].zip([3, 4], [5, 6]) # => [[1, 3, 5], [2, 4, 6]]
[1, 2, 3, 4, 5].take(2) # => [1, 2]
[1, 2, 3, 4, 5].drop(2) # => [3, 4, 5]
[1, 3, 5, 6, 7, 9].take_while(&:odd?) # => [1, 3, 5]
[1, 3, 5, 6, 7, 9].drop_while(&:odd?) # => [6, 7, 9]
all? any? chunk collect collect_concat count cycle detect drop drop_while each_cons each_entry each_slice each_with_index each_with_object entries find find_all find_index first flat_map grep group_by include? inject lazy map max max_by member? min min_by minmax minmax_by none? one? partition reduce reject reverse_each select slice_before sort sort_by take take_while to_a zip
Enumerable.instance_methods.
sort.
map { |name| name.to_s.ljust(16) }.
each_slice(5) { |row| puts row.join '' }
Disclaimer: Леко е редактиран whitespace-а, за да се събере в слайд.
След това ще продължим с наследяване.
Наследяването в Ruby става така:
class Person
def name() 'The Doctor' end
end
class PolitePerson < Person
def introduction
"Hi, I am #{name}"
end
end
PolitePerson.new.introduction # => "Hi, I am The Doctor"
Object
Object
(с изключение на родителя на Object
...)Имате достъп до private
методите:
class Person
private
def name() 'The Doctor' end
end
class PolitePerson < Person
def introduction() "Hi, I am #{name}" end
end
PolitePerson.new.introduction # => "Hi, I am The Doctor"
class Base; end
class SpaceStation < Base; end
base = Base.new
station = SpaceStation.new
base.is_a? Base # => true
station.is_a? SpaceStation # => true
station.is_a? Base # => true
base.instance_of? Base # => true
station.instance_of? SpaceStation # => true
station.instance_of? Base # => false
#is_a?
има синоним #kind_of?
class Base; end
class SpaceStation < Base; end
base = Base.new
station = SpaceStation.new
base.kind_of? Base # => true
station.kind_of? SpaceStation # => true
station.kind_of? Base # => true
#is_a?
по принцип.
#instance_of?
само когато искате да не бъде наследник.
Може да предефинирате метод и да извикате версията на родителя със super
.
class Person
def introduction_to(other)
"Hello #{other}."
end
end
class PolitePerson < Person
def introduction_to(other)
super("Mr. #{other}") + " How do you do?"
end
end
queen = PolitePerson.new
queen.introduction_to('Smith') # => "Hello Mr. Smith. How do you do?"
Ако извикате super
без скоби, родителският метод получава същите аргументи.
class Person
def introduction_to(other)
"Hello #{other}."
end
end
class PolitePerson < Person
def introduction_to(other)
super + " How do you do?"
end
end
queen = PolitePerson.new
queen.introduction_to('Smith') # => "Hello Smith. How do you do?"
super
и super()
са различни:
class Person
def introduction_to(other)
"Hello #{other}."
end
end
class PolitePerson < Person
def introduction_to(other)
super() + " How do you do?"
end
end
queen = PolitePerson.new
queen.introduction_to('Smith') # => error: ArgumentError
include
-нати
ancestor chain
module Foo; end
module Bar; end
module Qux; end
class Base
include Foo
end
class Derived < Base
include Bar
include Qux
end
Derived.ancestors # => [Derived, Qux, Bar, Base, Foo, Object, Kernel, BasicObject]
module Foo; end
module Bar; end
module Qux
include Foo
include Bar
end
class Thing
include Qux
end
Thing.ancestors # => [Thing, Qux, Bar, Foo, Object, Kernel, BasicObject]
Има само една версия на метода:
module Talking
def greeting() "Hello, #{name}" end
end
class Base
include Talking
def name() 'Base' end
def say_hi_base() greeting end
end
class Derived < Base
include Talking
def name() 'Derived' end
def say_hi_derived() greeting end
end
derived = Derived.new
derived.say_hi_base # => "Hello, Derived"
derived.say_hi_derived # => "Hello, Derived"
derived.ancestors # => # ~> -:20:in `<main>': undefined method `ancestors' for #<Derived:0x002b152b3da2f0> (NoMethodError)
alias
Module#alias_method
(клас макро)
String#size
и String#length
alias
: alias :new_method :old_method
alias new_method old_method
(забележете, че това не са символи или низове, a идентификатори)
Module#alias_method
:
alias_method :new_method, :old_method
alias_method 'new_method', 'old_method'
Въпреки името си, alias
прави копие на метод.
class Array
alias old_inject inject
def inject(*args, &block)
puts "I see you are using #inject. Let me help!"
old_inject(*args, &block) * 0.01
end
end
[1, 2, 3, 4, 5, 6].inject { |a, b| a + b } # => 0.21
Пример за реална употреба:
def to_s
to_html
end
По-добре да се запише така:
alias_method :to_s, :to_html
Или така:
alias to_s to_html
alias
е ключова дума, докато alias_method
е обикновен метод от Module
(клас макро)
alias
може да подавате като аргументи направо идентификатори на методи, например:
alias original_to_s to_s
ще направи нов синоним на to_s
под името original_to_s
alias_method original_to_s, to_s
ще интерпретира original_to_s
и to_s
като имена на променливи
alias
това не ставаclass Array
[:size, :count, :length].each do |method_name|
alias_method "old_#{method_name}", :size
end
def size
0
end
end
[1, 2, 3].size # => 0
[1, 2, 3].old_size # => 3
alias
е ключова дума и подлежи на статичен синтактичен анализ, такива инструменти разпознават тези синоними
Module#alias_method
обикновено не се разпознава в тези случаи, понеже не може да ползвате статичен синтактичен анализ за целтаИнтересен gem:
MethodFinder.find('abc', 'ABC')
%w[a b c].find_method { |a| a.unknown(1) ; a == %w[a c] }
"Всичко наследява от Object
"
Object
не наследява от Object
BasicObject
BasicObject
е nil
BasicObject
е минималистичен клас, подходящ за прокситаKernel
е миксиран в Object
Kernel
(#puts
, #eval
и т.н.)
Object
(#inspect
, #tap
, #methods
и т.н.)
Object
Само обекти от същия клас могат да викат protected методи
class Vector
def initialize(x, y) @x, @y = x, y end
def inspect() "Vector.new(#@x, #@y)" end
def +(other)
Vector.new(*coords.zip(other.coords).map { |a, b| a + b })
end
protected
def coords() [@x, @y] end
end
vector = Vector.new(1, 2) + Vector.new(3, 4)
vector # => Vector.new(4, 6)
vector.coords # => error: NoMethodError
private
се ползва постоянно
protected
почти никога
protected
, защото могат да се викат със self.
отпред
public
. Не го ползваме, а просто слагаме публичните методи отгоре
Module
и Class
Понеже private
и protected
са методи:
class Person
def name() end
def age() end
private :name, :age
end
Или дори:
class String
private :upcase!, :downcase!
end
"Foo".upcase! # => error: NoMethodError
Помните ли, че дефиницията на метод връща името на метод като символ?
def foo() end # :foo
Значи може да направите така:
class Person
private def name
"My name is a secret."
end
private def age
"I'm feeling feminine, so my age is a secret, too."
end
end