rubycut-sinatra-contrib 1.4.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.
Files changed (81) hide show
  1. data/LICENSE +20 -0
  2. data/README.md +136 -0
  3. data/Rakefile +75 -0
  4. data/ideas.md +29 -0
  5. data/lib/sinatra/capture.rb +124 -0
  6. data/lib/sinatra/config_file.rb +167 -0
  7. data/lib/sinatra/content_for.rb +125 -0
  8. data/lib/sinatra/contrib.rb +39 -0
  9. data/lib/sinatra/contrib/all.rb +2 -0
  10. data/lib/sinatra/contrib/setup.rb +53 -0
  11. data/lib/sinatra/contrib/version.rb +17 -0
  12. data/lib/sinatra/cookies.rb +331 -0
  13. data/lib/sinatra/decompile.rb +120 -0
  14. data/lib/sinatra/engine_tracking.rb +96 -0
  15. data/lib/sinatra/extension.rb +95 -0
  16. data/lib/sinatra/json.rb +130 -0
  17. data/lib/sinatra/link_header.rb +132 -0
  18. data/lib/sinatra/multi_route.rb +87 -0
  19. data/lib/sinatra/namespace.rb +284 -0
  20. data/lib/sinatra/reloader.rb +394 -0
  21. data/lib/sinatra/respond_with.rb +249 -0
  22. data/lib/sinatra/streaming.rb +267 -0
  23. data/lib/sinatra/test_helpers.rb +87 -0
  24. data/sinatra-contrib.gemspec +127 -0
  25. data/spec/capture_spec.rb +93 -0
  26. data/spec/config_file/key_value.yml +6 -0
  27. data/spec/config_file/key_value.yml.erb +6 -0
  28. data/spec/config_file/key_value_override.yml +2 -0
  29. data/spec/config_file/missing_env.yml +4 -0
  30. data/spec/config_file/with_envs.yml +7 -0
  31. data/spec/config_file/with_nested_envs.yml +11 -0
  32. data/spec/config_file_spec.rb +63 -0
  33. data/spec/content_for/different_key.erb +1 -0
  34. data/spec/content_for/different_key.erubis +1 -0
  35. data/spec/content_for/different_key.haml +2 -0
  36. data/spec/content_for/different_key.slim +2 -0
  37. data/spec/content_for/layout.erb +1 -0
  38. data/spec/content_for/layout.erubis +1 -0
  39. data/spec/content_for/layout.haml +1 -0
  40. data/spec/content_for/layout.slim +1 -0
  41. data/spec/content_for/multiple_blocks.erb +4 -0
  42. data/spec/content_for/multiple_blocks.erubis +4 -0
  43. data/spec/content_for/multiple_blocks.haml +8 -0
  44. data/spec/content_for/multiple_blocks.slim +8 -0
  45. data/spec/content_for/multiple_yields.erb +3 -0
  46. data/spec/content_for/multiple_yields.erubis +3 -0
  47. data/spec/content_for/multiple_yields.haml +3 -0
  48. data/spec/content_for/multiple_yields.slim +3 -0
  49. data/spec/content_for/passes_values.erb +1 -0
  50. data/spec/content_for/passes_values.erubis +1 -0
  51. data/spec/content_for/passes_values.haml +1 -0
  52. data/spec/content_for/passes_values.slim +1 -0
  53. data/spec/content_for/same_key.erb +1 -0
  54. data/spec/content_for/same_key.erubis +1 -0
  55. data/spec/content_for/same_key.haml +2 -0
  56. data/spec/content_for/same_key.slim +2 -0
  57. data/spec/content_for/takes_values.erb +1 -0
  58. data/spec/content_for/takes_values.erubis +1 -0
  59. data/spec/content_for/takes_values.haml +3 -0
  60. data/spec/content_for/takes_values.slim +3 -0
  61. data/spec/content_for_spec.rb +213 -0
  62. data/spec/cookies_spec.rb +802 -0
  63. data/spec/decompile_spec.rb +44 -0
  64. data/spec/extension_spec.rb +33 -0
  65. data/spec/json_spec.rb +117 -0
  66. data/spec/link_header_spec.rb +100 -0
  67. data/spec/multi_route_spec.rb +60 -0
  68. data/spec/namespace/foo.erb +1 -0
  69. data/spec/namespace/nested/foo.erb +1 -0
  70. data/spec/namespace_spec.rb +676 -0
  71. data/spec/okjson.rb +581 -0
  72. data/spec/reloader/app.rb.erb +40 -0
  73. data/spec/reloader_spec.rb +441 -0
  74. data/spec/respond_with/bar.erb +1 -0
  75. data/spec/respond_with/bar.json.erb +1 -0
  76. data/spec/respond_with/foo.html.erb +1 -0
  77. data/spec/respond_with/not_html.sass +2 -0
  78. data/spec/respond_with_spec.rb +297 -0
  79. data/spec/spec_helper.rb +7 -0
  80. data/spec/streaming_spec.rb +436 -0
  81. metadata +313 -0
@@ -0,0 +1,87 @@
1
+ require 'sinatra/base'
2
+
3
+ module Sinatra
4
+ # = Sinatra::MultiRoute
5
+ #
6
+ # Create multiple routes with one statement.
7
+ #
8
+ # == Usage
9
+ #
10
+ # Use this extension to create a handler for multiple routes:
11
+ #
12
+ # get '/foo', '/bar' do
13
+ # # ...
14
+ # end
15
+ #
16
+ # Or for multiple verbs:
17
+ #
18
+ # route :get, :post, '/' do
19
+ # # ...
20
+ # end
21
+ #
22
+ # Or for multiple verbs and multiple routes:
23
+ #
24
+ # route :get, :post, ['/foo', '/bar'] do
25
+ # # ...
26
+ # end
27
+ #
28
+ # Or even for custom verbs:
29
+ #
30
+ # route 'LIST', '/' do
31
+ # # ...
32
+ # end
33
+ #
34
+ # === Classic Application
35
+ #
36
+ # To use the extension in a classic application all you need to do is require
37
+ # it:
38
+ #
39
+ # require "sinatra"
40
+ # require "sinatra/multi_route"
41
+ #
42
+ # # Your classic application code goes here...
43
+ #
44
+ # === Modular Application
45
+ #
46
+ # To use the extension in a modular application you need to require it, and
47
+ # then, tell the application you will use it:
48
+ #
49
+ # require "sinatra/base"
50
+ # require "sinatra/multi_route"
51
+ #
52
+ # class MyApp < Sinatra::Base
53
+ # register Sinatra::MultiRoute
54
+ #
55
+ # # The rest of your modular application code goes here...
56
+ # end
57
+ #
58
+ module MultiRoute
59
+ def head(*args, &block) super(*route_args(args), &block) end
60
+ def delete(*args, &block) super(*route_args(args), &block) end
61
+ def get(*args, &block) super(*route_args(args), &block) end
62
+ def options(*args, &block) super(*route_args(args), &block) end
63
+ def patch(*args, &block) super(*route_args(args), &block) end
64
+ def post(*args, &block) super(*route_args(args), &block) end
65
+ def put(*args, &block) super(*route_args(args), &block) end
66
+
67
+ def route(*args, &block)
68
+ options = Hash === args.last ? args.pop : {}
69
+ routes = [*args.pop]
70
+ args.each do |verb|
71
+ verb = verb.to_s.upcase if Symbol === verb
72
+ routes.each do |route|
73
+ super(verb, route, options, &block)
74
+ end
75
+ end
76
+ end
77
+
78
+ private
79
+
80
+ def route_args(args)
81
+ options = Hash === args.last ? args.pop : {}
82
+ [args, options]
83
+ end
84
+ end
85
+
86
+ register MultiRoute
87
+ end
@@ -0,0 +1,284 @@
1
+ require 'backports'
2
+ require 'sinatra/base'
3
+ require 'sinatra/decompile'
4
+
5
+ module Sinatra
6
+
7
+ # = Sinatra::Namespace
8
+ #
9
+ # <tt>Sinatra::Namespace</tt> is an extension that adds namespaces to an
10
+ # application. This namespaces will allow you to share a path prefix for the
11
+ # routes within the namespace, and define filters, conditions and error
12
+ # handlers exclusively for them. Besides that, you can also register helpers
13
+ # and extensions that will be used only within the namespace.
14
+ #
15
+ # == Usage
16
+ #
17
+ # Once you have loaded the extension (see below), you can use the +namespace+
18
+ # method to define namespaces in your application.
19
+ #
20
+ # You can define a namespace by a path prefix:
21
+ #
22
+ # namespace '/blog' do
23
+ # get { haml :blog }
24
+ # get '/:entry_permalink' do
25
+ # @entry = Entry.find_by_permalink!(params[:entry_permalink])
26
+ # haml :entry
27
+ # end
28
+ #
29
+ # # More blog routes...
30
+ # end
31
+ #
32
+ # by a condition:
33
+ #
34
+ # namespace :host_name => 'localhost' do
35
+ # get('/admin/dashboard') { haml :dashboard }
36
+ # get('/admin/login') { haml :login }
37
+ #
38
+ # # More admin routes...
39
+ # end
40
+ #
41
+ # or both:
42
+ #
43
+ # namespace '/admin', :host_name => 'localhost' do
44
+ # get('/dashboard') { haml :dashboard }
45
+ # get('/login') { haml :login }
46
+ # post('/login') { login_user }
47
+ #
48
+ # # More admin routes...
49
+ # end
50
+ #
51
+ # When you define a filter or an error handler, or register an extension or a
52
+ # set of helpers within a namespace, they only affect the routes defined in
53
+ # it. For instance, lets define a before filter to prevent the access of
54
+ # unauthorized users to the admin section of the application:
55
+ #
56
+ # namespace '/admin' do
57
+ # helpers AdminHelpers
58
+ # before { authenticate unless request.path_info == '/admin/login' }
59
+ #
60
+ # get '/dashboard' do
61
+ # # Only authenticated users can access here...
62
+ # haml :dashboard
63
+ # end
64
+ #
65
+ # # More admin routes...
66
+ # end
67
+ #
68
+ # get '/' do
69
+ # # Any user can access here...
70
+ # haml :index
71
+ # end
72
+ #
73
+ # Well, they actually also affect the nested namespaces:
74
+ #
75
+ # namespace '/admin' do
76
+ # helpers AdminHelpers
77
+ # before { authenticate unless request.path_info == '/admin/login' }
78
+ #
79
+ # namespace '/users' do
80
+ # get do
81
+ # # Only authenticated users can access here...
82
+ # @users = User.all
83
+ # haml :users
84
+ # end
85
+ #
86
+ # # More user admin routes...
87
+ # end
88
+ #
89
+ # # More admin routes...
90
+ # end
91
+ #
92
+ # === Classic Application Setup
93
+ #
94
+ # To be able to use namespaces in a classic application all you need to do is
95
+ # require the extension:
96
+ #
97
+ # require "sinatra"
98
+ # require "sinatra/namespace"
99
+ #
100
+ # # The rest of your classic application code goes here...
101
+ #
102
+ # === Modular Application Setup
103
+ #
104
+ # To be able to use namespaces in a modular application all you need to do is
105
+ # require the extension, and then, register it:
106
+ #
107
+ # require "sinatra/base"
108
+ # require "sinatra/namespace"
109
+ #
110
+ # class MyApp < Sinatra::Base
111
+ # register Sinatra::Namespace
112
+ #
113
+ # # The rest of your modular application code goes here...
114
+ # end
115
+ #
116
+ module Namespace
117
+ def self.new(base, pattern, conditions = {}, &block)
118
+ Module.new do
119
+ extend NamespacedMethods
120
+ include InstanceMethods
121
+ @base, @extensions, @errors = base, [], {}
122
+ @pattern, @conditions = compile(pattern, conditions)
123
+ @templates = Hash.new { |h,k| @base.templates[k] }
124
+ namespace = self
125
+ before { extend(@namespace = namespace) }
126
+ class_eval(&block)
127
+ end
128
+ end
129
+
130
+ module InstanceMethods
131
+ def settings
132
+ @namespace
133
+ end
134
+
135
+ def template_cache
136
+ super.fetch(:nested, @namespace) { Tilt::Cache.new }
137
+ end
138
+ end
139
+
140
+ module SharedMethods
141
+ def namespace(pattern, conditions = {}, &block)
142
+ Sinatra::Namespace.new(self, pattern, conditions, &block)
143
+ end
144
+ end
145
+
146
+ module NamespacedMethods
147
+ include SharedMethods
148
+ include Sinatra::Decompile
149
+ attr_reader :base, :templates
150
+
151
+ def self.prefixed(*names)
152
+ names.each { |n| define_method(n) { |*a, &b| prefixed(n, *a, &b) }}
153
+ end
154
+
155
+ prefixed :before, :after, :delete, :get, :head, :options, :patch, :post, :put
156
+
157
+ def helpers(*extensions, &block)
158
+ class_eval(&block) if block_given?
159
+ include(*extensions) if extensions.any?
160
+ end
161
+
162
+ def register(*extensions, &block)
163
+ extensions << Module.new(&block) if block_given?
164
+ @extensions += extensions
165
+ extensions.each do |extension|
166
+ extend extension
167
+ extension.registered(self) if extension.respond_to?(:registered)
168
+ end
169
+ end
170
+
171
+ def invoke_hook(name, *args)
172
+ @extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
173
+ end
174
+
175
+ def not_found(&block)
176
+ error(404, &block)
177
+ end
178
+
179
+ def errors
180
+ base.errors.merge(namespace_errors)
181
+ end
182
+
183
+ def namespace_errors
184
+ @errors
185
+ end
186
+
187
+ def error(*codes, &block)
188
+ args = Sinatra::Base.send(:compile!, "ERROR", /^#{@pattern}/, block)
189
+ codes = codes.map { |c| Array(c) }.flatten
190
+ codes << Exception if codes.empty?
191
+ codes.each do |c|
192
+ errors = @errors[c] ||= []
193
+ errors << args
194
+ end
195
+ end
196
+
197
+ def respond_to(*args)
198
+ return @conditions[:provides] || base.respond_to if args.empty?
199
+ @conditions[:provides] = args
200
+ end
201
+
202
+ def set(key, value = self, &block)
203
+ raise ArgumentError, "may not set #{key}" if key != :views
204
+ return key.each { |k,v| set(k, v) } if block.nil? and value == self
205
+ block ||= proc { value }
206
+ singleton_class.send(:define_method, key, &block)
207
+ end
208
+
209
+ def enable(*opts)
210
+ opts.each { |key| set(key, true) }
211
+ end
212
+
213
+ def disable(*opts)
214
+ opts.each { |key| set(key, false) }
215
+ end
216
+
217
+ def template(name, &block)
218
+ filename, line = caller_locations.first
219
+ templates[name] = [block, filename, line.to_i]
220
+ end
221
+
222
+ def layout(name=:layout, &block)
223
+ template name, &block
224
+ end
225
+
226
+ private
227
+
228
+ def app
229
+ base.respond_to?(:base) ? base.base : base
230
+ end
231
+
232
+ def compile(pattern, conditions, default_pattern = nil)
233
+ if pattern.respond_to? :to_hash
234
+ conditions = conditions.merge pattern.to_hash
235
+ pattern = nil
236
+ end
237
+ base_pattern, base_conditions = @pattern, @conditions
238
+ pattern ||= default_pattern
239
+ base_pattern ||= base.pattern if base.respond_to? :pattern
240
+ base_conditions ||= base.conditions if base.respond_to? :conditions
241
+ [ prefixed_path(base_pattern, pattern),
242
+ (base_conditions || {}).merge(conditions) ]
243
+ end
244
+
245
+ def prefixed_path(a, b)
246
+ return a || b || // unless a and b
247
+ a, b = decompile(a), decompile(b) unless a.class == b.class
248
+ a, b = regexpify(a), regexpify(b) unless a.class == b.class
249
+ path = a.class.new "#{a}#{b}"
250
+ path = /^#{path}$/ if path.is_a? Regexp and base == app
251
+ path
252
+ end
253
+
254
+ def regexpify(pattern)
255
+ pattern = Sinatra::Base.send(:compile, pattern).first.inspect
256
+ pattern.gsub! /^\/(\^|\\A)?|(\$|\\Z)?\/$/, ''
257
+ Regexp.new pattern
258
+ end
259
+
260
+ def prefixed(method, pattern = nil, conditions = {}, &block)
261
+ default = '*' if method == :before or method == :after
262
+ pattern, conditions = compile pattern, conditions, default
263
+ result = base.send(method, pattern, conditions, &block)
264
+ invoke_hook :route_added, method.to_s.upcase, pattern, block
265
+ result
266
+ end
267
+
268
+ def method_missing(method, *args, &block)
269
+ base.send(method, *args, &block)
270
+ end
271
+ end
272
+
273
+ module BaseMethods
274
+ include SharedMethods
275
+ end
276
+
277
+ def self.extend_object(base)
278
+ base.extend BaseMethods
279
+ end
280
+ end
281
+
282
+ register Sinatra::Namespace
283
+ Delegator.delegate :namespace
284
+ end
@@ -0,0 +1,394 @@
1
+ require 'sinatra/base'
2
+
3
+ module Sinatra
4
+
5
+ # = Sinatra::Reloader
6
+ #
7
+ # Extension to reload modified files. Useful during development,
8
+ # since it will automatically require files defining routes, filters,
9
+ # error handlers and inline templates, with every incoming request,
10
+ # but only if they have been updated.
11
+ #
12
+ # == Usage
13
+ #
14
+ # === Classic Application
15
+ #
16
+ # To enable the reloader in a classic application all you need to do is
17
+ # require it:
18
+ #
19
+ # require "sinatra"
20
+ # require "sinatra/reloader" if development?
21
+ #
22
+ # # Your classic application code goes here...
23
+ #
24
+ # === Modular Application
25
+ #
26
+ # To enable the reloader in a modular application all you need to do is
27
+ # require it, and then, register it:
28
+ #
29
+ # require "sinatra/base"
30
+ # require "sinatra/reloader"
31
+ #
32
+ # class MyApp < Sinatra::Base
33
+ # configure :development do
34
+ # register Sinatra::Reloader
35
+ # end
36
+ #
37
+ # # Your modular application code goes here...
38
+ # end
39
+ #
40
+ # == Changing the Reloading Policy
41
+ #
42
+ # You can refine the reloading policy with +also_reload+ and
43
+ # +dont_reload+, to customize which files should, and should not, be
44
+ # reloaded, respectively.
45
+ #
46
+ # === Classic Application
47
+ #
48
+ # Simply call the methods:
49
+ #
50
+ # require "sinatra"
51
+ # require "sinatra/reloader" if development?
52
+ #
53
+ # also_reload '/path/to/some/file'
54
+ # dont_reload '/path/to/other/file'
55
+ #
56
+ # # Your classic application code goes here...
57
+ #
58
+ # === Modular Application
59
+ #
60
+ # Call the methods inside the +configure+ block:
61
+ #
62
+ # require "sinatra/base"
63
+ # require "sinatra/reloader"
64
+ #
65
+ # class MyApp < Sinatra::Base
66
+ # configure :development do
67
+ # register Sinatra::Reloader
68
+ # also_reload '/path/to/some/file'
69
+ # dont_reload '/path/to/other/file'
70
+ # end
71
+ #
72
+ # # Your modular application code goes here...
73
+ # end
74
+ #
75
+ module Reloader
76
+
77
+ # Watches a file so it can tell when it has been updated, and what
78
+ # elements does it contain.
79
+ class Watcher
80
+
81
+ # Represents an element of a Sinatra application that may need to
82
+ # be reloaded. An element could be:
83
+ # * a route
84
+ # * a filter
85
+ # * an error handler
86
+ # * a middleware
87
+ # * inline templates
88
+ #
89
+ # Its +representation+ attribute is there to allow to identify the
90
+ # element within an application, that is, to match it with its
91
+ # Sinatra's internal representation.
92
+ class Element < Struct.new(:type, :representation)
93
+ end
94
+
95
+ # Collection of file +Watcher+ that can be associated with a
96
+ # Sinatra application. That way, we can know which files belong
97
+ # to a given application and which files have been modified. It
98
+ # also provides a mechanism to inform a Watcher of the elements
99
+ # defined in the file being watched and if its changes should be
100
+ # ignored.
101
+ class List
102
+ @app_list_map = Hash.new { |hash, key| hash[key] = new }
103
+
104
+ # Returns the +List+ for the application +app+.
105
+ def self.for(app)
106
+ @app_list_map[app]
107
+ end
108
+
109
+ # Creates a new +List+ instance.
110
+ def initialize
111
+ @path_watcher_map = Hash.new do |hash, key|
112
+ hash[key] = Watcher.new(key)
113
+ end
114
+ end
115
+
116
+ # Lets the +Watcher+ for the file located at +path+ know that the
117
+ # +element+ is defined there, and adds the +Watcher+ to the +List+,
118
+ # if it isn't already there.
119
+ def watch(path, element)
120
+ watcher_for(path).elements << element
121
+ end
122
+
123
+ # Tells the +Watcher+ for the file located at +path+ to ignore
124
+ # the file changes, and adds the +Watcher+ to the +List+, if
125
+ # it isn't already there.
126
+ def ignore(path)
127
+ watcher_for(path).ignore
128
+ end
129
+
130
+ # Adds a +Watcher+ for the file located at +path+ to the
131
+ # +List+, if it isn't already there.
132
+ def watcher_for(path)
133
+ @path_watcher_map[File.expand_path(path)]
134
+ end
135
+ alias watch_file watcher_for
136
+
137
+ # Returns an array with all the watchers in the +List+.
138
+ def watchers
139
+ @path_watcher_map.values
140
+ end
141
+
142
+ # Returns an array with all the watchers in the +List+ that
143
+ # have been updated.
144
+ def updated
145
+ watchers.find_all(&:updated?)
146
+ end
147
+ end
148
+
149
+ attr_reader :path, :elements, :mtime
150
+
151
+ # Creates a new +Watcher+ instance for the file located at +path+.
152
+ def initialize(path)
153
+ @path, @elements = path, []
154
+ update
155
+ end
156
+
157
+ # Indicates whether or not the file being watched has been modified.
158
+ def updated?
159
+ !ignore? && !removed? && mtime != File.mtime(path)
160
+ end
161
+
162
+ # Updates the mtime of the file being watched.
163
+ def update
164
+ @mtime = File.mtime(path)
165
+ end
166
+
167
+ # Indicates whether or not the file being watched has inline
168
+ # templates.
169
+ def inline_templates?
170
+ elements.any? { |element| element.type == :inline_templates }
171
+ end
172
+
173
+ # Informs that the modifications to the file being watched
174
+ # should be ignored.
175
+ def ignore
176
+ @ignore = true
177
+ end
178
+
179
+ # Indicates whether or not the modifications to the file being
180
+ # watched should be ignored.
181
+ def ignore?
182
+ !!@ignore
183
+ end
184
+
185
+ # Indicates whether or not the file being watched has been removed.
186
+ def removed?
187
+ !File.exist?(path)
188
+ end
189
+ end
190
+
191
+ # When the extension is registed it extends the Sinatra application
192
+ # +klass+ with the modules +BaseMethods+ and +ExtensionMethods+ and
193
+ # defines a before filter to +perform+ the reload of the modified files.
194
+ def self.registered(klass)
195
+ @reloader_loaded_in ||= {}
196
+ return if @reloader_loaded_in[klass]
197
+
198
+ @reloader_loaded_in[klass] = true
199
+
200
+ klass.extend BaseMethods
201
+ klass.extend ExtensionMethods
202
+ klass.set(:reloader) { klass.development? }
203
+ klass.set(:reload_templates) { klass.reloader? }
204
+ klass.before do
205
+ if klass.reloader?
206
+ if Reloader.thread_safe?
207
+ Thread.exclusive { Reloader.perform(klass) }
208
+ else
209
+ Reloader.perform(klass)
210
+ end
211
+ end
212
+ end
213
+ klass.set(:inline_templates, klass.app_file) if klass == Sinatra::Application
214
+ end
215
+
216
+ # Reloads the modified files, adding, updating and removing the
217
+ # needed elements.
218
+ def self.perform(klass)
219
+ Watcher::List.for(klass).updated.each do |watcher|
220
+ klass.set(:inline_templates, watcher.path) if watcher.inline_templates?
221
+ watcher.elements.each { |element| klass.deactivate(element) }
222
+ $LOADED_FEATURES.delete(watcher.path)
223
+ require watcher.path
224
+ watcher.update
225
+ end
226
+ end
227
+
228
+ # Indicates whether or not we can and need to run thread-safely.
229
+ def self.thread_safe?
230
+ Thread and Thread.list.size > 1 and Thread.respond_to?(:exclusive)
231
+ end
232
+
233
+ # Contains the methods defined in Sinatra::Base that are overriden.
234
+ module BaseMethods
235
+ # Protects Sinatra::Base.run! from being called more than once.
236
+ def run!(*args)
237
+ if settings.reloader?
238
+ super unless running?
239
+ else
240
+ super
241
+ end
242
+ end
243
+
244
+ # Does everything Sinatra::Base#route does, but it also tells the
245
+ # +Watcher::List+ for the Sinatra application to watch the defined
246
+ # route.
247
+ #
248
+ # Note: We are using #compile! so we don't interfere with extensions
249
+ # changing #route.
250
+ def compile!(verb, path, block, options = {})
251
+ source_location = block.respond_to?(:source_location) ?
252
+ block.source_location.first : caller_files[1]
253
+ signature = super
254
+ watch_element(
255
+ source_location, :route, { :verb => verb, :signature => signature }
256
+ )
257
+ signature
258
+ end
259
+
260
+ # Does everything Sinatra::Base#inline_templates= does, but it also
261
+ # tells the +Watcher::List+ for the Sinatra application to watch the
262
+ # inline templates in +file+ or the file who made the call to this
263
+ # method.
264
+ def inline_templates=(file=nil)
265
+ file = (file.nil? || file == true) ?
266
+ (caller_files[1] || File.expand_path($0)) : file
267
+ watch_element(file, :inline_templates)
268
+ super
269
+ end
270
+
271
+ # Does everything Sinatra::Base#use does, but it also tells the
272
+ # +Watcher::List+ for the Sinatra application to watch the middleware
273
+ # being used.
274
+ def use(middleware, *args, &block)
275
+ path = caller_files[1] || File.expand_path($0)
276
+ watch_element(path, :middleware, [middleware, args, block])
277
+ super
278
+ end
279
+
280
+ # Does everything Sinatra::Base#add_filter does, but it also tells
281
+ # the +Watcher::List+ for the Sinatra application to watch the defined
282
+ # filter.
283
+ def add_filter(type, path = nil, options = {}, &block)
284
+ source_location = block.respond_to?(:source_location) ?
285
+ block.source_location.first : caller_files[1]
286
+ result = super
287
+ watch_element(source_location, :"#{type}_filter", filters[type].last)
288
+ result
289
+ end
290
+
291
+ # Does everything Sinatra::Base#error does, but it also tells the
292
+ # +Watcher::List+ for the Sinatra application to watch the defined
293
+ # error handler.
294
+ def error(*codes, &block)
295
+ path = caller_files[1] || File.expand_path($0)
296
+ result = super
297
+ codes.each do |c|
298
+ watch_element(path, :error, :code => c, :handler => @errors[c])
299
+ end
300
+ result
301
+ end
302
+
303
+ # Does everything Sinatra::Base#register does, but it also lets the
304
+ # reloader know that an extension is being registered, because the
305
+ # elements defined in its +registered+ method need a special treatment.
306
+ def register(*extensions, &block)
307
+ start_registering_extension
308
+ result = super
309
+ stop_registering_extension
310
+ result
311
+ end
312
+
313
+ # Does everything Sinatra::Base#register does and then registers the
314
+ # reloader in the +subclass+.
315
+ def inherited(subclass)
316
+ result = super
317
+ subclass.register Sinatra::Reloader
318
+ result
319
+ end
320
+ end
321
+
322
+ # Contains the methods that the extension adds to the Sinatra application.
323
+ module ExtensionMethods
324
+ # Removes the +element+ from the Sinatra application.
325
+ def deactivate(element)
326
+ case element.type
327
+ when :route then
328
+ verb = element.representation[:verb]
329
+ signature = element.representation[:signature]
330
+ (routes[verb] ||= []).delete(signature)
331
+ when :middleware then
332
+ @middleware.delete(element.representation)
333
+ when :before_filter then
334
+ filters[:before].delete(element.representation)
335
+ when :after_filter then
336
+ filters[:after].delete(element.representation)
337
+ when :error then
338
+ code = element.representation[:code]
339
+ handler = element.representation[:handler]
340
+ @errors.delete(code) if @errors[code] == handler
341
+ end
342
+ end
343
+
344
+ # Indicates with a +glob+ which files should be reloaded if they
345
+ # have been modified. It can be called several times.
346
+ def also_reload(*glob)
347
+ Dir[*glob].each { |path| Watcher::List.for(self).watch_file(path) }
348
+ end
349
+
350
+ # Indicates with a +glob+ which files should not be reloaded even if
351
+ # they have been modified. It can be called several times.
352
+ def dont_reload(*glob)
353
+ Dir[*glob].each { |path| Watcher::List.for(self).ignore(path) }
354
+ end
355
+
356
+ private
357
+
358
+ attr_reader :register_path
359
+
360
+ # Indicates an extesion is being registered.
361
+ def start_registering_extension
362
+ @register_path = caller_files[2]
363
+ end
364
+
365
+ # Indicates the extesion has already been registered.
366
+ def stop_registering_extension
367
+ @register_path = nil
368
+ end
369
+
370
+ # Indicates whether or not an extension is being registered.
371
+ def registering_extension?
372
+ !register_path.nil?
373
+ end
374
+
375
+ # Builds a Watcher::Element from +type+ and +representation+ and
376
+ # tells the Watcher::List for the current application to watch it
377
+ # in the file located at +path+.
378
+ #
379
+ # If an extension is being registered, it also tells the list to
380
+ # watch it in the file where the extesion has been registered.
381
+ # This prevents the duplication of the elements added by the
382
+ # extension in its +registered+ method with every reload.
383
+ def watch_element(path, type, representation=nil)
384
+ list = Watcher::List.for(self)
385
+ element = Watcher::Element.new(type, representation)
386
+ list.watch(path, element)
387
+ list.watch(register_path, element) if registering_extension?
388
+ end
389
+ end
390
+ end
391
+
392
+ register Reloader
393
+ Delegator.delegate :also_reload, :dont_reload
394
+ end