js-routes 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3c2993fd7d6c758d98b5f7024ab7c21e2be8f600
4
- data.tar.gz: d89427d56039f4f6922d4b508672fccd2aaf1497
3
+ metadata.gz: e61c1a78d1a68a39e174a3459048f939ac1a5491
4
+ data.tar.gz: 2690497a175312cb4fd282626eae3adf87718cbb
5
5
  SHA512:
6
- metadata.gz: 85c514ad1542f4d924611c822475b8e667bc4fcc851ddc0239af229bdab77a99c0a625a452af0900050dd90d517cb3696de9e6c26539ba7701f51d960df3b68e
7
- data.tar.gz: ee8810fec35db2da1c0f025663d994eea6d700e45cd4fb3a7c32ac9da2ab68719614dd9cdbccbad6f5ed4e8ebf9a7d9b6fdcc2175b474861387c8d48473ff564
6
+ metadata.gz: 1698494469561e6a34ae36c1f38ae74da0b2a94ecb6e6a4531e25725021772e852f9d1d55f3be7eb9b373c00ad63d57ef67f909c6bbe4ed4dc843096bbc53250
7
+ data.tar.gz: 04cc89d4715e92eb63c32ff6bd0498c1164d271cab2c976de65a3da3731a6f565691d169f3f1a8dfbf2c336ab6534ce4bdd60bf94cb848753a4082198ede0609
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  ## master
2
2
 
3
+ ## v1.2.0
4
+
5
+ * Support host, port and protocol inline parameters
6
+ * Support host, port and protocol parameters given to a route explicitly
7
+ * Remove all incompatibilities between actiondispatch and js-routes in handling route URLs
8
+
9
+ ## v1.1.2
10
+
11
+ * Bugfix support nested object null parameters #164
12
+ * Bugfix support for nested optional parameters #162 #163
13
+
3
14
  ## v1.1.1
4
15
 
5
16
  * Bugfix regression in serialisation on blank strings caused by [#155](https://github.com/railsware/js-routes/pull/155/files)
data/Readme.md CHANGED
@@ -45,8 +45,7 @@ Available options:
45
45
 
46
46
  * `default_url_options` - default parameters used when generating URLs
47
47
  * Note that only specific options are supported at this time.
48
- * Supported options: `:format`, `:trailing_slash`, `:protocol`, `:host`, `:port`
49
- * Example: {:format => "json", :trailing_slash => true, :protocol => "https", :host => "example.com", :port => 3000}
48
+ * Example: {:format => "json", :trailing\_slash => true, :protocol => "https", :host => "example.com", :port => 3000}
50
49
  * Default: {}
51
50
  * `exclude` - Array of regexps to exclude from js routes.
52
51
  * Default: []
@@ -65,7 +64,7 @@ Available options:
65
64
  * `url_links` (version >= 0.8.9) - Generate `*_url` helpers (in addition to the default `*_path` helpers).
66
65
  * Example: true
67
66
  * Default: false
68
- * Be sure to specify a default host in `default_url_options`. Routes which specify a specific host, protocol, or port will be used instead of their corresponding default.
67
+ * Note: generated URLs will first use the protocol, host, and port options specified in the route definition. Otherwise, the URL will be based on the option specified in the `default_url_options` config. If no default option has been set, then the URL will fallback to the current URL based on `window.location`.
69
68
  * `compact` (version > 0.9.9) - Remove `_path` suffix in path routes(`*_url` routes stay untouched if they were enabled)
70
69
  * Default: false
71
70
  * Sample route call when option is set to true: Routes.users() => `/users`
@@ -118,6 +117,7 @@ Configuration above will create a nice javascript file with `Routes` object that
118
117
  Routes.users_path() // => "/users"
119
118
  Routes.user_path(1) // => "/users/1"
120
119
  Routes.user_path(1, {format: 'json'}) // => "/users/1.json"
120
+ Routes.user_path(1, {anchor: 'profile'}) // => "/users/1#profile"
121
121
  Routes.new_user_project_path(1, {format: 'json'}) // => "/users/1/projects/new.json"
122
122
  Routes.user_project_path(1,2, {q: 'hello', custom: true}) // => "/users/1/projects/2?q=hello&custom=true"
123
123
  Routes.user_project_path(1,2, {hello: ['world', 'mars']}) // => "/users/1/projects/2?hello%5B%5D=world&hello%5B%5D=mars"
data/lib/js_routes.rb CHANGED
@@ -16,7 +16,7 @@ class JsRoutes
16
16
  include: //,
17
17
  file: DEFAULT_PATH,
18
18
  prefix: nil,
19
- url_links: nil,
19
+ url_links: false,
20
20
  camel_case: false,
21
21
  default_url_options: {},
22
22
  compact: false,
@@ -173,45 +173,44 @@ class JsRoutes
173
173
 
174
174
  def build_js(route, parent_route)
175
175
  name = [parent_route.try(:name), route.name].compact
176
- parent_spec = parent_route.try(:path).try(:spec)
177
- required_parts, optional_parts = route.required_parts.clone, (route.parts-route.required_parts)
178
- optional_parts.push(required_parts.delete :format) if required_parts.include?(:format)
179
176
  route_name = generate_route_name(name, (:path unless @options[:compact]))
180
- url_link = generate_url_link(name, route_name, required_parts, route)
177
+ parent_spec = parent_route.try(:path).try(:spec)
178
+ route_arguments = route_js_arguments(route, parent_spec)
179
+ url_link = generate_url_link(name, route_name, route_arguments, route)
181
180
  _ = <<-JS.strip!
182
181
  // #{name.join('.')} => #{parent_spec}#{route.path.spec}
183
- // function(#{[required_parts, LAST_OPTIONS_KEY].flatten.join(', ')})
184
- #{route_name}: Utils.route(#{json(required_parts)}, #{json(optional_parts)}, #{json(serialize(route.path.spec, parent_spec))}, arguments)#{",\n" + url_link if url_link.length > 0}
182
+ // function(#{build_params(route.required_parts, true)})
183
+ #{route_name}: Utils.route(#{route_arguments})#{",\n" + url_link if url_link.length > 0}
185
184
  JS
186
185
  end
187
186
 
188
- def generate_url_link(name, route_name, required_parts, route)
187
+ def route_js_arguments(route, parent_spec)
188
+ required_parts, optional_parts = route.required_parts.clone, (route.parts-route.required_parts)
189
+ optional_parts.push(required_parts.delete :format) if required_parts.include?(:format)
190
+ [json(required_parts), json(optional_parts), json(serialize(route.path.spec, parent_spec))].join(", ")
191
+ end
192
+
193
+ def generate_url_link(name, route_name, route_arguments, route)
189
194
  return "" unless @options[:url_links]
190
- _ = <<-JS.strip!
191
- #{generate_route_name(name, :url)}: function(#{build_params(required_parts)}) {
192
- return #{generate_base_url_js(route)} + this.#{route_name}(#{build_params(required_parts)});
193
- }
195
+ defaults = @options[:url_links] == true ? route.defaults.slice(:host, :port, :protocol) : deprecated_base_url
196
+ <<-JS.strip!
197
+ #{generate_route_name(name, :url)}: Utils.route(#{route_arguments}, #{json(defaults)})
194
198
  JS
195
199
  end
196
200
 
197
- def generate_base_url_js(route)
198
- # preserve and deprecate previous behavior
199
- unless @options[:url_links] == true
200
- ActiveSupport::Deprecation.warn('js-routes url_links config value must be a boolean. Use default_url_options for specifying a default host.')
201
- raise "invalid URL format in url_links (ex: http[s]://example.com)" if @options[:url_links].match(URI::Parser.new.make_regexp(%w(http https))).nil?
202
- return "#{@options[:url_links].inspect}"
203
- else
204
- protocol = route.defaults[:protocol] || @options[:default_url_options][:protocol] || 'http'
205
- hostname = route.defaults[:host] || @options[:default_url_options][:host]
206
- port = route.defaults[:port] || (@options[:default_url_options][:port] unless route.defaults[:host])
207
- port = ":#{port}" if port
208
-
209
- unless hostname
210
- raise "A :default_url_options[:host] must be configured in order to generate *_url helpers"
211
- end
212
-
213
- return %Q|'#{protocol}://#{hostname}#{port}'|
214
- end
201
+ def deprecated_base_url
202
+ ActiveSupport::Deprecation.warn('js-routes url_links config value must be a boolean. Use default_url_options for specifying a default host.')
203
+
204
+
205
+ raise "invalid URL format in url_links (ex: http[s]://example.com)" if @options[:url_links].match(URI::Parser.new.make_regexp(%w(http https))).nil?
206
+ uri = URI.parse(@options[:url_links])
207
+ default_port = uri.scheme == "https" ? 443 : 80
208
+ port = uri.port == default_port ? nil : uri.port
209
+ {
210
+ host: uri.host,
211
+ port: port,
212
+ protocol: uri.scheme,
213
+ }
215
214
  end
216
215
 
217
216
  def generate_route_name(name, suffix)
@@ -224,11 +223,11 @@ class JsRoutes
224
223
  self.class.json(string)
225
224
  end
226
225
 
227
- def build_params(required_parts)
226
+ def build_params(required_parts, unprotected = false)
228
227
  params = required_parts.map do |name|
229
228
  # prepending each parameter name with underscore
230
229
  # to prevent conflict with JS reserved words
231
- "_#{name}"
230
+ unprotected ? name : "_#{name}"
232
231
  end << LAST_OPTIONS_KEY
233
232
  params.join(", ")
234
233
  end
@@ -1,3 +1,3 @@
1
1
  class JsRoutes
2
- VERSION = "1.1.2"
2
+ VERSION = "1.2.0"
3
3
  end
data/lib/routes.js CHANGED
@@ -1,11 +1,13 @@
1
1
  /*
2
2
  File generated by js-routes GEM_VERSION
3
3
  Based on Rails routes of APP_CLASS
4
- */
4
+ */
5
+
5
6
 
6
7
  (function() {
7
- var NodeTypes, ParameterMissing, Utils, createGlobalJsRoutesObject, defaults, root,
8
- hasProp = {}.hasOwnProperty;
8
+ var NodeTypes, ParameterMissing, ReservedOptions, Utils, createGlobalJsRoutesObject, defaults, root,
9
+ __hasProp = {}.hasOwnProperty,
10
+ __slice = [].slice;
9
11
 
10
12
  root = typeof exports !== "undefined" && exports !== null ? exports : this;
11
13
 
@@ -22,9 +24,12 @@ Based on Rails routes of APP_CLASS
22
24
 
23
25
  NodeTypes = NODE_TYPES;
24
26
 
27
+ ReservedOptions = ['anchor', 'trailing_slash', 'host', 'port', 'protocol'];
28
+
25
29
  Utils = {
26
30
  default_serializer: function(object, prefix) {
27
- var element, i, j, key, len, prop, s;
31
+ var element, i, key, prop, s, _i, _len;
32
+
28
33
  if (prefix == null) {
29
34
  prefix = null;
30
35
  }
@@ -37,21 +42,21 @@ Based on Rails routes of APP_CLASS
37
42
  s = [];
38
43
  switch (this.get_object_type(object)) {
39
44
  case "array":
40
- for (i = j = 0, len = object.length; j < len; i = ++j) {
45
+ for (i = _i = 0, _len = object.length; _i < _len; i = ++_i) {
41
46
  element = object[i];
42
47
  s.push(this.default_serializer(element, prefix + "[]"));
43
48
  }
44
49
  break;
45
50
  case "object":
46
51
  for (key in object) {
47
- if (!hasProp.call(object, key)) continue;
52
+ if (!__hasProp.call(object, key)) continue;
48
53
  prop = object[key];
49
54
  if ((prop == null) && (prefix != null)) {
50
55
  prop = "";
51
56
  }
52
57
  if (prop != null) {
53
58
  if (prefix != null) {
54
- key = prefix + "[" + key + "]";
59
+ key = "" + prefix + "[" + key + "]";
55
60
  }
56
61
  s.push(this.default_serializer(prop, key));
57
62
  }
@@ -59,7 +64,7 @@ Based on Rails routes of APP_CLASS
59
64
  break;
60
65
  default:
61
66
  if (object != null) {
62
- s.push((encodeURIComponent(prefix.toString())) + "=" + (encodeURIComponent(object.toString())));
67
+ s.push("" + (encodeURIComponent(prefix.toString())) + "=" + (encodeURIComponent(object.toString())));
63
68
  }
64
69
  }
65
70
  if (!s.length) {
@@ -77,57 +82,28 @@ Based on Rails routes of APP_CLASS
77
82
  },
78
83
  clean_path: function(path) {
79
84
  var last_index;
85
+
80
86
  path = path.split("://");
81
87
  last_index = path.length - 1;
82
88
  path[last_index] = path[last_index].replace(/\/+/g, "/");
83
89
  return path.join("://");
84
90
  },
85
- set_default_url_options: function(optional_parts, options) {
86
- var i, j, len, part, results;
87
- results = [];
88
- for (i = j = 0, len = optional_parts.length; j < len; i = ++j) {
89
- part = optional_parts[i];
90
- if (!options.hasOwnProperty(part) && defaults.default_url_options.hasOwnProperty(part)) {
91
- results.push(options[part] = defaults.default_url_options[part]);
92
- }
93
- }
94
- return results;
95
- },
96
- extract_anchor: function(options) {
97
- var anchor;
98
- anchor = "";
99
- if (options.hasOwnProperty("anchor")) {
100
- anchor = "#" + options.anchor;
101
- delete options.anchor;
102
- }
103
- return anchor;
104
- },
105
- extract_trailing_slash: function(options) {
106
- var trailing_slash;
107
- trailing_slash = false;
108
- if (defaults.default_url_options.hasOwnProperty("trailing_slash")) {
109
- trailing_slash = defaults.default_url_options.trailing_slash;
110
- }
111
- if (options.hasOwnProperty("trailing_slash")) {
112
- trailing_slash = options.trailing_slash;
113
- delete options.trailing_slash;
114
- }
115
- return trailing_slash;
116
- },
117
91
  extract_options: function(number_of_params, args) {
118
92
  var last_el;
93
+
119
94
  last_el = args[args.length - 1];
120
- if (args.length > number_of_params || ((last_el != null) && "object" === this.get_object_type(last_el) && !this.look_like_serialized_model(last_el))) {
121
- return args.pop();
95
+ if ((args.length > number_of_params && last_el === void 0) || ((last_el != null) && "object" === this.get_object_type(last_el) && !this.looks_like_serialized_model(last_el))) {
96
+ return args.pop() || {};
122
97
  } else {
123
98
  return {};
124
99
  }
125
100
  },
126
- look_like_serialized_model: function(object) {
101
+ looks_like_serialized_model: function(object) {
127
102
  return "id" in object || "to_param" in object;
128
103
  },
129
104
  path_identifier: function(object) {
130
105
  var property;
106
+
131
107
  if (object === 0) {
132
108
  return "0";
133
109
  }
@@ -151,52 +127,99 @@ Based on Rails routes of APP_CLASS
151
127
  },
152
128
  clone: function(obj) {
153
129
  var attr, copy, key;
130
+
154
131
  if ((obj == null) || "object" !== this.get_object_type(obj)) {
155
132
  return obj;
156
133
  }
157
134
  copy = obj.constructor();
158
135
  for (key in obj) {
159
- if (!hasProp.call(obj, key)) continue;
136
+ if (!__hasProp.call(obj, key)) continue;
160
137
  attr = obj[key];
161
138
  copy[key] = attr;
162
139
  }
163
140
  return copy;
164
141
  },
165
- prepare_parameters: function(required_parameters, actual_parameters, options) {
166
- var i, j, len, result, val;
167
- result = this.clone(options) || {};
168
- for (i = j = 0, len = required_parameters.length; j < len; i = ++j) {
169
- val = required_parameters[i];
142
+ merge: function() {
143
+ var tap, xs;
144
+
145
+ xs = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
146
+ tap = function(o, fn) {
147
+ fn(o);
148
+ return o;
149
+ };
150
+ if ((xs != null ? xs.length : void 0) > 0) {
151
+ return tap({}, function(m) {
152
+ var k, v, x, _i, _len, _results;
153
+
154
+ _results = [];
155
+ for (_i = 0, _len = xs.length; _i < _len; _i++) {
156
+ x = xs[_i];
157
+ _results.push((function() {
158
+ var _results1;
159
+
160
+ _results1 = [];
161
+ for (k in x) {
162
+ v = x[k];
163
+ _results1.push(m[k] = v);
164
+ }
165
+ return _results1;
166
+ })());
167
+ }
168
+ return _results;
169
+ });
170
+ }
171
+ },
172
+ normalize_options: function(url_defaults, required_parameters, optional_parts, actual_parameters) {
173
+ var i, key, options, result, url_parameters, value, _i, _len;
174
+
175
+ options = this.extract_options(required_parameters.length, actual_parameters);
176
+ if (actual_parameters.length > required_parameters.length) {
177
+ throw new Error("Too many parameters provided for path");
178
+ }
179
+ options = this.merge(defaults.default_url_options, url_defaults, options);
180
+ result = {};
181
+ url_parameters = {};
182
+ result['url_parameters'] = url_parameters;
183
+ for (key in options) {
184
+ if (!__hasProp.call(options, key)) continue;
185
+ value = options[key];
186
+ if (ReservedOptions.indexOf(key) >= 0) {
187
+ result[key] = value;
188
+ } else {
189
+ url_parameters[key] = value;
190
+ }
191
+ }
192
+ for (i = _i = 0, _len = required_parameters.length; _i < _len; i = ++_i) {
193
+ value = required_parameters[i];
170
194
  if (i < actual_parameters.length) {
171
- result[val] = actual_parameters[i];
195
+ url_parameters[value] = actual_parameters[i];
172
196
  }
173
197
  }
174
198
  return result;
175
199
  },
176
- build_path: function(required_parameters, optional_parts, route, args) {
177
- var anchor, opts, parameters, result, trailing_slash, url, url_params;
200
+ build_route: function(required_parameters, optional_parts, route, url_defaults, args) {
201
+ var options, parameters, result, url, url_params;
202
+
178
203
  args = Array.prototype.slice.call(args);
179
- opts = this.extract_options(required_parameters.length, args);
180
- if (args.length > required_parameters.length) {
181
- throw new Error("Too many parameters provided for path");
182
- }
183
- parameters = this.prepare_parameters(required_parameters, args, opts);
184
- this.set_default_url_options(optional_parts, parameters);
185
- anchor = this.extract_anchor(parameters);
186
- trailing_slash = this.extract_trailing_slash(parameters);
204
+ options = this.normalize_options(url_defaults, required_parameters, optional_parts, args);
205
+ parameters = options['url_parameters'];
187
206
  result = "" + (this.get_prefix()) + (this.visit(route, parameters));
188
207
  url = Utils.clean_path("" + result);
189
- if (trailing_slash === true) {
208
+ if (options['trailing_slash'] === true) {
190
209
  url = url.replace(/(.*?)[\/]?$/, "$1/");
191
210
  }
192
211
  if ((url_params = this.serialize(parameters)).length) {
193
212
  url += "?" + url_params;
194
213
  }
195
- url += anchor;
214
+ url += options.anchor ? "#" + options.anchor : "";
215
+ if (url_defaults) {
216
+ url = this.route_url(options) + url;
217
+ }
196
218
  return url;
197
219
  },
198
220
  visit: function(route, parameters, optional) {
199
221
  var left, left_part, right, right_part, type, value;
222
+
200
223
  if (optional == null) {
201
224
  optional = false;
202
225
  }
@@ -235,6 +258,7 @@ Based on Rails routes of APP_CLASS
235
258
  },
236
259
  build_path_spec: function(route, wildcard) {
237
260
  var left, right, type;
261
+
238
262
  if (wildcard == null) {
239
263
  wildcard = false;
240
264
  }
@@ -263,6 +287,7 @@ Based on Rails routes of APP_CLASS
263
287
  },
264
288
  visit_globbing: function(route, parameters, optional) {
265
289
  var left, right, type, value;
290
+
266
291
  type = route[0], left = route[1], right = route[2];
267
292
  if (left.replace(/^\*/i, "") !== left) {
268
293
  route[1] = left = left.replace(/^\*/i, "");
@@ -283,16 +308,18 @@ Based on Rails routes of APP_CLASS
283
308
  },
284
309
  get_prefix: function() {
285
310
  var prefix;
311
+
286
312
  prefix = defaults.prefix;
287
313
  if (prefix !== "") {
288
- prefix = (prefix.match("/$") ? prefix : prefix + "/");
314
+ prefix = (prefix.match("/$") ? prefix : "" + prefix + "/");
289
315
  }
290
316
  return prefix;
291
317
  },
292
- route: function(required_parts, optional_parts, route_spec) {
318
+ route: function(required_parts, optional_parts, route_spec, url_defaults) {
293
319
  var path_fn;
320
+
294
321
  path_fn = function() {
295
- return Utils.build_path(required_parts, optional_parts, route_spec, arguments);
322
+ return Utils.build_route(required_parts, optional_parts, route_spec, url_defaults, arguments);
296
323
  };
297
324
  path_fn.required_params = required_parts;
298
325
  path_fn.toString = function() {
@@ -300,16 +327,49 @@ Based on Rails routes of APP_CLASS
300
327
  };
301
328
  return path_fn;
302
329
  },
330
+ route_url: function(route_defaults) {
331
+ var hostname, port, protocol;
332
+
333
+ if (typeof route_defaults === 'string') {
334
+ return route_defaults;
335
+ }
336
+ protocol = route_defaults.protocol || Utils.current_protocol();
337
+ hostname = route_defaults.host || window.location.hostname;
338
+ port = route_defaults.port || (!route_defaults.host ? Utils.current_port() : void 0);
339
+ port = port ? ":" + port : '';
340
+ return protocol + "://" + hostname + port;
341
+ },
342
+ has_location: function() {
343
+ return typeof window !== 'undefined' && typeof window.location !== 'undefined';
344
+ },
345
+ current_host: function() {
346
+ return this.has_location() && window.location.hostname;
347
+ },
348
+ current_protocol: function() {
349
+ if (this.has_location() && window.location.protocol !== '') {
350
+ return window.location.protocol.replace(/:$/, '');
351
+ } else {
352
+ return 'http';
353
+ }
354
+ },
355
+ current_port: function() {
356
+ if (this.has_location() && window.location.port !== '') {
357
+ return window.location.port;
358
+ } else {
359
+ return '';
360
+ }
361
+ },
303
362
  _classToTypeCache: null,
304
363
  _classToType: function() {
305
- var j, len, name, ref;
364
+ var name, _i, _len, _ref;
365
+
306
366
  if (this._classToTypeCache != null) {
307
367
  return this._classToTypeCache;
308
368
  }
309
369
  this._classToTypeCache = {};
310
- ref = "Boolean Number String Function Array Date RegExp Object Error".split(" ");
311
- for (j = 0, len = ref.length; j < len; j++) {
312
- name = ref[j];
370
+ _ref = "Boolean Number String Function Array Date RegExp Object Error".split(" ");
371
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
372
+ name = _ref[_i];
313
373
  this._classToTypeCache["[object " + name + "]"] = name.toLowerCase();
314
374
  }
315
375
  return this._classToTypeCache;
@@ -331,8 +391,10 @@ Based on Rails routes of APP_CLASS
331
391
 
332
392
  createGlobalJsRoutesObject = function() {
333
393
  var namespace;
394
+
334
395
  namespace = function(mainRoot, namespaceString) {
335
396
  var current, parts;
397
+
336
398
  parts = (namespaceString ? namespaceString.split(".") : []);
337
399
  if (!parts.length) {
338
400
  return;
data/lib/routes.js.coffee CHANGED
@@ -13,6 +13,14 @@ defaults =
13
13
 
14
14
  NodeTypes = NODE_TYPES
15
15
 
16
+ ReservedOptions = [
17
+ 'anchor'
18
+ 'trailing_slash'
19
+ 'host'
20
+ 'port'
21
+ 'protocol'
22
+ ]
23
+
16
24
  Utils =
17
25
 
18
26
  default_serializer: (object, prefix = null) ->
@@ -53,34 +61,14 @@ Utils =
53
61
  path[last_index] = path[last_index].replace(/\/+/g, "/")
54
62
  path.join "://"
55
63
 
56
- set_default_url_options: (optional_parts, options) ->
57
- for part, i in optional_parts when (not options.hasOwnProperty(part) and defaults.default_url_options.hasOwnProperty(part))
58
- options[part] = defaults.default_url_options[part]
59
-
60
- extract_anchor: (options) ->
61
- anchor = ""
62
- if options.hasOwnProperty("anchor")
63
- anchor = "##{options.anchor}"
64
- delete options.anchor
65
- anchor
66
-
67
- extract_trailing_slash: (options) ->
68
- trailing_slash = false
69
- if defaults.default_url_options.hasOwnProperty("trailing_slash")
70
- trailing_slash = defaults.default_url_options.trailing_slash
71
- if options.hasOwnProperty("trailing_slash")
72
- trailing_slash = options.trailing_slash
73
- delete options.trailing_slash
74
- trailing_slash
75
-
76
64
  extract_options: (number_of_params, args) ->
77
65
  last_el = args[args.length - 1]
78
- if args.length > number_of_params or (last_el? and "object" is @get_object_type(last_el) and !@look_like_serialized_model(last_el))
79
- args.pop()
66
+ if (args.length > number_of_params and last_el == undefined) or(last_el? and "object" is @get_object_type(last_el) and !@looks_like_serialized_model(last_el))
67
+ args.pop() || {}
80
68
  else
81
69
  {}
82
70
 
83
- look_like_serialized_model: (object) ->
71
+ looks_like_serialized_model: (object) ->
84
72
  # consider object a model if it have a path identifier properties like id and to_param
85
73
  "id" of object or "to_param" of object
86
74
 
@@ -107,33 +95,47 @@ Utils =
107
95
  copy[key] = attr for own key, attr of obj
108
96
  copy
109
97
 
110
- prepare_parameters: (required_parameters, actual_parameters, options) ->
111
- result = @clone(options) or {}
112
- for val, i in required_parameters when i < actual_parameters.length
113
- result[val] = actual_parameters[i]
98
+ merge: (xs...) ->
99
+ tap = (o, fn) -> fn(o); o
100
+ if xs?.length > 0
101
+ tap {}, (m) -> m[k] = v for k, v of x for x in xs
102
+
103
+ normalize_options: (url_defaults, required_parameters, optional_parts, actual_parameters) ->
104
+ options = @extract_options(required_parameters.length, actual_parameters)
105
+ if actual_parameters.length > required_parameters.length
106
+ throw new Error("Too many parameters provided for path")
107
+ options = @merge(defaults.default_url_options, url_defaults, options)
108
+ result = {}
109
+ url_parameters = {}
110
+ result['url_parameters'] = url_parameters
111
+ for own key, value of options
112
+ if ReservedOptions.indexOf(key) >= 0
113
+ result[key] = value
114
+ else
115
+ url_parameters[key] = value
116
+
117
+ for value, i in required_parameters when i < actual_parameters.length
118
+ url_parameters[value] = actual_parameters[i]
114
119
  result
115
120
 
116
- build_path: (required_parameters, optional_parts, route, args) ->
121
+ build_route: (required_parameters, optional_parts, route, url_defaults, args) ->
117
122
  args = Array::slice.call(args)
118
- opts = @extract_options(required_parameters.length, args)
119
123
 
120
- if args.length > required_parameters.length
121
- throw new Error("Too many parameters provided for path")
122
- parameters = @prepare_parameters(required_parameters, args, opts)
123
- @set_default_url_options optional_parts, parameters
124
- # options
125
- anchor = @extract_anchor(parameters)
126
- trailing_slash = @extract_trailing_slash(parameters)
124
+ options = @normalize_options(url_defaults, required_parameters, optional_parts, args)
125
+ parameters = options['url_parameters']
126
+
127
127
  # path
128
128
  result = "#{@get_prefix()}#{@visit(route, parameters)}"
129
129
  url = Utils.clean_path("#{result}")
130
130
  # set trailing_slash
131
- url = url.replace(/(.*?)[\/]?$/, "$1/") if trailing_slash is true
131
+ url = url.replace(/(.*?)[\/]?$/, "$1/") if options['trailing_slash'] is true
132
132
  # set additional url params
133
133
  if (url_params = @serialize(parameters)).length
134
134
  url += "?#{url_params}"
135
135
  # set anchor
136
- url += anchor
136
+ url += if options.anchor then "##{options.anchor}" else ""
137
+ if url_defaults
138
+ url = @route_url(options) + url
137
139
  url
138
140
 
139
141
  #
@@ -229,12 +231,43 @@ Utils =
229
231
  #
230
232
  # route function: create route path function and add spec to it
231
233
  #
232
- route: (required_parts, optional_parts, route_spec) ->
233
- path_fn = -> Utils.build_path(required_parts, optional_parts, route_spec, arguments)
234
+ route: (required_parts, optional_parts, route_spec, url_defaults) ->
235
+ path_fn = ->
236
+ Utils.build_route(required_parts, optional_parts, route_spec, url_defaults, arguments)
234
237
  path_fn.required_params = required_parts
235
238
  path_fn.toString = -> Utils.build_path_spec(route_spec)
236
239
  path_fn
237
240
 
241
+
242
+ route_url: (route_defaults) ->
243
+ return route_defaults if typeof route_defaults == 'string'
244
+ protocol = route_defaults.protocol || Utils.current_protocol()
245
+ hostname = route_defaults.host || window.location.hostname
246
+ port = route_defaults.port || (Utils.current_port() unless route_defaults.host)
247
+ port = if port then ":#{port}" else ''
248
+
249
+ protocol + "://" + hostname + port
250
+
251
+
252
+ has_location: ->
253
+ typeof window != 'undefined' && typeof window.location != 'undefined'
254
+
255
+ current_host: ->
256
+ @has_location() && window.location.hostname
257
+
258
+ current_protocol: () ->
259
+ if @has_location() && window.location.protocol != ''
260
+ # location.protocol includes the colon character
261
+ window.location.protocol.replace(/:$/, '')
262
+ else
263
+ 'http'
264
+
265
+ current_port: () ->
266
+ if @has_location() && window.location.port != ''
267
+ window.location.port
268
+ else
269
+ ''
270
+
238
271
  #
239
272
  # This is helper method to define object type.
240
273
  # The typeof operator is probably the biggest design flaw of JavaScript, simply because it's basically completely broken.
@@ -138,15 +138,17 @@ describe JsRoutes, "options" do
138
138
  expect(evaljs("Routes.inbox_path(1, {format: null})")).to eq(routes.inbox_path(1))
139
139
  end
140
140
 
141
- it "shouldn't include the format when {:format => false} is specified" do
142
- expect(evaljs("Routes.no_format_path()")).to eq(routes.no_format_path)
143
- end
144
141
 
145
142
  it "shouldn't require the format" do
146
143
  expect(evaljs("Routes.json_only_path()")).to eq(routes.json_only_path(:format => 'json'))
147
144
  end
148
145
  end
149
146
 
147
+ it "shouldn't include the format when {:format => false} is specified" do
148
+ expect(evaljs("Routes.no_format_path()")).to eq(routes.no_format_path())
149
+ expect(evaljs("Routes.no_format_path({format: 'json'})")).to eq(routes.no_format_path(format: 'json'))
150
+ end
151
+
150
152
  describe "when namespace option is specified" do
151
153
  let(:_options) { {:namespace => "PHM"} }
152
154
  it "should use this namespace for routing" do
@@ -192,7 +194,6 @@ describe JsRoutes, "options" do
192
194
  context "with required route parts" do
193
195
  let(:_options) { {:default_url_options => {:inbox_id => "12"}} }
194
196
  it "should use this opions to fill optional parameters" do
195
- pending
196
197
  expect(evaljs("Routes.inbox_messages_path()")).to eq(routes.inbox_messages_path(:inbox_id => 12))
197
198
  end
198
199
  end
@@ -308,12 +309,6 @@ describe JsRoutes, "options" do
308
309
  end
309
310
 
310
311
  context "when configuring with default_url_options" do
311
- context "when default host is not specified" do
312
- it "raises an error" do
313
- expect { JsRoutes.generate({ :url_links => true }) }.to raise_error RuntimeError
314
- end
315
- end
316
-
317
312
  context "when only host option is specified" do
318
313
  let(:_options) { { :url_links => true, :default_url_options => {:host => "example.com"} } }
319
314
 
@@ -366,7 +361,7 @@ describe JsRoutes, "options" do
366
361
  end
367
362
 
368
363
  it "does not override host, protocol, or port when host is specified in route" do
369
- expect(evaljs("Routes.sso_url()")).to eq(routes.sso_url)
364
+ expect(evaljs("Routes.sso_url()")).to eq("http://sso.example.com:3000" + routes.sso_path)
370
365
  end
371
366
 
372
367
  it "does not override port when specified in route" do
@@ -404,6 +399,54 @@ describe JsRoutes, "options" do
404
399
  end
405
400
  end
406
401
  end
402
+
403
+ context 'when window.location is present' do
404
+ let(:current_protocol) { 'http:' } # window.location.protocol includes the colon character
405
+ let(:current_hostname) { 'current.example.com' }
406
+ let(:current_port){ '' } # an empty string means port 80
407
+ let(:current_host) do
408
+ host = "#{current_hostname}"
409
+ host += ":#{current_port}" unless current_port == ''
410
+ host
411
+ end
412
+
413
+ before do
414
+ jscontext['window'] = {
415
+ :location => {
416
+ :protocol => current_protocol,
417
+ :hostname => current_hostname,
418
+ :port => current_port,
419
+ :host => current_host
420
+ }
421
+ }
422
+ end
423
+
424
+ context "without specifying a default host" do
425
+ let(:_options) { { :url_links => true } }
426
+
427
+ it "uses the current host" do
428
+ expect(evaljs("Routes.inbox_path")).not_to be_nil
429
+ expect(evaljs("Routes.inbox_url")).not_to be_nil
430
+ expect(evaljs("Routes.inbox_url(1)")).to eq("http://current.example.com#{routes.inbox_path(1)}")
431
+ expect(evaljs("Routes.inbox_url(1, { test_key: \"test_val\" })")).to eq("http://current.example.com#{routes.inbox_path(1, :test_key => "test_val")}")
432
+ expect(evaljs("Routes.new_session_url()")).to eq("https://current.example.com#{routes.new_session_path}")
433
+ expect(evaljs("Routes.sso_url()")).to eq("http://sso.example.com#{routes.sso_path}")
434
+
435
+ end
436
+
437
+ it "uses host option as an argument" do
438
+ expect(evaljs("Routes.portals_url({host: 'another.com'})")).to eq(routes.portals_url(host: 'another.com'))
439
+ end
440
+
441
+ it "uses port option as an argument" do
442
+ expect(evaljs("Routes.portals_url({host: 'localhost', port: 8080})")).to eq(routes.portals_url(host: 'localhost', port: 8080))
443
+ end
444
+
445
+ it "uses protocol option as an argument" do
446
+ expect(evaljs("Routes.portals_url({host: 'localhost', protocol: 'https'})")).to eq(routes.portals_url(protocol: 'https', host: 'localhost'))
447
+ end
448
+ end
449
+ end
407
450
  end
408
451
 
409
452
  describe "when the compact mode is enabled" do
data/spec/spec_helper.rb CHANGED
@@ -147,6 +147,22 @@ RSpec.configure do |config|
147
147
 
148
148
  config.before :each do
149
149
  evaljs("var window = this;", true)
150
- jscontext[:log] = lambda {|context, value| puts value.inspect}
150
+
151
+ def inspectify(value)
152
+ case value
153
+ when V8::Object
154
+ value.to_h.map do |k,v|
155
+ [k, inspectify(v)]
156
+ end.to_h
157
+ when String, nil
158
+ value
159
+ else
160
+ raise "wtf #{value.class}?"
161
+ end
162
+
163
+ end
164
+ jscontext[:log] = lambda do |context, value|
165
+ puts inspectify(value).to_json
166
+ end
151
167
  end
152
168
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: js-routes
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bogdan Gusiev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-14 00:00:00.000000000 Z
11
+ date: 2015-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties