Episodes

Resources

Download Source Code

Summary

# Terminal
rails g model consent name mandatory:boolean description:text
rails g model user_consent user:references consent:references agreed:boolean
rails db:migrate
rails g devise:views

# models/consent.rb
class Consent < ApplicationRecord
  has_many :user_consents
  # has_many :users, through: :user_consents
end

# models/user_consent.rb
class UserConsent < ApplicationRecord
  belongs_to :user
  belongs_to :consent
end

# models/user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable, :recoverable, 
         :rememberable, :trackable, :validatable

  include UserConsentValidation
end

# models/concerns/user_consent_validation.rb
module UserConsentValidation
  extend ActiveSupport::Concern
  included do
    has_many :user_consents, dependent: :destroy
    has_many :consents, through: :user_consents

    accepts_nested_attributes_for :user_consents

    validate :consent_given

    def build_consents
      return unless new_record?
      Consent.all.map { |consent| user_consents.build(consent_id: consent.id) }
    end
    
    private

    def consent_given
      errors.add(:consent, 'must be accepted to continue') unless mandatory_consents.map(&:agreed).all?
    end

    def mandatory_consents
      user_consents.select { |uc| uc.consent.mandatory? }
    end
  end
end

# views/devise/registrations/new.html.erb
  <div class="field">

    <%= f.fields_for :user_consents, resource.build_consents do |c| %>
      <%= c.hidden_field :consent_id %>
      <%= c.check_box :agreed %>
      <%= c.object.consent&.description %>
      <br>
    <% end %>
  </div>

# db/seeds.rb
Consent.create do |consent|
  consent.name = 'cookies'
  consent.mandatory = true
  consent.description = 'I agree for use of cookies on this site to distinguish me from other users.'
end

Consent.create do |consent|
  consent.name = 'terms_and_conditions'
  consent.mandatory = true
  consent.description = 'I agree to the terms and conditions.'
end

Consent.create do |consent|
  consent.name = 'privacy_policy'
  consent.mandatory = true
  consent.description = 'I agree to the privacy policy.'
end

Consent.create do |consent|
  consent.name = 'sell_your_information'
  consent.mandatory = false
  consent.description = 'I agree to allow my information to be sold to third parties.'
end

# controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  private

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up) do |user_params|
      user_params.permit(:email, :password, :password_confirmation, user_consents_attributes: {})
    end
  end
end

# config/initializers/filter_parameter_logging.rb
Rails.application.config.filter_parameters += [:first_name, :last_name, :email, :password]