tap-server 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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