Skip to content

Commit f355a59

Browse files
committed
Add the first chapter
1 parent fc31174 commit f355a59

File tree

2 files changed

+136
-1
lines changed

2 files changed

+136
-1
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/.idea
2+
/temp
3+
/tmp
4+
.DS_Store

README.md

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,132 @@
1-
# Learn-Rails-by-Reading-Source-Code
1+
# Learn-Rails-by-Reading-Source-Code
2+
3+
### Before you research Rails 5 source code
4+
1) I suggest you learn Rack [http://rack.github.io/](http://rack.github.io/) first. You need to know that an object respond to `call` method is the most important convention.
5+
6+
So which is the object with `call` method in Rails?
7+
8+
I will answer this question later.
9+
10+
2) You need a good IDE with debugging function. I use [RubyMine](https://www.jetbrains.com/).
11+
12+
### Follow the process of Rails when starting
13+
As Rack described, `config.ru` is the entry file.
14+
```ruby
15+
# ./config.ru
16+
# This file is used by Rack-based servers to start the application.
17+
18+
require_relative 'config/environment'
19+
20+
run Rails.application # We can guess 'Rails.application' has a 'call' method.
21+
22+
puts Rails.application.respond_to?(:call) # Returned 'true'. Bingo!
23+
```
24+
25+
Let's dig deeper for `Rails.application`.
26+
```ruby
27+
module Rails
28+
class << self
29+
@application = @app_class = nil
30+
31+
attr_accessor :app_class
32+
def application # Oh, 'application' is a class method for module 'Rails'. It is not an object.
33+
# But it returns an object which is an instance of 'app_class'.
34+
# So it is important for us to know what class 'app_class' is.
35+
@application ||= (app_class.instance if app_class)
36+
end
37+
end
38+
end
39+
```
40+
41+
Because `Rails.application.respond_to?(:call) # Returned 'true'.`, `app_class.instance` has a `call` method.
42+
43+
When was `app_class` set?
44+
```ruby
45+
module Rails
46+
class Application < Engine
47+
class << self
48+
def inherited(base) # This is a hooked method.
49+
Rails.app_class = base # This line set the 'app_class'.
50+
end
51+
end
52+
end
53+
end
54+
```
55+
56+
When `Rails::Application` is inherited like below,
57+
```ruby
58+
# ./config/application.rb
59+
module YourProject
60+
class Application < Rails::Application # Here the hooked method `inherited` defined in eigenclass of 'Rails::Application' is invoked.
61+
end
62+
end
63+
```
64+
`YourProject::Application` will become the `Rails.app_class`. Let's replace `app_class.instance` to `YourProject::Application.instance`.
65+
66+
But where is the `call` method? `call` method should be a method of `YourProject::Application.instance`.
67+
68+
Then Rack can `run YourProject::Application.new` (equal to `run Rails.application`).
69+
70+
The `call` method processes every request. Here it is.
71+
```ruby
72+
# ../gems/railties/lib/rails/engine.rb
73+
module Rails
74+
class Engine < Railtie
75+
def call(env) # This method will process every request. It is invoked by Rack. So it is very important.
76+
req = build_request env
77+
app.call req.env
78+
end
79+
end
80+
end
81+
82+
# ../gems/railties/lib/rails/application.rb
83+
module Rails
84+
class Application < Engine
85+
end
86+
end
87+
88+
# ./config/application.rb
89+
module YourProject
90+
class Application < Rails::Application
91+
end
92+
end
93+
94+
```
95+
96+
Ancestor's chain is `YourProject::Application < Rails::Application < Rails::Engine < Rails::Railtie`.
97+
98+
So `YourProject::Application.new.respond_to?(:call) # Will return 'true'`.
99+
100+
But what does `app_class.instance` really do?
101+
102+
`instance` is just a method name. What we really need is `app_class.new`.
103+
104+
When I was reading these code
105+
```ruby
106+
# ../gems/railties/lib/rails/application.rb
107+
module Rails
108+
class Application < Engine
109+
def instance
110+
super.run_load_hooks! # This line confused me.
111+
end
112+
end
113+
end
114+
```
115+
After a deep research, I realized that this code is equal to
116+
```ruby
117+
def instance
118+
a_returned_value = super # Keyword 'super' will call the ancestor's same name method: 'instance'.
119+
a_returned_value.run_load_hooks!
120+
end
121+
```
122+
123+
```ruby
124+
# ../gems/railties/lib/rails/railtie.rb
125+
module Rails
126+
class Railtie
127+
def instance
128+
@instance ||= new # 'Rails::Railtie' is the top ancestor. Now 'app_class.instance' is 'YourProject::Application.new'.
129+
end
130+
end
131+
end
132+
```

0 commit comments

Comments
 (0)