class Pakyow::Data::Proxy

@api private

Constants

IVARS_TO_DUP

Attributes

app[R]
nested_proxies[R]
proxied_calls[R]
source[R]

Public Class Methods

_load(state) click to toggle source
# File lib/pakyow/data/proxy.rb, line 217
def self._load(state)
  state = Marshal.load(state)

  case state[:source]
  when Sources::Ephemeral
    ephemeral = state[:app].data.ephemeral(state[:source].source_name)
    ephemeral.instance_variable_set(:@source, state[:source])
    ephemeral
  else
    state[:app].data.public_send(state[:source]).apply(state[:proxied_calls])
  end
end
new(source, subscribers, app) click to toggle source
# File lib/pakyow/data/proxy.rb, line 22
def initialize(source, subscribers, app)
  @source, @subscribers, @app = source, subscribers, app

  @proxied_calls = []
  @subscribable = true
  @nested_proxies = []
end

Public Instance Methods

_dump(_) click to toggle source
# File lib/pakyow/data/proxy.rb, line 207
def _dump(_)
  Marshal.dump(
    {
      app: @app,
      source: @source.is_a?(Sources::Ephemeral) ? @source : @source.source_name,
      proxied_calls: @proxied_calls
    }
  )
end
apply(proxied_calls) click to toggle source

@api private

# File lib/pakyow/data/proxy.rb, line 259
def apply(proxied_calls)
  proxied_calls.inject(self) { |proxy, proxied_call|
    if proxied_call[2].any?
      proxy.public_send(proxied_call[0], *proxied_call[1]) do
        apply(proxied_call[2])
      end
    else
      proxy.public_send(proxied_call[0], *proxied_call[1])
    end
  }
end
deep_dup() click to toggle source
Calls superclass method
# File lib/pakyow/data/proxy.rb, line 32
def deep_dup
  super.tap do |duped|
    IVARS_TO_DUP.each do |ivar|
      duped.instance_variable_set(ivar, duped.instance_variable_get(ivar).deep_dup)
    end
  end
end
method_missing(method_name, *args) { |duped_proxy| ... } click to toggle source
Calls superclass method
# File lib/pakyow/data/proxy.rb, line 40
def method_missing(method_name, *args, &block)
  if @source.command?(method_name)
    dup.tap { |duped_proxy|
      result = @source.command(method_name).call(*args) { |yielded_result|
        duped_proxy.instance_variable_set(:@source, yielded_result)
        yield duped_proxy if block_given?
      }

      if @source.respond_to?(:transaction) && @source.transaction?
        @source.on_commit do
          @subscribers.did_mutate(
            @source.source_name, args[0], result
          )
        end
      else
        @subscribers.did_mutate(
          @source.source_name, args[0], result
        )
      end
    }
  elsif @source.query?(method_name) || @source.modifier?(method_name)
    dup.tap { |duped_proxy|
      nested_calls = []

      new_source = if block_given? && @source.block_for_nested_source?(method_name)
        # In this case a block has been passed that would, without intervention,
        # be called in context of a source instance. We don't want that, since
        # it would provide full access to the underlying dataset. Instead the
        # exposed object should simply be another proxy.

        local_subscribers, local_app = @subscribers, @app
        @source.source_from_self.public_send(method_name, *args) {
          nested_proxy = Proxy.new(self, local_subscribers, local_app)
          nested_proxy.instance_variable_set(:@proxied_calls, nested_calls)
          nested_proxy.instance_exec(&block).source.tap do |nested_proxy_source|
            duped_proxy.nested_proxies << nested_proxy.dup.tap do |finalized_nested_proxy|
              finalized_nested_proxy.instance_variable_set(:@source, nested_proxy_source)
            end
          end
        }
      else
        @source.source_from_self.public_send(method_name, *args).tap do |working_source|
          working_source.included.each do |_, included_source|
            nested_proxy = Proxy.new(included_source, @subscribers, @app)
            duped_proxy.nested_proxies << nested_proxy
          end
        end
      end

      duped_proxy.instance_variable_set(:@source, new_source)
      duped_proxy.instance_variable_get(:@proxied_calls) << [
        method_name, args, nested_calls
      ]
    }
  else
    if Array.instance_methods.include?(method_name) && !@source.class.instance_methods.include?(method_name)
      build_result(
        @source.to_a.public_send(method_name, *args, &block),
        method_name, args
      )
    elsif @source.class.instance_methods.include?(method_name)
      build_result(
        @source.public_send(method_name, *args, &block),
        method_name, args
      )
    else
      super
    end
  end
end
qualifications() click to toggle source
# File lib/pakyow/data/proxy.rb, line 230
def qualifications
  @proxied_calls.inject(@source.qualifications) { |qualifications, proxied_call|
    qualifications_for_proxied_call = @source.class.qualifications(proxied_call[0]).dup

    # Populate argument qualifications with argument values.
    #
    qualifications_for_proxied_call.each do |qualification_key, qualification_value|
      if qualification_value.to_s.start_with?("__arg")
        arg_number = qualification_value.to_s.gsub(/[^0-9]/, "").to_i

        arg_value = proxied_call[1][arg_number]
        arg_value = case arg_value
        when Array
          arg_value.map { |each_value|
            @source.class.attributes[qualification_key][each_value]
          }
        else
          @source.class.attributes[qualification_key][arg_value]
        end

        qualifications_for_proxied_call[qualification_key] = arg_value
      end
    end

    qualifications.merge(qualifications_for_proxied_call)
  }
end
respond_to_missing?(method_name, include_private = false) click to toggle source
# File lib/pakyow/data/proxy.rb, line 111
def respond_to_missing?(method_name, include_private = false)
  return false if method_name == :marshal_dump || method_name == :marshal_load
  @source.command?(method_name) || @source.query?(method_name) || @source.modifier?(method_name) || @source.respond_to?(method_name, include_private)
end
subscribable(boolean) click to toggle source
# File lib/pakyow/data/proxy.rb, line 197
def subscribable(boolean)
  tap do
    @subscribable = boolean
  end
end
subscribable?() click to toggle source
# File lib/pakyow/data/proxy.rb, line 203
def subscribable?
  @subscribable == true
end
subscribe(subscriber, handler:, payload: nil, &block) click to toggle source
# File lib/pakyow/data/proxy.rb, line 124
def subscribe(subscriber, handler:, payload: nil, &block)
  subscriptions = []

  if subscribable?
    subscription = {
      source: @source.source_name,
      ephemeral: @source.is_a?(Sources::Ephemeral),
      handler: handler,
      payload: payload,
      qualifications: qualifications,
      proxy: self
    }

    unless subscriptions.include?(subscription)
      subscriptions << subscription
    end

    @nested_proxies.each do |related_proxy|
      subscriptions.concat(
        related_proxy.subscribe_related(
          parent_source: @source,
          serialized_proxy: self,
          handler: handler,
          payload: payload
        )
      )
    end
  end

  @subscribers.register_subscriptions(subscriptions, subscriber: subscriber, &block)
end
to_ary() click to toggle source
# File lib/pakyow/data/proxy.rb, line 116
def to_ary
  to_a
end
to_json(*) click to toggle source
# File lib/pakyow/data/proxy.rb, line 120
def to_json(*)
  @source.to_json
end
unsubscribe() click to toggle source
# File lib/pakyow/data/proxy.rb, line 193
def unsubscribe
  subscribable(false)
end

Private Instance Methods

build_result(value, method_name, args) click to toggle source
# File lib/pakyow/data/proxy.rb, line 273
def build_result(value, method_name, args)
  if method_name.to_s.end_with?("?")
    value
  else
    Result.new(value, self, originating_method: method_name, originating_args: args)
  end
end