Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Fetching latest commit…

Octocat-spinner-32-eaf2f5

Cannot retrieve the latest commit at this time

Octocat-spinner-32 config
Octocat-spinner-32 lib
Octocat-spinner-32 test
Octocat-spinner-32 .gitignore
Octocat-spinner-32 .travis.yml
Octocat-spinner-32 Gemfile
Octocat-spinner-32 README.textile
Octocat-spinner-32 Rakefile
Octocat-spinner-32 TODO
Octocat-spinner-32 simple_nested_set.gemspec
README.textile

SimpleNestedSet

SimpleNestedSet implements the nested set pattern for ActiveRecord 3.×.

It aims to be a no-fluff, simple and clean implementation based on and making heavy use of ActiveRecord scopes and Arel, making it as easy as possible to work with nested sets.

Installation

Just add it to your Gemfile:

  gem 'simple_nested_set'

Basics

Compared to other database tree implementation patterns (such as adjacent tree or materialized path) the nested set model is, all in all, most inexpensive and powerful on read operations but quite expensive on write operations. This is because it stores a rich amount of structure information in the lft/rgt columns that needs to be updated when the set/tree structure is changed.

simple_nested_set will, additionally to the basic nested set implementation, denormalize (store) a node’s parent_id and materialized path for you. It simply does that when the :parent_id and/or :path columns are present.

So simple_nested_set is mainly targeted at working with relatively small sets of nodes that do not need to be updated really frequently. If you do not need denormalization of parent_ids and/or paths you might try to just omit these columns from your schema (we’ll also accept patches that add additional options for turning this feature off).

Usage

Setup

simple_nested_set adds an act_macro to ActiveRecord::Base that you can call to make any ActiveRecord model a nested set:

 class Node < ActiveRecord::Base
    acts_as_nested_set
  end

The acts_as_nested_set method accepts a :scope option that will scope a nested set to the given column:

  acts_as_nested_set :scope => :site_id

This will make the nested set consider nodes that have the same site_id to belong to the same nested set.

Class methods

Nested set model classes implement a number of class methods that return scopes for looking up nodes of a particluar nested set. E.g.:

  Node.root                   # => the first root node
  Node.roots                  # => all root nodes
  Node.roots(:site_id => 1)   # => all root nodes scoped to the given site_id
  Node.leaves                 # => all leaf nodes

See the API docs for a full list of instance methods.

Instance methods

Nested set model classes also implement a number of instance methods that make it easy to look up related nodes or check the structure. E.g.

  node = Node.root
  node.root?        # => true
  node.children?    # => true

  child = node.children.first
  child.parent        # => root
  child.siblings      # => the child's siblings
  child.nest_sibling  # => the child's next sibling

See the API docs for a full list of instance methods.

Updating a nested set’s structure

simple_nested_set aims to make it easy to update the structure of a nested set without needing to care about its implementation.

When working with HTML views/forms or Javascript an API it is often quite inconvenient to be forced to a certain nested set API. Instead one wants APIs like accepts_nested_attributes in ActiveRecord to just work.

Therefor simple_nested_set implements a number of alternative ways to update the structure:

Calling instance methods directly

One can, obviously, call move_* methods directly on the node instance:

  node.move_to_root             # makes the node a root node
  node.move_to_child_of(parent) # makes the node a child of parent
  node.move_to_left             # moves the node to the left of its left_sibling (if any)
  node.move_to_right            # moves the node to the right of its right_sibling (if any)
  node.move_to_left_of(other)   # moves the node to the left of the given node
  node.move_to_right_of(other)  # moves the node to the right of the given node
  node.move_to_path('foo/bar')  # moves the node to the given path

Assigning structure-related attributes

One can also change the position of a node by assigning structure attributes. These are: :left_id, :right_id, :parent_id, :path.

  node.upate_attributes(:parent_id => other.id) # makes the node a child of the given parent
  node.upate_attributes(:left_id => other.id)   # moves the node to the *right* of the given node
  node.upate_attributes(:right_id => other.id)  # moves the node to the *left* of the given node
  node.upate_attributes(:path => 'foo/bar')     # moves the node to the given path

nested_set

A nested set ActiveRecord model has a scope object that is accessible through the nested_set method:

  Nodes.root.nested_set

This scope object both serves as the main encapsulation of nested set logic and can be used to perform ActiveRecord lookups on the nested set.

Inspecting a nested set

You can call #inspect_tree on both nested_set scopes and node instances to get a visual representation of the tree:

  Node.roots.inspect_tree

  # =>
  .
  └── Node id: 1
      ├── Node id: 2
      |   ├── Node id: 3
      |   └── Node id: 4
      └── Node id: 5
          └── Node id: 6

Developing

Running tests

Tests are run with rake. The tests are run against sqlite3 by default. To change this, you can set an environment variable:

  DATABASE=sqlite3 rake
  DATABASE=postgresql rake
  DATABASE=mysql rake

For PostgreSQL and MySQL the following configuration is assumed:

  username: sns
  password: sns
  database: sns
  encoding: utf8

The testsuite is being run against sqlite3 on travis: http://travis-ci.org/svenfuchs/simple_nested_set

Contributions

This gem is a collaborative work of:

  • Sven Fuchs
  • Matthias Viehweger
  • Artem Ignatiev
  • Levin Alexander
  • Niklas Hofer
  • Cristiam Castillo
  • Steve Hoeksema

If you want to participate, open an issue or send a pull-request.

Something went wrong with that request. Please try again.