sinatra-namespace 0.5.1 → 0.6.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/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