module Is::Stateful

public

Makes objects stateful.

Constants

ALLOWED_FLAGS

Public Class Methods

copied_objects() click to toggle source
# File lib/is/stateful.rb, line 285
def copied_objects
  localized(:__corerb_copied_objects) || localize(:__corerb_copied_objects, {})
end
copying?(object) click to toggle source
# File lib/is/stateful.rb, line 281
def copying?(object)
  copied_objects[object.object_id]
end
new(...) click to toggle source
Calls superclass method
# File lib/is/stateful.rb, line 241
def initialize(...)
  self.class.send(:defined_state).each_pair do |name, state|
    if state[:instance]
      instance_variable_set(name, state[:value])
    end
  end

  super
end
prevent_recursion(object) { || ... } click to toggle source
# File lib/is/stateful.rb, line 273
def prevent_recursion(object)
  object_id = object.object_id
  copied_objects[object_id] = true
  yield
ensure
  copied_objects.delete(object_id)
end

Public Instance Methods

allowed_flag?(flag) click to toggle source
# File lib/is/stateful.rb, line 221
        def allowed_flag?(flag)
  ALLOWED_FLAGS.include?(flag)
end
allowed_flags_string() click to toggle source
# File lib/is/stateful.rb, line 225
        def allowed_flags_string
  ALLOWED_FLAGS.map { |allowed_flag|
    "`#{allowed_flag.inspect}'"
  }.join(", ")
end
define_class_state?(flags) click to toggle source
# File lib/is/stateful.rb, line 231
        def define_class_state?(flags)
  flags.empty? || flags.include?(:class)
end
define_instance_state?(flags) click to toggle source
# File lib/is/stateful.rb, line 235
        def define_instance_state?(flags)
  flags.empty? || flags.include?(:instance)
end
defined_state() click to toggle source
# File lib/is/stateful.rb, line 196
        def defined_state
  @__defined_state ||= {}
end
defined_state_class_module() click to toggle source
# File lib/is/stateful.rb, line 200
        def defined_state_class_module
  @__defined_state_class_module ||= Module.new
end
defined_state_instance_module() click to toggle source
# File lib/is/stateful.rb, line 204
        def defined_state_instance_module
  @__defined_state_instance_module ||= Module.new
end
defined_state_isolations() click to toggle source
# File lib/is/stateful.rb, line 265
        def defined_state_isolations
  @__defined_state_isolations ||= []
end
enforce_allowed_flags(flags) click to toggle source
# File lib/is/stateful.rb, line 213
        def enforce_allowed_flags(flags)
  flags.each do |flag|
    unless allowed_flag?(flag)
      raise ArgumentError, "Expected flag `#{flag.inspect}' to be one of: #{allowed_flags_string}"
    end
  end
end
inherited(subclass) click to toggle source
Calls superclass method
# File lib/is/stateful.rb, line 183
def inherited(subclass)
  defined_state.each_pair do |name, state|
    if state[:class]
      subclass.instance_variable_set(name, state[:value])
    end
  end

  subclass.send(:prepend_defined_state_modules)
  subclass.instance_variable_set("@__defined_state", @__defined_state.dup)

  super
end
initialize_copy(...) click to toggle source
Calls superclass method
# File lib/is/stateful.rb, line 251
def initialize_copy(...)
  @__defined_state_isolations = []

  super
end
mutate_state(name, &block) click to toggle source
public

Safely mutates state by name, yielding a copy of the current value to the block.

# File lib/is/stateful.rb, line 261
def mutate_state(name, &block)
  send("#{name}=", block.call(send(name)))
end
prepend_defined_state_modules() click to toggle source
# File lib/is/stateful.rb, line 208
        def prepend_defined_state_modules
  prepend(defined_state_instance_module)
  singleton_class.prepend(defined_state_class_module)
end
state(name, *flags, default: nil, private: false, reader: true, writer: true) click to toggle source
public

Defines state by name.

flags - Changes how the state is defined. Possible values include:

  * `:class` - Defines state at the class level.

  * `:instance` - Defines state at the instance level.

Dependencies are applied with both the `:class` and `:instance` flags by default.

default - The default value for the defined state.

private - If `true`, the reader/writer will be defined as private.

reader - If `true`, defines a reader for the state.

writer - If `true`, defines a reader for the state.

# File lib/is/stateful.rb, line 110
      def state(name, *flags, default: nil, private: false, reader: true, writer: true)
        flags = flags.map(&:to_sym)
        enforce_allowed_flags(flags)

        state = {
          value: default,
          class: define_class_state?(flags),
          instance: define_instance_state?(flags)
        }

        method_name = name.to_sym
        ivar_name = "@#{method_name}"
        defined_state[ivar_name] = state
        defined_state_isolations << ivar_name

        prefix = if private
          "private "
        else
          ""
        end

        if reader
          if state[:class]
            defined_state_class_module.module_eval <<~CODE, __FILE__, __LINE__ + 1
              #{prefix}def #{method_name}
                unless defined_state_isolations.include?(#{ivar_name.inspect})
                  #{ivar_name} = #{ivar_name}.copy
                  defined_state_isolations << #{ivar_name.inspect}
                end

                #{ivar_name}
              end
            CODE
          end

          if state[:instance]
            defined_state_instance_module.module_eval <<~CODE, __FILE__, __LINE__ + 1
              #{prefix}def #{method_name}
                unless defined_state_isolations.include?(#{ivar_name.inspect})
                  #{ivar_name} = #{ivar_name}.copy
                  defined_state_isolations << #{ivar_name.inspect}
                end

                #{ivar_name}
              end
            CODE
          end
        end

        if writer
          if state[:class]
            defined_state_class_module.module_eval <<~CODE, __FILE__, __LINE__ + 1
              #{prefix}def #{method_name}=(value)
                defined_state[#{ivar_name.inspect}][:value] = value
                #{ivar_name} = value
              end
            CODE
          end

          if state[:instance]
            defined_state_instance_module.module_eval <<~CODE, __FILE__, __LINE__ + 1
              #{prefix}def #{method_name}=(value)
                #{ivar_name} = value
              end
            CODE
          end
        end

        if state[:class]
          instance_variable_set(ivar_name, default)
        end
      end