UrlMount

UrlMount is a universal mount point designed for use in rack applications.

It provides a simple way to pass a url mounting point to the mounted application.

This means that when you mount an application in the url space, it’s a simple call to url to get the mount point of where the application is.

Example

Say you mount an application at “/foo/bar”

router.mount("/foo/bar").to(my_app)

# behind the scenes, the routers mount method should do this:

  if my_app.respond_to?(:url_mount=)
    mount_point = UrlMount.new("/foo/bar")
    my_app.url_mount = mount_point
  end

This means that if an application can handle a url_mount, you give it one.

This then allows the my_app applciation, to know where it’s mounted. Generating a url is now as simple as

File.join(url_mount.url, "/local/url/path")

The benefit of this is that all routers or applciations can make use of this. If used, it can act as a universal glue between rack applications where parent apps can let the child applications know where they’re mounted so the child applciations can have a chance of generating full routes, even when they’re not in the request path.

Show me the code

url_mount is made to be used with many routers that are currently available.


# simple string mounting point
mount = UrlMount.new("/foo/bar")
mount.url == "/foo/bar"

# Mount Point including variables
mount = UrlMount.new("/foo/:bar", :bar => "bar")
mount.url(:bar => "something") == "/foo/something"
mount.url #=> Raises UrlMount::Ungeneratable because a required variable was not found
mount.required_variables == [:bar]

# UrlMount::Ungeneratable raised when a route cannot be generated without options
no_mount = UrlMount.new("/foo/:bar") # fails because the mount point cannot be generated with no options

# Mount Point including optional variables
mount = UrlMount.new("/foo/:bar(/:baz)", :bar => "bar")
mount.url(:bar => "doh") == "/foo/doh"
mount.url(:bar => "doh", :baz => "hah") == "/foo/doh/hah"
mount.required_variables == [:bar]
mount.optional_variables == [:baz]

# Mount Point with defaults
mount = UrlMount.new("/foo/:bar(/:baz)", :bar => "default_bar")
mount.url == "/foo/default_bar"
mount.url(:baz => "baz_value") == "/foo/default_bar/baz_value"
mount.url(:bar => "other_bar") == "/foo/other_bar"

# Using procs for mount point defaults
mount = UrlMount.new("/foo/:bar", :bar => proc{"some_bar"})
mount.url == "/foo/some_bar"

# Nested mounting point
mount_parent = UrlMount.new("/foo/bar")
mount_child  = UrlMount.new("/baz/:barry)

mount_child.url_mount = mount_parent

mount_parent.url == "/foo/bar"
mount_child.url(:barry =>"barry_value") == "/foo/bar/baz/barry_value"

Considering that UrlMounts can be nested, when you mount an application you should do it something like this.


if mounted_app.respond_to?(:url_mount=)
  mount = UrlMount.new(path, deafult_options)

  # If this app is mounted, add this apps mount point to
  # the mount point for the child app
  mount.url_mount = self.url_mount if self.url_mount

  mounted_app.url_mount = mount
end

Generating routes

When you generate routes, you can see which variables will be used by the mount point, so that you can remove these options from the options hash. otherwise, depending on the generating application, you may get query string options.


  url = UrlMount.new(/"foo/:bar(/:baz)", :bar => "some_bar")

  generation_options = {:baz => "baz", :bar => "bar"}
  mount_point = url.url(generation_options)

  # We can now at this point ask the mount point what variables it will use if present.
  url.variables.each{|v| generation_options.delete(v)}

  # use the generation_options now without the mount_point variables to interfere with a query string

Callbacks

When generating the routes, you can provide one or more callbacks to the mount point. Inside the callbacks you can set options for the routes based on the Rack request.

mount = UrlMount.new("/foo/:bar") do |env, opts|
  opts[:bar] ||= "my_bar"
end

mount.url(env) # Rack::Request === env
#=> /foo/my_bar

NOTE: callbacks are only run when a rack environment is given to the url method

Note on Patches/Pull Requests

  • Fork the project.
  • Make your feature addition or bug fix.
  • Add tests for it. This is important so I don’t break it in a future version unintentionally.
  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
  • Send me a pull request. Bonus points for topic branches.

Copyright

Copyright © 2010 Daniel Neighman. See LICENSE for details.