Question

I am perplexed by something that isn't actually a practical problem - just a conceptual conundrum - about deploying a Sinatra app on Heroku.

I have two apps, identical in just about every respect, except that one puts most of its logic in a file that does not contain the Sinatra::Base class and uses a 'require otherfilename' to pick up the logic it needs. That Sinatra:Base class is named Kincalc.

For the app with all the logic in one file (that is, the same file that contains the Sinatra:Base class), in my config.ru file, the last line reads "run Sinatra::Application" and it launches fine. But in the other app, if I include that as the last line, the app uploads properly and says it was "deployed to Heroku" but it brings up a "Not found" message. When I have the last line read 'run Kincalc', it loads fine.

I have checked this back and forth and there is nothing different about how these two apps are built, except for the fact that one uses a second file for the logic (which is also at root). Why should this be a problem, or is this really the problem? When I try to put the second file (the one without the Sinatra class) in a separate lib folder, it still works when I call the class name but not when I call "Sinatra::Application."

Was it helpful?

Solution

Code at top level will be delegated to Sinatra::Application, so this would be a scenario for running a classic application:

# app.rb
require 'sinatra'

get '/' do
  'hi'
end

# config.ru
require './app'
run Sinatra::Application

If you define a modular app, you would run it like this:

# app.rb
require 'sinatra/base'

class Kincalc < Sinatra::Base
  get '/' do
    'hi'
  end
end

# config.ru
require './app'
run Kincalc

Now I assume what you are trying to do is this:

# otherfilename.rb
require 'sinatra'

get '/' do
  'hi'
end

# app.rb
require 'sinatra/base'

class Kincalc < Sinatra::Base
  require './otherfilename'
end

# config.ru
require './app'
run Kincalc # Sinatra::Application seems to work

The behavior you experience (getting a 404 File Not Found) is actually correct, as require does not care about the lexical scope it is called in. Check out the following example

# a.rb
puts "in a, top level: #{self.inspect}"

module Example
  puts "in a, nested: #{self.inspect}"
  require 'b'
end

# b.rb
puts "in b: #{self.inspect}"

The resulting output should be:

in a, top level: main
in a, nested: Example
in b: main

So, if you want to use one modular application, you should do something like this:

# otherfilename.rb
Kincalc.get '/' do
  'hi'
end

Or open the class again:

# otherfilename.rb
class Kincalc
  get '/' do
    'hi'
  end
end

Or you could actually have otherfilename.rb make it's definitions on Sinatra::Application and utilize that in Kincalc.

# app.rb
require 'sinatra/base'
require './otherfilename'

class Kincalc < Sinatra::Base
  use Sinatra::Application
end

Or you could change where top level DSL methods are delegated to:

# app.rb
require 'sinatra/base'

class Kincalc < Sinatra::Base
  Sinatra::Delegator.target = self
  require './otherfilename'
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top