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
#...
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
- 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
#...
credit.rb
source
class Credit < ApplicationRecord
#...
belongs_to :user, optional: true
#...
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) }
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
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
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 inclassified_listings_controller.rb
source - And
create
method is inside a module/concern named asClassifiedListingsToolkit
source
def create_listing(purchaser, cost)
#...
ActiveRecord::Base.transaction do
# subtract credits
Credits::Buyer.call(
purchaser: purchaser,
purchase: @classified_listing,
cost: cost,
)
#...
end
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)