Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Playing in the Sandbox

Playing in the Sandbox

These slides where used as the 'creative concept' to the Kindergarten gem (https://rubygems.org/gems/kindergarten) as the concept and the gem started to grow up together; so did the slides.

The slides could be held as a full talk - just never happened (yet?)

Use the slides to get some insights in the Kindergarten gem.

Have fun!

Hartog C. de Mik

October 25, 2012
Tweet

More Decks by Hartog C. de Mik

Other Decks in Programming

Transcript

  1. Agenda • Concepts • Introducing the actors • Decomposing a

    kindergarten • A closer look at the actors • Bringing it all together
  2. The Concept Sandbox Theory: In a sandbox any module can

    play without interfering with other modules, or the applications stability Modules could be drop-in-drop-out replaceable (yeah right...)
  3. The Concept Modularity (not ruby modules) Goes beyond the sandbox

    theory. We want to prevent developers making 'stupid' mistakes. Should increase code coherence
  4. The Concept KISS (Keep It Simple, Stupid) Provide simplification of

    ORM complexities DRY (Do not Repeat Yourself) Enable DRYness in the most outer layers Reduces developer skill requirements. DORY (Do Repeat Yourself) Within the Kindergarten, wax-on - wax-off
  5. Kindergarten In order for our children to function on a

    kindergarten we need: • Safety • Guidance • Play
  6. Kindergarten In order for our children modules to function on

    a kindergarten in an application we need: • Safety - A perimeter • Guidance - A governess • Play - A sandbox
  7. Safety • Always a matter of (self-)discipline. • The perimeter

    controls access to the ORM • The perimeter is a mediator; ORM observers with side-effects should be moved into the perimeter • The perimeter is governed
  8. Guidance • The governess oversees the perimeter(s) • CanCan can

    help and is wrapped inside the Governess • The Governess provides attribute scrubbing (no more ORM blacklist/whitelist non-sense)
  9. Play • The Kindergarten provides the Sandbox • The Kindergarten

    governs the Sandbox • Adding modules to the Kindergarten expands the Perimeter • Modules should adhere to an interface (think contract, not java)
  10. Perimeter definition class SnowModule < Kindergarten::Perimeter purpose :snow govern do

    can :view, Snow end def shovel(snow, direction) snow.shovel(direction) end expose :shovel # like private end
  11. Perimeter purpose class SnowModule < Kindergarten::Perimeter purpose :snow end •

    Each module *must* have purpose. • The purpose provides namespace. • Modules can share a purpose.
  12. Perimeter loading sandbox = Kindergarten.sandbox(child_object) sandbox.snow.shovel(snow, :down) # raises MethodNotFound

    sandbox.load_module(SnowModule) sandbox.snow.shovel(snow, :up) # executes the sandboxed method
  13. Perimeter exposing class ProjectPerimeter < Kindergarten::Perimeter purpose :projects def internal_method

    # do something; module-wise end end sandbox = Kindergarten.sandbox(child_object) sandbox.load_perimeter(ProjectPerimeter) sandbox.projects.internal_method # raises MethodNotFound - was not sandboxed
  14. Perimeter governing class ProjectPerimeter < Kindergarten::Perimeter # assign this perimeter

    with an own governess governess Kindergarten::StrictGoverness def unchecked(project) project.do_something end sandbox :unchecked end sandbox.unchecked(project) # raises Kindergarten::Perimeter::Unguarded
  15. Governess available • HeadGoverness A base class for governesses, used

    by the Sandbox • StrictGoverness Demands each method invocation to be guarded • EasyGoverness Disables all guarding (testing purps only!)
  16. Governess rules class ProjectPerimeter < Kindergarten::Perimeter govern do # just

    as inside your CanCan Ability file! can :create, Project # only an owner can add members can :create_member, Project do |project| child == project.owner end # etc, etc. end end
  17. Governess hierarchy • The sandbox has a Head Governess •

    Each Perimeter can have it's own • Makes rules perimeter specific • The Head Governess is still aware • Governess can be user-defined
  18. Governess definition class LogGoverness < Kindergarten::HeadGoverness # the sandbox wraps

    all methods in the governed method # of the perimeters governess # def governed(method, &block) logger.info "#{method} performed" super # a chain of governesses (poor kid!) end end class ProjectPerimeter < Kindergarten::Perimeter governess LogGoverness # ... end
  19. Governess head governess # in self governed perimeter-1 govern do

    can :view, Project end # in self governed perimeter-2 govern do can :update, Project end guard(:view, Project) # => false - not in this perimeter # sandbox (both true) sandbox.allows?(:update, Project) sandbox.allows?(:view, Project)
  20. Governess scrubbing class ProjectPerimeter < Kindergarten::Perimeter def create_project(attributes) guard(:create, project)

    # remove any attributes not specified safe = scrub(attributes, :name, :description) Project.create(safe) end end
  21. Governess rinsing class ProjectPerimeter < Kindergarten::Perimeter def create_project(attributes) guard(:create, project)

    # remove any attributes like scrub, or those that do # not untaint with the given Regexp safer = rinse(attributes, :name => /^([a-zA-Z0-9\s]+)$/i, :description => /^(.*)$/ ) Project.create(safer) end end
  22. Governess orm class Project < ActiveRecord::Base # updates :create, :new

    & :update_attributes so that # they will only accept scrubbed attributes # include Kindergarten::ORM::Governess # or even stricter - only rinsed attributes force_rinsing end
  23. Sandbox mediation • Not truly a mediator, but a publish/subscribe

    pattern • Mediation could be done by keeping track of module purpose
  24. Sandbox mediation class UserPerimeter < Kindergarten::Perimeter subscribe :projects, create, ->(event)

    { event.target.request_invitation } subscribe :follows_user, :follow_back def follow_back(event) child.follow(event.target) end end
  25. Sandbox mediation cont'd class UserPerimeter < Kindergarten::Perimeter purpose :users #

    subscribe :purpose, :event subscribe :projects, create, ->(event) { event.target.request_invitation } subscribe :users, :follows, :follow_back def follow_back(event) child.follow(event.target) end end
  26. Sandbox mediation cont'd class UserPerimeter < Kindergarten::Perimeter purpose :users def

    follow(user) guard :view, user child.follow(user) # notify the other players in the sandbox of the # :follows event in the :users purpose publish :follows, :target => user end end
  27. Sandbox mediation cont'd sandbox.perimeters(purpose: :users) >> [UserPerimeter, ..., ...] #

    subscribe all project perimeters to do something on # task creation sandbox.perimeter(purpose: :projects).each do |perim| perim.subscribe(:tasks, :create) do |event| # do something end end # end all subscriptions on the :users purpose sandbox.unsubscribe(purpose: :users)
  28. Sandbox mediation broadcast # provide a self-written broadcast - could

    be Faye, # AMQP, Stomp, whatever # sandbox.broadcast_publish do |event| @faye_client.publish( "/#{event.purpose}/#{event.name}", event.to_json ) end
  29. Sandbox mediation broadcast cont'd # same goes for subscribe #

    stomp.subscribe("/users/create") do |msg| # using 'broadcasted' prevents broadcasting sandbox.broadcasted_publish(msg) # equal sandbox.publish( :users, :create, msg, broadcast: false ) end
  30. All together now Rails example class ApplicationController < ActionController::Base before_filter

    { @sandbox = Kindergarten.sandbox(current_user) } def sandbox @sandbox end helper :sandbox end
  31. All together now Rails example class ProjectsController < ApplicationController before_filter

    { @sandbox.extend_perimeter(Projects) } respond_to :html def index respond_with sandbox.projects.all end def show respond_with sandbox.projects.find(params[:id]) end end
  32. class ProjectsModule < Kindergarten::Perimeter purpose :projects govern do can :manage,

    Project do |project| project.owner == child end can [:view, :create], Project end def all child.projects end def find(id) guard(:view, Project.find(id)) end expose :all, :find end All together now Rails example