sinatra-namespace 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,20 +1,37 @@
1
- Sinatra::Namespace
2
- ==================
1
+ # Sinatra::Namespace
3
2
 
4
3
  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
4
 
8
- BigBand
9
- -------
5
+ ## Features
10
6
 
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.
7
+ ### Nesting by prefix
13
8
 
14
- Usage
15
- -----
9
+ require "sinatra"
10
+ require "sinatra/namespace"
11
+
12
+ namespace '/blog' do
13
+ get { haml :index }
14
+
15
+ get '/:entry_id' do |id|
16
+ @entry = Entry.find(id)
17
+ haml :entry
18
+ end
19
+ end
20
+
21
+ ### Nesting by condition
22
+
23
+ require "sinatra"
24
+ require "sinatra/namespace"
25
+
26
+ namespace :host_name => "www.example.com" do
27
+ # ...
28
+ end
29
+
30
+ namespace :host_name => "api.example.com" do
31
+ # ...
32
+ end
16
33
 
17
- Classic style:
34
+ ### Local helpers, filters and error handling
18
35
 
19
36
  require "sinatra"
20
37
  require "sinatra/namespace"
@@ -57,7 +74,7 @@ Classic style:
57
74
  end
58
75
  end
59
76
 
60
- Note how namespaces can have local helpers not available to the outer namespace but inherits the outer helpers.
77
+ ### With modules
61
78
 
62
79
  Modular style (you can of course use the `namespace` method there, too):
63
80
 
@@ -91,8 +108,7 @@ Modular style (you can of course use the `namespace` method there, too):
91
108
  end
92
109
 
93
110
  Wait, did that module turn into a namespace all by itself? No, actually it got turned into one by `Application` when it
94
- tried to call `prefix`, which is not defined. `Sinatra::Namespace` sets up `nested_method_missing` (from `monkey-lib`) to
95
- catch that cases.
111
+ tried to call `prefix`, which is not defined.
96
112
 
97
113
  You can influence that behavior by setting `auto_namespace`:
98
114
 
@@ -129,12 +145,11 @@ If that is no what you are looking for, you have two alternative directions.
129
145
  Simple prefixing, shares all state/helpers:
130
146
 
131
147
  require "sinatra/base"
132
- require "monkey-lib"
133
148
 
134
149
  admin_prefix = "/this/is/the/admin/prefix"
135
150
  get(admin_prefix) { haml :admin_index }
136
- get(admin_prefix / "new_user") { haml :new_user }
137
- get(admin_prefix / "admin_stuff") { haml :admin_stuff }
151
+ get("#{admin_prefix}/new_user") { haml :new_user }
152
+ get("#{admin_prefix}/admin_stuff") { haml :admin_stuff }
138
153
 
139
154
  Middleware, shares no state/helpers:
140
155
 
@@ -1,218 +1,156 @@
1
- require "monkey-lib"
2
- require "sinatra/base"
3
- require "sinatra/sugar"
1
+ require 'sinatra/base'
4
2
 
5
3
  module Sinatra
6
4
  module Namespace
7
- DONT_FORWARD = %w[call configure disable enable new register reset! run! set use template]
8
-
9
5
  module NestedMethods
10
- def errors
11
- @errors ||= {}
12
- end
13
-
14
- def prefix(value = nil)
15
- @prefix = value if value
16
- @prefix
6
+ DONT_FORWARD = %w[call configure disable enable new register reset! run! set use template layout]
7
+ attr_reader :prefix, :options, :base
8
+
9
+ def get(name = nil, options = {}, &block) prefixed(:get, name, options, &block) end
10
+ def put(name = nil, options = {}, &block) prefixed(:put, name, options, &block) end
11
+ def post(name = nil, options = {}, &block) prefixed(:post, name, options, &block) end
12
+ def delete(name = nil, options = {}, &block) prefixed(:delete, name, options, &block) end
13
+ def head(name = nil, options = {}, &block) prefixed(:head, name, options, &block) end
14
+ def before(name = "*", &block) prefixed(:before, name, &block) end
15
+ def after(name = "*", &block) prefixed(:after, name, &block) end
16
+
17
+ def helpers(*list, &block)
18
+ include(*list) unless list.empty?
19
+ class_eval(&block) if block
17
20
  end
18
21
 
19
- def base(value = nil)
20
- @base = value if value
21
- @base
22
+ def settings
23
+ return base if base.is_a? Class
24
+ base.settings
22
25
  end
23
26
 
24
- def helpers(*modules, &block)
25
- modules.each { |m| include m }
26
- class_eval(&block) if block
27
+ def respond_to?(*args)
28
+ return true if super
29
+ base.respond_to?(*args) and forward(args.first)
27
30
  end
28
31
 
29
- def get(name = nil, options = {}, &block); prefixed(:get, name, options, &block); end
30
- def put(name = nil, options = {}, &block); prefixed(:put, name, options, &block); end
31
- def post(name = nil, options = {}, &block); prefixed(:post, name, options, &block); end
32
- def delete(name = nil, options = {}, &block); prefixed(:delete, name, options, &block); end
33
- def head(name = nil, options = {}, &block); prefixed(:head, name, options, &block); end
34
-
35
- def error(codes=Exception, &block)
36
- [*codes].each { |c| errors[c] = block }
32
+ def errors
33
+ @errors ||= {}
37
34
  end
38
35
 
39
36
  def not_found(&block)
40
37
  error(404, &block)
41
38
  end
42
39
 
43
- def respond_to?(name)
44
- super or (base.respond_to? name and forward? name)
45
- end
46
-
47
- def methods(*args)
48
- (super + base.methods(*args).select { |m| forward? m }).uniq
49
- end
50
-
51
- def before_filters
52
- @before_filters ||= []
53
- end
54
-
55
- def after_filters
56
- @after_filters ||= []
57
- end
58
-
59
- def before(&block)
60
- before_filters << block
61
- end
62
-
63
- def after(&block)
64
- after_filters << block
40
+ def error(codes = Exception, &block)
41
+ [*codes].each { |c| errors[c] = block }
65
42
  end
66
43
 
67
44
  private
68
45
 
69
- def always_activate
70
- get(/.*/) { pass }
71
- end
72
-
73
- def application
74
- klass = self
75
- klass = klass.base while klass.respond_to? :base
76
- klass
77
- end
78
-
79
- def prefixed(verb, name = nil, options = {}, &block)
80
- name, options = nil, name if name.is_a? Hash and options.empty?
46
+ def prefixed_path(name)
81
47
  if prefix.is_a? Regexp or name.is_a? Regexp
82
48
  path = /#{prefix}#{name}/
83
49
  path = /^#{path}$/ if base.is_a? Class
50
+ path
84
51
  else
85
- path = prefix.to_s + name.to_s
52
+ prefix.to_s + name.to_s
86
53
  end
87
- application.send(:define_method, "#{verb} #{path}", &block)
88
- unbound_method, container = application.instance_method("#{verb} #{path}"), self
89
- if block.arity != 0
90
- wrapper = proc { |*args| container.send(:wrap, unbound_method, self, *args) }
91
- else
92
- wrapper = proc { container.send(:wrap, unbound_method, self) }
93
- end
94
- base.send(verb, path, options, &wrapper)
95
54
  end
96
55
 
97
- def prepare_instance(app)
98
- return if app.is_a? self
99
- base.prepare_instance app if base.respond_to? :prepare_instance
100
- class << app
101
- @filters ||= {}
102
- @filters[:after] ||= []
103
- @after_filters ||= []
56
+ def prefixed(method, name, *args, &block)
57
+ if name.respond_to? :key?
58
+ args.unshift name
59
+ name = nil
104
60
  end
105
- before_filters.each { |block| app.instance_eval(&block) }
106
- after_filters.each { |block| app.singleton_class.after(&block) }
107
- app.extend self
61
+ options.each { |o, a| settings.send(o, *a ) }
62
+ base.send(method, prefixed_path(name), *args, &block)
108
63
  end
109
64
 
110
- def wrap(unbound_method, app, *args)
111
- prepare_instance app
112
- app.current_namespace = self
113
- unbound_method.bind(app).call(*args)
65
+ def forward?(name)
66
+ not DONT_FORWARD.include? name.to_s
114
67
  end
115
68
 
116
69
  def method_missing(name, *args, &block)
117
- Monkey.invisible { super }
118
- rescue NameError => error # allowes adding method_missing in mixins
119
- raise error unless base.respond_to? name and forward? name
70
+ return super unless base.respond_to? name and forward? name
120
71
  base.send(name, *args, &block)
121
72
  end
73
+ end
122
74
 
123
- def forward?(name)
124
- not Sinatra::Namespace::DONT_FORWARD.include? name.to_s
75
+ module ClassMethods
76
+ def namespace(prefix = nil, options = {}, &block)
77
+ Namespace.setup(self, prefix, options, Module.new, &block)
125
78
  end
126
- end
127
79
 
128
- def self.registered(klass)
129
- klass.register Sinatra::Sugar
130
- klass.__send__ :include, InstanceMethods
131
- klass.enable :merge_namespaces, :auto_namespace, :always_activate_namespaces
132
- end
80
+ def make_namespace(mod, options = {})
81
+ options[:base] ||= self
82
+ Namespace.make_namespace(mod, options)
83
+ end
133
84
 
134
- def self.make_namespace(mod, options = {})
135
- unless options[:base] ||= options[:for]
136
- base = mod
137
- base = base.parent until base.is_a? Class
138
- raise ArgumentError, "base class not given/detected" if base == Object
139
- options[:base] = base
85
+ def make_namespace?(klass, meth)
86
+ return false if !auto_namespace? or klass.is_a? NestedMethods
87
+ meths = NestedMethods.instance_methods.map { |m| m.to_s }
88
+ if auto_namespace != true
89
+ meths = [auto_namespace[:only]].flatten.map { |m| m.to_s } if auto_namespace.include? :only
90
+ [auto_namespace[:except]].flatten.each { |m| meths.delete m.to_s } if auto_namespace.include? :except
91
+ end
92
+ meths.include? meth.to_s
140
93
  end
141
- options[:prefix] ||= "/" << mod.name.gsub(/^#{options[:base]}::/, '').to_const_path
142
- mod.extend self
143
- mod.extend NestedMethods
144
- options.each { |k,v| mod.send(k, v) }
145
- mod.send(:always_activate) if options[:base].always_activate_namespaces?
146
- mod
147
94
  end
148
95
 
149
- module InstanceMethods
150
- attr_accessor :current_namespace
151
-
152
- if Sinatra::VERSION > '1.0' or Sinatra::Base.respond_to? :filters # master is still 1.0
153
- def filter!(type, base = self.class)
154
- super
155
- if type == :after and base == self.class and singleton_class.filters and singleton_class.after_filters
156
- singleton_class.after_filters.each { |b| instance_eval(&b) }
157
- end
158
- end
159
- else
160
- def after_filter!(base = self.class)
161
- super
162
- if base == self.class and singleton_class.after_filters
163
- singleton_class.after_filters.each { |b| instance_eval(&b) }
96
+ module ModularMethods
97
+ def setup(base, prefix = nil, options = {}, mixin = nil, &block)
98
+ prefix, options = nil, prefix if options.empty? and prefix.respond_to? :key?
99
+ prefix ||= ""
100
+ mixin ||= self
101
+ mixin.class_eval { @prefix, @options, @base = prefix, options, base }
102
+ mixin.extend ClassMethods, NestedMethods
103
+ mixin.send(:define_method, :error_block!) do |*keys|
104
+ if block = keys.inject(nil) { |b,k| b ||= mixin.errors[k] }
105
+ instance_eval(&block)
106
+ else
107
+ super(*keys)
164
108
  end
165
109
  end
110
+ mixin.before { extend mixin }
111
+ mixin.class_eval(&block) if block
112
+ mixin
166
113
  end
114
+ end
167
115
 
168
- def error_block!(*keys)
169
- keys.detect do |key|
170
- base = current_namespace || self.class
171
- while base.respond_to? :errors
172
- if block = base.errors[key]
173
- # found a handler, eval and return result
174
- return instance_eval(&block)
175
- else
176
- base = base.respond_to?(:base) ? base.base : base.superclass
177
- end
178
- end
116
+ module NamespaceDetector
117
+ Module.send(:include, self)
118
+ def method_missing(meth, *args, &block)
119
+ return super if is_a? Class or !name
120
+ base = Object
121
+ detected = name.split('::').any? do |name|
122
+ base = base.const_get(name)
123
+ base < Sinatra::Base
124
+ end
125
+ if detected and base.make_namespace?(self, meth)
126
+ Sinatra::Namespace.make_namespace self, :base => base
127
+ send(meth, *args, &block)
128
+ else
129
+ super
179
130
  end
180
131
  end
181
132
  end
182
133
 
183
- def make_namespace(mod, options = {})
184
- Sinatra::Namespace.make_namespace mod, options.merge(:base => self)
185
- end
134
+ extend ModularMethods
186
135
 
187
- def namespace(prefix, merge = nil, &block)
188
- if merge or (merge.nil? and merge_namespaces?)
189
- @namespaces ||= {}
190
- @namespaces[prefix] ||= namespace prefix, false
191
- @namespaces[prefix].class_eval(&block) if block
192
- @namespaces[prefix]
193
- else
194
- mod = make_namespace Module.new, :prefix => prefix
195
- mod.class_eval(&block) if block
196
- mod
197
- end
136
+ def self.make_namespace(mod, options = {})
137
+ from = caller[0] =~ /make_namespace/ ? caller[1] : caller[0]
138
+ base = options.delete(:base) || options.delete(:for)
139
+ options[:prefix] ||= '/' << mod.name.gsub(/^#{base.name}::/, '').
140
+ gsub(/::/, '/').gsub(/([a-z\d]+)([A-Z][a-z])/,'\1_\2').downcase
141
+ setup base, options.delete(:prefix), options, mod
198
142
  end
199
143
 
200
- def nested_method_missing(klass, meth, *args, &block)
201
- return super unless make_namespace? klass, meth
202
- make_namespace klass
203
- klass.send(meth, *args, &block)
144
+ def self.included(klass)
145
+ klass.extend ModularMethods
146
+ super
204
147
  end
205
148
 
206
- def make_namespace?(klass, meth)
207
- return false if !auto_namespace? or klass.is_a? NestedMethods
208
- meths = NestedMethods.instance_methods.map { |m| m.to_s }
209
- if auto_namespace != true
210
- meths = [auto_namespace[:only]].flatten.map { |m| m.to_s } if auto_namespace.include? :only
211
- [auto_namespace[:except]].flatten.each { |m| meths.delete m.to_s } if auto_namespace.include? :except
212
- end
213
- meths.include? meth.to_s
149
+ def self.registered(klass)
150
+ klass.extend ClassMethods
151
+ klass.enable :auto_namespace
214
152
  end
215
153
  end
216
154
 
217
155
  register Namespace
218
- end
156
+ end
@@ -0,0 +1,20 @@
1
+ SPEC = Gem::Specification.new do |s|
2
+ # Get the facts.
3
+ s.name = "sinatra-namespace"
4
+ s.version = "0.6.0"
5
+ s.description = "Adds namespaces to Sinatra, allows namespaces to have local helpers."
6
+
7
+ # Dependencies
8
+ s.add_dependency "sinatra", "~> 1.1"
9
+ s.add_development_dependency "sinatra-test-helper", "~> 0.5.0"
10
+ s.add_development_dependency "rspec", "~> 1.3.0"
11
+
12
+ # Those should be about the same in any BigBand extension.
13
+ s.authors = ["Konstantin Haase"]
14
+ s.email = "konstantin.mailinglists@googlemail.com"
15
+ s.files = `git ls-files`.split("\n")
16
+ s.has_rdoc = 'yard'
17
+ s.homepage = "http://github.com/rkh/#{s.name}"
18
+ s.require_paths = ["lib"]
19
+ s.summary = s.description
20
+ end
@@ -182,29 +182,29 @@ describe Sinatra::Namespace do
182
182
  app.new.should_not respond_to(:bar)
183
183
  end
184
184
 
185
- it "allowes overwriting helpers for routes within a namespace" do
185
+ it "allows overwriting helpers for routes within a namespace" do
186
186
  helpers { define_method(:foo) { "foo" } }
187
187
  define_route(verb, "/foo") { foo }
188
- app.namespace("/foo") do
188
+ app.namespace("/bar") do
189
189
  define_method(:foo) { "bar" }
190
190
  send(verb, "/foo") { foo }
191
191
  end
192
192
  browse_route(verb, "/foo").should be_ok
193
- browse_route(verb, "/foo/foo").should be_ok
193
+ browse_route(verb, "/bar/foo").should be_ok
194
194
  unless verb == :head
195
195
  browse_route(verb, "/foo").body.should == "foo"
196
- browse_route(verb, "/foo/foo").body.should == "bar"
196
+ browse_route(verb, "/bar/foo").body.should == "bar"
197
197
  end
198
198
  end
199
199
 
200
- it "allowes accessing helpers defined outside the namespace" do
200
+ it "allows accessing helpers defined outside the namespace" do
201
201
  helpers { define_method(:foo) { "foo" } }
202
202
  app.namespace("/foo").send(verb, "") { foo }
203
203
  browse_route(verb, "/foo").should be_ok
204
204
  browse_route(verb, "/foo").body.should == "foo" unless verb == :head
205
205
  end
206
206
 
207
- it "allowes calling super in helpers overwritten inside a namespace" do
207
+ it "allows calling super in helpers overwritten inside a namespace" do
208
208
  helpers { define_method(:foo) { "foo" } }
209
209
  app.namespace("/foo") do
210
210
  define_method(:foo) { super().upcase }
@@ -220,9 +220,7 @@ describe Sinatra::Namespace do
220
220
 
221
221
  describe :errors do
222
222
  it "should allow custom error handlers with not found" do
223
- app.namespace('/en') { always_activate }
224
223
  app.namespace('/de') do
225
- always_activate
226
224
  not_found { 'nicht gefunden' }
227
225
  end
228
226
  get('/foo').status.should == 404
@@ -234,9 +232,7 @@ describe Sinatra::Namespace do
234
232
  end
235
233
 
236
234
  it "should allow custom error handlers with error" do
237
- app.namespace('/en') { always_activate }
238
235
  app.namespace('/de') do
239
- always_activate
240
236
  error(404) { 'nicht gefunden' }
241
237
  end
242
238
  get('/foo').status.should == 404
@@ -248,6 +244,30 @@ describe Sinatra::Namespace do
248
244
  end
249
245
  end
250
246
 
247
+ describe 'conditions' do
248
+ it 'allows using conditions' do
249
+ app.namespace(:host_name => 'example.com') do
250
+ get('/') { 'yes' }
251
+ end
252
+ app.get('/') { 'no' }
253
+ get('/', {}, { 'HTTP_HOST' => 'example.com' })
254
+ last_response.body.should == 'yes'
255
+ get('/', {}, { 'HTTP_HOST' => 'example.org' })
256
+ last_response.body.should == 'no'
257
+ end
258
+
259
+ it 'allows combining conditions with a prefix' do
260
+ app.namespace('/foo', :host_name => 'example.com') do
261
+ get { 'yes' }
262
+ end
263
+ app.get('/foo') { 'no' }
264
+ get('/foo', {}, { 'HTTP_HOST' => 'example.com' })
265
+ last_response.body.should == 'yes'
266
+ get('/foo', {}, { 'HTTP_HOST' => 'example.org' })
267
+ last_response.body.should == 'no'
268
+ end
269
+ end
270
+
251
271
  describe 'memory' do
252
272
  before do
253
273
  app.namespace('/foo') { get('/bar') { 'blah' }}
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sinatra-namespace
3
3
  version: !ruby/object:Gem::Version
4
- hash: 9
4
+ hash: 7
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 5
9
- - 1
10
- version: 0.5.1
8
+ - 6
9
+ - 0
10
+ version: 0.6.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Konstantin Haase
@@ -15,27 +15,26 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-07-18 00:00:00 +02:00
18
+ date: 2010-10-30 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- name: monkey-lib
22
+ name: sinatra
23
23
  prerelease: false
24
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
25
  none: false
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- hash: 15
29
+ hash: 13
30
30
  segments:
31
- - 0
32
- - 5
33
- - 2
34
- version: 0.5.2
31
+ - 1
32
+ - 1
33
+ version: "1.1"
35
34
  type: :runtime
36
35
  version_requirements: *id001
37
36
  - !ruby/object:Gem::Dependency
38
- name: sinatra-sugar
37
+ name: sinatra-test-helper
39
38
  prerelease: false
40
39
  requirement: &id002 !ruby/object:Gem::Requirement
41
40
  none: false
@@ -48,46 +47,15 @@ dependencies:
48
47
  - 5
49
48
  - 0
50
49
  version: 0.5.0
51
- type: :runtime
50
+ type: :development
52
51
  version_requirements: *id002
53
52
  - !ruby/object:Gem::Dependency
54
- name: sinatra-test-helper
53
+ name: rspec
55
54
  prerelease: false
56
55
  requirement: &id003 !ruby/object:Gem::Requirement
57
56
  none: false
58
57
  requirements:
59
58
  - - ~>
60
- - !ruby/object:Gem::Version
61
- hash: 11
62
- segments:
63
- - 0
64
- - 5
65
- - 0
66
- version: 0.5.0
67
- type: :development
68
- version_requirements: *id003
69
- - !ruby/object:Gem::Dependency
70
- name: sinatra
71
- prerelease: false
72
- requirement: &id004 !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ~>
76
- - !ruby/object:Gem::Version
77
- hash: 15
78
- segments:
79
- - 1
80
- - 0
81
- version: "1.0"
82
- type: :runtime
83
- version_requirements: *id004
84
- - !ruby/object:Gem::Dependency
85
- name: rspec
86
- prerelease: false
87
- requirement: &id005 !ruby/object:Gem::Requirement
88
- none: false
89
- requirements:
90
- - - ">="
91
59
  - !ruby/object:Gem::Version
92
60
  hash: 27
93
61
  segments:
@@ -96,8 +64,8 @@ dependencies:
96
64
  - 0
97
65
  version: 1.3.0
98
66
  type: :development
99
- version_requirements: *id005
100
- description: Adds namespaces to Sinatra, allows namespaces to have local helpers (part of BigBand).
67
+ version_requirements: *id003
68
+ description: Adds namespaces to Sinatra, allows namespaces to have local helpers.
101
69
  email: konstantin.mailinglists@googlemail.com
102
70
  executables: []
103
71
 
@@ -106,11 +74,12 @@ extensions: []
106
74
  extra_rdoc_files: []
107
75
 
108
76
  files:
77
+ - LICENSE
78
+ - README.md
109
79
  - lib/sinatra/namespace.rb
80
+ - sinatra-namespace.gemspec
110
81
  - spec/sinatra/namespace_spec.rb
111
82
  - spec/spec_helper.rb
112
- - README.md
113
- - LICENSE
114
83
  has_rdoc: yard
115
84
  homepage: http://github.com/rkh/sinatra-namespace
116
85
  licenses: []
@@ -144,6 +113,6 @@ rubyforge_project:
144
113
  rubygems_version: 1.3.7
145
114
  signing_key:
146
115
  specification_version: 3
147
- summary: Adds namespaces to Sinatra, allows namespaces to have local helpers (part of BigBand).
116
+ summary: Adds namespaces to Sinatra, allows namespaces to have local helpers.
148
117
  test_files: []
149
118