Skip to content

Instantly share code, notes, and snippets.

@DanielEFrampton
Last active March 22, 2020 02:35
Show Gist options
  • Save DanielEFrampton/452a0641b80d94d269d96caa21a7952f to your computer and use it in GitHub Desktop.
Save DanielEFrampton/452a0641b80d94d269d96caa21a7952f to your computer and use it in GitHub Desktop.
Introduction to Rails - How Data Is Passed and a Rails App Is Structured

Explain It Like I'm Front-End: Ruby on Rails

Some Ruby Basics

How Methods compare to Functions

Closest comparison is JavaScript class functions. In Ruby, it is very infrequent for a method to be defined outside of a class. Taking this example from the MDN docs on Classes:

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  // Getter
  get area() {
    return this.calcArea();
  }
  // Method
  calcArea() {
    return this.height * this.width;
  }
}

const square = new Rectangle(10, 10);

console.log(square.area); // 100

The equivalent Ruby class would be:

class Rectangle
    attr_reader :height, :width

    def initialize(height, width)
        @height = height
        @width = width
    end

    def calc_area
        @height * @width
    end

    def another_method(arg1, arg2)
        "do some stuff" + calc_area.to_s
    end
end

square = Rectangle.new(10, 10)

puts square.calc_area

Things you can see here:

  • Methods implicitly return the value of the last expression in the method, but that can be made explicit (or forced to return earlier than that and break off executing the method) with return just like in JS.
  • No end-of-line syntax like JS (;), line breaks are assumed to close an expression unless the next line starts with a . to chain methods.
  • initialize works just like constructor.
  • Ruby prefers do ... end instead of curly braces ({ ... }) but in some cases (such as iteration/enumerable blocks) accepts either. When defining classes and methods, the opening do is implied.
  • The equivalent of "instance properties" are "instance variables" in Ruby and they are preceded by an @; but unlike instance properties, they are not accessible from outside the Class scope unless you explicitly make a "getter" method for them with attr_reader followed by a symbol reference for it (the name of the property preceded by a colon), and then only read-only. For read/write (getter/setter), attr_accessor is used.
  • Unlike JS, if a method does not take an argument you don't need to include parentheses afterward, both when defining it and when calling it. This can make it challenging, at a glance, to tell the difference in Ruby between a local variable and a method; a quick "find in file" or "find in project" is the best way to clarify, but generally if you don't see a local variable by that name defined in that method or passed in as an argument then it is a method and you'll either find it elsewhere in that class or in another class (or module) it inherits from.

Scope Levels of Variables and Methods

  • As probably became clear in that last example, Ruby is much more encapsulated by default than JS, preferring to keep data contained to its class or method whenever possible.
  • Local variables are what you'll most frequently see. They are block-scoped and exist only for duration of the execution of a particular block, and are declared without any keyword: example_variable = "An example value!" They are mutable, so they can be re-assigned as many times as you'd like.
  • There is no (to my knowledge) equivalent of "hoisting" in Ruby; assignment occurs in the order lines of code are executed.
  • Instance variables and instance methods can be accessed anywhere within the class. There is an odd extension of this logic when it comes to Controllers and Views in Rails, which we'll see later.

MVC Overview using Dev.to Example

image from freeCodeCamp artickle

Routes.rb

  • Anatomy of a normal ("hand-rolled") route and how to find associated controller and "action"
  • How dynamic segments work and how they're passed to controller through params
  • A quick overview of how "resources" automate route creation
  • Namespaces and how they affect where the controller file is looked for
  • View available routes with rake routes

Controllers

  • Controllers are task-specific Ruby classes. From the Rails Action Controller docs:

    A controller is a Ruby class which inherits from ApplicationController and has methods just like any other class. When your application receives a request, the routing will determine which controller and action to run, then Rails creates an instance of that controller and runs the method with the same name as the action.

  • The ApplicationController class always lives at app/controllers/application_controller.rb, and because its methods are inherited by all controllers it often has methods which relate to user authentication, cookies, sessions, and other things that every part of the app needs access to.

  • By default, a controller action will render the view file corresponding to that controller and action. So this controller:

    class ClientsController < ApplicationController
        def new
        end
    end

    would render app/views/clients/new.html.erb. The important thing to note is that it renders that View file within the new action method after all other lines have executed, so that View has access to any instance variables (but not local variables) declared in that action.

  • However, a controller can explicitly render a View other than what the default would be, using the render function followed by a string with the folder and name of the appropriate view.

    • The view might be a "partial," i.e. a partial template, which is meant to be re-used across multiple views. If so, the filename of that partial will have an underscore prepended, like _example_partial.html.erb.
  • In the MVC flow, the first thing a Controller typically does is retrieve or change data by way of a Model.

Models, ActiveRecord, and SQL/Relational Databases

  • In SQL/relational databases (RDBs), information is stored on tables which are related to each other through primary keys (columns on each table with a unique identifier for each row) and foreign keys (a reference to another row's primary key, typically a row on a different table).
  • SQL (structured query language) is the language used to create, read, update, and delete (CRUD) data on the tables in a relational database. It is powerful but somewhat complex, following a different kind of logic than what we typically do while coding.
  • Rails comes shipped with ActiveRecord, an object relational mapper (ORM) library which allows you to interact with your RDB data as if each table was a class and each row on that table is an instance of that class. It also allows you to interact with the DB using Ruby methods instead of SQL by turning ActiveRecord functions into SQL queries behind the scenes.
  • For each table in the database, there will be a Model class. By default every instance of that class will have a reader method for each column on the table, but the Model class allows you to define additional instance methods; this is typically where data logic lives in a Rails app.
  • The Model class is also typically home to any (ActiveRecord) queries against that table as a whole which need to return instances of that class. For example, if I wanted to retrieve all the Users with a certain number of purchases that included items sold by a given merchant, even though that query involves multiple tables it would be a class method (not inherited by instances) on the User model because I want to retrieve User objects.

Views, Instance Variables, & ERB

  • As described earlier, Views are rendered by Controller actions.

Putting it together: how is the app structured & what should I pay attention to?

Models, views, and controllers

Everything else you can ignore unless you see it referenced.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment