Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

graph_mediator

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

graph_mediator

  • 0.2.3
  • Rubygems
  • Socket score

Version published
Maintainers
1
Created
Source

= Graph Mediator

GraphMediator is used to help coordinate state between a graph of ActiveRecord objects related to a single root node. Its role is assisting in cases where you are representing a complex concept as a graph of related objects with potentially circular interdependencies. Changing attributes in one object might require adding or removing of dependent objects. Adding these objects might necessitate a recalculation of memberships in a join table. Any such changes might require that cached calculations be redone. Touching any object in the graph might require a version bump for the concept of the graph as a whole.

We want changes to be made once, in a single transaction, with a single overall version change. The version change should be guarded by an optimistic lock check to avoid conflicts between two processes updates to the same graph.

To make interdependent state changes manageable, GraphMediator wraps an additional layer of callbacks around the ActiveRecord save cycle to ensure that a save occurs within a GraphMediator.mediated_transaction.

  • :before_mediation
    • save *
  • :after_mediation

The after_mediation callback is itself broken down into three phases:

  • :reconciliation - in this phase, any methods which bring the overall state of the graph into balance should be run to adjust for changes made during the save.
  • :cacheing - any calculations which rely on the state of a reconciled graph but which do not themselves alter the graph (in that they are reproducible from existing state) should be made in the cacheing phase.
  • :bumping - if the class has a +lock_column+ set (ActiveRecord::Locking::Optimistic) and has on updated_at/on timestamp then the instance will be touched, bumping the +lock_column+ and checking for stale data.

During a mediated_transaction, the +lock_column+ will only update during the +bumping+ phase of the after_mediation callback.

But if there is no update_at/on timestamp, then +lock_column+ cannot be incremented when dependent objects are updated. This is because there is nothing to touch on the root record to trigger the +lock_column+ update.

GraphMediator ensures that after_mediation is run only once within the context of a mediated transaction. If the block being mediated returns false, the after_mediation is skipped; this allows for validations.

== Usage

* :pen_number

* :dingo_count

* :biscuit_count

* :feed_rate

* :total_biscuit_weight

* :lock_version, :default => 0 # required for versioning

* :updated_at # required for versioning

class DingoPen < ActiveRecord::Base

has_many :dingos
has_many :biscuits

include GraphMediator
mediate :purchase_biscuits,
  :dependencies => [Dingo, Biscuit],
  :when_reconciling => [:adjust_biscuit_supply, :feed_dingos],
  :when_cacheing => :calculate_total_biscuit_weight

or

mediate :purchase_biscuits,
  :dependencies => [Dingo, Biscuit], # ensures a mediated_transaction on Dingo#save or Biscuit#save
mediate_reconciles :adjust_biscuit_supply, :feed_dingos
mediate_caches do |instance|
  instance.calculate_total_biscuit_weight
end

...

def purchase_biscuits; ... end
def adjust_biscuit_supply; ... end
def feed_dingos; ... end
def calculate_total_biscuit_weight; ... end

end

See spec/examples for real, dingo-free examples.

== Caveats

A lock_column and timestamp are not required, but without both columns in your schema there will be no versioning.

A lock_column by itself without a timestamp will not increment and will not provide any optimistic locking in a class including GraphMediator!

Using a lock_column along with a counter_cache in a dependent child will raise a StaleObject error during a mediated_transaction if you touch the dependent.

The cache_counters do not play well with optimistic locking because they are updated with a direct SQL call to the database, so ActiveRecord instance remain unaware of the lock_version change and assume it came from another transaction.

You should not need to declare lock_version for any children that are declared as a dependency of the root node, since updates will also update the root nodes lock_version. So if another transaction updates a child, root.lock_version should increment, and the first transaction should raise a StaleObject error when it too tries to update the child.

If you override save in the model hierarchy you are mediating, you must pass your override as a block to super or it will occur outside of mediation:

def save super do my_local_changes end end

You are probably better off hooking to before_save or after_save if they suffice.

== Threads

GraphMediator uses thread local variables to keep track of open mediators. It should be thread safe but this needs testing.

== Advice

Build a simple system first, rather than building a system to use GraphMediator.

But if you have a web of observers/callbacks struggling to maintain state, repeated, redundant update calls from observed changes in collection members, or are running into +lock_column+ issues within your own updates, then GraphMediator may help.

== Copyright

Copyright (c) 2010 Josh Partlow. See LICENSE for details.

FAQs

Package last updated on 28 Jan 2013

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc