2015-01-03 4 views
1

У меня есть куча переменных окружения с моей конфигурации в них:Есть ли лучший способ добавить, если не ноль в Ruby?

DB_HOSTNAME=something.rds.amazonaws.com 
DB_PORTNUM=9999 
DB_USERNAME=production 
DB_PASSWORD=xyzzy 

Чтобы сделать DB строку подключения из них это что-то вроде:

"postgres://" + 
"#{ENV['DB_USERNAME']}#{ENV['DB_PASSWORD'] ? ":#{ENV['DB_PASSWORD']}" : nil}" + 
"@#{ENV['DB_HOSTNAME']}#{ENV['DB_PORTNUM'] ? ":#{ENV['DB_PORTNUM']}" : nil}" + 
"/proper_scraper_#{$environment}" 

Таким образом, он работает в разработке/тест, где DB_PASSWORD и DB_PORTNUM не установлены и работают в производстве, где они есть. Но это немного некрасиво:

ENV['DB_PASSWORD'] ? ":#{ENV['DB_PASSWORD']}" : nil 

Нужные семантика: перед именем, если не ноль и возвращать ноль в противном случае. В идеале было бы что-то вроде этого:

ENV['DB_PASSWORD'].try(:prepend, ':') 

Использование Object.try что-то вроде этого:

def try method, *args 
    send(method, *args) if respond_to? method 
    end 

Но это не работает, потому что перед именем мутирует строку (почему?) И Env строки заморожены , Альтернативный вариант:

ENV['DB_PASSWORD'].dup.try(:prepend, ':') 

Но это не сработает, если переменная окружения не установлена, поскольку вы не можете использовать нуль.

Есть ли здесь хороший однострочный лайнер или я застрял в беспорядке?

ответ

4

Использование объектов и стандартной библиотеки:

require 'uri' 

u = URI::Generic.build(
    scheme: "postgres", 
    host: ENV["DB_HOSTNAME"], 
    port: ENV["DB_PORTNUM"], 
    path: "/proper_scraper_#{$environment}", 
) 

u.user = ENV["DB_USERNAME"] 
u.password = ENV["DB_PASSWORD"] 

puts u.to_s 
+0

Это правда и может быть лучшим решением. Но он чувствует себя излишним, чтобы вытащить совершенно новый 'require' только для некоторой простой конкатенации строк. –

+1

Если я не ошибаюсь, «Object # try» исходит из Rails, это, возможно, более тяжелое, требующее решения решение. –

+0

На самом деле я обезьяна-исправил его в себе, используя реализацию, которую я показал в вопросе. Но вы правы, и я думаю, что ваше решение, вероятно, является самым ярким проявлением, которое я видел до сих пор. –

2

Очень жаль, что оба String#insert и String#prepend изменить строки, но String#sub должны работать:

ENV['DB_PASSWORD'].try(:sub,'',':') 

Или с немного больше намерения:

ENV['DB_PASSWORD'].try(:sub,/^/,':') 
+0

Право! Было бы идеально, если бы 'prepend' был' preend! ', А также был симметричный 'preend'. 'sub' - твердый ответ, я не думал об этом и имеет правильную семантику, но это не так, как хотелось бы, как хотелось бы, - немного читает. –

1

If Object#try происходит для поддержки блоков (например, ActiveSupport-х),

ENV['DB_PASSWORD'].try { |s| ":#{s}" } 
+0

У меня есть обезьяна-исправлена, как я показал в вопросе, поэтому моя реализация в настоящее время не поддерживает блоки. Но, видимо, это мое предпочтение. Возможно, я украду реализацию ActiveSupport. –

0

Ugh. Ваша читаемость действительно пострадала, потому что вы пытаетесь сделать все это в одной строке. Не делай этого.

Я хотел бы сделать что-то вроде:

Настройка окр для примера ...

ENV['DB_HOSTNAME'] = 'something.rds.amazonaws.com' 
ENV['DB_PORTNUM'] = '9999' 
ENV['DB_USERNAME'] = 'production' 
ENV['DB_PASSWORD'] = 'xyzzy' 

Реальный код начинается здесь:

$environment = 'production' 
db_hostname, db_portnum, db_username, db_password = %w[ 
    DB_HOSTNAME 
    DB_PORTNUM 
    DB_USERNAME 
    DB_PASSWORD 
].map{ |e| ENV[e] } 

db_password = ':' + db_password if db_password 
db_portnum = ':' + db_portnum if db_portnum 

DSN = "postgres://%s%[email protected]%s%s%s" % [ 
    db_username, 
    db_password, 
    db_hostname, 
    db_portnum, 
    "/proper_scraper_#{$environment}" 
] 
DSN # => "postgres://production:[email protected]:9999/proper_scraper_production" 

Тройные утверждения являются заменой if/then/else выписки, а не простые if/then. Попытка сделать их пригодными только приводит к запутыванию кода, поэтому не ходите туда.

Люди влюблены в код cramming в одной строке, и, давным-давно, когда мы использовали интерпретированный BASIC, это помогло ускорить работу кода, но сегодняшние языки редко извлекают из этого выгоду. Вместо этого происходит то, что он делает код незаменимым. Написание кода, который является неразборчивым, - это быстрый путь к тому, чтобы быть вызванным, чтобы объяснить себя в обзоре кода, после чего ему сказали переписать его и никогда больше не делать этого.

+0

Я согласен в целом, но не согласен в этом случае. Семантика здесь не сложна, поэтому кода не должно быть. Для вашей реализации мне потребуется две минуты, чтобы понять, потому что там так много всего происходит. Вероятно, потому, что я плотный, но мы все время от времени. Должно быть возможно показать намерение этого кода намного быстрее, чем две минуты. Это просто не сложно. Ответ Амита является лучшим для этого, я думаю - хотя я все еще думаю, что Ruby должен обеспечить более удобный способ сделать это как строку concat. –

Смежные вопросы