configurationsinatrarackup

Deploying Sinatra app on Heroku


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."


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