Sha256: e4d8ac228c364655ab54224817f82214ff9d6eed318056bd6f686f1388f667df

Contents?: true

Size: 1.38 KB

Versions: 6

Compression:

Stored size: 1.38 KB

Contents

# frozen_string_literal: true

module Gamefic
  # A safe execution environment for narrative code.
  #
  module Stage
    module_function

    # @param narrative [Narrative]
    def run(narrative, *args, &code)
      container = narrative.clone
      narrative.instance_exec(*args, &code).tap { validate_changes narrative, container, code }
    end

    OVERWRITEABLE_CLASSES = [String, Numeric, Symbol].freeze

    SWAPPABLE_VALUES = [true, false, nil].freeze

    class << self
      private

      def validate_changes narrative, container, code
        container.instance_variables.each do |var|
          next unless narrative.instance_variables.include?(var)

          cval = container.instance_variable_get(var)

          nval = narrative.instance_variable_get(var)
          next if cval == nval

          validate_overwriteable(cval, nval, "Unsafe reassignment of #{var} in #{code}")
        end
      end

      def validate_overwriteable cval, nval, error
        raise error unless overwriteable?(cval, nval)
      end

      def overwriteable? cval, nval
        return true if swappable?(cval, nval)

        allowed = OVERWRITEABLE_CLASSES.find { |klass| cval.is_a?(klass) }
        allowed && cval.is_a?(allowed)
      end

      def swappable? *values
        values.all? { |val| SWAPPABLE_VALUES.include?(val) }
      end
    end
  end
end

Version data entries

6 entries across 6 versions & 1 rubygems

Version Path
gamefic-3.4.0 lib/gamefic/stage.rb
gamefic-3.3.0 lib/gamefic/stage.rb
gamefic-3.2.1 lib/gamefic/stage.rb
gamefic-3.2.0 lib/gamefic/stage.rb
gamefic-3.1.0 lib/gamefic/stage.rb
gamefic-3.0.0 lib/gamefic/stage.rb