JavaScript URLMapper

投稿者 nanki 2007-02-13 15:03:00 GMT

ちょっと複雑なパス体系を持つRailsアプリでJSONでAJAXする人には役に立つかもしれない。 GoogleMapsとかね。

要 prototype.js

使い方はこんな風。

URLMapper.connect(
  "/user/:user_id/category/:category_id/entry/:action/:entry_id",
  {controller: 'entry', action: 'index', entry_id: null});
URLMapper.connect(
  "/user/:user_id/category/:action/:category_id",
  {controller: 'category', action: 'index', category_id: null});
URLMapper.connect(
  "/user/:action/:user_id",
  {controller: 'user', action: 'index', user_id: null});
URLMapper.connect(
  "/:controller/:action/:id",
  {action: 'index', id: null});

URLMapper.url_for({controller: 'entry'});
// -> "/entry/index/"
URLMapper.url_for({controller: 'entry', action: 'new'});
// -> "/entry/new/"
URLMapper.url_for({controller: 'category', action: 'new'});
// -> "/category/new/"
URLMapper.url_for({controller: 'category', action: 'new', user_id: 3});
// -> "/user/3/category/new/"
URLMapper.url_for({controller: 'category', action: 'update', user_id: 3, category_id: 2});
// -> "/user/3/category/update/2"
URLMapper.url_for({controller: 'entry', action: 'update', user_id: 3, category_id: 2, entry_id: 1});
// -> "/user/3/category/2/entry/update/1"

url_for は可変長引数をとって、引数をHashとしてマージするので、

//サーバからJSONを取得。
//[{user_id: 3, category_id: 2, entry_id: 1}, ...]
var entries = getJSON();

URLMapper.url_for({controller: 'entry', action: 'update'}, entries[0]);
// -> "/user/3/category/2/entry/update/1"

とか。

ソースはこんなの。

Hash.prototype.subtract = function(op2) {
  var result = $H().merge(this);
  this.remove.apply(result, $H(op2).keys());
  return result;
};


var URLMapper = {
  url_options: $A(),

  UrlOption: function(url, required, defaults, match) {
    this.url      = url.gsub(/\/:([A-z_][A-z0-9_]*)/, function(match) {return '/#{' + match[1] + '}'});
    this.required = required;
    this.defaults = defaults;
    this.match    = match;
  },

  extractParameters: function(url) {
    var params = $H();
    url.scan(/\/:([A-z_][A-z0-9_]*)/, function(match) {params[match[1]] = true});
    return params;
  },

  connect: function(url, defaults) {
    url = url.gsub(/%3A/, ':');
    var params = this.extractParameters(url);
    var required = params.subtract(defaults);
    this.url_options.push(new this.UrlOption(url, required, $H(defaults), $H(defaults).subtract(params)));
  },

  url_for: function() {
    var options = $A(arguments).inject($H(), function(r, v){return r.merge(v)});
    var detected = this.url_options.select(
      function(url_option) {
        return url_option.required.subtract(options).size() == 0 && url_option.match.all(function(pair){return options[pair.key] == pair.value});
      }
    ).sortBy(function(url_option) {
      return $H(options).subtract(url_option.required).subtract(url_option.defaults).size();
    }).first();


    if (!detected) {
      throw "no URL matches.";
    }

    return (new Template(detected.url)).evaluate($H().merge(detected.defaults).merge(options));
  }
}

This entry was posted on 2007-02-13 15:03:00 GMT and カテゴリ , , . You can follow any response to this entry through the Atom feed. or a trackback from your own site.

タグ , ,


トラックバック

トラックバックリンク:
http://blog.netswitch.jp/trackbacks?article_id=5557