Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Project Structure Wiki Addition #453

Closed
cmhhelgeson opened this issue Feb 13, 2024 · 1 comment
Closed

Project Structure Wiki Addition #453

cmhhelgeson opened this issue Feb 13, 2024 · 1 comment

Comments

@cmhhelgeson
Copy link

cmhhelgeson commented Feb 13, 2024

Might be unnecessary, but I was wondering if a general project structure page on the wiki might help people get up to speed on how the repo works. This is unfinished, but contains information generally extensible to most Rails projects, while still connecting that knowledge to the particulars of how the Maybe-Finance project is set up. Additionally, if the text doesn't accord exactly with how Rails parses these folders or considers these objects, please let me know.

Project Structure

Below, you'll find a broad description of the function of various files and folders within the maybe-finance proejct. While much of the structure below is germaine to almost every Ruby on Rails project, this guide will take note of various files that are essential to the layout of the maybe-finance project.

Database (db)

Files related to database creation and managaement with the ActiveRecord ORM (aka Rail's default Object Relational Mapping tool). More about the purpose, design, and intended usage of ActiveRecord can be found at this link: https://guides.rubyonrails.org/active_record_basics.html

  • /migrate: A folder containing a collection of Rails ActiveRecord Migrations. Each active record migration abstracts away SQL-specific alterations to your program's database schema, allowing you to utilize Ruby to alter the structure of your database. While this description might make it sound as if migrations are only adding to the structure defined in our schema.rb file, our migrations code is actually the core of how we define our program's database. In fact, if you compare in schema.rb with the collected code from migration, you'll see that the table and class definitions are 1:1.

Each Rails migration file has a distinct structure:

# This line specifies that the migration CreateAccountLoans inherits from ActiveRecord::Migration[7.2]
# Specifying the version of our migration code allows future versions of ActiveRecord to properly
# execute/interpret older migrations, even if future versions of Rails contain new features.
class CreateAccountLoans < ActiveRecord::Migration[7.2]
  # Defines a change to the schema
  def change
    # Creates a table called 'account_loans'with a primary uuid key
    create_table :account_loans, id: :uuid do |t|
      # Automatically creates a 'created_at' and 'updated_at' datetime column to the table
      t.timestamps
    end
  end
end

To learn more about Rails migrations, including how to define a migration reversion, follow this guide: https://guides.rubyonrails.org/active_record_migrations.html

  • schema.rb: A representation of the current state of our program's database schema, automatically generated by Rails when running our schema migratons.
  • seedsr.rb: Populates the database with the initial data required to run the Maybe application. This file defines this initial data using models from the app/models directory.

App/Views

The app/views folder contains most of the html for our project, all found within Embedded Ruby files.

App/Models

Folder that defines the application's models. According to Rails conventions, model naming schemes will directly correlate to database tables with the same name (albeit pluralized). For instance, model Family will correlate to database table 'familes', and model Account::Vehicle will correlate to database table 'account_vehicles'. By coupling models and database tables in this way, Rails allows us to write generalizable ruby code that operates on our databases.

/app/models/conerns:

A folder where each file contains a module that extends ActiveSupport::Concern, otherwise known as a concern. A concern is the same as any other module that can be inherited by classes, just with a specific structure that delineates how instance methods and class methods are created on the inheriting class. To put it simply, any methods defined in the 'included do' block become instance methods of the inheriting class. An example call on such a method would look like this:

class_instance.new_method arg1, arg2

Any methods defined in the 'class_methods' do block become class methods of the inheriting class. An example call on this kind of method would look like this:

Class.new_method arg1, arg2

To learn more about concerns, read the article at this link: https://www.writesoftwarewell.com/how-rails-concerns-work-and-how-to-use-them/

To learn about how concerns allow us to create delegated types, read section 6 of the Ruby on Rails Guide for Active Record Associations.

Notable Concerns

Accountable: A concern that sets up a polymorphic association between our base Account model and multiple Account types, like Credit Accounts, Loan Accounts, Vehicle Accounts, and more. Within our account table, each Account contains a type and an id (for instance "LOAN" and "4367") that acts as a reference to a model that inherits from Accountable.

class Account < ApplicationRecord
  ###
  # Use delegation to dynamically create association between any account and multiple different
  # accountable types. Destroy the dependent accountable type when it's account is destroyed
  delegated_type :accountable, types: Accountable::TYPES, dependent: :destroy

  # When calling the type_name method on an account, call it on the accountable type associated 
  # with that account entry, as specified by the accounts accountable_type and accountable_id
  delegate :type_name, to: :accountable

  ###
end

Additionally, the Accountable module itself is associated with its parent account.

module Accountable extend ActiveSupport::Concern
  ###
  # Indicate a uni-directional association between any kind of Accountable object and a single 
  # account 
  included do
    has_one :account, as: :accountable, touch: true
  end
end

In essence, the Accountable concern allows us to use the Account model as an interface and accessor for more specific account types. While every type of account is referenced within the Account model via its type and id, each account type contains its own table of entries ('credits', 'loans', 'vehicles') and can define its own type-specific behavior. This gives us the flexibility to, for instance, model credit score behavior for credits accounts or vehicle financing for vehicle accounts types while still providing features under the general umbrella of an account.

/app/models/account:

A folder contained classes that include the Accountable concern. Accordingly, each class within this folder becomes a Accountable model type that an Account can associate with, with behavior that the Account can also dynamically delegate to that type.

@zachgoll
Copy link
Collaborator

This is a good overview of the current app, but since we're so early on and things are quickly changing, I'm going to hold off on publishing any definitive explanation. Once we reach a point of stability with the app I'll likely get a "walkthrough" published.

In the meantime, I think the Rails Guides are the best resource for newcomers, and as you mentioned with Accountable relationship, reading up on delegated types is a good place to start.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants