Contextualize

Adds the ability to add and remove specific modules to an object depending on the current context This can fx be used for Models in a MVC pattern, where the model could have different behavior depending on the context it operates in, fx if is currently accessed in a View or a Controller, or even depending on the scope (fx Admin) etc.

This way, a lot of the typical scenarios for helper methods can be avoided. Instead you simply create an ‘app/contexts’ folder for modules that define contextual behavior for the models. You can further subdivision these contexts as needed. This can even be used to redefine the logic of a method depending on the context it operates in.

The Decorator Pattern with Mixology has been used to great effect.

Basic Usage

Define each context specific module


module ProjectView
  # display the best project
  def show_best
    # calls method :best
  end
end

module ProjectControl
  # select the best project
  def best
  end
end

module Admin
  module ProjectControl
    # admin specific logic to select the best project!
    def best
    end
  end
end

# Only in order to faciliatet demonstrating results below!
class Object
  def own_methods
    methods - Object.methods
  end
end

Then bind these modules to one or more contexts


class Project
  contextualize
  contexts :view, :control # using naming conventions
  context :admin, Admin::ProjectControl
end

Now you can operate on the object according to the context you are in


project = Project.new

# add :view context
project.add_context :view
project.add_contexts :view, :control

# use the view context methods now available
project.own_methods.should include('view')
project.view.should == "view"

# remove the :view context
project.remove_context :view
project.remove_contexts :view, :control


# the methods of the view context are no longer available!
lambda {project.view}.should raise_error

# operate on object within one or more contexts
project.context_scope :view do |project|
  project.view.should == "view"
end

# contexts are automatically removed from object when block terminates
lambda {project.view}.should raise_error

project.context_scope :view, :control do |project|
  # ...
end

Usage in Rails

in Gemfile


gem 'contextualize'

In a Rails project, you should use the following file structure


+ app
  + contexts
      project_view.rb
      project_control.rb
    + admin
        project_view.rb

  + models
    project.rb

class ProjectsController < ApplicationController
  # "simulated" exposure
  def project
    @project ||= begin
      p = params[:id] ? Project.find(params[:id]) : Project.new(params[:project])
      p.add_icontext :view
    end
  end
  helper_method project
  hide_action project

  def projects
    @projects ||= Project.all.add_icontext :view
  end
  helper_method projects
  hide_action projects

  def index
  end

  def show
  end
  ...
end

Using decent_exposure integration

Contextualize monkey-patches the #expose method of the decent_exposure gem to add the icontext :view to each model (if present)

in Gemfile


gem 'decent_exposure'
gem 'contextualize'

class ProjectsController < ApplicationController
  expose(:projects) { Project.all }
  expose(:project)

  def index
  end

  def show
  end

  def new
  end

  def edit
  end

  def create
    if project.save
      redirect_to project, notice: 'Project was successfully created.'
    else
      render action: "new"
    end
  end

  def update
    if project.update_attributes(params[:project])
      redirect_to project, notice: 'Project was successfully updated.'
    else
      render action: "edit"
    end
  end

  def destroy
    project.destroy
    redirect_to projects_url
  end
end

Enjoy!

Contributing to contextualize

  • Check out the latest master to make sure the feature hasn’t been implemented or the bug hasn’t been fixed yet
  • Check out the issue tracker to make sure someone already hasn’t requested it and/or contributed it
  • Fork the project
  • Start a feature/bugfix branch
  • Commit and push until you are happy with your contribution
  • Make sure to add tests for it. This is important so I don’t break it in a future version unintentionally.
  • Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.

Copyright

Copyright © 2011 Kristian Mandrup. See LICENSE.txt for further details.