js-routes 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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