Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mount two applications in the same path #796

Closed
dcu opened this issue Mar 1, 2012 · 14 comments
Closed

Mount two applications in the same path #796

dcu opened this issue Mar 1, 2012 · 14 comments

Comments

@dcu
Copy link
Contributor

dcu commented Mar 1, 2012

for example I want to mount to apps in '/' but it doesn't work. I think this should be doable.

@EtienneLem
Copy link

Doesn't really make sense to me. Why would you want to do that?
None of your routes would work.

@dcu
Copy link
Contributor Author

dcu commented Mar 1, 2012

why wouldn't work? highest priority routes would take precedence. that's the rack's point: stackable apps, if a route doesn't match try the next app, no?

it seems to work if I do "use AnotherApp" in my main app so maybe I just need to do that

@skade
Copy link
Contributor

skade commented Mar 1, 2012

Yes, thats racks point: stackable apps. How should the router middleware decide which app to use without peeking upwards? Using Sinatra as a middleware does that implicitly: The used app is inserted before the second app in the apps middleware stack, not as an alternative.

The Padrino::Router does something different: it decides which path to between 2 different (and possibly large) middleware stacks. Allowing it to retry on a second app would be harmful, actually - we cannot guarantee that all middlewares should be allowed to run twice during a request and both branches could include such a middleware. Also, chances are that calling all stacks in worst case would be a very slow thing.

Padrino is Rack galore in that respect: you have multiple middleware stacks that can be switched between with all advantages and problems.

@dcu
Copy link
Contributor Author

dcu commented Mar 1, 2012

thanks for your replies Etienne, Florian. really appreciated them

if I use the sinatra's approach(use) with Padrino apps... what would be the problem? (besides performance), would router generator work?

@skade
Copy link
Contributor

skade commented Mar 1, 2012

It shouldn't be a problem. If it is, file a bug. Performance-wise, its the better approach, as Sinatra makes sure that it behaves well as a middleware. You shouldn't add things like exception handling, session handling etc. to the use app.

Performance-wise, its the better approach. Think about ordering a bit, but otherwise, its fine.

@dcu
Copy link
Contributor Author

dcu commented Mar 1, 2012

ok thanks man, I am closing this issue

@dcu dcu closed this as completed Mar 1, 2012
@christhekeele
Copy link

I ran in to this issue today on a project I'm planning out:

# apps.rb
# Mounts the core application for this project
Padrino.mount("AppAPI").to('/') # Global Grape API that serves only JSON, txt, and XML

# Mounts the assets for this project
Padrino.mount("Assets").to('/assets') # Sprockets App that serves only js, css, fonts, and images

# Mounts the web interface for this project

# These contain views (the two apps above don't need any),
# controllers, and, in some cases, the models that the api uses.

# This app never gets mounted. It serves the landing page and about me.
Padrino.mount("Root").to('/')

Padrino.mount("Admin").to("/admin") # Admin
Padrino.mount("Users").to("/users") # Authentication and Authorization
Padrino.mount("Feature").to("/feature") # Other

This use case doesn't seem exceptionally contrived or rare to me.

A lot of people build APIs with Padrino, and many APIs are RESTfully structured along the same URLs as their web interface, differing by extension. It's a common alternative to nesting everything under '/api'.
It's a little silly that the only way way of doing this is to split up one's API into tiny fragments and sprinkle them across the app, then make a 3-file Root app the 'core application' for such projects.

Also, Grape is an excellent and widely used Rack API gem. It's easy and efficient to develop your Grape APIs in a single file as a padrino app and it sucks not being able to mount it on top of your existing url structure for your web interface.

Particular to my scenario, all of my views get their data by consuming the API, so it really doesn't feel natural throwing the root views in with the api, which i what I plan on doing.

@christhekeele
Copy link

It didn't occur to me that Grape::API apps don't have any Sinatra get '/'... functionality, so I couldn't merge the apps. Mounting the Grape::API as Rack middleware removes its access to my models. So I ended up mounting the API under '/api' and implementing some hacky Rack middleware:

class TryAPI
  def initialize(app)
    @app = app
  end

  def call(env)
    response = nil
    # Use our Padrino app normally
    status, headers, body = @app.call(env)
    if status == 404
      # If the page isn't found, check the API
      api_env = env
      %w[REQUEST_URI REQUEST_PATH PATH_INFO].each do |path_string|
        api_env[path_string] = '/api' + api_env[path_string]
      end
      api_status, api_headers, api_body = @app.call(api_env)
      response = [api_status, api_headers, api_body] unless api_status == 404
    end
    response || [status, headers, body]
  end
end
Padrino.use TryAPI

Normally I'd override api_status to always be 301, but I'm integrating with a company API not served from '/api', so I want to keep the status codes consistent.

@skade
Copy link
Contributor

skade commented Mar 18, 2013

@christhekeele you could use Rack::Cascade for that:

http://rack.rubyforge.org/doc/classes/Rack/Cascade.html

@skade skade reopened this Mar 18, 2013
@skade
Copy link
Contributor

skade commented Mar 18, 2013

I'll reopen this. As we're talking about a router rework anyways and most routers now support Cascade-Like functionality, we should allow this. It still has interesting performance-implications though, so be warned.

@ujifgc
Copy link
Member

ujifgc commented Oct 17, 2013

If one app is mounted to '/foo' and the second to '/foo/bar' and we get '/foo/bar', should the first one attempt to serve the request or the second one should take priority?

Our tests depend on the second behavior https://github.com/padrino/padrino-framework/blob/master/padrino-core/test/test_router.rb#L47

@christhekeele
Copy link

Arguably, it should favor the order they're defined/mounted, since using route specificity to determine priority sounds highly arbitrary, hard to code, and hard to reason about.

@ujifgc
Copy link
Member

ujifgc commented Oct 17, 2013

Okay, then I have a branch ready to break that one test: https://github.com/padrino/padrino-framework/commits/cascade-apps

It respects the mounting order and passes to the next app on 404.

Commit: 57d2999

@ujifgc
Copy link
Member

ujifgc commented Oct 23, 2013

Closing in favor of #1471

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants