2

tl; drカスタマイズを介して、単一のSinatraアプリを異なるサーバーで非常に異なる方法で起動させるにはどうすればよいconfig.ruですか?

バックグラウンド

異なるサーバーで実行されるSinatraを使用して作成された単一のWebアプリケーションがあります。現在、これらのサーバーのコードベースは、サーバーの(個別の)部分の動作方法に重要な違いがあるため、フォークされています。例えば:

  • 1つのサーバーはイントラネットLDAPサーバーを介してユーザーを認証し、別のサーバーはより単純なローカルデータベーステーブルルックアップを使用します。
  • 1つのサーバーは外部のcronジョブを使用して統計を定期的に更新し、別の(Windowsベースの)サーバーは内部のスリープ状態のスレッドを使用します。
  • 1つのサーバーは特定のメタデータをローカルテーブルに格納し、別のサーバーは画面スクレイピング(!)を介して外部Wikiからメタデータを取得します。

…等々。

これらのコードベースを完全に共有したい(単一のGitリポジトリ)。各サーバーにはわずかに異なる構成ファイルが1つあり、アプリの起動が異なると思います。

放棄されたソリューション

環境変数に基づいてアプリの動作を変更できます。動作には少なからずバリエーションがあるので、環境変数の設定を非表示にしたくありません。

各マシンに固有の独自の「server-settings.rb」ファイルを作成し、それ自分のマシンで要求してから、app.rbそこで構成を変更することができます。しかし、これはおそらく車輪の再発明であるように思われます。サーバーごとに名前が付けられたファイルがすでにあります。config.ru私はこれを使うべきではありませんか?

現在のコード

config.ruのアプリは現在、単純に次のとおりです。

require ::File.join( ::File.dirname(__FILE__), 'app' )
run MyApp.new

そして、app.rbそれが必要とするのは、本質的に、次のとおりです。

require 'sinatra'
require_relative 'helpers/login' # customized for LDAP lookup on this server

class MyApp < Sinatra::Application
  use Rack::Session::Cookie, key:'foo.bar', path:'/', secret:'ohnoes'
  set :protection, except: [:path_traversal, :session_hijacking]
  configure :production do
    # run various code that depends on server settings, e.g.
    Snapshotter.start # there is no cron on this machine, so we do it ourselves
  end
  configure :development do
    # run various code that depends on server settings
  end
end

質問

その名に恥じないようにしたいのですが、config.ru次のようになります。

require ::File.join( ::File.dirname(__FILE__), 'app' )
run MyApp.new( auth: :ldap, snapshot:false, metadata: :remote_wiki, … )

経由で提供された設定に基づいて構成動作を変更するようにアプリケーションを変更するにはどうすればよいconfig.ruですか?config.ruそれとも、これは完全に間違った目的で使用しようとする虐待ですか?

4

1 に答える 1

2

質問を読み始めるとすぐに、頭に浮かぶ最初の答えは「環境変数」でしたが、あなたはすぐにそれをスコッチしました:)

私が物事を構造化する方法であるため、私はあなたの可能性の1つと望ましい結果コードの混合物を使用します…</ p>

アプリケーションをより簡単にテストできるようにしたいので、Rubyのほとんどをconfig.ruから別のconfig.rbファイルに取り出し、config.ruをブートストラップファイルのままにします。したがって、私の標準的なスケルは次のとおりです。

config.ru

# encoding: UTF-8

require 'rubygems'
require 'bundler'
Bundler.setup

root = File.expand_path File.dirname(__FILE__)
require File.join( root , "./app/config.rb" )

# everything was moved into a separate module/file to make it easier to set up tests

map "/" do
  run APP_NAME.app
end

app / config.rb

# encoding: utf-8
require_relative File.expand_path(File.join File.dirname(__FILE__), "../lib/ext/warn.rb")

require_relative "./init.rb"  # config
require_relative "./main.rb"  # routes and helpers
require 'encrypted_cookie'

# standard cookie settings
COOKIE_SETTINGS = {
  :key => 'usr',
  :path => "/",
  :expire_after => 86400, # In seconds, 1 day
  :secret => ENV["LLAVE"],
  :httponly => true
}

module APP_NAME # overall name of the app

  require 'rack/ssl' # force SSL
  require 'rack/csrf'

  if ENV["RACK_ENV"] == "development"
    require 'pry'
    require 'pry-nav'
  end

  # from http://devcenter.heroku.com/articles/ruby#logging
  $stdout.sync = true

  ONE_MONTH = 60 * 60 * 24 * 30

  def self.app
    Rack::Builder.app do

      cookie_settings = COOKIE_SETTINGS
      # more security if in production
      cookie_settings.merge!( :secure => true ) if ENV["RACK_ENV"] == "production"

      # AES encryption of cookies
      use Rack::Session::EncryptedCookie, cookie_settings

      if ENV["RACK_ENV"] == "production"
        use Rack::SSL, :hsts => {:expires => ONE_MONTH}
      end

      # to stop XSS
      use Rack::Csrf, :raise => true unless ENV["RACK_ENV"] == "test"

      run App # the main Sinatra app
    end
  end # self.app

end # APP_NAME

私がこれを行った最初の理由は、仕様でアプリを簡単に実行できるようにすることでした。

shared_context "All routes" do
  include Rack::Test::Methods
  let(:app){ APP_NAME.app }
end

しかし、このコードを残りのアプリケーションコードと一緒に保持することは理にかなっています。いわば、物事をまとめたり、他のアプリを実行したりできるためです。これを使用して、いくつかの仕様にさまざまな例を条件付きでロードしました。プロジェクト(重複する作業を削減し、例が実際に機能することを確認するのに役立ちます)、したがって、条件付きで構成をロードするためにそれを使用できなかった理由がわかりません。

このようにして、使用するconfig.rbファイルに関してconfig.ruで条件を使用するか、使用する定義についてconfig.rbでenv varをself.app使用するか、オプションハッシュを渡すかを選択できます。 self.appへ…</p>

セットアップで、APP_NAMEモジュールの名前をに変更しMyApp、Sinatraクラスの名前をに変更しますApp(フロントエンドとAPIを実行するWebサイトがあることが多いため、Sinatraクラスはその関数(App、APIなど)によって名前が付けられます。 )そしてサイトにちなんで名付けられたモジュールにラップされて)そして最終的に:

config.ru

map "/" do
  run MyApp.app( auth: :ldap, snapshot:false, metadata: :remote_wiki )
end

config.rb

def self.app( opts={} )
  opts = DEFAULT_OPTIONS.merge opts
  # …
  run App
end

他の人がこれにどのように取り組んでいるかを見るのは興味深いでしょう。

于 2013-02-25T09:25:07.583 に答える