js-routes 0.8.8 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/Readme.md CHANGED
@@ -61,6 +61,8 @@ Available options:
61
61
  * Default: blank
62
62
  * `camel_case` (version >= 0.8.8) - Generate camel case route names.
63
63
  * Default: false
64
+ * `url_links` (version >= 0.8.9) - Generate additional url links, where url_links value is beginning of url routes (ex: http[s]://example.com).
65
+ * Default: false
64
66
 
65
67
  You can generate routes files on the application side like this:
66
68
 
@@ -1,3 +1,3 @@
1
1
  class JsRoutes
2
- VERSION = "0.8.8"
2
+ VERSION = "0.9.0"
3
3
  end
data/lib/js_routes.rb CHANGED
@@ -1,4 +1,6 @@
1
+ require 'uri'
1
2
  require 'js_routes/version'
3
+
2
4
  class JsRoutes
3
5
 
4
6
  #
@@ -12,7 +14,8 @@ class JsRoutes
12
14
  :exclude => [],
13
15
  :include => //,
14
16
  :file => DEFAULT_PATH,
15
- :prefix => "",
17
+ :prefix => nil,
18
+ :url_links => nil,
16
19
  :camel_case => false,
17
20
  :default_url_options => {}
18
21
  }
@@ -29,6 +32,8 @@ class JsRoutes
29
32
  :DOT => 8
30
33
  }
31
34
 
35
+ LAST_OPTIONS_KEY = "options".freeze
36
+
32
37
  class Options < Struct.new(*DEFAULTS.keys)
33
38
  def to_hash
34
39
  Hash[*members.zip(values).flatten(1)].symbolize_keys
@@ -89,7 +94,7 @@ class JsRoutes
89
94
  js = File.read(File.dirname(__FILE__) + "/routes.js")
90
95
  js.gsub!("NAMESPACE", @options[:namespace])
91
96
  js.gsub!("DEFAULT_URL_OPTIONS", json(@options[:default_url_options].merge(deprecated_default_format)))
92
- js.gsub!("PREFIX", @options[:prefix])
97
+ js.gsub!("PREFIX", @options[:prefix] || "")
93
98
  js.gsub!("NODE_TYPES", json(NODE_TYPES))
94
99
  js.gsub!("ROUTES", js_routes)
95
100
  end
@@ -148,19 +153,34 @@ class JsRoutes
148
153
  def build_js(route, parent_route)
149
154
  name = [parent_route.try(:name), route.name].compact
150
155
  parent_spec = parent_route.try(:path).try(:spec)
151
- required_parts = route.required_parts.clone
152
- optional_parts = route.optional_parts.clone
156
+ required_parts, optional_parts = route.required_parts.clone, route.optional_parts.clone
153
157
  optional_parts.push(required_parts.delete :format) if required_parts.include?(:format)
154
- route_name = "#{name.join('_')}_path"
155
- route_name = route_name.camelize(:lower) if true == @options[:camel_case]
158
+ route_name = generate_route_name(name)
159
+ url_link = generate_url_link(name, route_name, required_parts)
156
160
  _ = <<-JS.strip!
157
161
  // #{name.join('.')} => #{parent_spec}#{route.path.spec}
158
162
  #{route_name}: function(#{build_params(required_parts)}) {
163
+ if (!#{LAST_OPTIONS_KEY}){ #{LAST_OPTIONS_KEY} = {}; }
159
164
  return Utils.build_path(#{json(required_parts)}, #{json(optional_parts)}, #{json(serialize(route.path.spec, parent_spec))}, arguments);
160
- }
165
+ }#{",\n" + url_link if url_link.length > 0}
161
166
  JS
162
167
  end
163
168
 
169
+ def generate_url_link(name, route_name, required_parts)
170
+ return "" unless @options[:url_links]
171
+ raise "invalid URL format in url_links (ex: http[s]://example.com)" if @options[:url_links].match(URI::regexp(%w(http https))).nil?
172
+ _ = <<-JS.strip!
173
+ #{generate_route_name(name, true)}: function(#{build_params(required_parts)}) {
174
+ return "" + #{@options[:url_links].inspect} + this.#{route_name}(#{build_params(required_parts)});
175
+ }
176
+ JS
177
+ end
178
+
179
+ def generate_route_name(name, is_url = false)
180
+ route_name = "#{name.join('_')}_#{is_url ? "url" : "path"}"
181
+ @options[:camel_case] ? route_name.camelize(:lower) : route_name
182
+ end
183
+
164
184
  def json(string)
165
185
  self.class.json(string)
166
186
  end
@@ -169,8 +189,8 @@ class JsRoutes
169
189
  params = required_parts.map do |name|
170
190
  # prepending each parameter name with underscore
171
191
  # to prevent conflict with JS reserved words
172
- "_" + name.to_s
173
- end << "options"
192
+ "_#{name}"
193
+ end << LAST_OPTIONS_KEY
174
194
  params.join(", ")
175
195
  end
176
196
 
data/lib/routes.js CHANGED
@@ -24,14 +24,14 @@
24
24
  }
25
25
  if (window.jQuery) {
26
26
  result = window.jQuery.param(obj);
27
- return (!result ? "" : "?" + result);
27
+ return (!result ? "" : result);
28
28
  }
29
29
  s = [];
30
30
  for (key in obj) {
31
31
  if (!__hasProp.call(obj, key)) continue;
32
32
  prop = obj[key];
33
33
  if (prop != null) {
34
- if (prop instanceof Array) {
34
+ if (this.getObjectType(prop) === "array") {
35
35
  for (i = _i = 0, _len = prop.length; _i < _len; i = ++_i) {
36
36
  val = prop[i];
37
37
  s.push("" + key + (encodeURIComponent("[]")) + "=" + (encodeURIComponent(val.toString())));
@@ -44,7 +44,7 @@
44
44
  if (!s.length) {
45
45
  return "";
46
46
  }
47
- return "?" + (s.join("&"));
47
+ return s.join("&");
48
48
  },
49
49
  clean_path: function(path) {
50
50
  var last_index;
@@ -74,7 +74,7 @@
74
74
  anchor = "";
75
75
  if (options.hasOwnProperty("anchor")) {
76
76
  anchor = "#" + options.anchor;
77
- options.anchor = null;
77
+ delete options.anchor;
78
78
  }
79
79
  return anchor;
80
80
  },
@@ -82,7 +82,7 @@
82
82
  var ret_value;
83
83
 
84
84
  ret_value = {};
85
- if (args.length > number_of_params && typeof args[args.length - 1] === "object") {
85
+ if (args.length > number_of_params && this.getObjectType(args[args.length - 1]) === "object") {
86
86
  ret_value = args.pop();
87
87
  }
88
88
  return ret_value;
@@ -97,9 +97,9 @@
97
97
  return "";
98
98
  }
99
99
  property = object;
100
- if (typeof object === "object") {
100
+ if (this.getObjectType(object) === "object") {
101
101
  property = object.to_param || object.id || object;
102
- if (typeof property === "function") {
102
+ if (this.getObjectType(property) === "function") {
103
103
  property = property.call(object);
104
104
  }
105
105
  }
@@ -108,7 +108,7 @@
108
108
  clone: function(obj) {
109
109
  var attr, copy, key;
110
110
 
111
- if (null === obj || "object" !== typeof obj) {
111
+ if ((obj == null) || "object" !== this.getObjectType(obj)) {
112
112
  return obj;
113
113
  }
114
114
  copy = obj.constructor();
@@ -130,7 +130,7 @@
130
130
  return result;
131
131
  },
132
132
  build_path: function(required_parameters, optional_parts, route, args) {
133
- var opts, parameters, result;
133
+ var opts, parameters, result, url, url_params;
134
134
 
135
135
  args = Array.prototype.slice.call(args);
136
136
  opts = this.extract_options(required_parameters.length, args);
@@ -139,17 +139,25 @@
139
139
  }
140
140
  parameters = this.prepare_parameters(required_parameters, args, opts);
141
141
  this.set_default_url_options(optional_parts, parameters);
142
- result = "" + (Utils.get_prefix()) + (this.visit(route, parameters));
143
- return Utils.clean_path("" + result + (Utils.extract_anchor(parameters))) + Utils.serialize(parameters);
142
+ result = "" + (this.get_prefix()) + (this.visit(route, parameters));
143
+ url = Utils.clean_path("" + result + (this.extract_anchor(parameters)));
144
+ if ((url_params = this.serialize(parameters)).length) {
145
+ url += "?" + url_params;
146
+ }
147
+ return url;
144
148
  },
145
149
  visit: function(route, parameters, optional) {
146
150
  var left, left_part, right, right_part, type, value;
147
151
 
152
+ if (optional == null) {
153
+ optional = false;
154
+ }
148
155
  type = route[0], left = route[1], right = route[2];
149
156
  switch (type) {
150
157
  case NodeTypes.GROUP:
151
- case NodeTypes.STAR:
152
158
  return this.visit(left, parameters, true);
159
+ case NodeTypes.STAR:
160
+ return this.visit_globbing(left, parameters, true);
153
161
  case NodeTypes.LITERAL:
154
162
  case NodeTypes.SLASH:
155
163
  case NodeTypes.DOT:
@@ -164,7 +172,7 @@
164
172
  case NodeTypes.SYMBOL:
165
173
  value = parameters[left];
166
174
  if (value != null) {
167
- parameters[left] = null;
175
+ delete parameters[left];
168
176
  return this.path_identifier(value);
169
177
  }
170
178
  if (optional) {
@@ -177,6 +185,24 @@
177
185
  throw new Error("Unknown Rails node type");
178
186
  }
179
187
  },
188
+ visit_globbing: function(route, parameters, optional) {
189
+ var left, right, type, value;
190
+
191
+ type = route[0], left = route[1], right = route[2];
192
+ value = parameters[left];
193
+ if (value == null) {
194
+ return this.visit(route, parameters, optional);
195
+ }
196
+ parameters[left] = (function() {
197
+ switch (this.getObjectType(value)) {
198
+ case "array":
199
+ return value.join("/");
200
+ default:
201
+ return value;
202
+ }
203
+ }).call(this);
204
+ return this.visit(route, parameters, optional);
205
+ },
180
206
  get_prefix: function() {
181
207
  var prefix;
182
208
 
@@ -186,6 +212,30 @@
186
212
  }
187
213
  return prefix;
188
214
  },
215
+ _classToTypeCache: null,
216
+ _classToType: function() {
217
+ var name, _i, _len, _ref;
218
+
219
+ if (this._classToTypeCache != null) {
220
+ return this._classToTypeCache;
221
+ }
222
+ this._classToTypeCache = {};
223
+ _ref = "Boolean Number String Function Array Date RegExp Undefined Null".split(" ");
224
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
225
+ name = _ref[_i];
226
+ this._classToTypeCache["[object " + name + "]"] = name.toLowerCase();
227
+ }
228
+ return this._classToTypeCache;
229
+ },
230
+ getObjectType: function(obj) {
231
+ var strType;
232
+
233
+ if (window.jQuery && (window.jQuery.type != null)) {
234
+ return window.jQuery.type(obj);
235
+ }
236
+ strType = Object.prototype.toString.call(obj);
237
+ return this._classToType()[strType] || "object";
238
+ },
189
239
  namespace: function(root, namespaceString) {
190
240
  var current, parts;
191
241
 
data/lib/routes.js.coffee CHANGED
@@ -6,21 +6,21 @@ defaults =
6
6
 
7
7
  NodeTypes = NODE_TYPES
8
8
  Utils =
9
+
9
10
  serialize: (obj) ->
10
11
  return "" unless obj
11
12
  if window.jQuery
12
13
  result = window.jQuery.param(obj)
13
- return (if not result then "" else "?#{result}")
14
+ return (if not result then "" else result)
14
15
  s = []
15
- for own key, prop of obj
16
- if prop?
17
- if prop instanceof Array
18
- for val, i in prop
19
- s.push "#{key}#{encodeURIComponent("[]")}=#{encodeURIComponent(val.toString())}"
20
- else
21
- s.push "#{key}=#{encodeURIComponent(prop.toString())}"
16
+ for own key, prop of obj when prop?
17
+ if @getObjectType(prop) is "array"
18
+ for val, i in prop
19
+ s.push "#{key}#{encodeURIComponent("[]")}=#{encodeURIComponent(val.toString())}"
20
+ else
21
+ s.push "#{key}=#{encodeURIComponent(prop.toString())}"
22
22
  return "" unless s.length
23
- "?#{s.join("&")}"
23
+ s.join("&")
24
24
 
25
25
  clean_path: (path) ->
26
26
  path = path.split("://")
@@ -37,12 +37,12 @@ Utils =
37
37
  anchor = ""
38
38
  if options.hasOwnProperty("anchor")
39
39
  anchor = "##{options.anchor}"
40
- options.anchor = null
40
+ delete options.anchor
41
41
  anchor
42
42
 
43
43
  extract_options: (number_of_params, args) ->
44
44
  ret_value = {}
45
- if args.length > number_of_params and typeof (args[args.length - 1]) is "object"
45
+ if args.length > number_of_params and @getObjectType(args[args.length - 1]) is "object"
46
46
  ret_value = args.pop()
47
47
  ret_value
48
48
 
@@ -51,13 +51,13 @@ Utils =
51
51
  # null, undefined, false or ''
52
52
  return "" unless object
53
53
  property = object
54
- if typeof (object) is "object"
54
+ if @getObjectType(object) is "object"
55
55
  property = object.to_param or object.id or object
56
- property = property.call(object) if typeof (property) is "function"
56
+ property = property.call(object) if @getObjectType(property) is "function"
57
57
  property.toString()
58
58
 
59
59
  clone: (obj) ->
60
- return obj if null is obj or "object" isnt typeof obj
60
+ return obj if !obj? or "object" isnt @getObjectType(obj)
61
61
  copy = obj.constructor()
62
62
  copy[key] = attr for own key, attr of obj
63
63
  copy
@@ -73,8 +73,10 @@ Utils =
73
73
  throw new Error("Too many parameters provided for path") if args.length > required_parameters.length
74
74
  parameters = @prepare_parameters(required_parameters, args, opts)
75
75
  @set_default_url_options optional_parts, parameters
76
- result = "#{Utils.get_prefix()}#{@visit(route, parameters)}"
77
- Utils.clean_path("#{result}#{Utils.extract_anchor(parameters)}") + Utils.serialize(parameters)
76
+ result = "#{@get_prefix()}#{@visit(route, parameters)}"
77
+ url = Utils.clean_path("#{result}#{@extract_anchor(parameters)}")
78
+ url += "?#{url_params}" if (url_params = @serialize(parameters)).length
79
+ url
78
80
  #
79
81
  # This function is JavaScript impelementation of the
80
82
  # Journey::Visitors::Formatter that builds route by given parameters
@@ -86,11 +88,13 @@ Utils =
86
88
  # If set to `true`, this method will not throw when encountering
87
89
  # a missing parameter (used in recursive calls).
88
90
  #
89
- visit: (route, parameters, optional) ->
91
+ visit: (route, parameters, optional = false) ->
90
92
  [type, left, right] = route
91
93
  switch type
92
- when NodeTypes.GROUP, NodeTypes.STAR
94
+ when NodeTypes.GROUP
93
95
  @visit left, parameters, true
96
+ when NodeTypes.STAR
97
+ @visit_globbing left, parameters, true
94
98
  when NodeTypes.LITERAL, NodeTypes.SLASH, NodeTypes.DOT
95
99
  left
96
100
  when NodeTypes.CAT
@@ -101,7 +105,7 @@ Utils =
101
105
  when NodeTypes.SYMBOL
102
106
  value = parameters[left]
103
107
  if value?
104
- parameters[left] = null
108
+ delete parameters[left]
105
109
  return @path_identifier(value)
106
110
  if optional
107
111
  "" # missing parameter
@@ -115,11 +119,65 @@ Utils =
115
119
  else
116
120
  throw new Error("Unknown Rails node type")
117
121
 
122
+ #
123
+ # This method convert value for globbing in right value for rails route
124
+ #
125
+ visit_globbing: (route, parameters, optional) ->
126
+ [type, left, right] = route
127
+ value = parameters[left]
128
+ return @visit(route, parameters, optional) unless value?
129
+ parameters[left] = switch @getObjectType(value)
130
+ when "array"
131
+ value.join("/")
132
+ else
133
+ value
134
+ @visit route, parameters, optional
135
+
136
+ #
137
+ # This method check and return prefix from options
138
+ #
118
139
  get_prefix: ->
119
140
  prefix = defaults.prefix
120
141
  prefix = (if prefix.match("/$") then prefix else "#{prefix}/") if prefix isnt ""
121
142
  prefix
122
143
 
144
+ #
145
+ # This is helper method to define object type.
146
+ # The typeof operator is probably the biggest design flaw of JavaScript, simply because it's basically completely broken.
147
+ #
148
+ # Value Class Type
149
+ # -------------------------------------
150
+ # "foo" String string
151
+ # new String("foo") String object
152
+ # 1.2 Number number
153
+ # new Number(1.2) Number object
154
+ # true Boolean boolean
155
+ # new Boolean(true) Boolean object
156
+ # new Date() Date object
157
+ # new Error() Error object
158
+ # [1,2,3] Array object
159
+ # new Array(1, 2, 3) Array object
160
+ # new Function("") Function function
161
+ # /abc/g RegExp object
162
+ # new RegExp("meow") RegExp object
163
+ # {} Object object
164
+ # new Object() Object object
165
+ #
166
+ # What is why I use Object.prototype.toString() to know better type of variable. Or use jQuery.type, if it available.
167
+ # _classToTypeCache used for perfomance cache of types map (underscore at the beginning mean private method - of course it doesn't realy private).
168
+ #
169
+ _classToTypeCache: null
170
+ _classToType: ->
171
+ return @_classToTypeCache if @_classToTypeCache?
172
+ @_classToTypeCache = {}
173
+ for name in "Boolean Number String Function Array Date RegExp Undefined Null".split(" ")
174
+ @_classToTypeCache["[object " + name + "]"] = name.toLowerCase()
175
+ @_classToTypeCache
176
+ getObjectType: (obj) ->
177
+ return window.jQuery.type(obj) if window.jQuery and window.jQuery.type?
178
+ strType = Object::toString.call(obj)
179
+ @_classToType()[strType] or "object"
180
+
123
181
  namespace: (root, namespaceString) ->
124
182
  parts = (if namespaceString then namespaceString.split(".") else [])
125
183
  return unless parts.length
@@ -129,4 +187,4 @@ Utils =
129
187
 
130
188
  Utils.namespace window, "NAMESPACE"
131
189
  window.NAMESPACE = ROUTES
132
- window.NAMESPACE.options = defaults
190
+ window.NAMESPACE.options = defaults
@@ -172,4 +172,51 @@ describe JsRoutes, "options" do
172
172
  end
173
173
  end
174
174
  end
175
+
176
+ describe "url_links" do
177
+ context "with default option" do
178
+ let(:_options) { Hash.new }
179
+ it "should generate only path links" do
180
+ evaljs("Routes.inbox_path(1)").should == routes.inbox_path(1)
181
+ evaljs("Routes.inbox_url").should be_nil
182
+ end
183
+ end
184
+
185
+ context "with host" do
186
+ let(:_options) { { :url_links => "http://localhost" } }
187
+ it "should generate path and url links" do
188
+ evaljs("Routes.inbox_path").should_not be_nil
189
+ evaljs("Routes.inbox_url").should_not be_nil
190
+ evaljs("Routes.inbox_path(1)").should == routes.inbox_path(1)
191
+ evaljs("Routes.inbox_url(1)").should == "http://localhost#{routes.inbox_path(1)}"
192
+ evaljs("Routes.inbox_url(1, { test_key: \"test_val\" })").should == "http://localhost#{routes.inbox_path(1, :test_key => "test_val")}"
193
+ end
194
+ end
195
+
196
+ context "with invalid host" do
197
+ it "should raise error" do
198
+ expect { JsRoutes.generate({ :url_links => "localhost" }) }.to raise_error RuntimeError
199
+ end
200
+ end
201
+
202
+ context "with host and camel_case" do
203
+ let(:_options) { { :camel_case => true, :url_links => "http://localhost" } }
204
+ it "should generate path and url links" do
205
+ evaljs("Routes.inboxPath").should_not be_nil
206
+ evaljs("Routes.inboxUrl").should_not be_nil
207
+ evaljs("Routes.inboxPath(1)").should == routes.inbox_path(1)
208
+ evaljs("Routes.inboxUrl(1)").should == "http://localhost#{routes.inbox_path(1)}"
209
+ end
210
+ end
211
+
212
+ context "with host and prefix" do
213
+ let(:_options) { { :prefix => "/api", :url_links => "https://example.com" } }
214
+ it "should generate path and url links" do
215
+ evaljs("Routes.inbox_path").should_not be_nil
216
+ evaljs("Routes.inbox_url").should_not be_nil
217
+ evaljs("Routes.inbox_path(1)").should == "/api#{routes.inbox_path(1)}"
218
+ evaljs("Routes.inbox_url(1)").should == "https://example.com/api#{routes.inbox_path(1)}"
219
+ end
220
+ end
221
+ end
175
222
  end
@@ -76,14 +76,28 @@ describe JsRoutes, "compatibility with Rails" do
76
76
  evaljs("Routes.book_path('thrillers', 1)").should == routes.book_path('thrillers', 1)
77
77
  end
78
78
 
79
- it "should support routes globbing as hash" do
80
- pending
81
- evaljs("Routes.book_path(1, {section: 'thrillers'})").should == routes.book_path(1, :section => 'thrillers')
79
+ it "should support routes globbing as array" do
80
+ evaljs("Routes.book_path(['thrillers'], 1)").should == routes.book_path(['thrillers'], 1)
81
+ end
82
+
83
+ it "should bee support routes globbing as array" do
84
+ evaljs("Routes.book_path([1, 2, 3], 1)").should == routes.book_path([1, 2, 3], 1)
82
85
  end
83
86
 
84
87
  it "should bee support routes globbing as hash" do
85
- pending
86
- evaljs("Routes.book_path(1)").should == routes.book_path(1)
88
+ evaljs("Routes.book_path('a_test/b_test/c_test', 1)").should == routes.book_path('a_test/b_test/c_test', 1)
89
+ end
90
+
91
+ it "should support routes globbing as array with optional params" do
92
+ evaljs("Routes.book_path([1, 2, 3, 5], 1, {c: '1'})").should == routes.book_path([1, 2, 3, 5], 1, { :c => "1" })
93
+ end
94
+
95
+ it "should support routes globbing in book_title route as array" do
96
+ evaljs("Routes.book_title_path('john', ['thrillers', 'comedian'])").should == routes.book_title_path('john', ['thrillers', 'comedian'])
97
+ end
98
+
99
+ it "should support routes globbing in book_title route as array with optional params" do
100
+ evaljs("Routes.book_title_path('john', ['thrillers', 'comedian'], {some_key: 'some_value'})").should == routes.book_title_path('john', ['thrillers', 'comedian'], {:some_key => 'some_value'})
87
101
  end
88
102
  end
89
103
 
@@ -98,6 +112,7 @@ describe JsRoutes, "compatibility with Rails" do
98
112
  it "should support serialization of objects" do
99
113
  evaljs("window.jQuery.param(#{_value.to_json})").should == _value.to_param
100
114
  evaljs("Routes.inboxes_path(#{_value.to_json})").should == routes.inboxes_path(_value)
115
+ evaljs("Routes.inbox_path(1, #{_value.to_json})").should == routes.inbox_path(1, _value)
101
116
  end
102
117
  end
103
118
  context "when parameters is a hash" do
@@ -202,5 +217,25 @@ describe JsRoutes, "compatibility with Rails" do
202
217
  "Routes.inbox_message_path({id:1, to_param: 'my'}, {id:2}, {custom: true, format: 'json'})"
203
218
  ).should == routes.inbox_message_path(inbox, 2, :custom => true, :format => "json")
204
219
  end
220
+
221
+ context "when globbing" do
222
+ it "should prefer to_param property over id property" do
223
+ evaljs("Routes.book_path({id: 1, to_param: 'my'}, 1)").should == routes.book_path(inbox, 1)
224
+ end
225
+
226
+ it "should call to_param if it is a function" do
227
+ evaljs("Routes.book_path({id: 1, to_param: function(){ return 'my';}}, 1)").should == routes.book_path(inbox, 1)
228
+ end
229
+
230
+ it "should call id if it is a function" do
231
+ evaljs("Routes.book_path({id: function() { return 'technical';}}, 1)").should == routes.book_path('technical', 1)
232
+ end
233
+
234
+ it "should support options argument" do
235
+ evaljs(
236
+ "Routes.book_path({id:1, to_param: 'my'}, {id:2}, {custom: true, format: 'json'})"
237
+ ).should == routes.book_path(inbox, 2, :custom => true, :format => "json")
238
+ end
239
+ end
205
240
  end
206
241
  end
data/spec/spec_helper.rb CHANGED
@@ -68,6 +68,7 @@ def draw_routes
68
68
  match "/other_optional/(:optional_id)" => "foo#foo", :as => :foo
69
69
 
70
70
  match 'books/*section/:title' => 'books#show', :as => :book
71
+ match 'books/:title/*section' => 'books#show', :as => :book_title
71
72
 
72
73
  mount BlogEngine::Engine => "/blog", :as => :blog_app
73
74
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: js-routes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.8
4
+ version: 0.9.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -169,7 +169,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
169
169
  version: '0'
170
170
  segments:
171
171
  - 0
172
- hash: -1021076515432540813
172
+ hash: 1949067975122087130
173
173
  required_rubygems_version: !ruby/object:Gem::Requirement
174
174
  none: false
175
175
  requirements: