Skip to content

Commit

Permalink
Renamed package to nURL and updated docs
Browse files Browse the repository at this point in the history
  • Loading branch information
codeinthehole committed Oct 10, 2010
1 parent 6ed442d commit 074d16c
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 98 deletions.
3 changes: 2 additions & 1 deletion .gitignore
@@ -1,2 +1,3 @@
.project
.settings
.settings
*.swp
136 changes: 56 additions & 80 deletions README.md
Expand Up @@ -7,105 +7,81 @@ immutable: whenever a value is altered, a new instance is returned leaving the o
This module builds on top of the core 'url' and 'querystring' modules.

## Sample usage
### Creation:

var urls = require('urls');

// Create from string
var myUrl = urls.parse("http://www.example.com/path/to/here?q=test");

// Create using chained methods
var anotherUrl = (new urls.Url()).setProtocol('https')
.setHostname('www.google.com')
.setQueryParam('q', 'node.js');

// Create by mutating an existing URL
var modifiedUrl = myUrl.setQueryParam('q', 'javascript');
anotherUrl.toString(); // 'http://www.google.com/q=javascript'
anotherUrl == modifiedUrl; // false

// Create by merging two existing URLs
var url1 = urls.createFromString("http://www.example.com");
var url2 = urls.createFromString("/path/#frag");
var url3 = url1.mergeWith(url2);
url3.toString(); // 'http://www.example.com/path#frag'

### Interrogation:

// Public properties
myUrl['protocol']; // 'http';
myUrl['hostname']; // 'www.example.com';

// Getter methods
myUrl.getProtocol(); // 'http'
myUrl.getHostname(); // 'www.example.com'
myUrl.getSubdomain(0); // 'www'
myUrl.getQueryParam('q'); // 'test'

## API

Starting with:
URLs are modelled as:

var urls = require('urls');
var u = urls.parse('http://www.google.com/search?q=node.js#top');
scheme://user:password@hostname:port/pathname?search#fragment

then the various components of `u` can be accessed through properties:
Note that the '//' following the scheme is optional for certain schema (eg mailto).

- `u.protocol`, `u.scheme`
- `u.auth`
- `u.hostname`
- `u.port`
- `u.pathname`
- `u.search`
- `u.fragment`, `u.hash`
- `u.href`
Now, starting with:

and getters:
var urls = require('urls');

- `u.getProtocol()`, `u.getScheme()`
- `u.getUser()`
- `u.getPassword()`
- `u.getHostname()`
- `u.getPort()`
- `u.getPathname()`
- `u.getSearch()`
- `u.getHash()`, `u.getFragment()`
- `u.getHref()`, `u.toString()`
you can create a URL object using one of:

Note that these return `Null` when the component has no value.
var u = urls.parse('http://www.google.com/search?q=node.js#top');
var u = (new urls.Url()).setScheme('http')
.setHostname('www.google.com')
.setPathname('/search')
.setQueryParam('q', 'node.js')
.setFragment('top');
var u = new urls.Url('http', Null, Null, 'www.google.com', Null, '/search', 'q=node.js', 'top');

The various components of `u` can be accessed through both read-only properties and getter methods:

u.protocol, u.scheme, u.getProtocol(), u.getScheme() // => 'http'
u.getUser() // => Null
u.getPassword() // => Null
u.auth, u.getAuth() // => Null
u.hostname, u.getHostname() // => 'www.google.com'
u.port, u.getPort() // => 80
u.pathname, u.getPathname() // => '/search'
u.search, u.getSearch() // => '?node.js'
u.fragment, u.hash, u.getFragment(), u.getHash() // => 'top'
u.href, u.getHref() // => 'http://www.google.com/search?q=node.js#top'

Note that:

- both the properties and getters return `Null` when the component has no value.
w
- some property names are aliases (such as 'protocol' and 'scheme')

More detailed interrogation can be performed using:

- `u.getSubdomain(0)`
- `u.getSubdomains()`
- `u.hasQueryParam('q')`
- `u.getQueryParam('q')`
- `u.getPathSegments()`
- `u.getPathSegment(0)`
- `u.isRelative()`
- `u.isAbsolute()`
u.getSubdomains() // => ['www', 'google', 'com']
u.getSubdomain(0) // => 'www'
u.hasQueryParam('q') // => True
u.getQueryParam('q') // => 'node.js'
u.getPathSegments() // => ['search']
u.getPathSegment(0) // => 'search'
u.isRelative() // => False
u.isAbsolute() // => True

Setters follow a similar pattern, each returning a new URL object:

- `u.setProtocol('https')`, `u.setScheme('https')`
- `u.setAuth('user', 'secret')`
- `u.setHostname('example.com')`
- `u.setPort('80')`
- `u.setPathname('/')`
- `u.setQueryParam('q', 'testing)`
- `u.setHash('top')`
u.setProtocol('https'), u.setScheme('https')
u.setAuth('user', 'secret')
u.setHostname('example.com')
u.setSubdomain(0, 'sample') // => 'sample.google.com'
u.setPort('80')
u.setPathname('/')
u.setPathSegment(1, 'extension') // => '/search/extension'
u.setQueryParam('q', 'testing')
u.setHash('top')

These can be chained together to create a URL in a fluent, readable manner:
URL objects can be merged to create a new object - the properties of the passed in
URL will fill in any missing components:

var v = (new urls.Url()).setProtocol('http')
.setHostname('example.com')
.setQueryParam('q', 'node.js');
var u1 = urls.parse('http://www.google.com');
var u2 = urls.parse('/search?q=test.js');
u1.mergeWith(u2) // => 'http://www.google.com/search?q=test.js'



## Testing

All tests are written in the excellent 'vows' library. To run them, use
All tests are written in the excellent [vows](http://vowsjs.org/) library. To run them, use

$ cd /path/to/urls
$ vows --spec

Expand Down
5 changes: 5 additions & 0 deletions TODO
@@ -0,0 +1,5 @@
Rename package to nurl
Publish using npm

Route to method (from ruby)
Filter API by scheme - ie throw errors when setting query on a file: URL
35 changes: 23 additions & 12 deletions lib/urls.js → lib/nurl.js
@@ -1,7 +1,7 @@
var urllib = require('url'),
qs = require('querystring');

// Url object
// Url object - standard constructor takes all possible values as arguments
var Url = exports.Url = function(scheme, user, password, hostname, port, pathname, search, hash) {
// Store properties in a private obj and provide "privileged" getter method to allow access.
var properties = {
Expand All @@ -10,14 +10,15 @@ var Url = exports.Url = function(scheme, user, password, hostname, port, pathnam
"password": password || null,
"hostname": hostname || null,
"port": port || null,
"pathname": pathname || "",
"search": search || "",
"pathname": pathname || null,
"search": search || null,
"hash": hash || null
};
this.__getProperty = function(property) {
return properties[property];
};
// Return a clone of the properties to keep this object immutable
// Returns a clone of the properties object - this is to keep each object immutable
// there is no write access to the core properties
this.__getProperties = function() {
var clonedProperties = {};
for (key in properties) {
Expand Down Expand Up @@ -55,10 +56,8 @@ Url.prototype.getHostname = function() {
};
Url.prototype.getPort = function() {
var port = this.__getProperty('port');
if (null === this.__getProperty('port') && this.__getProperty('scheme') == 'http') {
port = 80;
} else if (null === this.__getProperty('port') && this.__getProperty('scheme') == 'https') {
port = 443;
if (null === port) {
port = getDefaultPort(this.__getProperty('scheme'));
}
return port;
};
Expand All @@ -79,7 +78,8 @@ Url.prototype.getQueryParam = function(param, defaultValue) {
return this.getQueryParams()[param];
};
Url.prototype.getQueryParams = function() {
return qs.parse(this.__getProperty('search'));
var search = this.__getProperty('search');
return qs.parse(search ? search : '');
};
Url.prototype.getPathSegment = function(index) {
var segments = this.getPathSegments();
Expand Down Expand Up @@ -117,7 +117,9 @@ Url.prototype.getHref = function() {
if (this.__getProperty('port')) {
str += ':'+this.__getProperty('port');
}
str += this.__getProperty('pathname');
if (this.__getProperty('pathname')) {
str += this.__getProperty('pathname');
}
if (this.__getProperty('search')) {
str += '?'+this.__getProperty('search');
}
Expand Down Expand Up @@ -188,13 +190,15 @@ Url.prototype.setQueryParam = function(param, value) {
params[param] = value;
return createFromProperties(this.__setProperty('search', qs.stringify(params)));
};

Url.prototype.mergeWith = function(url) {
if (typeof url.__getProperties != 'function') {
throw Error("Url.mergeWith must be passed a Url object");
}
var currentProperties = this.__getProperties();
var mergeProperties = url.__getProperties();
var newProperties = {};
var newProperties = {};
// If a property already exists, do not replace it
for (key in currentProperties) {
if (!currentProperties[key]) {
newProperties[key] = mergeProperties[key];
Expand Down Expand Up @@ -250,11 +254,18 @@ exports.parse = exports.createFromString;
// Utility
// =======

// Utility function to return the scheme separator
// These need expanding to cover other schemes
var getSchemeSeparator = function(scheme) {
if (scheme == 'mailto') {
return '';
} else {
return '//';
}
};
var getDefaultPort = function(scheme) {
var defaultPorts = {
'http': 80,
'https': 443,
};
return defaultPorts.hasOwnProperty(scheme) ? defaultPorts[scheme] : null;
}
6 changes: 3 additions & 3 deletions package.json
@@ -1,7 +1,7 @@
{ "name" : "urls"
, "description" : "Module that provides URL objects that can be manipulated"
{ "name" : "nurl"
, "description" : "Module that provides a simple, immutable URL object for access and manipulation"
, "version" : "0.0.1"
, "main" : "./lib/urls.js"
, "main" : "./lib/nurl.js"
, "directories" : { "test": "./test" }
, "author" : "David Winterbottom <david.winterbottom@gmail.com>"
}
4 changes: 2 additions & 2 deletions test/url-vows.js
@@ -1,6 +1,6 @@
var vows = require('vows'),
assert = require('assert')
urls = require('urls');
urls = require('nurl');

// A list of publicly exposed properties on the URL object - we use these to dynamically
// define tests
Expand All @@ -14,7 +14,7 @@ var testUrls = {
'user': null,
'password': null,
'hostname': 'www.google.com',
'pathname': '/',

'port': 80,
'search': null,
'hash': null
Expand Down

0 comments on commit 074d16c

Please sign in to comment.