模块化、基于组件的 Sinatra 应用程序的架构

问题描述:

我正在开发一个 Sinatra 应用,其中包含大约 10 个不同的功能组件.我们希望能够将这些组件混合和匹配到应用程序的单独实例中,完全由一个 config.yaml 文件配置,如下所示:

I'm working on a Sinatra app that contains about 10 different components of functionality. We'd like to be able to mix and match these components into separate instances of the application, configured entirely from a config.yaml file that looks something like:

components:

- route: '/chunky'
  component_type: FoodLister
  component_settings: 
    food_type: bacon
    max_items: 400

- route: 'places/paris'
  component_type: Mapper
  component_settings: 
    latitude: 48.85387273165654
    longitude: 2.340087890625  

- route: 'places/losangeles'
  component_type: Mapper
  component_settings:
    latitude: 34.043556504127466
    longitude: -118.23486328125

如您所见,组件可以被多次实例化,每个组件都有自己的上下文设置.

As you can see, components can be instantiated more than once, each with their own contextual settings.

每个组件至少包含一个路由,带有用于基础的配置文件中的路由"属性.

Each component consists of at least one route, with the "route" property from the config file used for the base.

组织和实例化模块代码的最佳方式是什么?

What is the best way to organize and instantiate the module code?

这类似于 include 的提议,但它不需要访问 rackup 文件.

This is similar to include's proposal, but it doesn't require access to the rackup file.

编写您的各种处理程序,例如:

Write your various Handlers like:

class FoodHandler < Sinatra::Base
  get '/chunky/:food' do
    "Chunky #{params[:food]}!"
  end
end

然后在您的主应用程序文件中:

Then in your main application file:

require './lib/handlers/food_handler.rb'

class Main < Sinatra::Base
  enable :sessions
  ... bla bla bla
  use FoodHandler
end

我已经使用这种结构构建了一些相当复杂的 Sinatra 应用程序.它的伸缩性和 Rails 一样好.

I've used this kind of structure to build some fairly complex Sinatra apps. It scales just as well as Rails.

编辑

要让您的配置文件定义路由,您可以执行以下操作:

To have your config file define the routes, you could do something like this:

class PlacesHandler < Sinatra::Base
  # Given your example, this would define 'places/paris' and 'places/losangeles'
  CONFIG['components'].select { |c| c['compontent_type'] == 'Mapper' }.each do |c|
    get c['route'] do
      @latitude = c['component_settings']['latitude']
      @longitude = c['component_settings']['longitude']
    end
  end
end