# encoding: utf-8

module Axiom
  module Types

    # Abstract base class for every type
    class Type
      extend Options, DescendantsTracker

      accept_options :constraint
      constraint proc { true }

      def self.new(constraint = Undefined, &block)
        ::Class.new(self, &block).constraint(constraint).finalize
      end

      def self.finalize
        IceNine.deep_freeze(@constraint)
        freeze
      end

      def self.finalized?
        frozen?
      end

      def self.include?(object)
        included = @constraint.call(object)
        if included != true && included != false
          raise TypeError,
            "constraint must return true or false, but was #{included.inspect}"
        end
        included
      end

      def self.constraint(constraint = Undefined, &block)
        constraint = block if constraint.equal?(Undefined)
        add_constraint(constraint) unless constraint.nil?
        self
      end

      singleton_class.class_eval { protected :constraint }

      # TODO: move this into a module. separate the constraint setup from
      # declaration of the members, like the comparable modules.
      def self.includes(*members)
        set = IceNine.deep_freeze(members.to_set)
        constraint(&set.method(:include?))
      end

      def self.add_constraint(constraint)
        current = @constraint
        @constraint = if current
          lambda { |object| constraint.call(object) && current.call(object) }
        else
          constraint
        end
      end

      private_class_method :includes, :add_constraint

    end # class Type
  end # module Types
end # module Axiom

# encoding: utf-8

module Axiom
  module Types

    # Abstract base class for every type
    class Type
      extend Options, DescendantsTracker

      accept_options :constraint
      constraint proc { true }

      def self.new(constraint = Undefined, &block)
        ::Class.new(self, &block).constraint(constraint).finalize
      end

      def self.finalize
        IceNine.deep_freeze(@constraint)
        freeze
      end

      def self.finalized?
        frozen?
      end

      def self.include?(object)
        included = @constraint.call(object)
        if included != true && included != false
          raise TypeError,
            "constraint must return true or false, but was #{included.inspect}"
        end
        included
      end

      def self.constraint(constraint = Undefined, &block)
        constraint = block if constraint.equal?(Undefined)
        add_constraint(constraint) unless constraint.nil?
        self
      end

      # TODO: move this into a module. separate the constraint setup from
      # declaration of the members, like the comparable modules.
      def self.includes(*members)
        set = IceNine.deep_freeze(members.to_set)
        constraint(&set.method(:include?))
      end

      def self.add_constraint(constraint)
        current = @constraint
        @constraint = if current
          lambda { |object| constraint.call(object) && current.call(object) }
        else
          constraint
        end
      end

      private_class_method :includes, :add_constraint

    end # class Type
  end # module Types
end # module Axiom

Remove protected visibility from constraint
Remove protected visibility from constraint
