0

我正在 Sinatra 中构建 API,并已转换为模块化样式。但是,我遇到了无法识别路由文件中的方法调用的问题。

我已经简化了应用程序,因此帖子更短,但基本问题是,如果我GET /testWorkoutHandler- 它无法识别其中的方法,WardenStrategies或者LoginHelper除非我还将这些文件包含在处理程序中(它们已经包含在 app.rb 中)。但是,一旦我这样做,他们使用的 gem 中声明的方法就无法识别。所有这些都在 app.rb 中注册,并且在我的 Rackup 文件中是必需的。

这是我的 app.rb 文件

require 'sinatra/base'
require 'sinatra/activerecord'

class WorkoutApp < Sinatra::Base

  register Sinatra::ActiveRecordExtension
  register WardenStrategies

  use WorkoutHandler

  helpers LoginHelper
  helpers HashHelpers

  use Rack::Session::Cookie
  use Warden::Manager do |manager|
    manager.default_strategies :password
    manager.intercept_401 = false
    manager.failure_app = WorkoutApp
    manager.serialize_into_session(&:id)
    manager.serialize_from_session { |id| User.find(id) }
  end
  set :database_file, '../config/database.yml'

  Warden::Manager.before_failure do |env, _opts|
    env['REQUEST_METHOD'] = 'POST'
  end
end

以下是 config.ru、Handler 和 Helper 文件。(处理程序只是我用于控制器/路由文件的名称。

module LoginHelper
  def warden_handler
    env['warden']
  end

  def current_user
    warden_handler.user
  end

  def check_authentication
    return if warden_handler.authenticated?
    body 'User not authenticated'
    halt 401
  end
end

我假设helpers,useregister只需要在 app.rb 中声明(因为我需要我的 rackup 文件中的所有文件并从那里运行应用程序)。但是,check_authentication除非我在这里也注册了帮助程序,否则无法识别该方法。

class WorkoutHandler < Sinatra::Base
  helpers LoginHelper                   # Is there a way to not need this?
  register WardenStrategies             # Or this?

  get '/test' do
    check_authentication
  end
end

module WardenStrategies
  Warden::Strategies.add(:password) do
    def valid?
      params['email'] || params['password']
    end

    def authenticate!
      user = User.find_by(email: params['email'])
      if user && user.authenticate(params['password'])
        success!(user)
      else
        fail!('Could not log in')
      end
    end
  end
end

require 'rubygems'
require 'bundler'

Bundler.require     #Shouldn't this give my Handler access to gem methods?

ENV['APP_NAME'] = 'workout'

require_all 'app'

run WorkoutApp

截至撰写本文时,我在运行规范时收到以下错误:

NoMethodError: undefined method `authenticated?' for nil:NilClass

这意味着 env 没有 :warden 键/值(我在控制台中确认)。该authenticated?方法在 Warden gem 目录中,所以我想我可以require 'warden'在每个 Handler 文件中 - 我不认为这是模块化 Sinatra 应用程序应该构建的方式。

我已经阅读了关于模块化 Sinatra 应用程序的所有博客文章和书籍章节,但我似乎无法调试我的问题。据我了解,需要在所有使用它们的文件中重新注册(包含)助手是无关紧要的。我认为通过扩展 Sinatra::Base,我可以访问 app.rb 中所有声明的类。

任何帮助,将不胜感激。谢谢!

4

1 回答 1

1

对您的问题的简短回答是,每次您将 Sinatra::Base 子类化时,它都会创建一个完全独立的 Sinatra 应用程序。仅仅因为多个类是 Sinatra::Base 的子类并不意味着它们都继承了彼此的属性。这就是为什么它被称为“模块化”风格!

如果您有一些要在不同 Sinatra 应用程序之间共享的通用功能,您可以创建一个 mixin 并在每个应用程序中扩展/包含它(这基本上就是您现在使用helpersand所做的事情register),或者您可以创建一个中间类您的应用程序子类。例如:

my_base_app.rb:

require 'sinatra/base'
require 'warden'

require_relative 'login_helper'

class MyBaseApp < Sinatra::Base
  register WardenStrategies

  helpers LoginHelper
end

锻炼应用程序.rb:

require 'sinatra/activerecord'
require 'warden'

require_relative 'my_base_app'
require_relative 'hash_helpers'
require_relative 'workout_handler'

class WorkoutApp < MyBaseApp
  register Sinatra::ActiveRecordExtension

  use WorkoutHandler

  helpers HashHelpers

  use Rack::Session::Cookie
  use Warden::Manager do |manager|
    # yadda yadda...
  end
end

锻炼处理程序.rb:

require_relative 'my_base_app'

class WorkoutHandler < MyBaseApp
  get '/test' do
    check_authentication
  end
end

至于您的问题“这不应该让我的 Handler 访问 gem 方法吗?” 这取决于您的 Gemfile 的布局方式。如果你有这样的事情:

gem 'sinatra', :require => 'sinatra/base'
gem 'sinatra-activerecord', :require => 'sinatra/activerecord'
gem 'warden'

那么是的,Bundle.require在您的机架脚本中执行将消除require在您的类文件中执行单个语句的需要。

于 2015-11-23T05:39:55.970 回答