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 +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
|