0

我有一个在 Ubuntu/Apache2/Passenger 上运行的 Sinatra 应用程序。

这是一个简单的 URL 缩短器,可在我的登台服务器上使用,但在我导入旧数据库(包含缩短的 URL)时开始抛出以下错误:

undefined method `include?' for nil:NilClass
file: resource.rb location: block in attributes= line: 332

完整的回溯在这里:

/usr/lib/ruby/gems/1.9.1/gems/dm-core-1.2.0/lib/dm-core/resource.rb in block in attributes=
            if model.allowed_writer_methods.include?(setter = "#{name}=")
/usr/lib/ruby/gems/1.9.1/gems/dm-core-1.2.0/lib/dm-core/resource.rb in each
      attributes.each do |name, value|
/usr/lib/ruby/gems/1.9.1/gems/dm-core-1.2.0/lib/dm-core/resource.rb in attributes=
      attributes.each do |name, value|
/websites/sinatra/shortener/application.rb in block in <top (required)>
        ct.attributes   =   { 
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in call
            proc { |a,p| unbound_method.bind(a).call } ]
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in block in compile!
            proc { |a,p| unbound_method.bind(a).call } ]
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in []
            route_eval { block[*args] }
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in block (3 levels) in route!
            route_eval { block[*args] }
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in route_eval
      throw :halt, yield
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in block (2 levels) in route!
            route_eval { block[*args] }
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in block in process_route
        block ? block[self, values] : yield(self, values)
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in catch
      catch(:pass) do
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in process_route
      catch(:pass) do
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in block in route!
          pass_block = process_route(pattern, keys, conditions) do |*args|
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in each
        routes.each do |pattern, keys, conditions, block|
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in route!
        routes.each do |pattern, keys, conditions, block|
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in dispatch!
      route!
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in block in call!
      invoke { dispatch! }
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in block in invoke
      res = catch(:halt) { yield }
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in catch
      res = catch(:halt) { yield }
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in invoke
      res = catch(:halt) { yield }
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in call!
      invoke { dispatch! }
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in call
      dup.call!(env)
/usr/lib/ruby/gems/1.9.1/gems/rack-protection-1.2.0/lib/rack/protection/xss_header.rb in call
        status, headers, body = @app.call(env)
/usr/lib/ruby/gems/1.9.1/gems/rack-protection-1.2.0/lib/rack/protection/path_traversal.rb in call
        app.call env
/usr/lib/ruby/gems/1.9.1/gems/rack-protection-1.2.0/lib/rack/protection/json_csrf.rb in call
        status, headers, body = app.call(env)
/usr/lib/ruby/gems/1.9.1/gems/rack-protection-1.2.0/lib/rack/protection/base.rb in call
        result or app.call(env)
/usr/lib/ruby/gems/1.9.1/gems/rack-protection-1.2.0/lib/rack/protection/xss_header.rb in call
        status, headers, body = @app.call(env)
/usr/lib/ruby/gems/1.9.1/gems/rack-1.4.1/lib/rack/logger.rb in call
      @app.call(env)
/usr/lib/ruby/gems/1.9.1/gems/rack-1.4.1/lib/rack/commonlogger.rb in call
      status, header, body = @app.call(env)
/usr/lib/ruby/gems/1.9.1/gems/rack-1.4.1/lib/rack/head.rb in call
    status, headers, body = @app.call(env)
/usr/lib/ruby/gems/1.9.1/gems/rack-1.4.1/lib/rack/methodoverride.rb in call
      @app.call(env)
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/showexceptions.rb in call
      @app.call(env)
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in block in call
        synchronize { prototype.call(env) }
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in synchronize
          yield
/usr/lib/ruby/gems/1.9.1/gems/sinatra-1.3.2/lib/sinatra/base.rb in call
        synchronize { prototype.call(env) }
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/rack/request_handler.rb in process_request
            status, headers, body = @app.call(env)
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/abstract_request_handler.rb in accept_and_process_next_request
                    process_request(headers, input_stream, connection, full_http_response)
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/abstract_request_handler.rb in main_loop
                if !accept_and_process_next_request(socket_wrapper, channel, buffer)
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/rack/application_spawner.rb in start_request_handler
            handler.main_loop
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/rack/application_spawner.rb in block in handle_spawn_application
                self.class.send(:start_request_handler, MessageChannel.new(b),
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/utils.rb in safe_fork
                        yield
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/rack/application_spawner.rb in handle_spawn_application
        safe_fork('application', true) do
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/abstract_server.rb in server_main_loop
                            __send__(@message_handlers[name], client, *args)
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/abstract_server.rb in start_synchronously
                server_main_loop(password, server_socket)
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/abstract_server.rb in start
                start_synchronously(@socket_filename, @password, server_socket, b)
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/rack/application_spawner.rb in start
        super
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/spawn_manager.rb in block (2 levels) in spawn_rack_application
                    spawner.start
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/abstract_server_collection.rb in lookup_or_add
            server = yield
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/spawn_manager.rb in block in spawn_rack_application
                spawner = @spawners.lookup_or_add(key) do
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/abstract_server_collection.rb in block in synchronize
                yield
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/abstract_server_collection.rb in synchronize
        @lock.synchronize do
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/spawn_manager.rb in spawn_rack_application
            @spawners.synchronize do
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/spawn_manager.rb in spawn_application
            return spawn_rack_application(options)
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/spawn_manager.rb in handle_spawn_application
            app_process = spawn_application(options)
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/abstract_server.rb in server_main_loop
                            __send__(@message_handlers[name], client, *args)
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/lib/phusion_passenger/abstract_server.rb in start_synchronously
                server_main_loop(password, server_socket)
/usr/lib/ruby/gems/1.9.1/gems/passenger-3.0.12/helper-scripts/passenger-spawn-server in <main>
    spawn_manager.start_synchronously(socket_filename, socket_password, server_socket, owner_socket)

主要的 application.rb 文件如下:

require 'rubygems'
require 'bundler/setup'
require 'sinatra'
require File.join(File.dirname(__FILE__), 'environment')

configure do
    set :views, "#{File.dirname(__FILE__)}/views"
end

configure :development do
    DataMapper.auto_upgrade!

    # very useful for debugging parameters sent via the console
    before do
      puts '[Params]'
      p params
    end

end

error do
    e   =   request.env['sinatra.error']
    Kernel.puts e.backtrace.join("\n")
    'Application error'
end

helpers do  
    include Rack::Utils  
    alias_method :h, :escape_html  

    def random_string(length)  
        rand(36**length).to_s(36)  
    end 

    def get_site_url(short_url)
        SiteConfig.url_base + short_url
    end

    def generate_short_url(long_url)
      @shortcode = random_string 5

    su = ShortURL.first_or_create(
            { :url => long_url  }, 
            {
              :short_url  =>  @shortcode,
              :created_at =>  Time.now,
              :updated_at =>  Time.now
            })

    get_site_url(su.short_url)
    end

end


# root page
get '/' do

    if params[:url] and not params[:url].empty?

        generate_short_url(params[:url])

    else
        # you can use this page to redirect to another location
        # or to display a front-end form for any site visitors

        # get the current count of all links stored
    # @urls = ShortURL.all;

    # erb :index

    end

end

post '/' do

  if params[:url] and not params[:url].empty?

    generate_short_url(params[:url])

  end
    # you can use this page to redirect to another location
    # or to display a front-end form for any site visitors

    # get the current count of all links stored
    # @urls = ShortURL.all;

    # erb :index

end


# display short url from root
["/get/:short_url", "/:short_url"].each do |path|
get path do
    @URLData = ShortURL.get(params[:short_url])

    if @URLData

        # log the click in the database     
        ct = ClickTrack.new
        ct.attributes   =   { 
                :short_url  =>  params[:short_url],
                :url        =>  @URLData.url,   
                :clicked_at =>  Time.now
            }
        ct.save

        redirect @URLData.url
    else
        'no short url found'
    end

end
end

# expand url data
get '/expand/:hash/?' do
    @URLData = ShortURL.get(params[:hash])  

    if @URLData

        content_type :json
        { :url => get_site_url(@URLData.short_url), :long_url => @URLData.url, :hash => params[:hash] }.to_json

    else

        content_type :json
        { :message => 'No hash parameter was specified or no short URL was found to match the provided hash' }.to_json

    end
end

返回缩短的 URL 时发生错误(# display short url from root)

如果有人能帮我弄清楚它失败的原因,我将不胜感激。

非常感谢

4

1 回答 1

9

诀窍是读取堆栈跟踪。我猜 ClickTrack 是一个 DataMapper 对象,而您正在调用attributes=它。堆栈跟踪非常友好,可以从 dm-core 中提供一些代码: if model.allowed_writer_methods.include?(setter = "#{name}=")

显然,从数据映射器内部,model.allowed_writer_methods返回 nil。我根本没有使用过数据映射器,但是通过谷歌搜索发现了一种可能性:

https://github.com/datamapper/dm-core/issues/152

根据http://datamapper.org/getting-started.html 您应该DataMapper.finalize在使用模型之前调用。

无论如何,这看起来像是一个 DataMapper 问题。查看 ClickTrack 并确保它已完成(如果我没看错的话)和/或检查您的 DataMapper 使用情况。

于 2012-05-31T13:38:57.337 回答