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 +4 -4
- data/CHANGELOG.md +11 -0
- data/Readme.md +3 -3
- data/lib/js_routes.rb +31 -32
- data/lib/js_routes/version.rb +1 -1
- data/lib/routes.js +131 -69
- data/lib/routes.js.coffee +73 -40
- data/spec/js_routes/options_spec.rb +54 -11
- data/spec/spec_helper.rb +17 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e61c1a78d1a68a39e174a3459048f939ac1a5491
|
4
|
+
data.tar.gz: 2690497a175312cb4fd282626eae3adf87718cbb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
*
|
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
|
-
*
|
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:
|
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
|
-
|
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(#{
|
184
|
-
#{route_name}: Utils.route(#{
|
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
|
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
|
-
|
191
|
-
|
192
|
-
|
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
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
port
|
208
|
-
|
209
|
-
|
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
|
data/lib/js_routes/version.rb
CHANGED
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
|
-
|
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,
|
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 =
|
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 (!
|
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.
|
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
|
-
|
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 (!
|
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
|
-
|
166
|
-
var
|
167
|
-
|
168
|
-
|
169
|
-
|
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
|
-
|
195
|
+
url_parameters[value] = actual_parameters[i];
|
172
196
|
}
|
173
197
|
}
|
174
198
|
return result;
|
175
199
|
},
|
176
|
-
|
177
|
-
var
|
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
|
-
|
180
|
-
|
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.
|
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
|
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
|
-
|
311
|
-
for (
|
312
|
-
name =
|
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
|
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
|
-
|
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
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
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
|
-
|
121
|
-
|
122
|
-
|
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 = ->
|
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.
|
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
|
-
|
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.
|
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-
|
11
|
+
date: 2015-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|