diff --git a/CHANGELOG.md b/CHANGELOG.md index 8103d857..b700a7a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## master + * Add the compact mode [#125](https://github.com/railsware/js-routes/pull/125) + * Add support for host, protocol, and port configuration [#137](https://github.com/railsware/js-routes/pull/137) + * Routes path specs [#135](https://github.com/railsware/js-routes/pull/135) + ## v0.9.9 * Bugfix Rails Engine subapplication route generation when they are nested [#120](https://github.com/railsware/js-routes/pull/120) diff --git a/Readme.md b/Readme.md index 913a86c5..b5211ed2 100644 --- a/Readme.md +++ b/Readme.md @@ -1,5 +1,5 @@ # JsRoutes -[![Build Status](https://travis-ci.org/railsware/js-routes.png)](https://travis-ci.org/railsware/js-routes) +[![Build Status](https://travis-ci.org/railsware/js-routes.svg?branch=master)](https://travis-ci.org/railsware/js-routes) Generates javascript file that defines all Rails named routes as javascript helpers @@ -134,6 +134,29 @@ In order to make routes helpers available globally: jQuery.extend(window, Routes) ``` +## Get spec of routes and required params + +Possible to get `spec` of route by function `toString`: + +```js +Routes.users_path.toString() // => "/users(.:format)" +Routes.user_path.toString() // => "/users/:id(.:format)" +``` + +This function allow to get the same `spec` for route, if you will get string representation of the route function: + +```js +'' + Routes.users_path // => "/users(.:format)", a string representation of the object +'' + Routes.user_path // => "/users/:id(.:format)" +``` + +Route function also contain inside attribute `required_params` required param names as array: + +```js +Routes.users_path.required_params // => [] +Routes.user_path.required_params // => ['id'] +``` + ## What about security? js-routes itself do not have security holes. It makes URLs diff --git a/lib/js_routes.rb b/lib/js_routes.rb index 3237c16e..2d957c81 100644 --- a/lib/js_routes.rb +++ b/lib/js_routes.rb @@ -159,9 +159,8 @@ def build_js(route, parent_route) url_link = generate_url_link(name, route_name, required_parts, route) _ = <<-JS.strip! // #{name.join('.')} => #{parent_spec}#{route.path.spec} - #{route_name}: function(#{build_params(required_parts)}) { - return Utils.build_path(#{json(required_parts)}, #{json(optional_parts)}, #{json(serialize(route.path.spec, parent_spec))}, arguments); - }#{",\n" + url_link if url_link.length > 0} + // function(#{[required_parts, LAST_OPTIONS_KEY].flatten.join(', ')}) + #{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} JS end @@ -204,7 +203,7 @@ def json(string) self.class.json(string) end - def build_params required_parts + def build_params(required_parts) params = required_parts.map do |name| # prepending each parameter name with underscore # to prevent conflict with JS reserved words diff --git a/lib/routes.js b/lib/routes.js index 9ab3942d..ca233d27 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -222,6 +222,34 @@ throw new Error("Unknown Rails node type"); } }, + build_path_spec: function(route, wildcard) { + var left, right, type; + if (wildcard == null) { + wildcard = false; + } + type = route[0], left = route[1], right = route[2]; + switch (type) { + case NodeTypes.GROUP: + return "(" + (this.build_path_spec(left)) + ")"; + case NodeTypes.CAT: + return "" + (this.build_path_spec(left)) + (this.build_path_spec(right)); + case NodeTypes.STAR: + return this.build_path_spec(left, true); + case NodeTypes.SYMBOL: + if (wildcard === true) { + return "" + (left[0] === '*' ? '' : '*') + left; + } else { + return ":" + left; + } + break; + case NodeTypes.SLASH: + case NodeTypes.DOT: + case NodeTypes.LITERAL: + return left; + default: + throw new Error("Unknown Rails node type"); + } + }, visit_globbing: function(route, parameters, optional) { var left, right, type, value; type = route[0], left = route[1], right = route[2]; @@ -250,6 +278,17 @@ } return prefix; }, + route: function(required_parts, optional_parts, route_spec) { + var path_fn; + path_fn = function() { + return Utils.build_path(required_parts, optional_parts, route_spec, arguments); + }; + path_fn.required_params = required_parts; + path_fn.toString = function() { + return Utils.build_path_spec(route_spec); + }; + return path_fn; + }, _classToTypeCache: null, _classToType: function() { var name, _i, _len, _ref; diff --git a/lib/routes.js.coffee b/lib/routes.js.coffee index bde28bf4..a0f68d7e 100644 --- a/lib/routes.js.coffee +++ b/lib/routes.js.coffee @@ -167,6 +167,29 @@ Utils = else throw new Error("Unknown Rails node type") + # + # This method build spec for route + # + build_path_spec: (route, wildcard=false) -> + [type, left, right] = route + switch type + when NodeTypes.GROUP + "(#{@build_path_spec(left)})" + when NodeTypes.CAT + "#{@build_path_spec(left)}#{@build_path_spec(right)}" + when NodeTypes.STAR + @build_path_spec(left, true) + when NodeTypes.SYMBOL + if wildcard is true + "#{if left[0] is '*' then '' else '*'}#{left}" + else + ":#{left}" + when NodeTypes.SLASH, NodeTypes.DOT, NodeTypes.LITERAL + left + # Not sure about this one + # when NodeTypes.OR + else throw new Error("Unknown Rails node type") + # # This method convert value for globbing in right value for rails route # @@ -191,6 +214,15 @@ Utils = prefix = (if prefix.match("/$") then prefix else "#{prefix}/") if prefix isnt "" prefix + # + # route function: create route path function and add spec to it + # + route: (required_parts, optional_parts, route_spec) -> + path_fn = -> Utils.build_path(required_parts, optional_parts, route_spec, arguments) + path_fn.required_params = required_parts + path_fn.toString = -> Utils.build_path_spec(route_spec) + path_fn + # # This is helper method to define object type. # The typeof operator is probably the biggest design flaw of JavaScript, simply because it's basically completely broken. diff --git a/spec/js_routes/generated_javascript_spec.rb b/spec/js_routes/generated_javascript_spec.rb index 82c4fc60..d86519cb 100644 --- a/spec/js_routes/generated_javascript_spec.rb +++ b/spec/js_routes/generated_javascript_spec.rb @@ -9,14 +9,24 @@ describe "generated js" do subject { JsRoutes.generate } + it "should call route function for each route" do + is_expected.to include("inboxes_path: Utils.route(") + end it "should have correct function without arguments signature" do - is_expected.to include("inboxes_path: function(options)") + is_expected.to include("inboxes_path: Utils.route([]") end it "should have correct function with arguments signature" do - is_expected.to include("inbox_message_path: function(_inbox_id, _id, options)") + is_expected.to include("inbox_message_path: Utils.route([\"inbox_id\",\"id\"]") end it "should have correct function signature with unordered hash" do - is_expected.to include("inbox_message_attachment_path: function(_inbox_id, _message_id, _id, options)") + is_expected.to include("inbox_message_attachment_path: Utils.route([\"inbox_id\",\"message_id\",\"id\"]") + end + + it "should have correct function comment with options argument" do + is_expected.to include("// function(options)\n inboxes_path: Utils.route") + end + it "should have correct function comment with arguments" do + is_expected.to include("// function(inbox_id, message_id, options)\n new_inbox_message_attachment_path: Utils.route") end it "routes should be sorted in alphabetical order" do @@ -51,7 +61,7 @@ describe "compiled javascript asset" do subject { ERB.new(File.read("app/assets/javascripts/js-routes.js.erb")).result(binding) } it "should have js routes code" do - is_expected.to include("inbox_message_path: function(_inbox_id, _id, options)") + is_expected.to include("inbox_message_path: Utils.route([\"inbox_id\",\"id\"]") end end end diff --git a/spec/js_routes/rails_routes_compatibility_spec.rb b/spec/js_routes/rails_routes_compatibility_spec.rb index c1ed6ec9..4575fc2d 100644 --- a/spec/js_routes/rails_routes_compatibility_spec.rb +++ b/spec/js_routes/rails_routes_compatibility_spec.rb @@ -296,4 +296,32 @@ end end end + + context "when specs" do + it "should show inbox spec" do + expect(evaljs("Routes.inbox_path.toString()")).to eq('/inboxes/:id(.:format)') + end + + it "should show inbox spec convert to string" do + expect(evaljs("'' + Routes.inbox_path")).to eq('/inboxes/:id(.:format)') + end + + it "should show inbox message spec" do + expect(evaljs("Routes.inbox_message_path.toString()")).to eq('/inboxes/:inbox_id/messages/:id(.:format)') + end + + it "should show inbox message spec convert to string" do + expect(evaljs("'' + Routes.inbox_message_path")).to eq('/inboxes/:inbox_id/messages/:id(.:format)') + end + end + + context "when params" do + it "should show inbox spec" do + expect(evaljs("Routes.inbox_path.required_params").to_a).to eq(["id"]) + end + + it "should show inbox message spec" do + expect(evaljs("Routes.inbox_message_path.required_params").to_a).to eq(["inbox_id", "id"]) + end + end end