Можно ли в моем коде установить переменные ENV для среды разработки рельсов?

83

Я знаю, что могу установить свои переменные ENV в bash через

export admin_password = "secret"

Но есть ли способ сделать это где-нибудь в моем исходном коде rails? Моя первая попытка была примерно такой вenvironment/development.rb

ENV['admin_password'] = "secret"

Но это не сработало. Есть ли способ сделать это?

Lan
источник
1
Обратите внимание, что команда bash должна быть export admin_password="secret", а не export admin_password = "secret".
Джейкоб Локкард

Ответы:

81

[Обновить]

Хотя решение в разделе «старый ответ» будет работать для общих проблем, этот раздел предназначен для ответа на ваш конкретный вопрос после разъяснения из вашего комментария.

Вы должны иметь возможность устанавливать переменные среды точно так, как вы указываете в своем вопросе. В качестве примера у меня есть приложение Heroku, которое использует базовую аутентификацию HTTP.

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery
  before_filter :authenticate

  def authenticate
    authenticate_or_request_with_http_basic do |username, password|
      username == ENV['HTTP_USER'] && password == ENV['HTTP_PASS']
    end
  end
end

# config/initializers/dev_environment.rb
unless Rails.env.production?
  ENV['HTTP_USER'] = 'testuser'
  ENV['HTTP_PASS'] = 'testpass'
end

Итак, в вашем случае вы бы использовали

unless Rails.env.production?
  ENV['admin_password'] = "secret"
end

Не забудьте перезапустить сервер, чтобы конфигурация была перезагружена!

[Старый ответ]

Для конфигурации всего приложения вы можете рассмотреть следующее решение:

Создайте файл config/application.ymlс хешем параметров, к которым вы хотите иметь доступ:

admin_password: something_secret
allow_registration: true
facebook:
  app_id: application_id_here
  app_secret: application_secret_here
  api_key: api_key_here

Теперь создайте файл config/initializers/app_config.rbи включите в него следующее:

require 'yaml'

yaml_data = YAML::load(ERB.new(IO.read(File.join(Rails.root, 'config', 'application.yml'))).result)
APP_CONFIG = HashWithIndifferentAccess.new(yaml_data)

Теперь вы можете получить доступ в любом месте вашего приложения APP_CONFIG[:admin_password]вместе со всеми своими данными. (Обратите внимание, что, поскольку инициализатор включает ERB.new, ваш файл YAML может содержать разметку ERB.)

Мишель Тилли
источник
2
Ну, я использую Heroku, и они позволяют вам устанавливать некоторые переменные ENV для таких вещей, как пароли, которые вы не хотите проверять в системе управления версиями. Я пытаюсь понять, что лучше всего делать на моей машине разработчика. И да, я пытаюсь использовать данные внутри своего приложения.
Лан
Рад слышать это! Если вы считаете, что ответ правильный, отметьте его, поставив галочку рядом с вопросом. Это принесет вам и вашему респонденту репутацию, а также поможет людям, которые зададут этот вопрос в будущем, найти правильный ответ.
Мишель Тилли
О да, я новый пользователь, поэтому мне приходится ждать 19 часов, прежде чем я смогу это проверить. Осталось еще 3 часа :) Но спасибо за вашу помощь! На самом деле, если бы вы отредактировали свой ответ, удалив dev_environment.rbкод и заменив его моим, я бы с радостью отметил ваш ответ, который является гораздо более подробным.
Lan
Мне это нравится, но по какой-то причине (rails 3 v 4?) не удалось загрузить app_config.rb. Я поместил код в config / environment.rb и получил тот же эффект. # Для загрузки приложения rails требуется File.expand_path ('../ application', FILE ) require 'yaml' yaml_data = YAML :: load (ERB.new (IO.read (File.join (Rails.root, 'config', 'local_env.yml'))). result) APP_CONFIG = HashWithIndifferentAccess.new (yaml_data) # Инициализируйте приложение rails Rails3 :: Application.initialize!
Бен
Извините, что открываю такую ​​старую ветку, но разве настройки, специфичные для среды разработки, не имеют большего смысла в config/environments/development.rbфайле?
jeffdill2
130

Никогда не кодируйте конфиденциальную информацию (учетные данные, пароли и т . Д.) . Вместо этого создайте файл для хранения этой информации в виде переменных среды (пары ключ / значение) и исключите этот файл из системы управления исходным кодом. Например, с точки зрения Git (система управления исходным кодом), исключите этот файл, добавив его в. gitignore :

-bash> echo '/config/app_environment_variables.rb' >> .gitignore 

/config/app_environment_variables.rb

ENV['HTTP_USER'] = 'devuser'
ENV['HTTP_PASS'] = 'devpass'

Также добавьте следующие строки /config/environment.rbмежду requireлинией и Application.initializeлинией:

# Load the app's custom environment variables here, so that they are loaded before environments/*.rb
app_environment_variables = File.join(Rails.root, 'config', 'app_environment_variables.rb')
load(app_environment_variables) if File.exists?(app_environment_variables)

Это оно!

Как говорится в комментарии выше, сделав это, вы загрузите свои переменные среды раньше environments/*.rb, что означает, что вы сможете ссылаться на свои переменные внутри этих файлов (например environments/production.rb). Это большое преимущество перед помещением файла переменных среды внутрь /config/initializers/.

Внутри app_environment_variables.rbнет необходимости различать окружающую среду, насколько это развитие или производство , потому что вы никогда не совершите этот файл в системе управления исходным кодом, следовательно, для развития контекста по умолчанию. Но если вам нужно установить что-то особенное для тестовой среды (или для тех случаев, когда вы тестируете производственный режим локально ), просто добавьте условный блок под всеми другими переменными:

if Rails.env.test?
  ENV['HTTP_USER'] = 'testuser'
  ENV['HTTP_PASS'] = 'testpass'
end

if Rails.env.production?
  ENV['HTTP_USER'] = 'produser'
  ENV['HTTP_PASS'] = 'prodpass'
end

При каждом обновлении app_environment_variables.rbперезапускайте сервер приложений. Предполагая, что вы используете Apache / Passenger или rails server:

-bash> touch tmp/restart.txt

В своем коде обращайтесь к переменным среды следующим образом:

def authenticate
  authenticate_or_request_with_http_basic do |username, password|
    username == ENV['HTTP_USER'] && password == ENV['HTTP_PASS']
  end
end

Обратите внимание, что внутри app_environment_variables.rbвы должны указать логические значения и числа в виде строк (например, ENV['SEND_MAIL'] = 'false'не только falseи ENV['TIMEOUT'] = '30'не только 30), иначе вы получите ошибки can't convert false into Stringи can't convert Fixnum into String, соответственно.

Хранение и обмен конфиденциальной информацией

Последний узел, который нужно связать: как поделиться этой конфиденциальной информацией с вашими клиентами и / или партнерами? В целях обеспечения непрерывности бизнеса (например, когда вас поразит падающая звезда, как ваши клиенты и / или партнеры возобновят полноценную работу сайта?) Вашим клиентам и / или партнерам необходимо знать все учетные данные, необходимые для вашего приложения. . Отправка таких сообщений по электронной почте или Skype небезопасна и приводит к беспорядку. Хранить его в общих Документах Google неплохо (если все используют https), но приложение, предназначенное для хранения и обмена небольшими кусочками, такими как пароли, было бы идеальным.

Как установить переменные среды на Heroku

Если у вас единая среда на Heroku:

-bash> heroku config:add HTTP_USER='herouser'
-bash> heroku config:add HTTP_USER='heropass'

Если у вас несколько сред на Heroku:

-bash> heroku config:add HTTP_USER='staguser' --remote staging
-bash> heroku config:add HTTP_PASS='stagpass' --remote staging

-bash> heroku config:add HTTP_USER='produser' --remote production
-bash> heroku config:add HTTP_PASS='prodpass' --remote production

Форман и .env

Многие разработчики используют Foreman (установленный вместе с Heroku Toolbelt ) для локального запуска своих приложений (в отличие от Apache / Passenger или rails server). Foreman и Heroku используют его Procfileдля объявления команд, выполняемых вашим приложением , поэтому переход от локального разработчика к Heroku в этом отношении не вызывает затруднений. Я использую Foreman и Heroku в каждом проекте Rails, так что это удобство велико. Но вот в чем дело ... Форман загружает переменные среды, хранящиеся в, /.envчерез dotenv, но, к сожалению, dotenv, по сути, только разбирает файл на key=valueпары; эти пары не становятся переменными сразу же, поэтому вы не можете ссылаться на уже установленные переменные (чтобы сохранить СУХИЕ вещи), а также не можете использовать там «Ruby» (как указано выше с условными операторами), что вы можете делать в /config/app_environment_variables.rb. Например, что касается сохранения СУХИХ вещей, я иногда делаю такие вещи:

ENV['SUPPORT_EMAIL']='Company Support <support@company.com>'
ENV['MAILER_DEFAULT_FROM'] = ENV['SUPPORT_EMAIL']
ENV['MAILER_DEFAULT_TO']   = ENV['SUPPORT_EMAIL']

Следовательно, я использую Foreman для локального запуска своих приложений, но не использую его .envфайл для загрузки переменных среды; скорее я использую Foreman в сочетании с /config/app_environment_variables.rbподходом, описанным выше.

user664833
источник
3
Мне нравится это решение, так как я хотел быть уверенным, что конфиденциальные настройки (ключи API и т. Д.) Надежно защищены в хранилище ключей, а не в виде открытого текста и не связаны с репозиторием Git. Благодарю.
Рори Штумпф
10

То, как я пытаюсь это сделать в своем вопросе, действительно работает!

# environment/development.rb

ENV['admin_password'] = "secret" 

Мне просто пришлось перезапустить сервер. Я думал, что работы reload!с консолью rails будет достаточно, но мне также пришлось перезапустить веб-сервер.

Я выбираю свой собственный ответ, потому что считаю, что это лучшее место для размещения и установки переменных ENV.

Lan
источник
Рад, что вы узнали, почему это не работает! Я лично люблю экономить config/application.rbи config/environments/*.rbдля настройки среды Rails и использования других методов для настройки среды моего приложения , но это, конечно, не означает, что это единственный способ (или даже «правильный» путь :)
Мишель Тилли,
Я был немного сбит с толку, потому что пытался по-разному расставить вещи между производством и разработкой. Но теперь я полностью с вами согласен! Спасибо, что не только ответили на мой первоначальный вопрос, но и помогли мне лучше понять структуру приложения rails!
Лан,
8

Помимо решений здесь есть более чистые альтернативы, если вы используете определенные серверы разработки.

С помощью Heroku's Foreman вы можете создавать переменные среды для каждого проекта в .envфайле:

ADMIN_PASSOWRD="secret"

С Pow вы можете использовать .powenvфайл:

export ADMIN_PASSOWRD="secret"
Aupajo
источник
2

Я думаю, что лучший способ - сохранить их в каком-либо файле yml, а затем загрузить этот файл с помощью этой команды в файле инициализатора

APP_CONFIG = YAML.load_file("#{Rails.root}/config/CONFIG.yml")[Rails.env].to_hash

вы можете легко получить доступ к переменным конфигурации, связанным с окружением.

Структура значения ключа файла Yml:

development:
  app_key: 'abc'
  app_secret: 'abc'

production:
  app_key: 'xyz'
  app_secret: 'ghq'
Таймор Чангайз
источник
1

Системное окружение и окружение рельсов - разные вещи. ENVдавайте работать со средой rails, но если вы хотите изменить среду системы во время выполнения, вы можете просто заключить команду в кавычки.

# ruby code
`export admin_password="secret"`
# more ruby code
Goncalossilva
источник
1
Просто заметка, которая exportбудет жаловаться на пробелы; попробуйexport admin_password="secret"
Мишель Тилли
Я забыл эту (очень важную) деталь. Подправил свой ответ, спасибо!
goncalossilva 06
Это не работает, поскольку обратные кавычки порождают подоболочку, а переменные, которые вы устанавливаете в подоболочке, не могут влиять на среду родителя.
AndrewF
Нет, это работает. Ответ, как ясно объяснено, не нацелен на окружающую среду родителя. Есть совершенно хорошие ответы, которые показывают, как это сделать.
goncalossilva
1

Скрипт для загрузки пользовательского .envфайла: Добавьте следующие строки /config/environment.rbмежду requireстрокой и Application.initializeстрокой:

# Load the app's custom environment variables here, so that they are loaded before environments/*.rb

app_environment_variables = File.join(Rails.root, 'config', 'local_environment.env')
if File.exists?(app_environment_variables)
  lines = File.readlines(app_environment_variables)
  lines.each do |line|
    line.chomp!
    next if line.empty? or line[0] == '#'
    parts = line.partition '='
    raise "Wrong line: #{line} in #{app_environment_variables}" if parts.last.empty?
    ENV[parts.first] = parts.last
  end
end

И config/local_environment.env(вы захотите .gitignore) будет выглядеть так:

# This is ignored comment
DATABASE_URL=mysql2://user:psw@0.0.0:3307/database
RACK_ENV=development

(На основе решения @ user664833)

Даниил Гармошка
источник