@alexcameron89
PERUSING THE RAILS SOURCE CODE
A BEGINNERS GUIDE
⋆ Star 30,000
Time
Internals
Comprehension
Me
Time
Internals
Comprehension
You
CAUTION
Side Effects
Perusing Rails
You might experience
an increase in
Ruby knowledge
You might experience
an increase in
Git knowledge
You might experience
enlightenment to
magic
Rails GitHub Repository
Rails Dependencies Setup
guides.rubyonrails.org/development_dependencies_install
github.com/rails/rails
actioncable
actionmailer
actionpack
activejob
guides
Gemfile
Gemfile.lock
rails.gemspec
actionview
activerecord
activesupport
activemodel
railties
MIT-LICENSE
CONTRIBUTING.md
README.md
~ ❯ cd rails/
~/rails ❯ ls
actioncable
actionmailer
actionpack
activejob
guides
Gemfile
Gemfile.lock
rails.gemspec
actionview
activerecord
activesupport
activemodel
railties
MIT-LICENSE
CONTRIBUTING.md
README.md
~ ❯ cd rails/
~/rails ❯ ls
~ ❯ rails server
Request
~ ❯ rails server
Routes
Request
~ ❯ rails server
Controller
Routes
Request
~ ❯ rails server
Controller
Routes
Request
~ ❯ rails server
Model
Controller
Routes
Request
~ ❯ rails server
View
Model
Controller
Routes
Request
~ ❯ rails server
View
Model
Controller
Routes
Request
Response
~ ❯ rails server
View
Model
Controller
Routes
ActionPack
Request
Response
~ ❯ rails server
View
Model
Controller
ActiveRecord
ActiveModel
Routes
ActionPack
Request
Response
~ ❯ rails server
View
Model
Controller
ActiveRecord
ActiveModel
Routes
ActionPack
ActionView
Request
Response
~ ❯ rails server
View
Model
Controller
ActiveRecord
ActiveModel
Routes
ActionPack
ActionView
Request
Response
~ ❯ rails server
View
Model
Railties
Controller
ActiveRecord
ActiveModel
Routes
⋆
⋆
⋆
⋆ ActiveSupport
ActionPack
ActionView
Request
Response
~ ❯ rails server
View
Model
Railties
ActionMailer
Mail Delivery
ActiveJob
Jobs/Tasks
ActionCable
Integrated
WebSockets
+
Your Story tenderlove github
ActionMailer
Mail Delivery
ActiveJob
Jobs/Tasks
ActionCable
Integrated
WebSockets
+
Your Story tenderlove github
ActionMailer
Mail Delivery
ActiveJob
Jobs/Tasks
ActionCable
Integrated
WebSockets
tenderlove
Aaron Patterson
What room are you in?
South Ballroom
actioncable
actionmailer
actionpack
activejob
guides
Gemfile
Gemfile.lock
rails.gemspec
~/rails ❯ ls
lib
test
README.rdoc
activerecord.gemspec
CHANGELOG.md
bin
examples
MIT-LICENSE
RUNNING_UNIT_TESTS.rdoc
~/rails ❯ cd activerecord/
~/rails/activerecord ❯ ls
s.add_dependency "activesupport", version
s.add_dependency "activemodel", version
s.add_dependency "arel", "~> 8.0"
activerecord/active_record.gemspec
~/rails/activerecord ❯ cd lib
~/rails/activerecord/lib ❯ ls
active_record
rails
active_record.rb
attributes.rb
attribute_mutation_tracker.rb
base.rb
persistence.rb
reflection.rb
relation.rb
schema.rb
. . .
~/rails/activerecord/lib ❯ cd active_record
~/rails/activerecord/lib/active_record ❯ ls
~/rails/activerecord/lib ❯ cd active_record
~/rails/activerecord/lib/active_record ❯ ls
attributes.rb
attribute_mutation_tracker.rb
base.rb
persistence.rb
reflection.rb
relation.rb
schema.rb
. . .
~/rails/activerecord/lib ❯ cd active_record
~/rails/activerecord/lib/active_record ❯ ls
attributes.rb
attribute_mutation_tracker.rb
base.rb
persistence.rb
reflection.rb
relation.rb
schema.rb
. . .
Post.find_by
# => #<Post id:1, title: "Example", ...>
Post.where
# => #<ActiveRecord::Relation [#<Post ...>]>
activerecord/lib/active_record/relation/finder_methods.rb
77 def find_by(arg, *args)
78 where(arg, *args).take
79 rescue ::RangeError
80 nil
81 end
Ǹ
Digging Through
Code With Ruby
Post.method(:create).source_location
~/blog ❯ rails console
Post.method(:create).source_location
=> [“…/lib/active_record/persistence.rb”, 29]
~/blog ❯ rails console
activerecord/lib/active_record/persistence.rb
29 def create(attributes = nil, &block)
. . .
33 object = new(. . .)
34 object.save
35 object
36 . . .
37 end
Post.new.method(:save).source_location
=> [“…/lib/active_record/suppressor.rb”, 41]
Persistence
41: def save(*) # :nodoc:
=> 42: SuppressorRegistry.suppressed[self.class.name] ?
true : super
43: end
activerecord/lib/active_record/suppressor.rb
306: def save(*) #:nodoc:
307: rollback_active_record_state! do
=> 308: with_transaction_returning_status { super }
309: end
310: end
activerecord/lib/active_record/transactions.rb
34: def save(*)
=> 35: if status = super
36: changes_applied
37: end
38: status
39: end
activerecord/lib/active_record/attribute_methods/dirty.rb
43: def save(options = {})
=> 44: perform_validations(options) ? super : false
45: end
activerecord/lib/active_record/validations.rb
124: def save(*args)
=> 125: create_or_update(*args)
126: rescue ActiveRecord::RecordInvalid
127: false
128: end
activerecord/lib/active_record/persistence.rb
post = Post.new
post.method(:save).source_location
=> [“. . . /suppressor.rb”]
post.method(:save).super_method.source_location
=> [“. . . /transactions.rb”]
...
require "byebug"
...
byebug
Post.create
activerecord/active_record.gemspec
~/rails ❯ ls rails/guides/bug_report_templates/
action_controller_master.rb
active_job_master.rb
active_record_master.rb
active_record_migrations_master.rb
benchmark.rb
generic_master.rb
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
gem "rails", github: "rails/rails"
gem "arel", github: "rails/arel"
gem "sqlite3" # or "pg" or "mysql2" . . .
end
require "active_record"
require "minitest/autorun"
require "logger"
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
gem "rails", path: “path/to/your/rails“
gem "arel", github: "rails/arel"
gem "sqlite3" # or "pg" or "mysql2" . . .
end
require "active_record"
require "minitest/autorun"
require "logger"
ActiveRecord::Base.establish_connection(adapter: "sqlite3",
database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :posts do |t|
t.string :title
end
end
class Post < ActiveRecord::Base
end
. . .
. . .
class BugTest < Minitest::Test
def test_association_stuff
post = Post.create(title: “Hello”)
assert_equal “Hello”, post.title
end
end
. . .
class BugTest < Minitest::Test
def test_association_stuff
post = Post.create(title: “Hello”)
assert_equal “Hello”, post.title
end
end
. . .
post = Post.create(title: “Hello”)
require “pry”
. . .
post = Post.create(title: “Hello”)
binding.pry
[1] pry(main)>
require “pry”
. . .
post = Post.create(title: “Hello”)
binding.pry
[1] pry(main)> Post.all
=> [#<Post id: 1, title: "Hello">]
require “byebug”
. . .
byebug
Post.create(title: "Fancy Post")
[31, 33] in active_record_master.rb
31:
32: byebug
=> 33: Post.create
(byebug) s
in activerecord/lib/active_record/persistence.rb
29: def create(attributes = nil, &block)
=> 30: if attributes.is_a?(Array)
. . .
32: else
33: object = new(attributes, &block)
(byebug) n
in activerecord/lib/active_record/persistence.rb
29: def create(attributes = nil, &block)
30: if attributes.is_a?(Array)
. . .
32: else
=> 33: object = new(attributes, &block)
(byebug) var local
in activerecord/lib/active_record/persistence.rb
29: def create(attributes = nil, &block)
30: if attributes.is_a?(Array)
. . .
32: else
=> 33: object = new(attributes, &block)
(byebug) var local
attributes = {:title=>"Hello"}
block = nil
It can be troubling to know
simultaneously the exact value
and correct execution of a
variable.
- Werner Heisenbug
I know this I don’t know this
Association Reflection
Attributes
Attribute Mutation
Tracker
Relation
. . .
I know this I don’t know this
Association
Reflection
Attributes
Attribute Mutation
Tracker
Relation
. . .
Delete It
Delete It
def define_method_attribute=(name)
safe_name = name.unpack("h*".freeze).first
. . .
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def __temp__#{safe_name}=(value)
. . .
end
alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
undef_method :__temp__#{safe_name}=
STR
end
activerecord/lib/active_record/attribute_methods/write.rb
Creates post.title=
def define_method_attribute=(name)
safe_name = name.unpack(“h*".freeze).first
. . .
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def __temp__#{safe_name}=(value)
. . .
end
alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
undef_method :__temp__#{safe_name}=
STR
end
activerecord/lib/active_record/attribute_methods/write.rb
=> “479647c656”
def define_method_attribute=(name)
safe_name = name.unpack(“h*".freeze).first
. . .
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def __temp__#{safe_name}=(value)
. . .
end
alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
undef_method :__temp__#{safe_name}=
STR
end
activerecord/lib/active_record/attribute_methods/write.rb
def define_method_attribute=(name)
safe_name = name.unpack(“h*".freeze).first
. . .
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def __temp__#{safe_name}=(value)
. . .
end
alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
undef_method :__temp__#{safe_name}=
STR
end
activerecord/lib/active_record/attribute_methods/write.rb
=> “__temp__479647c656”
def define_method_attribute=(name)
safe_name = name.unpack(“h*".freeze).first
. . .
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def __temp__#{safe_name}=(value)
. . .
end
alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
undef_method :__temp__#{safe_name}=
STR
end
activerecord/lib/active_record/attribute_methods/write.rb
def define_method_attribute=(name)
safe_name = name.unpack(“h*".freeze).first
. . .
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def __temp__#{safe_name}=(value)
. . .
end
alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
undef_method :__temp__#{safe_name}=
STR
end
activerecord/lib/active_record/attribute_methods/write.rb
def define_method_attribute=(name)
safe_name = name.unpack(“h*".freeze).first
. . .
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def __temp__#{safe_name}=(value)
. . .
end
alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
undef_method :__temp__#{safe_name}=
STR
end
activerecord/lib/active_record/attribute_methods/write.rb
def define_method_attribute=(name)
safe_name = name.unpack(“h*".freeze).first
. . .
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def #{name}=(value)
. . .
end
alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
undef_method :__temp__#{safe_name}=
STR
end
activerecord/lib/active_record/attribute_methods/write.rb
def define_method_attribute=(name)
safe_name = name.unpack(“h*".freeze).first
. . .
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def #{name}=(value)
. . .
end
alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
undef_method :__temp__#{safe_name}=
STR
end
activerecord/lib/active_record/attribute_methods/write.rb
def define_method_attribute=(name)
safe_name = name.unpack(“h*".freeze).first
. . .
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def #{name}=(value)
. . .
end
STR
end
activerecord/lib/active_record/attribute_methods/write.rb
1) Error:
BasicsTest#test_non_valid_identifier_column_name:
SyntaxError: formal argument cannot be a global variable
def a$b=(value)
def test_non_valid_identifier_column_name
weird = Weird.create("a$b" => "value")
weird.reload
assert_equal "value", weird.send("a$b")
assert_equal "value", weird.read_attribute("a$b")
. . .
end
activerecord/lib/active_record/attribute_methods/write.rb
ƶ
Finding Information
With Git
~/rails ❯ git blame
. . .
7c70430c Ryuta Kamizono 2016-08-23 def __temp__#{safe_name}=(value)
7c70430c Ryuta Kamizono 2016-08-23 name = . . .
7c70430c Ryuta Kamizono 2016-08-23 write_attribute(name, value)
7c70430c Ryuta Kamizono 2016-08-23 end
. . .
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -15,13 +15,13 @@ def define_method_attribute=(name)
ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
- def __temp__#{safe_name}=(value)
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
- write_attribute(name, value)
- end
- alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
- undef_method :__temp__#{safe_name}=
- STR
+ def __temp__#{safe_name}=(value)
+ name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
+ write_attribute(name, value)
+ end
+ alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
+ undef_method :__temp__#{safe_name}=
+ STR
end
end
~/rails ❯ git blame -w
. . .
b785e921 Aaron Patterson 2013-07-03 def __temp__#{safe_name}=(value)
b785e921 Aaron Patterson 2013-07-03 name = . . .
b785e921 Aaron Patterson 2013-07-03 write_attribute(name, value)
b785e921 Aaron Patterson 2013-07-03 end
. . .
~/rails ❯ git log 
-S "alias_method #{(name + '=').inspect}"
commit b785e921d186753d905c1d0415b91d0987958028
Author: Aaron Patterson <lovingtenderly@tenderlove.com>
Date: Wed Jul 3 14:18:31 2013 -0700
method transplanting between modules isn't supported on 1.9
commit 55ac7db11bd2fc0cf06d7184f013018fa4be0e9a
Author: Aaron Patterson <lovingtenderly@tenderlove.com>
Date: Wed Jul 3 10:56:56 2013 -0700
keep a cache of writer methods
. . .
~/rails ❯ git log 
--patch 
--pickaxe-all 
-S "alias_method #{(name + '=').inspect}"
commit b785e921d186753d905c1d0415b91d0987958028
Author: Aaron Patterson <lovingtenderly@tenderlove.com>
Date: Wed Jul 3 14:18:31 2013 -0700
method transplanting between modules isn't supported on 1.9
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/module/method_transplanting'
+
module ActiveRecord
module AttributeMethods
@@ -62,9 +64,30 @@ def cache_attribute?(attr_name)
~/rails ❯ git log 
--reverse 
--patch 
--pickaxe-all 
-S "alias_method #{(name + '=').inspect}"
commit f1765019ce9b6292f2264b4601dad5daaffe3a89
Author: Jon Leighton <j@jonathanleighton.com>
Date: Fri Oct 12 12:33:11 2012 +0100
Don't allocate new strings in compiled attribute methods
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -9,15 +9,19 @@ module Write
module ClassMethods
protected
- def define_method_attribute=(attr_name)
- if attr_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP
❯ Initial Commit
commit baa237c974fee8023dd704a4efb418ff0e963de0
Author: Santiago Pastorino <santiago@wyeworks.com>
Date: Mon Mar 21 21:36:05 2011 -0300
Allow to read and write AR attributes with non valid identifiers
Vim Fugitive
GitHub
Learning More
with GitHub
DEPRECATION WARNING: `redirect_to :back` is
deprecated and will be removed from Rails 5.1.
Please use `redirect_back(fallback_location:
fallback_location)` . . .
~/rails ❯ git log -S 
"Please use `redirect_back(fallback_loc"
~/rails ❯ git log -S 
"Please use `redirect_back(fallback_loc"
commit 13fd5586cef628a71e0e2900820010742a911099
Author: Derek Prior
Date: Tue Dec 15 20:17:32 2015 -0500
Add `redirect_back` for safer referrer redirects
. . .
When there is no referrer available on the request,
`redirect_to :back` will raise `ActionController::RedirectBackError`,
usually resulting in an application error.
. . .
General
Rails Knowledge
Reproduction Script
Issue: #51367
General
Rails Knowledge
Reproduction Script
Issue: #51367
General
Rails Knowledge
Pull Request Received
Discussion between
Contributors & Maintainers
More Commits Added
Rebased,
Merged into master
ƶ
Takeaways from PRs & Issues
With reproduction steps
Attached PR
Resources
the_act_of_making_love_tenderly.rb
I am a puts debuggerer
tenderlovemaking.com
Aaron Patterson
you_should_tell.rb
eileencodes.com/speaking/
Breaking Down the
Barrier: Demystifying
Contributing to Rails
System Tests PR
github.com/rails/rails/pull/26703
Eileen Uchitelle
hi_sean_hi_derek.rb
The Bike Shed
@_bikeshed
Derek Prior
Sean Griffin
Amanda Hill
magical_learning_elixir.rb
Crafting Rails 4
Applications
pragprog.com
José Valim
fxn.rb
The Rails Boot Process
https://youtu.be/vSza2FZucV8
Xavier Noria
Thank You
@alexcameron89
Slides: LINK HERE

Perusing the Rails Source Code