named-routes 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES ADDED
File without changes
data/MIT.LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Brian Takita
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # NamedRoutes
2
+
3
+ A simple and generic named routes api. It works really well with Sinatra.
4
+
5
+ ## Installation/Usage
6
+
7
+ gem install named-routes
8
+
9
+ ## Route Definitions
10
+
11
+ You can define a named route by providing the name of the method and the definition.
12
+
13
+ NamedRoutes.path(:user, "/users/:user_id") # => "/users/:user_id"
14
+
15
+ You can use this in conjunction with Sinatra routes like so:
16
+
17
+ include NamedRoutes
18
+ get path(:user, "/users/:user_id") do
19
+ # ...
20
+ end
21
+
22
+ If you have multiple handlers for the same route, you can use the block syntax:
23
+
24
+ include NamedRoutes
25
+ path(:user, "/users/:user_id") do |_|
26
+ get _ do
27
+ # ...
28
+ end
29
+
30
+ post _ do
31
+ # ...
32
+ end
33
+ end
34
+
35
+ You can also define prefixes on the route definitions:
36
+
37
+ include NamedRoutes
38
+ routes.prefix = "admin"
39
+
40
+ path(:user, "/users/:user_id") do |_| # => /admin/users/:user_id
41
+ get _ do
42
+ # ...
43
+ end
44
+
45
+ post _ do
46
+ # ...
47
+ end
48
+ end
49
+
50
+ ## Route Helpers
51
+
52
+ You can access the routes by doing the following.
53
+
54
+ include NamedRoutes
55
+ routes.host = "example.com"
56
+ path(:user, "/users/:user_id")
57
+ routes.user(:user_id => 42) # => "/users/42"
58
+ routes.http.user(:user_id => 42) # => "http://example.com/users/42"
59
+ routes.https.user(:user_id => 42) # => "http://example.com/users/42"
60
+
61
+ It also works with prefixes:
62
+
63
+ include NamedRoutes
64
+ routes.host = "example.com"
65
+ routes.prefix = "admin"
66
+ path(:user, "/users/:user_id")
67
+ routes.user(:user_id => 42) # => "/users/42"
68
+ routes.http.user(:user_id => 42) # => "http://example.com/admin/users/42"
69
+ routes.https.user(:user_id => 42) # => "http://example.com/admin/users/42"
70
+
71
+ And with query params:
72
+
73
+ include NamedRoutes
74
+ routes.host = "example.com"
75
+ routes.prefix = "admin"
76
+ path(:user, "/users/:user_id")
77
+ routes.user(:user_id => 42, :foo => "bar of soap") # => "/users/42&foo=bar+of+soap"
78
+
79
+ ## Advanced Usages
80
+
81
+ You can also inherit Routes to have different sets of Routes. This is useful if you want route sets with different prefixes.
82
+
83
+ class AdminRoutes < NamedRoutes::Routes
84
+ self.prefix = "admin"
85
+ end
86
+
87
+ class PartayRoutes < NamedRoutes::Routes
88
+ self.prefix = "partay"
89
+ end
90
+
91
+ def admin_routes
92
+ AdminRoutes
93
+ end
94
+
95
+ def partay_routes
96
+ PartayRoutes
97
+ end
98
+
99
+ get admin_routes.path(:user, "/users/:user_id") do # => /admin/users/:user_id
100
+ # ...
101
+ end
102
+
103
+ admin_routes.user(:user_id => 42) # => "/admin/users/42"
104
+
105
+ get partay_routes.path(:user, "/users/:user_id") do # => /partay/users/:user_id
106
+ # ...
107
+ end
108
+
109
+ partay_routes.user(:user_id => 42, :beer => "pabst") # => "/partay/users/42&beer=pabst"
110
+
111
+ Copyright (c) 2010 Brian Takita. This software is licensed under the MIT License.
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require "rake"
2
+ require 'rake/clean'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |s|
9
+ s.name = "named-routes"
10
+ s.summary = "A simple and generic named routes api. It works really well with Sinatra."
11
+ s.email = "brian.takita@gmail.com"
12
+ s.homepage = "http://github.com/btakita/named-routes"
13
+ s.summary = "A simple and generic named routes api. It works really well with Sinatra."
14
+ s.authors = ["Brian Takita"]
15
+ s.files = FileList[
16
+ '[A-Z]*',
17
+ '*.rb',
18
+ 'lib/**/*.rb',
19
+ 'spec/**/*.rb'
20
+ ].to_a
21
+ s.test_files = Dir.glob('spec/*_spec.rb')
22
+ s.has_rdoc = true
23
+ s.extra_rdoc_files = [ "README.md", "CHANGES" ]
24
+ s.rdoc_options = ["--main", "README.md", "--inline-source", "--line-numbers"]
25
+ end
26
+ rescue LoadError
27
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
28
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,78 @@
1
+ module NamedRoutes
2
+ class Routes
3
+ class_inheritable_accessor :host, :prefix
4
+
5
+ extend(Module.new do
6
+ def instance
7
+ @instance ||= new
8
+ end
9
+
10
+ def http
11
+ Uri.new(self, "http")
12
+ end
13
+
14
+ def https
15
+ Uri.new(self, "https")
16
+ end
17
+
18
+ def path(name, definition, include_prefix=true)
19
+ full_definition = (include_prefix && prefix) ? File.join("", prefix, definition) : definition
20
+ define_method name do |*args|
21
+ self.class.eval(full_definition, [args.first].compact.first || {})
22
+ end
23
+ yield full_definition if block_given?
24
+ full_definition
25
+ end
26
+
27
+ def eval(definition, params_arg={})
28
+ params = ActiveSupport::HashWithIndifferentAccess.new(params_arg)
29
+ path_string = if params.empty?
30
+ definition
31
+ else
32
+ definition.split("/").map do |segment|
33
+ segment_value = segment[/^:(.*)/, 1]
34
+ segment_value_parts = segment_value.to_s.split(".")
35
+ segment_name = segment_value_parts[0]
36
+ if segment_name
37
+ param_name = params.delete(File.basename(segment_name, '.*').to_s)
38
+ URI.escape([param_name, *segment_value_parts[1..-1]].join("."))
39
+ else
40
+ segment
41
+ end
42
+ end.join("/")
43
+ end
44
+ unless params.empty?
45
+ path_string << "?#{params.to_param}"
46
+ end
47
+ path_string
48
+ end
49
+
50
+ # TODO: Create eval_without_prefix
51
+
52
+ def escape_params(params={})
53
+ params.inject({}) do |memo, kv|
54
+ memo[URI.escape(kv[0])] = if kv[1].is_a?(Hash)
55
+ escape_params(kv[1])
56
+ elsif kv[1].is_a?(Array)
57
+ kv[1].map { |v| URI.escape(v.to_s) }
58
+ else
59
+ URI.escape(kv[1].to_s)
60
+ end
61
+ memo
62
+ end
63
+ end
64
+
65
+ def normalize(path)
66
+ path.gsub(Regexp.new("^#{File.join("", prefix.to_s)}"), "/").gsub("//", "/")
67
+ end
68
+
69
+ def method_missing(method_name, *args, &block)
70
+ if instance.respond_to?(method_name)
71
+ instance.send(method_name, *args, &block)
72
+ else
73
+ super
74
+ end
75
+ end
76
+ end)
77
+ end
78
+ end
@@ -0,0 +1,13 @@
1
+ module NamedRoutes
2
+ class Uri
3
+ attr_reader :routes, :scheme
4
+
5
+ def initialize(routes, scheme)
6
+ @routes, @scheme = routes, scheme
7
+ end
8
+
9
+ def method_missing(method_name, *args, &block)
10
+ "#{scheme}://#{routes.host}#{routes.send(method_name, *args, &block)}"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,20 @@
1
+ require "active_support"
2
+ require "active_support/hash_with_indifferent_access"
3
+ require "active_support/core_ext"
4
+ require "active_support/core_ext"
5
+ require "uri"
6
+
7
+ module NamedRoutes
8
+ def path(*args, &block)
9
+ routes.path(*args, &block)
10
+ end
11
+
12
+ def routes
13
+ ::NamedRoutes::Routes
14
+ end
15
+ extend self
16
+ end
17
+
18
+ dir = File.dirname(__FILE__)
19
+ require "#{dir}/named-routes/routes"
20
+ require "#{dir}/named-routes/uri"
@@ -0,0 +1,146 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
2
+
3
+ module NamedRoutes
4
+ describe Routes do
5
+ before do
6
+ NamedRoutes.routes.host = "example.com"
7
+ end
8
+
9
+ def routes
10
+ @routes ||= begin
11
+ paths_class = Class.new(NamedRoutes::Routes)
12
+ paths_class.path(:root, "/")
13
+ paths_class.path(:current_user_category_top_choices, "/current-user/:category/top-choices")
14
+ paths_class.path(:decision_stream, "/decision-streams/:stream_id")
15
+ paths_class
16
+ end
17
+ end
18
+
19
+ describe "path definition" do
20
+ context "when params hash is not given" do
21
+ it "returns the definition", :focus => true do
22
+ routes.root.should == "/"
23
+ routes.current_user_category_top_choices.should == "/current-user/:category/top-choices"
24
+ routes.decision_stream.should == "/decision-streams/:stream_id"
25
+ end
26
+ end
27
+
28
+ context "when params hash is given" do
29
+ it "returns the path with the param replaced with the given param value with additional params added as url params" do
30
+ uri_1 = routes.current_user_category_top_choices(:category => "cars", :foo => "bar", :baz => {"one" => "two three"})
31
+
32
+ path, query = uri_1.split("?")
33
+ path.should == "/current-user/cars/top-choices"
34
+ query.should include("foo=bar")
35
+ query.should include("baz[one]=two+three")
36
+ routes.decision_stream(:stream_id => 99).should == "/decision-streams/99"
37
+ end
38
+ end
39
+
40
+ context "when a prefix is given" do
41
+ def routes
42
+ @routes ||= begin
43
+ paths_class = Class.new(NamedRoutes::Routes)
44
+ paths_class.prefix = "general"
45
+ paths_class.path(:root, "/")
46
+ paths_class.path(:current_user_category_top_choices, "/current-user/:category/top-choices")
47
+ paths_class.path(:decision_stream, "/decision-streams/:stream_id")
48
+ paths_class
49
+ end
50
+ end
51
+
52
+ context "when default and include_prefix argument is true" do
53
+ it "appends the prefix to the returned paths" do
54
+ routes.root.should == "/general/"
55
+ routes.current_user_category_top_choices.should == "/general/current-user/:category/top-choices"
56
+ routes.decision_stream.should == "/general/decision-streams/:stream_id"
57
+ routes.current_user_category_top_choices(:category => "cars").should == "/general/current-user/cars/top-choices"
58
+ routes.decision_stream(:stream_id => 99).should == "/general/decision-streams/99"
59
+ end
60
+ end
61
+
62
+ context "when include_prefix argument is false in the path definition" do
63
+ it "does not append the prefix to the returned paths" do
64
+ routes.path(:raw_path, "/raw/path", false).should == "/raw/path"
65
+ routes.raw_path.should == "/raw/path"
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ describe ".eval" do
72
+ context "when params hash is not given" do
73
+ it "returns the definition" do
74
+ routes.eval("/current-user/:category/top-choices").should == "/current-user/:category/top-choices"
75
+ end
76
+ end
77
+
78
+ context "when params hash is given" do
79
+ it "returns the path with the param replaced with the given param value with additional params added as url params" do
80
+ uri_1 = routes.current_user_category_top_choices(:category => "cars", :foo => "bar", :baz => {"one" => "two three"})
81
+
82
+ path, query = uri_1.split("?")
83
+ path.should == "/current-user/cars/top-choices"
84
+ query.should include("foo=bar")
85
+ query.should include("baz[one]=two+three")
86
+ routes.eval("/decision-streams/:stream_id", :stream_id => 99).should == "/decision-streams/99"
87
+ end
88
+ end
89
+ end
90
+
91
+ describe "#normalize" do
92
+ def routes
93
+ @routes ||= begin
94
+ path_class = Class.new(NamedRoutes::Routes)
95
+ path_class
96
+ end
97
+ end
98
+
99
+ context "when there is no prefix" do
100
+ before do
101
+ routes.prefix.should_not be_present
102
+ end
103
+
104
+ it "returns the given path" do
105
+ routes.normalize("/prefix/foo/bar").should == "/prefix/foo/bar"
106
+ end
107
+ end
108
+
109
+ context "when there is a prefix" do
110
+ context "when the prefix begins with a /" do
111
+ before do
112
+ routes.prefix = "/prefix"
113
+ end
114
+
115
+ it "strips out the prefix from the beginning" do
116
+ routes.normalize("/prefix/foo/bar").should == "/foo/bar"
117
+ routes.normalize("/prefix/foo/prefix/bar").should == "/foo/prefix/bar"
118
+ end
119
+ end
120
+
121
+ context "when the prefix does not begin with a /" do
122
+ before do
123
+ routes.prefix = "prefix"
124
+ end
125
+
126
+ it "strips out the prefix from the beginning" do
127
+ routes.normalize("/prefix/foo/bar").should == "/foo/bar"
128
+ routes.normalize("/prefix/foo/prefix/bar").should == "/foo/prefix/bar"
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ describe ".http" do
135
+ it "returns a full http uri (with ::NamedRoutes.host) for the given named route" do
136
+ routes.http.decision_stream(:stream_id => "11").should == "http://example.com/decision-streams/11"
137
+ end
138
+ end
139
+
140
+ describe ".https" do
141
+ it "returns a full https uri (with ::NamedRoutes.host) for the given named route" do
142
+ routes.https.decision_stream(:stream_id => "11").should == "https://example.com/decision-streams/11"
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,17 @@
1
+ require "rubygems"
2
+
3
+ $LOAD_PATH.unshift(File.expand_path("#{File.dirname(__FILE__)}/../lib"))
4
+ require "named-routes"
5
+ require "rspec"
6
+
7
+ ARGV.push("-b")
8
+ unless ARGV.include?("--format") || ARGV.include?("-f")
9
+ ARGV.push("--format", "nested")
10
+ end
11
+
12
+ require 'rr'
13
+
14
+ RSpec.configure do |c|
15
+ c.mock_with :rr
16
+ # c.filter_run :focus => true
17
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: named-routes
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Brian Takita
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-20 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description:
23
+ email: brian.takita@gmail.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - CHANGES
30
+ - README.md
31
+ files:
32
+ - CHANGES
33
+ - MIT.LICENSE
34
+ - README.md
35
+ - Rakefile
36
+ - VERSION
37
+ - lib/named-routes.rb
38
+ - lib/named-routes/routes.rb
39
+ - lib/named-routes/uri.rb
40
+ - spec/named-routes/routes_spec.rb
41
+ - spec/spec_helper.rb
42
+ has_rdoc: true
43
+ homepage: http://github.com/btakita/named-routes
44
+ licenses: []
45
+
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --main
49
+ - README.md
50
+ - --inline-source
51
+ - --line-numbers
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 3
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.3.7
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: A simple and generic named routes api. It works really well with Sinatra.
79
+ test_files: []
80
+