named-routes 0.1.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.
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
+