ruby-on-railscookiesruby-on-rails-5

Why isn't referred_by being set?


I am trying to follow/reproduce this tutorial here https://gorails.com/episodes/referral-program-from-scratch?autoplay=1 Code is here: https://github.com/gorails-screencasts/referral-program

So far my code does create a referral code for each new signup. But it does not record who the referrer was, referred_by. That part is not working.

In my ApplicationController I have:

before_action :set_referred_by_cookie

  protected
  
    def set_referred_by_cookie
      if params[:ref]
        cookies[:referred_by] = {
          value: params[:ref],
          expires: 30.days.from_now,
        }
      end
    end

It appears this part works bc when I look at this with browser tools I see: Cookie: referred_by=d6733c0da6f0; (but it's not saving to db)

And here's the sample link from the user I created to use for a new signup, so it does look like the ref is converted into the "referred_by" http://localhost:3000/?ref=d6733c0da6f0

Here's my user.rb:

class User < ApplicationRecord
  has_prefix_id :user

  belongs_to :referred_by, class_name: "User", optional: true
  has_many :referred_users, class_name: "User", foreign_key: :referred_by_id
  
  before_validation :set_referral_code

  validates :referral_code, uniqueness: true

  def set_referral_code
    if self.referral_code.nil?
      loop do
        self.referral_code = SecureRandom.hex(6)
        break unless self.class.exists?(referral_code: referral_code)
      end
    end
  end

  # Call this from anywhere in your application when the user has completed their referral steps
  #after_create :complete_referral!
  def complete_referral!
    update(referral_completed_at: Time.zone.now)
    # TODO: add credit to referred_by user
  end

end

Here's registration controller/devise:

class Users::RegistrationsController < Devise::RegistrationsController
  invisible_captcha only: :create


  protected

  def build_resource(hash = {})
    if cookies[:referred_by] && referrer = User.find_by(referred_by: cookies[:referred_by])
      self.resource.referred_by = referrer
    end

    self.resource = resource_class.new_with_session(hash, session)
  end

  def sign_up(resource_name, resource)
    sign_in(resource_name, resource)
  end
end

It appears that this code in the registration controller isn't working?:

if cookies[:referred_by] && referrer = User.find_by(referred_by: cookies[:referred_by])
      self.resource.referred_by = referrer

This is where I'm stuck trying to get the "referred_by" to work, which should add it to "referred_by_id" in database. Ignore the "referral_completed_at", I know that works.. From database:

["referral_code", "ae922a5c9df8"], ["referred_by_id", nil], ["referral_completed_at", nil]]

Solution

  • Your build_resource is a bit backwards, you need to set self.resource first then assign referred_by, and you're searching on the wrong column:

    def build_resource(hash = {})
      super 
    
      if cookies[:referred_by] && referrer = User.find_by(referral_code: cookies[:referred_by])
        self.resource.referred_by = referrer
      end
    end
    

    sign_up method is also unnecessary.