Metadata, Metaprogramming And ActiveRecord

Eric
3 min readAug 2, 2018

--

Metadata is data that describes user data. It summarizes basic information about data, which can make finding and working with particular instances of data easier. For example, author, date created, date modified and file size are examples of very basic document metadata.

Metaprogramming refers to a variety of ways a program has knowledge of itself or can manipulate itself.

ActiveRecord is an awesome, amazing ORM framework. It is very easy to covert RMDBS data to our Domain object. So here, I will introduce Metadata and Metaprogramming by making a simple ActiveRecord.

Requirement

We can inherit our SimpleActiveRecord::Base below to build our object from the database:

  class Player < SimpleActiveRecord::Base

end

player = Player.create(name:"player1", age:10)
player.age = 20
player.save

Cool! That’s it. Then we can manipulate the database data by using create,update,find etc. In this article, we will build an Object Mapper, but we will leave Relation Mapper for another topic. That means there is no has_many, belongs_to features in the first version.

Get Data Structure From Table

The first challenge we will encounter is How we can get our attributes? As we see above, there is nothing in our class definition. So, is it possible to get the structure/attributes from the database? The answer is yes, we can use the SQL statement below to get all of the columns which belong to the table (the table name is players):

pragma table_info(players);

cid name type notnull dflt_value pk
---------- ---------- ---------- ---------- ---------- ----------
0 id integer 0 1
1 name text 0 0
2 age integer 0 0

So, here we can see the database has more data to describe the table info, this sort of data is called Metadata.

Adding attributes into class dynamically

We have all of the columns from the table, the next step is to figure out how to set the value to the object’s attributes. There is no attr_accessor to define getter and setter and no instance variables in the class. Because of this, Ruby provides class APIs which are called meta methods. Here is the code:

alt text

attrs is an array of strings, so where is it from? Remember that we got all of the columns above? We can use the column names as attribute names and use define_method(attr) { instance_variable_get("@#{attr}") } to define a getter and use define_method("#{attr}=") { |val| instance_variable_set("@#{attr}", val) } to define a setter. define_method and instance_variable_get are meta methods which can manipulate the structure of class. So, when we modify the program itself, including its methods and attributes in runtime, it's called Metaprogramming.

Generate the SQL CRUD statement

Now that we have the table name and the column names, we can generate our CRUD statement. Let’s start with insert statement, remember that its formation is insert into table_name(col1, col2...) values (?, ?...), Suppose we have the column_names array, we can generate the column part like this "(#{column_names.join(',')})" and the ? part like this "(#{(['?']*column_names.size).join(',')})". Finally, we will generate the value array by using the getter and passing it into the execute method, the pseudocode looks like:

alt text

We can use the same approach to generate the update,select and delete statement.

Conclusion

In this article we used Metadata and Metaprogramming to solve the problems we will encounter when we are building the SimpleActiveRecord. There are still a lot of details to cover, but we finished the most important parts. Metaprogramming is a powerful approach and it makes your program more flexible.

Reference

Diving Into Ruby’s attr_accessor

Metaprogramming in Ruby

--

--