ActiveRecord Internals : You are not ready for this
Tags: rails, ruby
Published: 2023-08-22 05:05:21

## Introduction to sorcery

I like this quote: Magic is the technology we still need to understand. Rails is an excellent framework; it hides complexity from developers, but because of that, many people qualify it as magical.

This article will demystify Rails by looking deeper into the Rails active record association internals. 
In the meantime, we will also try to understand pretty cool programming concepts.

I hope you are ready ( you are not ), it's going to be fun, let's go.

Oh yes, and if you like to learn or read about Rails, Ruby, databases, and a lot of tech-related stuff :

### Keep in Touch 

On Twitter/X : [@yet_anotherDev] (https://twitter.com/yet_anotherDev)

On Linkedin : [Lucas Barret](https://www.linkedin.com/in/lucas-barret-67194b164/)

## Did you say association? 

Let's begin with defining active record associations and why they are helpful.

If you are a Rails developer, you should know, and I am sure you know it, this fantastic website : 
[Rails Guides](https://guides.rubyonrails.org/association_basics.html). 

Going to this link and you can read that : association is a connection between two active record models, and that's all. 
But Why do they exist? Because it makes everyday operations straightforward and more accessible. 

Nice, keep reading and we see that Associations are implemented using macro-style programming ? 

## Macro style Ruby Fu

A lot of definition in this article but yet this is useful to be sure we all understand the same thing.
Now going to [Wikipedia](https://en.wikipedia.org/wiki/Macro_(computer_science)) we can see that : a macro is a rule or a pattern that specifies how a specific input should be mapped to a replacement output. 

This makes everyday tasks or definitions available for programmers as a single statement. ( Like your associations statements ).

This kind of programming style enables define our association in a declarative way. If you define an Athlete class that `has_many` Medal(s), you will write it as it : 

```ruby
class Medal < ActiveRecord::Base
	belongs_to :athlete
end

class Athlete < ActiveRecord::Base
	has_many :medals
end
```

You declare that your athlete `has_many` medals and that medal `belongs_to` athlete.

In ruby, macros and this kind of declarative style of programming are possible thanks to the class-level method but also thanks to how ruby classes work.

## Go back to class 

Ruby Classes are no unique code; as with everything else in Ruby, they are executable. 

If I define a Ruby class called: Athlete, with a workout class method. I could call it inside the class, and when my ruby interpreter reads my class code, it will execute my workout method. 

```ruby
#./athlete.rb
class Athlete 
  def self.workout
    p '1 push up'
   end
	
   workout
end
```

```shell
> ruby athlete.rb
> 1 push up
```

As you can see, your class definition code is executed!

It is precisely what is happening with your has_many declaration. It is a method call with an argument which is the name of your associate model in a simple case we illustrated earlier.
 
## Dancing in the dark

It is time to dive into rails code; let's see what we can find.
As we want to find out how has_many associations work, the first step is to look for a definition of a method called has_many.  

If you look in the ActiveRecord code, you will find :

```ruby
#rails/activerecord/associations.rb
def has_many(name, scope = nil, **options, &extension)
  reflection = Builder::HasMany.build(self, name, scope, options, &extension)
  Reflection.add_reflection self, name, reflection
end
```

But this is weird, and you could ask why this is not defined as a class method.

Looking around, this is a class method defined in the module ClassMethods, with another pattern we won't cover here. 

Now in the internal of this function, little is done. We build a reflection and add it to the Reflection class with the association name.

If we go to the `rails/activerecord/associations/builder/has_many.rb` file, what we see is not helpful for what we want to understand at first sight.

We see the options available for our association and the dependent options. But there has yet to be a build class methods function.

```ruby
module ActiveRecord::Associations::Builder # :nodoc:
  class HasMany < CollectionAssociation # :nodoc:
    def self.macro
      :has_many
    end

    def self.valid_options(options)
...
    end

    def self.valid_dependent_options
...
    end
		private_class_method :macro, :valid_options, :valid_dependent_options
  end
end

```

Nevertheless, this class inherits from CollectionAssociation let's see what we got in `rails/activerecord/associations/builder/collection_association.rb`; there are many cool things here, but still no build methods.

```ruby
module ActiveRecord::Associations::Builder
  class CollectionAssociation < Association 
```

But as before, this class inherits from another class, Association. Let's see this one.

And this is what we are looking for. We have the build methods, which all the associations share.

And in this method, the famous reflection is built and returned to the has_many class methods.

```ruby
def self.build(model, name, scope, options, &block)
  if model.dangerous_attribute_method?(name)
    raise ArgumentError, "You tried to define an association named #{name} on the model #{model.name}, but " \
                         "This will conflict with a method #{name} already defined by Active Record. " \
                         "Please choose a different association name."
  end

  reflection = create_reflection(model, name, scope, options, &block)
  define_accessors model, reflection
  define_callbacks model, reflection
  define_validations model, reflection
  define_change_tracking_methods model, reflection
  reflection
end
```

But why is reflection needed? What is a reflection? 

This will be the subject of my next article...


## Conclusion

We have begin our journey in the dark magic Rails. If you want to feel powerful, wait for the second one next week.

If you liked this article and would like to know more or hear from it differently: check this [keynote](https://www.youtube.com/watch?v=6qHKtAkqguc&t=377s) from Daniel Colson. 

I dove into the code and began this article before seeing the keynote. Then I was like, should I not publish it? Eventually, I got more knowledge from this conference and tried to improve the article the best I could. 
I hope this article will help my fellow rubyists.

See you for the second part next week.