Module#extend
Method
Какво прави caller
?
Връща списък с низове, всеки ред от който представлява елемент от текущия (в мястото на извикването на caller
) call stack. Нещо такова:
.../irb/ruby-lex.rb:232:in `catch'
.../irb/ruby-lex.rb:232:in `each_top_level_statement'
.../irb.rb:488:in `eval_input'
.../irb.rb:397:in `block in start'
.../irb.rb:396:in `catch'
.../irb.rb:396:in `start'
.../bin/irb:11:in `<main>'
Кои изключения обикновено е добра идея да хващаме и кои не?
За какво служат throw
и catch
в Ruby?
catch
и throw
се ползват за control flow; не служат за обработка на грешки
Какво правеше клас-макрото include
?
module Bar
def public_method() end
private
def private_method() end
end
class Foo
include Bar
end
"Копира" методите, дефинирани в модула, със съответната им видимост (public
, private
, ...) като инстанционни методи в класа-получател.
Помните Module#include
, нали?
module Introductions
def ahoy() "Ahoy my mate, I'm #{name}!" end
def hi() "Hey there, I'm #{name}!" end
end
class Person
include Introductions
attr_accessor :name
def initialize(name) @name = name end
end
Person.new('Silver').ahoy # "Ahoy my mate, I'm Silver!"
Person.new('Silver').hi # "Hey there, I'm Silver!"
Module#extend
добавя методите на дадения модул като класови методи:
module Introductions
def ahoy() "Ahoy my mate, I'm #{name}!" end
def hi() "Hey there, I'm #{name}!" end
end
class Person
extend Introductions
end
Person.ahoy # "Ahoy my mate, I'm Person!"
Person.hi # "Hey there, I'm Person!"
Аналогично на include
, може да ползвате extend
и в модули:
module Person
extend Creation
extend Traversing
extend Reflection
end
Може дори да include
-нете и да extend
-нете с един и същи модул:
class Person
include Introductions
extend Introductions
end
Person.new('Why').hi # "Hey there, I'm Why!"
Person.hi # "Hey there, I'm Person!"
Долното работи и се ползва понякога:
module UrlHelpers
extend self
def encode(url)
end
def decode(url)
end
end
UrlHelpers.encode('foo bar')
UrlHelpers.decode('foo%20bar')
Преди да продължим с интроспекция.
class
ви дава класа на даден обект; всеки обект има клас, дори класовете
superclass
- родителският клас
kind_of?
(със синоним is_a?
)
instance_of?
ancestors
- списъкът от класове и модули, в който се търси метод при извикване на такъвmethods
- списък с методите, на които отговаря дадена инстанция
foo.methods != foo.class.methods
'foo'.methods.size # 165
String.methods.size # 101
private_instance_methods
protected_instance_methods
public_instance_methods
singleton_methods
ви връща класовите методиclass Ruby
def self.author() 'Matz' end
def version() '2.1.0' end
def repository() 'Git' end
def implementation() 'MRI C' end
protected :repository
private :implementation
end
Ruby.public_instance_methods # [:version, :nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :extend, :display, :method, :public_method, :singleton_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :gem, :==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]
Ruby.protected_instance_methods # [:repository]
Ruby.private_instance_methods # [:implementation, :Digest, :timeout, :initialize_copy, :initialize_dup, :initialize_clone, :sprintf, :format, :Integer, :Float, :String, :Array, :Hash, :warn, :raise, :fail, :global_variables, :__method__, :__callee__, :__dir__, :eval, :local_variables, :iterator?, :block_given?, :catch, :throw, :loop, :respond_to_missing?, :trace_var, :untrace_var, :at_exit, :syscall, :open, :printf, :print, :putc, :puts, :gets, :readline, :select, :readlines, :`, :p, :test, :srand, :rand, :trap, :exec, :fork, :exit!, :system, :spawn, :sleep, :exit, :abort, :load, :require, :require_relative, :autoload, :autoload?, :proc, :lambda, :binding, :caller, :caller_locations, :Rational, :Complex, :set_trace_func, :gem_original_require, :Pathname, :URI, :rubygems_require, :initialize, :singleton_method_added, :singleton_method_removed, :singleton_method_undefined, :method_missing]
Ruby.singleton_methods # [:author]
object.respond_to?(:method_name)
true
, ако можете безопасно да изпълните object.method_name
:method_name
false
за private
и protected
методи
true
като втори аргумент, ще игнорира видимостта на методаinstance_variables
instance_variable_get
instance_variable_set
instance_variable_defined?
remove_instance_variable
- този метод е private
@
в името на променливатаclass Foo
def initialize
@baba = 42
end
def touch
@touched = true
end
end
foo = Foo.new
foo.instance_variables # [:@baba]
foo.touch
foo.instance_variables # [:@baba, :@touched]
class Foo
def initialize
@baba = 42
end
end
foo = Foo.new
foo.instance_variable_defined? :@baba # true
foo.send(:remove_instance_variable, :@baba)
foo.instance_variable_defined? :@baba # false
Object
Module
(Class < Module
)
constants
връща списък с константите, дефинирани в дадения клас/модул
const_get
ви връща стойността на дадена константа от име, подадено като низ или символ
Object.const_get('String').new
е интересен начин да запишете ''
const_set
ви дава възможност да създадете (или промените) константа в класа/модула
const_defined?
- проверка дали дадена константа съществува в класа/модула
remove_const
- премахва константа от клас/модулRegexp.constants # [:IGNORECASE, :EXTENDED, :MULTILINE, :FIXEDENCODING, :NOENCODING]
Object.constants.take(5) # [:Object, :Module, :Class, :BasicObject, :Kernel]
Object.constants.size # 145
Object.constants
е списък на всички top-level константи, дефинирани към моментаString # String
Object.const_set(:String, Fixnum)
String # Fixnum
Kernel#global_variables
ще ви върне списък с всички глобални променливи,
дефинирани към момента. Например:
puts global_variables.map { |var| var.to_s.ljust(16) }
.each_slice(4)
.map { |vars_per_line| vars_per_line.join(' ') }
.join("\n")
$; $-F $@ $! $SAFE $~ $& $` $' $+ $= $KCODE $-K $, $/ $-0 $\ $_ $stdin $stdout $stderr $> $< $. $FILENAME $-i $* $? $$ $: $-I $LOAD_PATH $" $LOADED_FEATURES $VERBOSE $-v $-w $-W $DEBUG $-d $0 $PROGRAM_NAME $-p $-l $-a $binding $1 $2 $3 $4 $5 $6 $7 $8 $9
Kernel#global_variables
, има метод Kernel#local_variables
foo = :bar
die_antwoord = 42
local_variables # [:foo, :die_antwoord, :_xmp_1421262103_16916_232677]
defined?
nil
foo ||= default_value
foo
да не е била дефинирана към момента. Проблемът с този код?
foo
е имала стойност false
и това е валидно за вашия случай, ще я презапишете
unless defined? cache
cache = true
end
x = 42 unless defined? x
x # nil
defined?
да се изпълни, x
ще се дефинира и ще има стойност nil
defined? 1 # "expression"
defined? foo # nil
defined? puts # "method"
defined? String # "constant"
defined? $& # nil
defined? $_ # "global-variable"
defined? Math::PI # "constant"
defined? answer = 42 # "assignment"
defined? 42.abs # "method"
Можете да ползвате Kernel#block_given?
, за да проверите дали
някой ви е подал блок, например:
def block_or_no_block
if block_given?
'We got a block, go this way...'
else
'No block man, go that way...'
end
end
block_or_no_block # "No block man, go that way..."
block_or_no_block {} # "We got a block, go this way..."
Proc.new
ще върне блока, асоцииран с обкръжаващия го метод, като обект
Proc.new
извън контекста на метод, без блок, ще се продуцира грешка
yield
-ваме даден блок, той не се обръща в обектПример с Proc.new:
def foo
Proc.new if block_given?
end
foo # nil
foo {} # #<Proc:0x426af0e4@-:6>
proc = foo { "hello" }
proc.call # "hello"
ObjectSpace
ви дава прост интерфейс за манипулация на обектите в паметта
ObjectSpace.each_object
ще yield
-ва на подадения му блок всеки такъв обект
ObjectSpace.each_object(String)
ще обходи всички низове, заредени в паметта в момента
ObjectSpace.each_object(Class)
- всички класове
Fixnum
, Symbol
, true
, false
и nil
, тъй като те се интернират
Enumerator
ObjectSpace.each_object.count # 26901
ObjectSpace.each_object(String).count # 18375
ObjectSpace.each_object(Float).to_a # [NaN, Infinity, 2.220446049250313e-16, 1.7976931348623157e+308, 2.2250738585072014e-308, 2.718281828459045, 3.141592653589793, -Infinity, Infinity, 0.0, 100.0, 100.0, 100000.0, 100.0, 10000000.0, 100.0, 1000.0, 1000.0, 1000.0, 60000.0, 3600000.0, 0.0]
ObjectSpace.each_object(Class).count # 681
ObjectSpace.each_object(Class) do |klass|
# Do something with klass
end
Дава проста статистика на бройките обекти в паметта по типове. Връща хеш. Например:
puts ObjectSpace.count_objects
.map { |type, count| "#{type}:".ljust(10) + count.to_s.rjust(7) }
.each_slice(3)
.map { |counts_per_row| counts_per_row.join(' ') }
.join("\n")
TOTAL: 419019 FREE: 289687 T_OBJECT: 12958 T_CLASS: 880 T_MODULE: 34 T_FLOAT: 8 T_STRING: 85708 T_REGEXP: 175 T_ARRAY: 21544 T_HASH: 707 T_STRUCT: 1 T_BIGNUM: 6 T_FILE: 7 T_DATA: 2188 T_MATCH: 643 T_COMPLEX: 1 T_NODE: 4436 T_ICLASS: 36
Object#object_id
?
ObjectSpace._id2ref
е обратният му метод
id = 'larodi'.object_id # 550842600
ObjectSpace._id2ref(id) # "larodi"
ObjectSpace.define_finalizer(object, proc)
proc
когато object
бъде garbage collect-нат
foo = 'Memory is plentiful!'
ObjectSpace.define_finalizer foo, proc { puts 'foo is gone' }
ObjectSpace.garbage_collect
foo = nil
ObjectSpace.garbage_collect
# Тук proc-ът от по-горе бива извикан
# и на STDOUT се извежда "foo is gone"
Method
NameError
)
Method
се държат като closureclass Person
def name
'Matz'
end
end
Person.new.method(:name) # #<Method: Person#name>
call
ще извика въпросния метод
name
ще ви даде името на метода
owner
- класът или модулът, откъдето е дошъл този метод
receiver
- обектът получател на този метод (инстанцията, обвързана с метода)
arity
връща колко аргумента приема метода (с врътки за променлив брой аргументи)
parameters
- детайлна информация за приеманите аргументи - имена, дали са задължителни и т.н.
source_location
- в кой файл и на кой ред е дефиниран този метод; много полезно
unbind
- "разкача" този метод от получателя му; връща ви инстанция на UnboundMethod
(пример след няколко слайда)class Person
attr_accessor :name
end
someone = Person.new
someone.name = 'Murakami Haruki'
name_method = someone.method(:name)
name_method.call # "Murakami Haruki"
def foo(x, y, z); end
method(:foo).arity # 3
method(:foo).parameters # [[:req, :x], [:req, :y], [:req, :z]]
def bar(x, y, z = 1); end
method(:bar).arity # -3
method(:bar).parameters # [[:req, :x], [:req, :y], [:opt, :z]]
call
, и Proc
-обектите имат метод arity
със същото действие
Proc
Proc.new { |x| }.arity # 1
lambda { |x, y| }.arity # 2
require 'set'
Set.new.method(:to_a).source_location # ["/Users/.../ruby-1.9.3/lib/ruby/1.9.1/set.rb", 144]
class Set; def to_a; super; end; end
Set.new.method(:to_a).source_location # ["-", 4]
UnboundMethod
Method
)
class Person
attr_reader :name
def initialize(name)
@name = name
end
end
stefan = Person.new 'Stefan'
mitio = Person.new 'Mitio'
stefan.name # "Stefan"
stefans_name = stefan.method(:name)
stefans_name.call # "Stefan"
stefans_name.unbind.bind(mitio).call # "Mitio"
Следва продължение...
parse
def serialize
"#{data_type}:#{@data}"
end
def self.parse(string)
File.new parse_data_with_type(*string.split(':', 2))
end
private
def self.parse_data_with_type(type, data)
case type
when 'nil' then nil
when 'string' then data
when 'symbol' then data.to_sym
when 'number' then data.to_f
else data == 'true'
end
end
def serialize
files = "#{@files.size}:#{serialize_entities(@files)}"
directories = "#{@directories.size}:#{serialize_entities(@directories)}"
"#{files}#{directories}"
end
def serialize_entities(entities)
entities.map do |name, entity|
serialized_entity = entity.serialize
"#{name}:#{serialized_entity.size}:#{serialized_entity}"
end.join('')
end
Да си дефинираме нов клас Parser
със следния публичен интерфейс:
initialize(serialized_string)
each
- yield
-ва последователност от файлове или директории
Parser#each
ще ползва и следните два private
метода:
read_next_record
- чете от низа до първото срещане на :
и го връща
read_next_entity
- чете един елемент от формата :
def read_next_record
record, @data = @data.split(':', 2)
record
end
def read_next_entity
size = read_next_record.to_i
entity = @data[0...size]
@data = @data[size...@data.size]
entity
end
def each
array_size = read_next_record.to_i
array_size.times do
entity_name = read_next_record
entity_string = read_next_entity
yield entity_name, entity_string
end
end
def self.parse(string_data)
parser = Parser.new(string_data)
files = {}
directories = {}
parser.each { |name, entity| files[name] = File.parse(entity) }
parser.each { |name, entity| directories[name] = Directory.parse(entity) }
Directory.new(files, directories)
end