Reputation: 1268
I have a Sinatra application running on Ubuntu / Apache2 / Passenger.
It's a simple URL shortener that works on my staging server but started throwing the following error when I imported the old database (containing shortened URLS):
undefined method `include?' for nil:NilClass
file: resource.rb location: block in attributes= line: 332
Full backtrace is here:
/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)
The main application.rb file is below:
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
The error is occurring when returning a shortened URL (# display short url from root)
If anyone can help me figure out why it's failing, I would really appreciate it.
Many thanks
Upvotes: 0
Views: 6054
Reputation: 26979
The trick is reading the stack trace. I'm guessing that ClickTrack is a DataMapper object, and you are calling attributes=
on it. The stack trace is kind enough to provide a little bit of code from within dm-core: if model.allowed_writer_methods.include?(setter = "#{name}=")
Obviously, from within data mapper, model.allowed_writer_methods
is returning nil. I haven't used data mapper at all, but a little googling comes up with one possibility:
https://github.com/datamapper/dm-core/issues/152
According to http://datamapper.org/getting-started.html you should call DataMapper.finalize
before using the models.
At any rate, this looks like a DataMapper issue. Look at ClickTrack and make sure it is finalized (if I'm reading that correctly) and/or check on your DataMapper usage.
Upvotes: 9