2017.05.15 - Kait Wang @ Rails Study Group
Kait Wang - 2017.05.15 @ Rails Study Group
Kait Wang a.k.a Sibevin Wang
# config/routes.rb
root "homes#show"
Started GET "/" for 172.18.0.1 at 2017-05-08 05:34:30 +0000
Cannot render console from 172.18.0.1! Allowed networks: 127.0.0.1, ::1, ...
ActionController::RoutingError (uninitialized constant HomesController):
activesupport (5.1.0) lib/active_support/inflector/methods.rb:269:in `const_get'
# app/controllers/homes_controller.rb
class HomesController < ApplicationController
end
<!-- app/views/homes/show.html.erb -->
Hello world !!
It's not working in a regular ruby code, even the file is given ... (source code)
# homes_controller.rb
class HomesController
def show
p "The homes#show is called!!"
end
end
# main.rb
homes_ctrl = HomesController.new
homes_ctrl.show
main.rb:1:in `<main>': uninitialized constant HomesController (NameError)
You need require the file
# main.rb
require './homes_controller'
homes_ctrl = HomesController.new
homes_ctrl.show
The homes#show is called!!
There are two ways to load codes: require v.s. load
See also: ruby的load與require是差在什麼地方?
But we DON'T require files in rails ...
What happened?
Started GET "/" for 172.18.0.1 at 2017-05-08 05:34:30 +0000
Cannot render console from 172.18.0.1! Allowed networks: 127.0.0.1, ::1, ...
ActionController::RoutingError (uninitialized constant HomesController):
activesupport (5.1.0) lib/active_support/inflector/methods.rb:269:in `const_get'
When we create a class
class HomesController do ... end
It means
HomesController = Class.new do ... end
class Object
def self.const_missing(c)
if c == :HomesController
require './homes_controller'
const_get(c)
else
super
end
end
end
homes_ctrl = HomesController.new
homes_ctrl.show
Try to find "Xxx::YYYZzzController" in "app/controllers/xxx/y_y_y_zzz_controller.rb"
def self.const_missing(c)
if c =~ /.*Controller\Z/
file_name = c.to_s.gsub(/::/, '/').
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
tr("-", "_").
downcase
require "./app/controllers/#{file_name}"
const_get(c)
else
# ...
Add the folder path to $LOAD_PATH, ruby would search these paths when we require files
$LOAD_PATH << File.join(File.dirname(__FILE__), "app", "controllers")
class Object
def self.const_missing(c)
if c =~ /.*Controller\Z/
file_name = c.to_s.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
require file_name
const_get(c)
else
# ...
If we use a wrong controller name ...
# app/controllers/homes_controller.rb
class HomeController
def show
p "The homes#show is called!!"
end
end
ruby-2.2.3/lib/ruby/2.2.0/rubygems.rb:1119:in `each': stack level too deep (SystemStackError)
from ruby-2.2.3/lib/ruby/2.2.0/rubygems.rb:1119:in `find_unresolved_default_spec'
from ruby-2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:43:in `require'
from main.rb:11:in `const_missing'
from main.rb:12:in `const_get'
from main.rb:12:in `const_missing'
from main.rb:12:in `const_get'
from main.rb:12:in `const_missing'
from main.rb:12:in `const_get'
def self.const_missing(c)
super if @is_reloaded
if c =~ /.*Controller\Z/
@is_reloaded = true
file_name = c.to_s.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
require file_name
result = const_get(c)
@is_reloaded = false
return result
else
super
end
end
The magic only works with a precondition ...
Convention over Configuration
The "HomesController" MUST be in "app/controllers/homes_controller.rb"
More details: Rails Guide - Autoloading and Reloading Constants
def const_missing(const_name)
from_mod = anonymous? ? guess_for_anonymous(const_name) : self
Dependencies.load_missing_constant(from_mod, const_name)
end