ArrayStore
HashStore
DataModel
#create
#find
#delete
#update
def initialize
@storage = {}
end
def create(record)
@storage[record[:id]] = record
end
def find(query)
@storage.values.select do |record|
query.all? { |key, value| record[key] == value }
end
end
def delete(query)
find(query).each { |record| @storage.delete(record[:id]) }
end
def update(id, record)
return unless @storage.key? id
@storage[id] = record
end
def find(search_hash)
result = []
@storage.each do |_, current_hash|
result << current_hash if sub_hash?(current_hash, search_hash)
end
result
end
def find(query)
@storage.select do |_, record|
sub_hash?(record, query)
end
end
def find(query)
@storage.select { |_, record| sub_hash?(record, query) }
end
to_overload.each do |key, value|
desired_hash[key] = value
end
desired_hash.merge!(to_overload)
class DataStore
def create; end
def find; end
def delete; end
def update; end
end
class ArrayStore < DataStore
# ...
end
class HashStore < DataStore
# ...
end
HashStore#delete
метода ще връща nil
, вместо NoMethodError
module DataStore
def delete(query)
find(query).each do |obj|
delete_by_id(obj[:id])
end
end
end
class HashStore
include DataStore
def delete_by_id(id)
@storage.delete(id)
end
end
class HashStore
def delete(query)
find(query) { |record| @storage.delete(record[:id]) }
end
end
DataStore
id
ArrayStore
и HashStore
SQLStore
?
delete from users where first_name = 'Ivan' and last_name = 'Ivanov'
id
?
Existing code exerts a powerful influence. Its very presence argues that it is both correct and necessary. We know that code represents effort expended, and we are very motivated to preserve the value of this effort. [...] It's as if our unconscious tell us "Goodness, that's so confusing, it must have taken ages to get right. Surely it's really, really important. It would be a sin to let all that effort go to waste."
Представете си StackOverflow:
class HashStore
def initialize
# ...
@next_id = 0
end
def next_id
@next_id += 1
end
end
next_id
и инстанционната променлива?attributes.each do |attribute|
class_eval { attr_accessor attribute }
end
target.class_eval
сетва self
-а на блока на target
target
е self
self
-а на блока на self
class_eval { class_eval { class_eval { attr_accessor attribute } } }
attributes.each do |attribute|
attr_accessor attribute
end
self.class.instance_variable_get(:@repository)
# vs
self.class.data_model
instance_variable_get
е лош подход
class DataModel
def self.attributes(*attributes)
return @attributes if attributes.empty?
@attributes = attributes
# ...
end
end
class User < DataModel
attributes :name
end
class Picture < DataModel
attributes :date
end
User.attributes #=> [:name]
Picture.attributes #=> [:date]
DataModel.attributes #=> nil
attributes
в тялото на User
, self
-ът ви е User
, НЕ DataModel
class Person
@@count = 0
def initialize
@@count += 1
end
def self.how_many
@@count
end
end
Person.new
Person.new
Person.how_many # => 2
@@
NameError
(направете разлика с инстанционните променливи)
class B
@@foo = 1
def self.foo() @@foo end
def self.hmm() @@bar end
end
class D < B
@@bar = 2
def self.bar() @@bar end
def self.change() @@foo = 3; @@bar = 4; end
end
[B.foo, D.foo, D.bar] # => [1, 1, 2]
B.hmm # => error: NameError
D.change
[B.foo, D.foo, D.bar] # => [3, 3, 4]
B.hmm # => error: NameError
D.hmm # => error: NameError
def initialize(attributes)
attributes.each do |attribute, value|
send("#{attribute}=", value)
end
end
def to_hash
attributes.map do |attribute|
[attribute, send(attribute)]
end.to_h
end
def create
self.class.data_store.create(to_hash)
end
def initialize(attributes)
@attributes = attributes
end
def create
self.class.data_store.create(@attributes)
end
attr_accessor(attribute)
# vs
define_method("#{attribute}=") { |value| @attributes[attribute] = value }
define_method(attribute) { @attributes[attribute] }
Продължаваме с регулярните изрази...
[
и ]
[a-z]
или [0-9A-F]
\w
, \s
и т.н.^
съвпада с началото на ред (Ruby е в multiline режим по подразбиране)
$
съвпада с края на ред
\A
съвпада с началото на текстов низ
\z
съвпада с края на низs*
означава нула или повече повторения на s
s+
търси едно или повече повторения на s
s?
съвпада с нула или едно повторение на s
s{m,n}
означава между m и n повторения на s
?
след повторителя(
и )
day
или dance
: /\bda(y|nce)\b/
/".*"/.match '"Quoted"' # => #<MatchData "\"Quoted\"">
Частта от шаблона .*
хваща Quoted"
, тъй като е алчна. Това води до невъзможност да се намери съвпадение и алгоритъмът backtrack-ва -- връща се една стъпка/символ назад.
(?>pattern)
/"(?>.*)"/.match('"Quote"') # => nil
\g<name>
, където name
е номер или име на група в шаблона
/(\w+), \1/.match 'testing, twice' # => nil
/(\w+), \g<1>/.match 'testing, twice' # => #<MatchData "testing, twice" 1:"twice">
Да валидирате изрази от следния тип за правилно отворени/затворени скоби:
(car (car (car ...)))
- Например:
(car (car (car (car list))))
- Целта е израз, чийто резултат да може да се ползва в условен оператор (
true
/false
-еквивалент)- Можете да ползвате произволни методи от класа
Regexp
- И регулярен израз, разбира се
validator = /^(\(car (\g<1>*|\w*)\))$/
valid = '(car (car (car (car list))))'
invalid = '(car (car (car list))'
validator.match(valid) ? true : false # => true
validator.match(invalid) ? true : false # => false
Проверете дали нещо е валиден математически израз
- Произволно цяло число
1337
- Променлива (малка латинска буква)
x
- Знак пред валиден израз (+, -)
-33 + 22 * -y
- Операция между валидни изрази (+, -, *, /)
x + y - 21 / 3
- Скоби, ограждащи валидни изрази
-x * (y + -5 * (7 - 13)) / 44 - 9000
validator = /^([-+]?(\d+|[a-z]|\(\g<1>\)|\g<1> [-+*\/] \g<1>))$/
valid = '-(3 + (x * (7 / y))) * (44 * y - z / 22)'
invalid = '((33 - 7) * x'
validator.match(valid) ? true : false
validator.match(invalid) ? true : false
/^([-+]?(\d+|[a-z]|\(\g<1>\)|\g<1> [-+*\/] \g<1>))$/# ~> -:1: never ending recursion: /^([-+]?(\d+|[a-z]|\(\g<1>\)|\g<1> [-+*\/] \g<1>))$/
validator = /^([-+]?(\d+|[a-z]|\(\g<1>\))( [-+*\/] \g<1>)?)$/
valid = '-(3 + (x * (7 / y))) * (44 * y - z / 22)'
invalid = '((33 - 7) * x'
validator.match(valid) ? true : false # => true
validator.match(invalid) ? true : false # => false
/(?=pattern)/
/(?!pattern)/
/(?<=pattern)/
/(?<!pattern)/
/(?<=<b>)\w+(?=<\/b>)/.match("Fortune favours the <b>bold</b>") # => #<MatchData "bold">
Сменете * на % ако тя не е екранирана (escape-ната)
foo*
=>foo%
foo\*
=>foo\*
foo\\*
=>foo\\%
*\\**
=>%\\*%
"*\\**".gsub(/((?<!\\)(?:\\\\)*)\*/, '\1%') # => "%\\*%"
\
, които нямат пред себе си \
*
и готово :)"*\\**".gsub(/\G([^*\\]*(?:\\.[^*\\]*)*)\*/, '\1%') # => "%\\*%"