sinatra-namespace 0.4.0.a

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,135 @@
1
+ Sinatra::Namespace
2
+ ==================
3
+
4
+ Adds namespaces to [Sinatra](http://sinatrarb.com). Allows namespaces to have local helpers.
5
+ Not wanting local helpers probably is a sign that what you really want is writing rack middleware instead.
6
+ See alternatives section.
7
+
8
+ BigBand
9
+ -------
10
+
11
+ Sinatra::Namespace is part of the [BigBand](http://github.com/rkh/big_band) stack.
12
+ Check it out if you are looking for other fancy Sinatra extensions.
13
+
14
+ Usage
15
+ -----
16
+
17
+ Classic style:
18
+
19
+ require "sinatra"
20
+ require "sinatra/namespace"
21
+
22
+ helpers do
23
+ def title
24
+ "foo bar"
25
+ end
26
+
27
+ def posts
28
+ Post.all
29
+ end
30
+ end
31
+
32
+ get "/" do
33
+ haml :index
34
+ end
35
+
36
+ namspace "/ruby" do
37
+ helpers do
38
+ def posts
39
+ super.where :topic => "ruby"
40
+ end
41
+ end
42
+
43
+ get { haml :index }
44
+
45
+ get "/new" do
46
+ haml :new, {}, :object => Post.new(:topic => "ruby")
47
+ end
48
+ end
49
+
50
+ Note how namespaces can have local helpers not available to the outer namespace but inherits the outer helpers.
51
+
52
+ Modular style (you can of course use the `namespace` method there, too):
53
+
54
+ require "sinatra/base"
55
+ require "sinatra/namespace"
56
+
57
+ class Application < Sinatra::Base
58
+ def title
59
+ "foo bar"
60
+ end
61
+
62
+ def posts
63
+ Post.all
64
+ end
65
+
66
+ module RubyPosts
67
+ # If you would have called that module Ruby, you would not have to set
68
+ # your prefix by hand, ain't that cool, huh?
69
+ prefix "/ruby"
70
+
71
+ def posts
72
+ super.where :topic => "ruby"
73
+ end
74
+
75
+ get { haml :index }
76
+
77
+ get "/new" do
78
+ haml :new, {}, :object => Post.new(:topic => "ruby")
79
+ end
80
+ end
81
+ end
82
+
83
+ Wait, did that module turn into a namespace all by itself? No, actually it got turned into one by `Application` when it
84
+ tried to call `prefix`, which is not defined. `Sinatra::Namspace` sets up `nested_method_missing` (from `monkey-lib`) to
85
+ catch that cases.
86
+
87
+ You can influence that behavior by setting `auto_namespace`:
88
+
89
+ class Application < Sinatra::Base
90
+ # enables auto namespacing, is default
91
+ enable :auto_namespace
92
+
93
+ # disables auto namespacing, is default
94
+ disable :auto_namespace
95
+
96
+ # triggers auto namespaceing only on prefix and get
97
+ set :auto_namespace, :only => [:prefix, :get]
98
+
99
+ # triggers auto namespacing on all public methods of Sinatra::Namspace::NestedMethods except prefix
100
+ set :auto_namespace, :except => :prefix
101
+ end
102
+
103
+ So, how does one create a namespace from a module without that auto detection? Simple:
104
+
105
+ Application.make_namespace SomeModule, :prefix => "/somewhere"
106
+
107
+ Alternatives
108
+ ------------
109
+
110
+ Sinatra::Namespace is made for sharing some state/helpers.
111
+ If that is no what you are looking for, you have two alternative directions.
112
+
113
+ Simple prefixing, shares all state/helpers:
114
+
115
+ require "sinatra/base"
116
+ require "monkey"
117
+
118
+ admin_prefix = "/this/is/the/admin/prefix"
119
+ get(admin_prefix) { haml :admin_index }
120
+ get(admin_prefix / "new_user") { haml :new_user }
121
+ get(admin_prefix / "admin_stuff") { haml :admin_stuff }
122
+
123
+ Middleware, shares no state/helpers:
124
+
125
+ require "sinatra/base"
126
+
127
+ class Application < Sinatra::Base
128
+ class AdminNamespace < Sinatra::Base
129
+ get("admin/prefix") { haml :admin_index }
130
+ get("admin/new_user") { haml :new_user }
131
+ get("admin/admin_stuff") { haml :admin_stuff }
132
+ end
133
+
134
+ use AdminNamespace
135
+ end
@@ -0,0 +1,132 @@
1
+ require "monkey"
2
+ require "sinatra/base"
3
+ require "sinatra/sugar"
4
+ require "sinatra/advanced_routes"
5
+
6
+ module Sinatra
7
+ module Namespace
8
+ DONT_FORWARD = %w[after before call configure disable enable error new not_found register reset! run! set use]
9
+
10
+ module NestedMethods
11
+ def prefix(value = nil)
12
+ @prefix = value if value
13
+ @prefix
14
+ end
15
+
16
+ def base(value = nil)
17
+ @base = value if value
18
+ @base
19
+ end
20
+
21
+ def helpers(*modules, &block)
22
+ modules.each { |m| include m }
23
+ class_eval(&block) if block
24
+ end
25
+
26
+ def get(name = nil, options = {}, &block); prefixed(:get, name, options, &block); end
27
+ def put(name = nil, options = {}, &block); prefixed(:put, name, options, &block); end
28
+ def post(name = nil, options = {}, &block); prefixed(:post, name, options, &block); end
29
+ def delete(name = nil, options = {}, &block); prefixed(:delete, name, options, &block); end
30
+ def head(name = nil, options = {}, &block); prefixed(:head, name, options, &block); end
31
+
32
+ def respond_to?(name)
33
+ super or (base.respond_to? name and forward? name)
34
+ end
35
+
36
+ def methods(*args)
37
+ (super + base.methods(*args).select { |m| forward? m }).uniq
38
+ end
39
+
40
+ private
41
+
42
+ def application
43
+ klass = self
44
+ klass = klass.base while klass.respond_to? :base
45
+ klass
46
+ end
47
+
48
+ def prefixed(verb, name = nil, options = {}, &block)
49
+ name, options = nil, name if name.is_a? Hash and options.empty?
50
+ path = prefix.to_s + name.to_s
51
+ application.send(:define_method, "#{verb} #{path}", &block)
52
+ unbound_method, container = application.instance_method("#{verb} #{path}"), self
53
+ if block.arity != 0
54
+ wrapper = proc { |*args| container.send(:wrap, unbound_method, self, *args) }
55
+ else
56
+ wrapper = proc { container.send(:wrap, unbound_method, self) }
57
+ end
58
+ base.send(verb, path, options, &wrapper)
59
+ end
60
+
61
+ def wrap(unbound_method, app, *args)
62
+ app.extend self
63
+ unbound_method.bind(app).call(*args)
64
+ end
65
+
66
+ def method_missing(name, *args, &block)
67
+ Monkey.invisible { super }
68
+ rescue NameError => error # allowes adding method_missing in mixins
69
+ raise error unless base.respond_to? name and forward? name
70
+ base.send(name, *args, &block)
71
+ end
72
+
73
+ def forward?(name)
74
+ not Sinatra::NameError::DONT_FORWARD.include? name.to_s
75
+ end
76
+ end
77
+
78
+ def self.registered(klass)
79
+ klass.register Sinatra::Sugar
80
+ klass.set :merge_namespaces => false, :auto_namespace => true
81
+ end
82
+
83
+ def self.make_namespace(mod, options = {})
84
+ unless options[:base] ||= options[:for]
85
+ base = mod
86
+ base = base.parent until base.is_a? Class
87
+ raise ArgumentError, "base class not given/detected" if base == Object
88
+ options[:base] = base
89
+ end
90
+ options[:prefix] ||= "/" << mod.name.gsub(/^#{options[:base]}::/, '').to_const_path
91
+ mod.extend self
92
+ mod.extend NestedMethods
93
+ options.each { |k,v| mod.send(k, v) }
94
+ mod
95
+ end
96
+
97
+ def make_namespace(mod, options = {})
98
+ Sinatra::Namespace.make_namespace mod, options.merge(:base => self)
99
+ end
100
+
101
+ def namespace(prefix, merge = nil, &block)
102
+ prefix = prefix.to_s
103
+ if merge or (merge.nil? and merge_namespaces?)
104
+ @namespaces ||= {}
105
+ @namespaces[prefix] ||= namespace prefix, false
106
+ @namespaces[prefix].class_eval(&block) if block
107
+ else
108
+ mod = make_namespace Module.new, :prefix => prefix
109
+ mod.class_eval(&block) if block
110
+ mod
111
+ end
112
+ end
113
+
114
+ def nested_method_missing(klass, meth, *args, &block)
115
+ return super unless make_namespace? klass, meth
116
+ make_namespace klass
117
+ klass.send(meth, *args, &block)
118
+ end
119
+
120
+ def make_namespace?(klass, meth)
121
+ return false if !auto_namespace? or klass.is_a? NestedMethods
122
+ meths = NestedMethods.instance_methods.map { |m| m.to_s }
123
+ if auto_namespace != true
124
+ meths = [auto_namespace[:only]].flatten.map { |m| m.to_s } if auto_namespace.include? :only
125
+ [auto_namespace[:except]].flatten.each { |m| meths.delete m.to_s } if auto_namespace.include? :except
126
+ end
127
+ meths.include? meth.to_s
128
+ end
129
+ end
130
+
131
+ register Namespace
132
+ end
@@ -0,0 +1,165 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe Sinatra::Namespace do
4
+ [:get, :head, :post, :put, :delete].each do |verb|
5
+ describe "HTTP #{verb.to_s.upcase}" do
6
+ before :each do
7
+ Object.send :remove_const, :App if Object.const_defined? :App
8
+ class ::App < Sinatra::Base
9
+ register Sinatra::Namespace
10
+ end
11
+ app App
12
+ end
13
+
14
+ describe :namespace do
15
+ it "should add routes including prefix to the base app" do
16
+ app.namespace "/foo" do
17
+ send(verb, "/bar") { "baz" }
18
+ end
19
+ browse_route(verb, "/foo/bar").should be_ok
20
+ browse_route(verb, "/foo/bar").body.should == "baz" unless verb == :head
21
+ end
22
+
23
+ it "should allowes adding routes with no path" do
24
+ app.namespace "/foo" do
25
+ send(verb) { "bar" }
26
+ end
27
+ browse_route(verb, "/foo").should be_ok
28
+ browse_route(verb, "/foo").body.should == "bar" unless verb == :head
29
+ end
30
+ end
31
+
32
+ describe :make_namespace do
33
+ it "extends modules make_namespace is called on" do
34
+ mod = Module.new
35
+ mod.should_not respond_to(verb)
36
+ app.make_namespace(mod, :prefix => "/foo")
37
+ mod.should respond_to(verb)
38
+ end
39
+
40
+ it "returns the module" do
41
+ mod = Module.new
42
+ app.make_namespace(mod, :prefix => "/foo").should == mod
43
+ end
44
+
45
+ it "sets base" do
46
+ app.make_namespace(Module.new, :prefix => "/foo").base.should == app
47
+ end
48
+
49
+ it "sets prefix" do
50
+ app.make_namespace(Module.new, :prefix => "/foo").prefix.should == "/foo"
51
+ end
52
+
53
+ it "automatically sets a prefix based on module name if none is given" do
54
+ # FooBar = Module.new <= does not work in Ruby 1.9
55
+ module ::FooBar; end
56
+ app.make_namespace ::FooBar
57
+ ::FooBar.prefix.should == "/foo_bar"
58
+ end
59
+
60
+ it "does not add the application name to auto-generated prefixes" do
61
+ #App::FooBar = Module.new <= does not work in Ruby 1.9
62
+ class ::App < Sinatra::Base; module FooBar; end; end
63
+ app.make_namespace App::FooBar
64
+ App::FooBar.prefix.should == "/foo_bar"
65
+ end
66
+ end
67
+
68
+ describe :auto_namespace do
69
+ before do
70
+ class ::App < Sinatra::Base; module Foo; end; end
71
+ end
72
+
73
+ it "detects #{verb}" do
74
+ App::Foo.should_not respond_to(verb)
75
+ App::Foo.send(verb, "/bar") { "baz" }
76
+ App::Foo.should respond_to(verb)
77
+ browse_route(verb, "/foo/bar").should be_ok
78
+ browse_route(verb, "/foo/bar").body.should == "baz" unless verb == :head
79
+ end
80
+
81
+ it "ignores #{verb} if auto namespaceing is disabled" do
82
+ app.disable :auto_namespace
83
+ App::Foo.should_not respond_to(verb)
84
+ proc { App::Foo.send(verb, "/bar") { "baz" } }.should raise_error(NameError)
85
+ App::Foo.should_not respond_to(verb)
86
+ end
87
+
88
+ it "ignores #{verb} if told to via :except" do
89
+ app.set :auto_namespace, :except => verb
90
+ App::Foo.should_not respond_to(verb)
91
+ proc { App::Foo.send(verb, "/bar") { "baz" } }.should raise_error(NameError)
92
+ App::Foo.should_not respond_to(verb)
93
+ end
94
+
95
+ it "does not ignore #{verb} if not included in :except" do
96
+ app.set :auto_namespace, :except => ["prefix"]
97
+ App::Foo.should_not respond_to(verb)
98
+ App::Foo.send(verb, "/bar") { "baz" }
99
+ App::Foo.should respond_to(verb)
100
+ end
101
+
102
+ it "does ignore #{verb} if not included in :only" do
103
+ app.set :auto_namespace, :only => "prefix"
104
+ App::Foo.should_not respond_to(verb)
105
+ proc { App::Foo.send(verb, "/bar") { "baz" } }.should raise_error(NameError)
106
+ App::Foo.should_not respond_to(verb)
107
+ end
108
+
109
+ it "does not ignore #{verb} if included in :only" do
110
+ app.set :auto_namespace, :only => ["prefix", verb]
111
+ App::Foo.should_not respond_to(verb)
112
+ App::Foo.send(verb, "/bar") { "baz" }
113
+ App::Foo.should respond_to(verb)
114
+ end
115
+
116
+ it "detects prefix" do
117
+ App::Foo.should_not respond_to(:prefix)
118
+ App::Foo.prefix.should == "/foo"
119
+ end
120
+ end
121
+
122
+ describe :helpers do
123
+ it "makes helpers defined inside a namespace not available to routes outside that namespace" do
124
+ helpers { define_method(:foo) { 42 } }
125
+ app.namespace("/foo").helpers { define_method(:bar) { 42 } }
126
+ app.new.should respond_to(:foo)
127
+ app.new.should_not respond_to(:bar)
128
+ end
129
+
130
+ it "allowes overwriting helpers for routes within a namespace" do
131
+ helpers { define_method(:foo) { "foo" } }
132
+ define_route(verb, "/foo") { foo }
133
+ app.namespace("/foo") do
134
+ define_method(:foo) { "bar" }
135
+ send(verb, "/foo") { foo }
136
+ end
137
+ browse_route(verb, "/foo").should be_ok
138
+ browse_route(verb, "/foo/foo").should be_ok
139
+ unless verb == :head
140
+ browse_route(verb, "/foo").body.should == "foo"
141
+ browse_route(verb, "/foo/foo").body.should == "bar"
142
+ end
143
+ end
144
+
145
+ it "allowes accessing helpers defined outside the namespace" do
146
+ helpers { define_method(:foo) { "foo" } }
147
+ app.namespace("/foo").send(verb, "") { foo }
148
+ browse_route(verb, "/foo").should be_ok
149
+ browse_route(verb, "/foo").body.should == "foo" unless verb == :head
150
+ end
151
+
152
+ it "allowes calling super in helpers overwritten inside a namespace" do
153
+ helpers { define_method(:foo) { "foo" } }
154
+ app.namespace("/foo") do
155
+ define_method(:foo) { super().upcase }
156
+ send(verb) { foo }
157
+ end
158
+ browse_route(verb, "/foo").should be_ok
159
+ browse_route(verb, "/foo").body.should == "FOO" unless verb == :head
160
+ end
161
+ end
162
+
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,2 @@
1
+ require "sinatra/rspec"
2
+ require "sinatra/namespace"
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sinatra-namespace
3
+ version: &id001 !ruby/object:Gem::Version
4
+ prerelease: true
5
+ segments:
6
+ - 0
7
+ - 4
8
+ - 0
9
+ - a
10
+ version: 0.4.0.a
11
+ platform: ruby
12
+ authors:
13
+ - Konstantin Haase
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-03-01 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: monkey-lib
23
+ prerelease: false
24
+ requirement: &id002 !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "="
27
+ - *id001
28
+ type: :runtime
29
+ version_requirements: *id002
30
+ - !ruby/object:Gem::Dependency
31
+ name: sinatra-sugar
32
+ prerelease: false
33
+ requirement: &id003 !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - "="
36
+ - *id001
37
+ type: :runtime
38
+ version_requirements: *id003
39
+ - !ruby/object:Gem::Dependency
40
+ name: sinatra-test-helper
41
+ prerelease: false
42
+ requirement: &id004 !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "="
45
+ - *id001
46
+ type: :development
47
+ version_requirements: *id004
48
+ - !ruby/object:Gem::Dependency
49
+ name: sinatra
50
+ prerelease: false
51
+ requirement: &id005 !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ - 9
58
+ - 4
59
+ version: 0.9.4
60
+ type: :runtime
61
+ version_requirements: *id005
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ prerelease: false
65
+ requirement: &id006 !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 1
71
+ - 3
72
+ - 0
73
+ version: 1.3.0
74
+ type: :development
75
+ version_requirements: *id006
76
+ description: Adds namespaces to Sinatra, allows namespaces to have local helpers (part of BigBand).
77
+ email: konstantin.mailinglists@googlemail.com
78
+ executables: []
79
+
80
+ extensions: []
81
+
82
+ extra_rdoc_files: []
83
+
84
+ files:
85
+ - lib/sinatra/namespace.rb
86
+ - spec/sinatra/namespace_spec.rb
87
+ - spec/spec_helper.rb
88
+ - README.md
89
+ has_rdoc: yard
90
+ homepage: http://github.com/rkh/sinatra-namespace
91
+ licenses: []
92
+
93
+ post_install_message:
94
+ rdoc_options: []
95
+
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ segments:
103
+ - 0
104
+ version: "0"
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">"
108
+ - !ruby/object:Gem::Version
109
+ segments:
110
+ - 1
111
+ - 3
112
+ - 1
113
+ version: 1.3.1
114
+ requirements: []
115
+
116
+ rubyforge_project:
117
+ rubygems_version: 1.3.6
118
+ signing_key:
119
+ specification_version: 3
120
+ summary: Adds namespaces to Sinatra, allows namespaces to have local helpers (part of BigBand).
121
+ test_files: []
122
+