gosuri/roomer

View on GitHub
lib/roomer/extensions/model.rb

Summary

Maintainability
A
45 mins
Test Coverage
module Roomer
  module Extensions
    module Model
      def self.included(base)
        base.extend(ClassMethods)
      end

      module ClassMethods
        # Sets the roomer scope for the model and changes the model's table_name_prefix
        # Sets the table name prefix (schema name) to current_tenant's
        # If :shared is passed, the global schema will be used as the table name prefix
        # if :tenanted is pased, the current tenant's schema will be used as the table name prefix
        # @return [Symbol] :shared or :tenanted
        def roomer(scope)
          case scope
            when :shared
              @roomer_scope = :shared
            when :tenanted
              @roomer_scope = :tenanted
            else
              raise "Invalid roomer model scope.  Choose :shared or :tenanted"
          end
          roomer_initialize_model!
        end

        # Confirms if model is shared
        # @return [True,False]
        def shared?
          @roomer_scope == :shared
        end

        # Confirms if model is scoped by tenant
        # @return [True,False]
        def tenanted?
          @roomer_scope == :tenanted
        end

        # Resets model's cached table_name and
        # column information.
        # This method needs to get called whenever
        # the current tenant changes
        def roomer_reset
          roomer_set_table_name_prefix
          roomer_reset_table_name
          reset_column_information
          reset_associations
        end

        protected

        # Save the table name declared by `set_table_name`.
        def roomer_initialize_model!
          Roomer.register_model(self)
          @roomer_original_table_name = @table_name
          roomer_set_table_name_prefix
          roomer_ensure_table_name_prefix
        end

        # Reset original table name.
        #
        # reset_table_name will not acknowledge the newly-changed
        # table_name_prefix if set_table_name was used.
        #
        def roomer_reset_table_name
          if @roomer_original_table_name
            self.table_name = "#{table_name_prefix}#{@roomer_original_table_name}"
          else
            reset_table_name
          end
          roomer_ensure_table_name_prefix
        end

        def roomer_ensure_table_name_prefix
          unless table_name.start_with?(table_name_prefix)
            self.table_name = "#{table_name_prefix}#{table_name}"
          end
          table_name
        end

        # Resets cached data in associations
        # Fixes bug that mixed table_name_prefix
        # between tenants
        def reset_associations
          reflections.each_value do |r|
            r.instance_variable_set(:@quoted_table_name, nil)
            table_name = r.instance_variable_get(:@table_name)
            if (table_name)
              table_name = table_name.split(".").last
              klass = r.class_name.constantize
              schema_name   = klass.tenanted? ? Roomer.current_tenant.try(:schema_name) : Roomer.shared_schema_name
              schema_name &&= schema_name.to_s
              schema_name ||= "__no_tenant__"
              table_name = "#{schema_name}#{Roomer.schema_seperator}#{table_name}"
              r.instance_variable_set(:@table_name, table_name)
              r.instance_variable_set(:@quoted_table_name, connection.quote_table_name(table_name))
            end
          end
        end

        # Resolves the full table name prefix
        def roomer_full_table_name_prefix(schema_name)
          "#{schema_name.to_s}#{Roomer.schema_seperator}"
        end

        # Sets the model's table name prefix to the current tenant's schema name
        # Defaults to public if model is marked as tenanted but tenant table
        # hasn't been populated
        def roomer_set_table_name_prefix
          self.table_name_prefix = begin
            case @roomer_scope
              when :shared
                roomer_full_table_name_prefix(Roomer.shared_schema_name)
              when :tenanted
                roomer_full_table_name_prefix(Roomer.current_tenant.try(Roomer.tenant_schema_name_column))
              else
                ""
            end
          end
        end
      end
    end
  end
end