В итоге я решил это так. Я использую общий сервер, где я размещаю это, и я не смог найти имя файла для сертификата или путь для его получения. В итоге я просто открыл магазин и нашел его таким. Не очень эффективно, но будет работать, пока я не перенесу его на выделенный сервер и не получу больше контроля.
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2 cert = null;
foreach (X509Certificate2 certificate in store.Certificates)
{
if (!string.IsNullOrWhiteSpace(certificate?.SubjectName?.Name) && certificate.SubjectName.Name.StartsWith("CN=*.mysite.com"))
{
cert = certificate;
break;
}
}
Вам не нужна фабрика LogFileReaderFactory; просто научите свой класс LogFileReader, как инстанцировать свои подклассы:
class LogFileReader
def self.create type
case type
when :git
GitLogFileReader.new
when :bzr
BzrLogFileReader.new
else
raise "Bad log file type: #{type}"
end
end
end
class GitLogFileReader < LogFileReader
def display
puts "I'm a git log file reader!"
end
end
class BzrLogFileReader < LogFileReader
def display
puts "A bzr log file reader..."
end
end
Как видите, суперкласс может действовать как собственная фабрика. Теперь, как насчет автоматической регистрации? Ну, почему бы нам просто не хранить хэш наших зарегистрированных подклассов и регистрировать каждый из них, когда мы их определяем:
class LogFileReader
@@subclasses = { }
def self.create type
c = @@subclasses[type]
if c
c.new
else
raise "Bad log file type: #{type}"
end
end
def self.register_reader name
@@subclasses[name] = self
end
end
class GitLogFileReader < LogFileReader
def display
puts "I'm a git log file reader!"
end
register_reader :git
end
class BzrLogFileReader < LogFileReader
def display
puts "A bzr log file reader..."
end
register_reader :bzr
end
LogFileReader.create(:git).display
LogFileReader.create(:bzr).display
class SvnLogFileReader < LogFileReader
def display
puts "Subersion reader, at your service."
end
register_reader :svn
end
LogFileReader.create(:svn).display
Вот и все. Просто разделите это на несколько файлов и требуйте их соответствующим образом.
Вам следует прочитать книгу Питера Норвига Design Patterns in Dynamic Languages, если вы интересуетесь подобными вещами. Он показывает, что многие паттерны проектирования на самом деле являются обходом ограничений или недостатков вашего языка программирования; и если у вас достаточно мощный и гибкий язык, вам не нужен паттерн проектирования, вы просто реализуете то, что хотите сделать. В качестве примеров он использует Dylan и Common Lisp, но многие из его положений актуальны и для Ruby.
Возможно, вы также захотите взглянуть на Why's Poignant Guide to Ruby, особенно на главы 5 и 6, но только если вы можете справиться с сюрреалистическим техническим письмом.
edit: Сейчас я отталкиваюсь от ответа Йорга; мне нравится сокращать количество повторений, и поэтому я не повторяю название системы контроля версий ни в классе, ни в регистрации. Добавив к моему второму примеру следующее, вы сможете писать гораздо более простые определения классов, оставаясь при этом довольно простыми и понятными.
def log_file_reader name, superclass=LogFileReader, &block
Class.new(superclass, &block).register_reader(name)
end
log_file_reader :git do
def display
puts "I'm a git log file reader!"
end
end
log_file_reader :bzr do
def display
puts "A bzr log file reader..."
end
end
Конечно, в производственном коде вы, возможно, захотите действительно давать имена этим классам, генерируя определение константы на основе переданного имени, для лучшего отображения сообщений об ошибках.
def log_file_reader name, superclass=LogFileReader, &block
c = Class.new(superclass, &block)
c.register_reader(name)
Object.const_set("#{name.to_s.capitalize}LogFileReader", c)
end
Это на самом деле просто подрывает решение Брайана Кэмпбелла. Если вам это нравится, , пожалуйста, upvote , его ответ : он сделал всю работу.
#!/usr/bin/env ruby
class Object; def eigenclass; class << self; self end end end
module LogFileReader
class LogFileReaderNotFoundError < NameError; end
class << self
def create type
(self[type] ||= const_get("#{type.to_s.capitalize}LogFileReader")).new
rescue NameError => e
raise LogFileReaderNotFoundError, "Bad log file type: #{type}" if e.class == NameError && e.message =~ /[^: ]LogFileReader/
raise
end
def []=(type, klass)
@readers ||= {type => klass}
def []=(type, klass)
@readers[type] = klass
end
klass
end
def [](type)
@readers ||= {}
def [](type)
@readers[type]
end
nil
end
def included klass
self[klass.name[/[[:upper:]][[:lower:]]*/].downcase.to_sym] = klass if klass.is_a? Class
end
end
end
def LogFileReader type
Здесь мы создаем глобальный метод (более похожий на процедуру, на самом деле), называемый LogFileReader
, то же имя, что и у нашего модуля LogFileReader
. Это законно в Ruby. Неоднозначность разрешается следующим образом: модуль всегда будет предпочтительным, кроме случаев, когда это, очевидно, вызов метода, т.е. вы либо ставите круглые скобки в конце ( Foo ()
), либо передаете аргумент ( Foo : bar
).
Это трюк, который используется в нескольких местах в stdlib, а также в Camping и других фреймворках. Потому что такие вещи, как включают
или , расширяют
aren ' Фактически, ключевые слова, но обычные методы, которые принимают обычные параметры, вам не нужно передавать им фактический модуль
в качестве аргумента, вы также можете передать все, что оценивает , в . ] Модуль
. На самом деле, это даже работает для наследования, совершенно законно написать класс Foo
С помощью этого трюка вы можете сделать так, как будто вы наследуете от универсальный класс, хотя в Ruby нет универсальных. Это используется, например, в библиотеке делегирования, где вы делаете что-то вроде класса MyFoo
SimpleDelegator
динамически создает и возвращает анонимный подкласс класса SimpleDelegator
, который делегирует все вызовы метода экземпляру класса Foo
.
Здесь мы используем похожую хитрость: мы собираемся динамически создать модуль
, который при смешивании в класс, автоматически зарегистрирует этот класс в реестре LogFileReader
.
LogFileReader.const_set type.to_s.capitalize, Module.new {
В этой строке происходит многое. Давайте начнем справа: Module.new
создает новый анонимный модуль. Блок, переданный ему, становится телом модуля - это в основном то же самое, что и использование ключевого слова module
.
Теперь перейдем к const_set
. Это метод установки константы. Таким образом, это то же самое, что сказать FOO =: bar
, за исключением того, что мы можем передать имя константы в качестве параметра, вместо того, чтобы знать это заранее. Так как мы вызываем метод в модуле LogFileReader
, константа будет определена внутри этого пространства имен, поэтому она будет называться LogFileReader :: Something
.
Итак, что имя константы? Ну, это аргумент типа типа
, переданный в метод с заглавной буквы. Итак, когда я передаю в : cvs
, полученная константа будет LogFileParser :: Cvs
.
И для чего мы устанавливаем константу? Нашему недавно созданному анонимному модулю, который теперь больше не является анонимным!
Все это на самом деле является просто длинным способом сказать module LogFileReader :: Cvs
, за исключением того, что мы не знали "Cvs" "заранее, и поэтому не мог написать это так.
eigenclass.send :define_method, :included do |klass|
Это тело нашего модуля. Здесь мы используем define_method
для динамического определения метода, называемого , включенного
. И мы фактически не определяем метод на самом модуле, а на собственном классе модуля (с помощью небольшого вспомогательного метода, который мы определили выше), что означает, что метод не станет методом экземпляра, но скорее «статический» метод (в терминах Java / .NET).
включенный
на самом деле является специальным методом ловушки, который вызывается средой выполнения Ruby каждый раз, когда модуль включается в класс, и класс получает передан в качестве аргумента. Итак, наш недавно созданный модуль теперь имеет метод ловушки, который будет информировать его всякий раз, когда он куда-нибудь включается.
LogFileReader[type] = klass
И вот что делает наш метод ловушки: он регистрирует класс, который передается методу hook, в реестр LogFileReader
. И ключ, под которым он его регистрирует, это аргумент type
из метода LogFileReader
, описанного выше, который благодаря магии замыканий фактически доступен внутри включенного Метод
.
end
include LogFileReader
И наконец, мы включаем модуль LogFileReader
в анонимный модуль. [Примечание: я забыл эту строку в исходном примере.]
}
end
class GitLogFileReader
def display
puts "I'm a git log file reader!"
end
end
class BzrFrobnicator
include LogFileReader
def display
puts "A bzr log file reader..."
end
end
LogFileReader.create(:git).display
LogFileReader.create(:bzr).display
class NameThatDoesntFitThePattern
include LogFileReader(:darcs)
def display
puts "Darcs reader, lazily evaluating your pure functions."
end
end
LogFileReader.create(:darcs).display
puts 'Here you can see, how the LogFileReader::Darcs module ended up in the inheritance chain:'
p LogFileReader.create(:darcs).class.ancestors
puts 'Here you can see, how all the lookups ended up getting cached in the registry:'
p LogFileReader.send :instance_variable_get, :@readers
puts 'And this is what happens, when you try instantiating a non-existent reader:'
LogFileReader.create(:gobbledigook)
Эта новая расширенная версия позволяет три различных способа определения LogFileReader
s:
<Имя > LogFileReader
будет автоматически найден и зарегистрирован как LogFileReader
для : имя
(см .: GitLogFileReader
), LogFileReader
и чье имя соответствует шаблону <Имя> Что бы
было зарегистрировано для : name
обработчик (см .: BzrFrobnicator
) и LogFileReader (: name)
, будут зарегистрированы для : name Обработчик
, независимо от их имени (см .: NameThatDoesntFitThePattern
). Обратите внимание, что это очень надуманная демонстрация. Это, например, определенно не поточно-ориентированный. Это также может привести к утечке памяти. Используйте с осторожностью!
Все, что будет зарегистрировано для обработчика: name
(см .: BzrFrobnicator
) и
LogFileReader (: name)
модуль, будет зарегистрирован для обработчика : name
, независимо от его имени (см .: NameThatDoesntFitThePattern
). Обратите внимание, что это всего лишь надуманная демонстрация. Это, например, определенно не поточно-ориентированный. Это также может привести к утечке памяти. Используйте с осторожностью!
Все, что будет зарегистрировано для обработчика: name
(см .: BzrFrobnicator
) и
LogFileReader (: name)
модуль, будет зарегистрирован для обработчика : name
, независимо от его имени (см .: NameThatDoesntFitThePattern
). Обратите внимание, что это всего лишь надуманная демонстрация. Это, например, определенно не поточно-ориентированный. Это также может привести к утечке памяти. Используйте с осторожностью!
Обратите внимание, что это просто очень надуманная демонстрация. Это, например, определенно не поточно-ориентированный. Это также может привести к утечке памяти. Используйте с осторожностью!
Обратите внимание, что это просто очень надуманная демонстрация. Это, например, определенно не поточно-ориентированный. Это также может привести к утечке памяти. Используйте с осторожностью!