tap-server 0.3.0 → 0.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 (58) hide show
  1. data/History +5 -0
  2. data/cmd/server.rb +36 -8
  3. data/lib/tap/controller/rest_routes.rb +77 -0
  4. data/lib/tap/controller/utils.rb +29 -0
  5. data/lib/tap/controller.rb +244 -145
  6. data/lib/tap/controllers/app.rb +97 -55
  7. data/lib/tap/controllers/data.rb +132 -0
  8. data/lib/tap/controllers/schema.rb +137 -223
  9. data/lib/tap/controllers/server.rb +70 -27
  10. data/lib/tap/server/data.rb +178 -0
  11. data/lib/tap/server/runner.rb +71 -0
  12. data/lib/tap/server/server_error.rb +35 -0
  13. data/lib/tap/server.rb +60 -275
  14. data/lib/tap/tasks/echo.rb +39 -6
  15. data/lib/tap/tasks/render.rb +65 -0
  16. data/public/stylesheets/tap.css +15 -2
  17. data/views/configurable/_config.erb +1 -0
  18. data/views/configurable/_configs.erb +28 -0
  19. data/views/configurable/_flag.erb +2 -0
  20. data/views/configurable/_list_select.erb +8 -0
  21. data/views/configurable/_select.erb +6 -0
  22. data/views/configurable/_switch.erb +2 -0
  23. data/views/layout.erb +1 -1
  24. data/views/object/obj.erb +1 -0
  25. data/views/tap/controllers/app/_action.erb +3 -0
  26. data/views/tap/controllers/app/build.erb +18 -0
  27. data/views/tap/controllers/app/enque.erb +13 -0
  28. data/views/tap/controllers/app/info.erb +20 -7
  29. data/views/tap/controllers/data/_controls.erb +21 -0
  30. data/views/tap/controllers/data/_index_entry.erb +1 -0
  31. data/views/tap/controllers/data/_upload.erb +8 -0
  32. data/views/tap/controllers/data/entry.erb +8 -0
  33. data/views/tap/controllers/data/index.erb +14 -0
  34. data/views/tap/controllers/schema/_build.erb +6 -0
  35. data/views/tap/controllers/schema/_index_entry.erb +6 -0
  36. data/views/tap/controllers/schema/entry.erb +135 -0
  37. data/views/tap/controllers/schema/join.erb +6 -4
  38. data/views/tap/controllers/schema/task.erb +6 -0
  39. data/views/tap/controllers/server/access.erb +4 -0
  40. data/views/tap/controllers/server/admin.erb +21 -0
  41. data/views/tap/controllers/server/help.erb +23 -0
  42. data/views/tap/controllers/server/index.erb +43 -3
  43. data/views/tap/task/input.erb +17 -0
  44. data/views/tap/tasks/load/input.erb +11 -0
  45. metadata +35 -17
  46. data/lib/tap/server_error.rb +0 -34
  47. data/lib/tap/support/persistence.rb +0 -71
  48. data/lib/tap/tasks/server.rb +0 -30
  49. data/views/tap/controllers/app/index.erb +0 -44
  50. data/views/tap/controllers/schema/config/default.erb +0 -4
  51. data/views/tap/controllers/schema/config/flag.erb +0 -3
  52. data/views/tap/controllers/schema/config/switch.erb +0 -6
  53. data/views/tap/controllers/schema/configurations.erb +0 -27
  54. data/views/tap/controllers/schema/node.erb +0 -47
  55. data/views/tap/controllers/schema/preview.erb +0 -3
  56. data/views/tap/controllers/schema/round.erb +0 -30
  57. data/views/tap/controllers/schema/schema.erb +0 -28
  58. data/views/tap/tasks/echo/result.html +0 -1
@@ -0,0 +1,178 @@
1
+ module Tap
2
+ class Server
3
+
4
+ # A very simple wrapper for root providing a CRUD interface for reading and
5
+ # writing files. Data ids may be integers (if you want to pretend Data is
6
+ # a database), or they can be relative paths.
7
+ class Data < Tap::Root
8
+
9
+ attr_reader :cache
10
+
11
+ def initialize(config_or_dir=Dir.pwd)
12
+ @cache = Hash.new([])
13
+
14
+ if config_or_dir.kind_of?(Tap::Root)
15
+ config_or_dir = config_or_dir.config.to_hash
16
+ end
17
+
18
+ super(config_or_dir)
19
+ end
20
+
21
+ # A restricted version of the original. Path raises an error if the
22
+ # final path is not relative to als.
23
+ def entry_path(als, id)
24
+ id = id.to_s
25
+ if id.empty?
26
+ raise "no id specified"
27
+ end
28
+
29
+ path = self.path(als, id)
30
+ unless relative?(als, path)
31
+ raise "not a subpath: #{id.inspect} (#{als.inspect})"
32
+ end
33
+
34
+ path
35
+ end
36
+
37
+ # Returns a list of entry paths.
38
+ def entries(als)
39
+ glob(als).select do |path|
40
+ File.file?(path)
41
+ end
42
+ end
43
+
44
+ # Returns a list of existing ids.
45
+ def index(als)
46
+ entries(als).collect do |path|
47
+ relative_path(als, path)
48
+ end
49
+ end
50
+
51
+ # Returns an available integer id, usually the number of entries in self,
52
+ # but a random integer is generated if that number is taken.
53
+ def next_id(als)
54
+ # try the next in the sequence
55
+ id = entries(als).length
56
+
57
+ # if that already exists, go for a random id
58
+ while find(als, id)
59
+ id = rand(id * 10000)
60
+ end
61
+
62
+ id
63
+ end
64
+
65
+ # Returns the path for the specified entry, if it exists. Returns nil
66
+ # if no such entry can be found.
67
+ def find(als, id)
68
+ return nil unless id
69
+
70
+ path = entry_path(als, id)
71
+ File.file?(path) ? path : nil
72
+ end
73
+
74
+ # Creates an entry (ie a file) for the specified id. Yields the open
75
+ # file to the block if given, otherwise the file will be created
76
+ # without content. Returns the path to the file.
77
+ #
78
+ # Raises an error if the file already exists.
79
+ def create(als, id)
80
+ path = non_existant_path(als, id)
81
+ create!(path) {|io| yield(io) if block_given? }
82
+ end
83
+
84
+ # Reads and returns the data for the specified entry, or nil if
85
+ # the entry doesn't exist.
86
+ def read(als, id)
87
+ path = find(als, id)
88
+ path ? File.read(path) : nil
89
+ end
90
+
91
+ # Overwrites the data for the specified entry. A block must be given to
92
+ # provide the new content; an error is raised if the entry does not
93
+ # already exist.
94
+ def update(als, id)
95
+ path = existing_path(als, id)
96
+ create!(path) {|io| yield(io) }
97
+ end
98
+
99
+ # Removes the specified entry (ie file), if it exists. Returns true if
100
+ # the file was removed and false otherwise.
101
+ def destroy(als, id)
102
+ if path = find(als, id)
103
+ FileUtils.rm(path)
104
+ cache[als].delete(id)
105
+ true
106
+ else
107
+ false
108
+ end
109
+ end
110
+
111
+ def create_or_update(als, id)
112
+ path = entry_path(als, id)
113
+ create!(path) {|io| yield(io) }
114
+ end
115
+
116
+ def import(als, upload, id=nil)
117
+ id = upload[:filename] unless id && !id.empty?
118
+ path = non_existant_path(als, id)
119
+
120
+ prepare(path)
121
+ FileUtils.mv(upload[:tempfile].path, path)
122
+ path
123
+ end
124
+
125
+ def move(als, id, new_id)
126
+ path = existing_path(als, id)
127
+ new_path = non_existant_path(als, new_id)
128
+
129
+ prepare(new_path)
130
+ FileUtils.mv(path, new_path)
131
+
132
+ if cache[als].include?(id)
133
+ cache[als].delete(id)
134
+ cache[als] << new_id
135
+ end
136
+
137
+ new_id
138
+ end
139
+
140
+ def copy(als, id, new_id)
141
+ path = existing_path(als, id)
142
+ new_path = non_existant_path(als, new_id)
143
+
144
+ prepare(new_path)
145
+ FileUtils.copy(path, new_path)
146
+ new_id
147
+ end
148
+
149
+ protected
150
+
151
+ # helper to optimize the creation of entries when path is already
152
+ # resolved (using the instance prepare requires a second path
153
+ # resolution)
154
+ def create!(path) # :nodoc:
155
+ Utils.prepare(path) {|io| yield(io) }
156
+ end
157
+
158
+ # like find but raises an error if the path doesn't exist
159
+ def existing_path(als, id)
160
+ path = entry_path(als, id)
161
+ unless File.exists?(path)
162
+ raise "does not exist: #{id.inspect} (#{als.inspect})"
163
+ end
164
+ path
165
+ end
166
+
167
+ # like find but raises an error if the path exists
168
+ def non_existant_path(als, id) # :nodoc:
169
+ path = entry_path(als, id)
170
+ if File.exists?(path)
171
+ raise "already exists: #{id.inspect} (#{als.inspect})"
172
+ end
173
+ path
174
+ end
175
+
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,71 @@
1
+ require 'rack'
2
+
3
+ module Tap
4
+ class Server
5
+ module Runner
6
+ include Rack::Utils
7
+ include Configurable
8
+
9
+ config :servers, %w[thin mongrel webrick], { # the preferred server handlers
10
+ :long => :server
11
+ }, &c.list
12
+
13
+ config :host, 'localhost', &c.string # the server host
14
+ config :port, 8080, &c.integer_or_nil # the server port
15
+
16
+ attr_reader :handler
17
+
18
+ def initialize(config={})
19
+ @handler = nil
20
+ initialize_config(config)
21
+ end
22
+
23
+ def running?
24
+ @handler != nil
25
+ end
26
+
27
+ # Runs self as configured, on the specified server, host, and port. Use an
28
+ # INT signal to interrupt.
29
+ def run!(rack_app, handler=rack_handler)
30
+ return self if running?
31
+
32
+ handler.run(rack_app, :Host => host, :Port => port) do |handler_instance|
33
+ @handler = handler_instance
34
+ trap(:INT) { stop! }
35
+ yield if block_given?
36
+ end
37
+
38
+ self
39
+ end
40
+
41
+ # Stops the server if running (ie a handler is set).
42
+ def stop!
43
+ if running?
44
+ # Use thins' hard #stop! if available, otherwise just #stop
45
+ @handler.respond_to?(:stop!) ? @handler.stop! : @handler.stop
46
+ @handler = nil
47
+
48
+ yield if block_given?
49
+ end
50
+
51
+ self
52
+ end
53
+
54
+ protected
55
+
56
+ # Looks up and returns the first available Rack::Handler as listed in the
57
+ # servers configuration. (Note rack_handler returns a handler class, not
58
+ # an instance). Adapted from Sinatra.detect_rack_handler
59
+ def rack_handler # :nodoc:
60
+ servers.each do |server_name|
61
+ begin
62
+ return Rack::Handler.get(server_name)
63
+ rescue LoadError
64
+ rescue NameError
65
+ end
66
+ end
67
+ raise "Server handler (#{servers.join(',')}) not found."
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,35 @@
1
+ module Tap
2
+ class Server
3
+ # A special type of error used for specifiying controller errors.
4
+ class ServerError < RuntimeError
5
+ class << self
6
+
7
+ # A helper to format a non-ServerError into a ServerError response.
8
+ def response(err)
9
+ new("500 #{err.class}: #{err.message}\n#{err.backtrace.join("\n")}").response
10
+ end
11
+ end
12
+
13
+ # The error status
14
+ attr_reader :status
15
+
16
+ # Headers for the error response
17
+ attr_reader :headers
18
+
19
+ # The error response body
20
+ attr_reader :body
21
+
22
+ def initialize(body="500 Server Error", status=500, headers={'Content-Type' => 'text/plain'})
23
+ @body = body
24
+ @status = status
25
+ @headers = headers
26
+ super(body)
27
+ end
28
+
29
+ # Formats self as a rack response array (ie [status, headers, body]).
30
+ def response
31
+ [status, headers, [body]]
32
+ end
33
+ end
34
+ end
35
+ end
data/lib/tap/server.rb CHANGED
@@ -1,252 +1,76 @@
1
- require 'rack'
2
- require 'rack/mock'
3
-
4
1
  require 'tap'
5
- require 'tap/server_error'
2
+ require 'tap/server/data'
3
+ require 'tap/server/runner'
4
+ require 'tap/server/server_error'
6
5
 
7
6
  module Tap
8
- Env.manifest(:controllers) do |env|
9
- controllers = Support::ConstantManifest.new('controller')
10
- env.load_paths.each do |path_root|
11
- controllers.register(path_root, '**/*.rb')
12
- end
13
- controllers
14
- end
15
-
16
- # :::-
17
- # Server is a Rack application that dispatches calls to other Rack apps, most
18
- # commonly a Tap::Controller.
19
- #
20
- # == Routes
21
- #
22
- # Routing is fixed and very simple:
23
- #
24
- # /:controller/path/to/resource
25
- #
26
- # Server dispatches the request to the controller keyed by :controller after
27
- # shifting the key from PATH_INFO to SCRIPT_NAME.
28
- #
29
- # server = Server.new
30
- # server.controllers['sample'] = lambda do |env|
31
- # [200, {}, ["Sample got #{env['SCRIPT_NAME']} : #{env['PATH_INFO']}"]]
32
- # end
33
- #
34
- # req = Rack::MockRequest.new(server)
35
- # req.get('/sample/path/to/resource').body # => "Sample got /sample : /path/to/resource"
36
- #
37
- # Server automatically maps unknown keys to controllers discovered via the
38
- # env.controllers manifest. The only requirement is that the controller
39
- # constant is a Rack application. For instance:
40
- #
41
- # # [lib/example.rb] => %q{
42
- # # ::controller
43
- # # class Example
44
- # # def self.call(env)
45
- # # [200, {}, ["Example got #{env['SCRIPT_NAME']} : #{env['PATH_INFO']}"]]
46
- # # end
47
- # # end
48
- # # }
49
- #
50
- # req.get('/example/path/to/resource').body # => "Example got /example : /path/to/resource"
51
- #
52
- # If desired, controllers can be set with aliases to map a path key to a
53
- # lookup key.
54
- #
55
- # server.controllers['sample'] = 'example'
56
- # req.get('/sample/path/to/resource').body # => "Example got /sample : /path/to/resource"
57
- #
58
- # If no controller can be found, the request is routed using the
59
- # default_controller_key and the request is NOT adjusted.
60
- #
61
- # server.default_controller_key = 'app'
62
- # server.controllers['app'] = lambda do |env|
63
- # [200, {}, ["App got #{env['SCRIPT_NAME']} : #{env['PATH_INFO']}"]]
64
- # end
65
- #
66
- # req.get('/unknown/path/to/resource').body # => "App got : /unknown/path/to/resource"
67
- #
68
- # In development mode, the controller constant is removed and the constant
69
- # require path is reloaded each time it gets called. This system allows many
70
- # web frameworks to be hooked into a Tap server.
71
- #
72
- # :::+
7
+ # ::configurable
73
8
  class Server
74
- class << self
75
-
76
- # Instantiates a Server in the specified directory, configured as
77
- # specified in root/server.yml. If shutdown_key is specified, a
78
- # random shutdown key will be generated and set on the sever.
79
- #
80
- def instantiate(root, shutdown_key=false)
81
- # setup the server directory
82
- root = File.expand_path(root)
83
- FileUtils.mkdir_p(root) unless File.exists?(root)
84
-
85
- # initialize the server
86
- app = Tap::App.instance
87
- env = Tap::Exe.instantiate(root)
88
- env.activate
89
- config = Configurable::Utils.load_file(env.root['server.yml'])
90
-
91
- server = new(env, app, config)
92
- server.config[:shutdown_key] = rand(1000000) if shutdown_key
93
- server
94
- end
95
-
96
- # Runs the server
97
- def run(server)
98
- cookie_server = Rack::Session::Pool.new(server)
99
- server.run!
100
- end
101
- end
9
+ include Runner
102
10
 
103
- include Rack::Utils
104
- include Configurable
11
+ # Server implements a secret for HTTP administration of the server (ex
12
+ # remote shutdown). Under many circumstances this functionality is
13
+ # undesirable; specify a nil secret, the default, to prevent remote
14
+ # administration.
15
+ config :secret, nil, &c.string_or_nil # the admin secret
105
16
 
106
- config :environment, :development
107
- config :servers, %w[thin mongrel webrick], &c.list # a list of preferred handlers
108
- config :host, 'localhost', &c.string # the server host
109
- config :port, 8080, &c.integer # the server port
17
+ config :development, false, &c.flag
110
18
 
111
- # A hash of (key, controller) pairs mapping the controller part of a route
112
- # to a Rack application. Typically controllers is used to specify aliases
113
- # when the defaults are not preferable.
114
- config :controllers, {}
19
+ config :router, true, &c.switch
115
20
 
116
- # config :infer_controllers, true, &c.switch
21
+ nest :env, Tap::Env, :type => :hidden
117
22
 
118
- # The default controller key used in routes that cannot be directly mapped
119
- # to a controller
120
- #--
121
- # Set to nil to force controller mapping?
122
- config :default_controller_key, 'app'
23
+ nest :app, Tap::App, :type => :hidden
123
24
 
124
- # Server implements a shutdown key so the server can be shutdown remotely
125
- # via an HTTP request to the app/shutdown method. Remote shutdown is
126
- # useful when the user is running a local server (especially from a
127
- # background process). Under many circumstances remote shutdown is
128
- # undesirable; specify a nil shutdown key, the default, to turn off
129
- # shutdown.
130
- config :shutdown_key, nil, &c.integer_or_nil # specifies a public shutdown key
25
+ nest :data, Data, :type => :hidden
131
26
 
132
- attr_reader :env
133
- attr_reader :handler
27
+ attr_accessor :controller
134
28
 
135
- def initialize(env=Env.new, app=Tap::App.instance, config={})
136
- @env = env
137
- @app = app
138
- @cache = {}
139
- @handler = nil
140
- initialize_config(config)
141
- end
29
+ attr_accessor :thread
142
30
 
143
- # Runs self as configured, on the specified server, host, and port. Use an
144
- # INT signal to interrupt.
145
- def run!(handler=rack_handler)
146
- app.log :run, "#{host}:#{port} (#{handler})"
147
- handler.run self, :Host => host, :Port => port do |handler_instance|
148
- @handler = handler_instance
149
- trap(:INT) { stop! }
150
- end
31
+ def initialize(controller=nil, config={})
32
+ @controller = controller
33
+ @thread = nil
34
+ super(config)
151
35
  end
152
36
 
153
- # Stops the server if running (ie a handler is set). Returns true if the
154
- # server was stopped, and false otherwise.
155
- def stop!
156
- if handler
157
- # Use thins' hard #stop! if available, otherwise just #stop
158
- handler.respond_to?(:stop!) ? handler.stop! : handler.stop
159
- @handler = nil
160
- false
161
- else
162
- true
163
- end
37
+ # Returns true if input is equal to the secret, if a secret is set. Used
38
+ # to test if a particular request has rights to a remote administrative
39
+ # action.
40
+ def admin?(input)
41
+ secret != nil && input == secret
164
42
  end
165
43
 
166
- # Currently a stub for initializing a session. initialize_session returns
167
- # an integer session id.
168
- def initialize_session
169
- id = 0
170
- session_app = app(id)
171
- session_root = root(id)
172
-
173
- # setup expiration information...
174
-
175
- # setup a session log
176
- log_path = session_root.prepare(:log, 'session.log')
177
- session_app.logger = Logger.new(log_path)
178
- session_app.on_complete do |_result|
179
- # find the template
180
- class_name = _result.key.class.to_s.underscore
181
- pattern = "#{class_name}/result\.*"
182
- template = nil
183
- env.each do |e|
184
- templates = e.root.glob(views_dir, pattern)
185
- unless templates.empty?
186
- template = templates[0]
187
- break
188
- end
189
- end
190
-
191
- if template
192
- extname = File.extname(template)
193
- env.root.prepare(:results, id.to_s, "#{class_name}#{extname}") do |file|
194
- file << Support::Templater.new(File.read(template)).build(:_result => _result)
195
- end
196
- end
197
- end unless session_app.on_complete_block
198
-
199
- id
200
- end
201
-
202
- # Returns the session-specific App, or the server app if id is nil.
203
- def app(id=nil)
204
- @app
205
- end
206
-
207
- # Returns the session-specific Root, or the server env.root if id is nil.
208
- def root(id=nil)
209
- @env.root
210
- end
211
-
212
- # Returns a uri mapping to the specified controller and action. Parameters
213
- # may be specified; they are built as a query and attached to the uri as
214
- # normal.
215
- #
216
- # Currenlty uri does not map the controller to a minipath, but in the
217
- # future it will.
218
- def uri(controller=nil, action=nil, params={})
219
- query = build_query(params)
220
- uri = ["http://#{host}:#{port}", escape(controller), action].compact.join("/")
221
- query.empty? ? uri : "#{uri}?#{query}"
222
- end
223
-
224
- # The {Rack}[http://rack.rubyforge.org/doc/] interface method.
225
- def call(rack_env)
226
- if development?
227
- env.reset
228
- @cache.clear
229
- end
44
+ def route(rack_env)
45
+ return controller unless router
230
46
 
231
47
  # route to a controller
232
- blank, key, path_info = rack_env['PATH_INFO'].split("/", 3)
233
- controller = lookup(unescape(key))
48
+ blank, path, path_info = rack_env['PATH_INFO'].split("/", 3)
49
+ controller = lookup_controller(unescape(path))
234
50
 
235
51
  if controller
236
- # adjust env if key routes to a controller
237
- rack_env['SCRIPT_NAME'] = ["#{rack_env['SCRIPT_NAME'].chomp('/')}/#{key}"]
52
+ # adjust rack_env if route routes to a controller
53
+ rack_env['SCRIPT_NAME'] = ["#{rack_env['SCRIPT_NAME'].chomp('/')}/#{path}"]
238
54
  rack_env['PATH_INFO'] = ["/#{path_info}"]
239
55
  else
240
- # use default controller key
241
- controller = lookup(default_controller_key)
242
-
243
- unless controller
244
- raise ServerError.new("404 Error: could not route to controller", 404)
245
- end
56
+ # use default controller
57
+ controller = self.controller
58
+ path = nil
246
59
  end
247
60
 
61
+ rack_env['tap.controller_path'] = path
62
+ controller
63
+ end
64
+
65
+ # The {Rack}[http://rack.rubyforge.org/doc/] interface method.
66
+ def call(rack_env)
248
67
  # handle the request
249
68
  rack_env['tap.server'] = self
69
+
70
+ unless controller = route(rack_env)
71
+ raise ServerError.new("404 Error: could not route to controller", 404)
72
+ end
73
+
250
74
  controller.call(rack_env)
251
75
  rescue ServerError
252
76
  $!.response
@@ -254,66 +78,27 @@ module Tap
254
78
  ServerError.response($!)
255
79
  end
256
80
 
257
- # Searches env for the first matching file, directories are not matched.
258
- def search(dir, path)
259
- env.search(dir, path) {|file| File.file?(file) }
81
+ def run!
82
+ super(self)
260
83
  end
261
84
 
262
85
  protected
263
86
 
264
- # Returns true if environment is :development.
265
- def development? # :nodoc:
266
- environment == :development
267
- end
268
-
269
- # Looks up and returns the first available Rack::Handler as listed in the
270
- # servers configuration. (Note rack_handler returns a handler class, not
271
- # an instance). Adapted from Sinatra.detect_rack_handler
272
- def rack_handler # :nodoc:
273
- servers.each do |server_name|
274
- begin
275
- return Rack::Handler.get(server_name)
276
- rescue LoadError
277
- rescue NameError
278
- end
279
- end
280
- raise "Server handler (#{servers.join(',')}) not found."
281
- end
282
-
283
- # a helper method for routing a key to a controller
284
- def lookup(key) # :nodoc:
285
- return @cache[key] if @cache.has_key?(key)
286
- minikey = controllers[key] || key
287
-
288
- # return registered controllers
289
- if minikey.respond_to?(:call)
290
- @cache[key] = minikey
291
- return minikey
292
- end
293
-
294
- # return if no controller can be found
295
- unless const = env.controllers.search(minikey)
296
- @cache[key] = nil
297
- return nil
298
- end
299
-
300
- # load the require_path in dev mode so that
301
- # controllers will be reloaded each time
302
- if development? && const.require_path
303
- parent = if const.nesting.empty?
304
- Object
87
+ def lookup_controller(key) # :nodoc:
88
+ if development
89
+ # unload the controller in development mode so that
90
+ # controllers will be reloaded each request
91
+
92
+ env.reset
93
+ if const = env.seek(:controller, key)
94
+ const.unload
95
+ const.constantize
305
96
  else
306
- Tap::Support::Constant.constantize(const.nesting) { nil }
97
+ nil
307
98
  end
308
-
309
- if parent && parent.const_defined?(const.const_name)
310
- parent.send(:remove_const, const.const_name)
311
- end
312
-
313
- load const.require_path
99
+ else
100
+ env[:controller][key]
314
101
  end
315
-
316
- @cache[key] = const.constantize
317
102
  end
318
103
  end
319
104
  end