Created
April 27, 2012 05:51
-
-
Save aseemk/2506270 to your computer and use it in GitHub Desktop.
Simplified wrapper around Node's native 'url' module
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# url.coffee | |
# by Aseem Kishore ( https://github.com/aseemk ), under MIT license | |
# | |
# A simplified wrapper around the native 'url' module that returns "live" | |
# objects: updates to properties are reflected in related properties. E.g. | |
# updates to the `port` property will be reflected on the `host` property. | |
# | |
# The public API and behavior are pretty close to the native 'url' module's: | |
# http://nodejs.org/docs/latest/api/url.html Currently lacking / known issues: | |
# | |
# - No support for `parseQueryString`; `query` can only be a string. | |
# Similarly, no support for `slashesDenoteHost`. | |
# | |
# - Parsing of URLs beyond the initial parse (which uses the native 'url' | |
# module) isn't thorough or strict right now. It assumes plenty. | |
# | |
# This is mainly just a simple and easy way to mutate/tweak URLs (e.g. to | |
# change a URL's port). Feedback and patches welcome! Thanks. =) | |
NativeURL = require 'url' | |
class URL | |
# Private constructor: | |
constructor: (href) -> | |
@_reparse href | |
# Language helpers: | |
get = (props) => | |
@::__defineGetter__ name, getter for name, getter of props | |
set = (props) => | |
@::__defineSetter__ name, setter for name, setter of props | |
# Public properties: | |
get href: -> | |
# Postfix http/https/ftp/gopher/file protocols (trailing colon) with | |
# colon-slash-slash; all others just colon: | |
protocol = @protocol | |
protocol += '//' if protocol.replace(/:$/, '') in ['http', 'https', 'ftp', 'gopher', 'file'] | |
# Add auth via @ if needed: | |
auth = @auth | |
auth += '@' if auth | |
# Finally: | |
"#{protocol}#{auth}#{@host}#{@pathname}#{@search}#{@hash}" | |
set href: (val='') -> | |
@_reparse val | |
get protocol: -> @_protocol | |
set protocol: (val='') -> | |
# Lowercase, and ensure trailing colon: | |
@_protocol = val.toLowerCase().replace(/:$/, '') + ':' | |
# WARNING: The latest Node docs are inaccurate on host. They say host | |
# includes auth, but it doesn't in practice: | |
# https://github.com/joyent/node/issues/1626#issuecomment-5373917 | |
get host: -> | |
host = @hostname | |
host = [host, @port].join ':' if @port | |
host | |
set host: (val='') -> | |
[@hostname, @port] = val.split ':' | |
get auth: -> @_auth | |
set auth: (@_auth='') -> | |
get hostname: -> @_hostname | |
set hostname: (@_hostname='') -> | |
get port: -> @_port | |
set port: (@_port='') -> | |
get pathname: -> @_pathname | |
set pathname: (val='') -> | |
# Ensure trailing slash: | |
@_pathname = '/' + val.replace(/^\//, '') | |
get search: -> | |
# Leading question mark if there's a query string: | |
@query and "?#{@query}" | |
set search: (val='') -> | |
# Ensure leading question mark / strip it when assigning to @query: | |
@_query = val.replace(/^\?/, '') | |
get path: -> | |
"#{@pathname}#{@search}" | |
set path: (val='') -> | |
[@pathname, @query] = val.split '?' | |
get query: -> @_query | |
set query: (@_query='') -> | |
get hash: -> @_hash | |
set hash: (val='') -> | |
# Ensure leading anchor: | |
@_hash = '#' + val.replace(/^#/, '') | |
# Private methods: | |
# Reparses the given URL and updates this instance in-place: | |
_reparse: (href) -> | |
# Save all properties as private(-ish) properties so that we can | |
# validate/adjust updates to them, e.g. add trailing colons. | |
# NOTE: All properties will be present -- missing ones will be the | |
# empty string. This is consistent with browsers, but not w/ Node's | |
# native 'url' module. That's fine hopefully? | |
for prop, value of NativeURL.parse href | |
@["_#{prop}"] = value or '' | |
# Public methods: | |
equals: (other) -> | |
@href is other.href | |
toString: -> | |
@href | |
@parse: (str) -> | |
new URL str | |
@format: (url) -> | |
# Don't assume the given param will be an instance of this class: | |
NativeURL.format url | |
@resolve: (from, to) -> | |
NativeURL.resolve from, to | |
# Export static functions only: | |
for name, func of URL when typeof func is 'function' | |
exports[name] = func |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
assert = require 'assert' | |
NativeURL = require 'url' | |
URL = require './url' | |
# Helper func for testing all getters: | |
test = (url, props) -> | |
# Ignore 'slashes' property; we don't implement that yet. | |
for prop, expected of props when prop isnt 'slashes' | |
assert.equal url[prop], expected, """ | |
url.#{prop}: | |
expected: #{JSON.stringify expected} | |
actual: #{JSON.stringify url[prop]} | |
""" | |
# Test case: a full URL, straight from the Node docs! | |
# http://nodejs.org/docs/latest/api/url.html | |
STR = 'http://user:[email protected]:8080/p/a/t/h?query=string#hash' | |
exp = NativeURL.parse STR | |
# Test initial parse: | |
url = URL.parse STR | |
test url, exp | |
# Test protocol set w/out trailing colon: | |
url.protocol = 'https' | |
exp.protocol = 'https:' | |
exp.href = exp.href.replace 'http:', 'https:' | |
test url, exp | |
# Test auth set: | |
url.auth = 'usr:pwd' | |
exp.auth = 'usr:pwd' | |
exp.href = exp.href.replace 'user:pass', 'usr:pwd' | |
test url, exp | |
# Test host set, clearing port: | |
url.host = 'example.com' | |
exp.host = 'example.com' | |
exp.port = '' | |
exp.hostname = 'example.com' | |
exp.href = exp.href.replace 'host.com:8080', 'example.com' | |
test url, exp | |
# Test hostname set: | |
url.hostname = 'foobar.net' | |
exp.hostname = 'foobar.net' | |
exp.host = 'foobar.net' | |
exp.href = exp.href.replace 'example.com', 'foobar.net' | |
test url, exp | |
# Test port set: | |
url.port = '1234' | |
exp.port = '1234' | |
exp.host = 'foobar.net:1234' | |
exp.href = exp.href.replace 'foobar.net', 'foobar.net:1234' | |
test url, exp | |
# Test path set w/out leading slash, clearing query string: | |
url.path = 'hello/world' | |
exp.path = '/hello/world' | |
exp.pathname = '/hello/world' | |
exp.search = '' | |
exp.query = '' | |
exp.href = exp.href.replace '/p/a/t/h?query=string', '/hello/world' | |
test url, exp | |
# Test pathname set w/out leading slash: | |
url.pathname = 'foo/bar' | |
exp.pathname = '/foo/bar' | |
exp.path = exp.path.replace '/hello/world', '/foo/bar' | |
exp.href = exp.href.replace '/hello/world', '/foo/bar' | |
test url, exp | |
# Test search set w/out leading question mark: | |
url.search = 'a=b' | |
exp.search = '?a=b' | |
exp.query = 'a=b' | |
exp.path = exp.path + '?a=b' | |
exp.href = exp.href.replace '/foo/bar', exp.path | |
test url, exp | |
# Test query set -- w/ (double) question mark: | |
# TODO Is this actually legitimate/correct?? It works... | |
url.query = '?foo=bar' | |
exp.query = '?foo=bar' | |
exp.search = '??foo=bar' | |
exp.path = exp.path.replace '?a=b', '??foo=bar' | |
exp.href = exp.href.replace '?a=b', '??foo=bar' | |
test url, exp | |
# Test hash set w/out leading anchor: | |
url.hash = 'baz' | |
exp.hash = '#baz' | |
exp.href = exp.href.replace '#hash', '#baz' | |
test url, exp | |
# Test hash clear: | |
url.hash = '' | |
exp.hash = '#' | |
exp.href = exp.href.replace '#baz', '#' | |
test url, exp | |
# Test search clear: | |
# TODO Should we be testing query clear too? Should question mark remain then? | |
url.search = '' | |
exp.search = '' | |
exp.query = '' | |
exp.path = exp.path.replace '??foo=bar', '' | |
exp.href = exp.href.replace '??foo=bar', '' | |
test url, exp | |
# Test path clear: | |
url.path = '' | |
exp.path = '/' | |
exp.pathname = '/' | |
exp.href = exp.href.replace '/foo/bar', '/' | |
test url, exp | |
# Test port clear: | |
url.port = '' | |
exp.port = '' | |
exp.host = exp.host.replace ':1234', '' | |
exp.href = exp.href.replace ':1234', '' | |
test url, exp | |
# Test auth clear: | |
url.auth = '' | |
exp.auth = '' | |
exp.href = exp.href.replace 'usr:pwd@', '' | |
test url, exp | |
# Finally, test set href: | |
url.href = STR | |
exp = NativeURL.parse STR | |
test url, exp | |
console.log 'All tests passed!' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
this is a really clever hack for native looking getters/setters!