sinatra-namespace 0.4.0.a

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/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
+