-
Notifications
You must be signed in to change notification settings - Fork 5
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
Join affiliates with owners #118
base: master
Are you sure you want to change the base?
Conversation
…code in owners table
…ing affiliate code
…ith no corresponding affiliate
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to make sure that for new owners we set the affiliate on creation. This probably needs to happen in the owners_controller, so that based on the affiliate code we associate the right model directly.
We should also consider how to handle this in the frontend. I think it might actually be nice if the backend returns an error if an affiliate code is sent be the frontend that does not exist in the database. But in that case we should make sure that the frontend handles that error gracefully and displays a helpful error message to the user, so that they can double check the affiliate link or simply create an account without affiliate link.
@@ -50,8 +51,7 @@ def blocked? | |||
|
|||
def affiliate_logo | |||
return unless affiliate | |||
|
|||
Affiliate.find_by(code: affiliate)&.logo_url | |||
affiliate.logo_url |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
affiliate.logo_url | |
affiliate&.logo_url |
In case you have not seen before, it's called the safe navigation operator and makes sure we do not get an exception when affiliate is nil here :)
@@ -74,8 +74,7 @@ def stripe_price_id | |||
main_price_id = ENV['STRIPE_SUBSCRIPTION_PRICE_ID'] | |||
|
|||
raise "ENV['STRIPE_SUBSCRIPTION_PRICE_ID'] is empty" unless main_price_id.present? | |||
|
|||
Affiliate.find_by(code: affiliate)&.stripe_price_id_monthly || main_price_id | |||
affiliate.stripe_price_id_monthly || main_price_id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
affiliate.stripe_price_id_monthly || main_price_id | |
affiliate&.stripe_price_id_monthly || main_price_id |
Elsewise this will also crash for empty affiliates
@@ -106,4 +105,9 @@ def trial_end | |||
# There is not trial if the trial is blank or has already been passed, else it has to be at least two days in the future | |||
trial_ends_at? && trial_ends_at.future? ? [trial_ends_at, 50.hours.from_now].max.to_i : nil | |||
end | |||
|
|||
def self.non_associated_affiliates |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a scope instead of a class method, then one can also chain this, e.g.:
Owner.where('created_at > ?', 1.week.ago).non_associated_affiliates
Perhaps a better name for this scope would then also be "with_missing_affiliate" or similar
|
||
def self.non_associated_affiliates | ||
# To identify owners that have used an affiliate thats not properly registered in the affiliates table. | ||
Owner.where(affiliate: nil).where.not(affiliate_code: nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can actually leave the Owner.
, since where
is also defined as an instance method. This would also enable chaining.
@@ -0,0 +1,9 @@ | |||
owners_with_affiliate = Owner.all.select { |owner| owner.affiliate_code } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a rake task :)
@@ -0,0 +1,9 @@ | |||
owners_with_affiliate = Owner.all.select { |owner| owner.affiliate_code } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be written as a db query. Right now you first load all owners from the database to an array, and then filter it. This operation can be done way more efficiently by the database using the active record query interface. In this case one might use:
Owner.where.not(affiliate_code: nil)
Affiliate.destroy_all | ||
Owner.destroy_all | ||
|
||
7.times do | ||
FactoryBot.create(:affiliate) | ||
end | ||
affiliates = Affiliate.all | ||
affiliates.each { |affiliate| puts "#{affiliate.name} #{affiliate.code}" } | ||
|
||
20.times do | ||
FactoryBot.create(:owner) | ||
end | ||
owners = Owner.all | ||
owners.each { |owner| puts owner.name } | ||
|
||
|
||
# give 9 owners an affilate | ||
owners_with_affiliates = owners[0..8] | ||
owners_with_affiliates.each do |owner| | ||
owner.update!(affiliate_code: affiliates.sample.code) | ||
end | ||
|
||
# give 3 owners affiliates which are not yet entities in the db | ||
owners_with_other_affiliates = owners[9..11] | ||
owners_with_other_affiliates.each do |owner| | ||
owner.update!(affiliate_code: affiliates.sample.code.prepend('a').chop) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should go into a spec (probably spec/modles/owner_spec.rb
)
validates :password, | ||
allow_nil: true, | ||
length: { in: Devise.password_length }, | ||
confirmation: true, | ||
on: :update | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now, when migrating, we are going through three stages:
- The app as is right now,
owner.affiliate
will return a string - The apps db is migrated, but the data migration script has not been run yet. Since the code is updated, the app should expect
.affiliate
to return a model, but it's always empty. If payments are executed in this period, owners with affiliates might pay the full price, or similar weird behaviour might occur - The data migration has run, now
.affilaite
will return the affilaite model
So I think for the transition it might make sense to overwrite the owner.affiliate
method so it always returns the affiliate model. Something like
def affiliate | |
return super if super.present? # make sure the association is used after the data migration | |
Affiliate.find_by(code: affiliate_code) # before the data migration is run, we try to find the affiliate "the old fashioned way" | |
end |
Seeded my local db with 7 affiliates and 20 owners
Ran migration - change column name in owners table from affiliate to affiliate_code
Ran migration - add foreign key affiliates to owners table.
Ran script to join data in tables. Used
rails runner
(this script is in /db/scripts/join_affiliate_to_owner.rb)Add class method to owner.rb to quickly identify owners that have a string in affiliate_code which doesn’t correspond to an affiliate instances.
Owner.non_associated_affiliates.each { |owner| puts owner.affiliate_code }
(there should be 3, each string begins with 'a' based on my seed.rb)Review models to update any code that gets an affiliate with
find_by
Testing was done in rails console.