DEV Community

Zubair Mohsin
Zubair Mohsin

Posted on

Looking into Credits feature on DEV from Laravel Developer perspective (Code Dive)

Important!!

  • I am a PHP/Laravel Developer and I have no idea how Ruby/Rails work.
  • We will make assumptions based on Laravel's conventions.
  • We will focus on Credits feature only to keep our interest piqued.
  • This is just a code reading exercise πŸ€·πŸ»β€β™‚οΈ

If you visit https://dev.to/credits, you will see how many credits you have in your account. You can see use these credits to create listings.

Getting ready

  • First, let's clone the dev.to repository on local.
  • Open it in your favorite code editor

We are not going to look into how to set it up on local, you can read about that in docs

DIVE

Our first hint is the URI, /credits.
Let's find out where routes are defined. A quick file search config/routes.rb can be found.

#...

  resources :credits, only: %i[index new create] do
    get "purchase", on: :collection, to: "credits#new"
  end

#...
Enter fullscreen mode Exit fullscreen mode

Following the Resourceful controllers convention, we can safely assume that there must be CreditsController with index function.

credits_controller.rb source

class CreditsController < ApplicationController
  before_action :authenticate_user!

  def index
    @user_unspent_credits_count = current_user.credits.unspent.size
    @ledger = Credits::Ledger.call(current_user)

    @organizations = current_user.admin_organizations
  end
Enter fullscreen mode Exit fullscreen mode
  • Current logged in user's unspent credits are being calculated
  • Then there is some kind of Ledger is being maintained
  • Finally, organizations of current user

User and Credits

A straightforward assumption is that these both are Models.

user.rb source

class User < ApplicationRecord

#...

has_many :credits, dependent: :destroy

#...
Enter fullscreen mode Exit fullscreen mode

credit.rb source

class Credit < ApplicationRecord

#...

belongs_to :user, optional: true

#...

Enter fullscreen mode Exit fullscreen mode

Relationships are defined as we would define in Laravel Eloquent.

unspent scope

unspent in current_user.credits.unspent.size is defined as a scope in Credit model.

  scope :unspent, -> { where(spent: false) }
Enter fullscreen mode Exit fullscreen mode

Ledger

ledger.rb can be found in Credits module. source

    def call
      # build the ledger for the user
      ledger = {
        [User.name, user.id] => build_ledger_for(user.credits)
      }

      # build the ledger for the organizations the user is an admin at
      user.admin_organizations.find_each do |org|
        ledger[[Organization.name, org.id]] = build_ledger_for(org.credits)
      end

      ledger
    end
Enter fullscreen mode Exit fullscreen mode

build_ledger_for method is responsible for the Payment history on /credits view. source

How Credits are added?

  • On every Badge Achievement on DEV you get 5 credits
  • You can purchase credits here

Badge Achievement

badge_achievement.rb source

class BadgeAchievement < ApplicationRecord
  belongs_to :user
  belongs_to :badge

#...

after_create :award_credits

#...

  def award_credits
    Credit.add_to(user, 5)
  end
Enter fullscreen mode Exit fullscreen mode

After creating BadgeAchievement, award 5 credits to user. We can relate this to Laravel Eloquent Events

Purchasing Credits

In CreditsController,

  • new method is responsible for presenting the view somehow πŸ™ˆ
  • create method is responsible for the actual Purchase logic (Stripe) and adding credits to our account.

How Credits are subtracted?

Credits are subtracted/removed when we create a listing. Based on this, we can assume that there would be a ListingsController with create containing this logic.

  • Well, its actually ClassifiedListingsController defined in classified_listings_controller.rb source
  • And create method is inside a module/concern named as ClassifiedListingsToolkit source
def create_listing(purchaser, cost)

#...

ActiveRecord::Base.transaction do
      # subtract credits
      Credits::Buyer.call(
        purchaser: purchaser,
        purchase: @classified_listing,
        cost: cost,
      )

#...

end
Enter fullscreen mode Exit fullscreen mode

This is how credits are subtracted from our account when we create a listing.

Conclusion

We looked at how Credits feature works on DEV and where to find its implementation in the codebase.

I hope someone finds it useful.

Top comments (0)