Merge branch 'wiki'

This commit is contained in:
Benjamin Meichsner 2009-08-15 17:24:08 +02:00
commit e75a42d509
146 changed files with 6300 additions and 261 deletions

View file

@ -0,0 +1,100 @@
class PagesController < ApplicationController
def index
@page = Page.find_by_permalink "Home"
if @page
render :action => 'show'
else
redirect_to all_pages_path
end
end
def show
@page = Page.find_by_permalink(params[:permalink])
if @page.nil?
redirect_to new_page_path(:title => params[:permalink])
elsif @page.redirect?
flash[:notice] = "Weitergeleitet von #{@page.title} ..."
redirect_to wiki_page_path(Page.find(@page.redirect).permalink)
end
end
def new
@page = Page.new
@page.title = params[:title].gsub("_", " ") if params[:title]
@page.parent = Page.find_by_permalink(params[:parent]) if params[:parent]
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @page }
end
end
def edit
@page = Page.find(params[:id])
end
def create
@page = current_user.pages.build(params[:page])
if params[:preview]
render :action => 'new'
else
if @page.save
flash[:notice] = 'Seite wurde angelegt.'
redirect_to(wiki_page_path(@page.permalink))
else
render :action => "new"
end
end
end
def update
@page = Page.find(params[:id])
@page.attributes = params[:page].merge({:user => current_user})
if params[:preview]
@page.attributes = params[:page]
render :action => 'edit'
else
if @page.save
flash[:notice] = 'Seite wurde aktualisiert.'
redirect_to wiki_page_path(@page.permalink)
else
render :action => "edit"
end
end
rescue ActiveRecord::StaleObjectError
flash[:error] = "Achtung, die Seite wurde gerade von jemand anderes bearbeitet. Bitte versuche es erneut."
redirect_to wiki_page_path(@page.permalink)
end
def destroy
@page = Page.find(params[:id])
@page.destroy
respond_to do |format|
format.html { redirect_to(pages_url) }
format.xml { head :ok }
end
end
def all
@pages = Page.all :order => 'created_at', :conditions => {:redirect => nil}
end
def version
@page = Page.find(params[:id])
@version = Page::Version.find_by_page_id_and_lock_version params[:id], params[:version]
end
def revert
@page = Page.find(params[:id])
@page.revert_to!(params[:version].to_i)
redirect_to wiki_page_path(@page.permalink)
end
end

View file

@ -103,8 +103,13 @@ module ApplicationHelper
end
# to set a title for both the h1-tag and the title in the header
def title(page_title)
content_for(:title) { page_title }
def title(page_title, show_title = true)
@content_for_title = page_title.to_s
@show_title = show_title
end
def show_title?
@show_title
end
def tab_is_active?(tab)

View file

@ -0,0 +1,37 @@
module PagesHelper
include WikiCloth
# def build_anchors(body)
# body.gsub(/(<h\d{1}>(.+)<\/h\d{1}>)/) do
# header = $1
# token = $2.downcase.gsub(' ', '-')
# "<a name='#{token}'> </a>#{header}"
# end
# end
def wikified_body(body, title = nil)
WikiCloth.new({:data => body+"\n", :link_handler => Wikilink.new, :params => {:referer => title}}).to_html
end
def link_to_wikipage(page)
link_to page.title, "/wiki/#{page.title}"
end
# def generate_toc(body)
# toc = ""
# body.gsub(/^([=]{1,6})\s*(.*?)\s*(\1)/) do
# number = $1.length - 1
# name = $2
#
# toc << "#" * number + " #{name}\n"
# end
# toc = WikiCloth.new({:data => toc, :link_handler => Wikilink.new}).to_html
#
# toc.gsub(/<li>([^<>\n]*)/) do
# name = $1
# token = name.downcase.gsub(' ', '-')
#
# "<li><a href='##{token}'>#{name}</a>"
# end
# end
end

View file

@ -2,26 +2,26 @@
#
# Table name: articles
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# name :string(255) default(""), not null
# supplier_id :integer(4) default(0), not null
# article_category_id :integer(4) default(0), not null
# supplier_id :integer default(0), not null
# article_category_id :integer default(0), not null
# unit :string(255) default(""), not null
# note :string(255)
# availability :boolean(1) default(TRUE), not null
# availability :boolean default(TRUE), not null
# manufacturer :string(255)
# origin :string(255)
# shared_updated_on :datetime
# price :decimal(8, 2)
# price :decimal(, )
# tax :float
# deposit :decimal(8, 2) default(0.0)
# unit_quantity :integer(4) default(1), not null
# deposit :decimal(, ) default(0.0)
# unit_quantity :integer default(1), not null
# order_number :string(255)
# created_at :datetime
# updated_at :datetime
# quantity :integer default(0)
# deleted_at :datetime
# type :string(255)
# quantity :integer(4) default(0)
#
class Article < ActiveRecord::Base

View file

@ -2,7 +2,7 @@
#
# Table name: article_categories
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# name :string(255) default(""), not null
# description :string(255)
#

View file

@ -2,12 +2,12 @@
#
# Table name: article_prices
#
# id :integer(4) not null, primary key
# article_id :integer(4)
# id :integer not null, primary key
# article_id :integer
# price :decimal(8, 2) default(0.0), not null
# tax :decimal(8, 2) default(0.0), not null
# deposit :decimal(8, 2) default(0.0), not null
# unit_quantity :integer(4)
# unit_quantity :integer
# created_at :datetime
#

View file

@ -2,10 +2,10 @@
#
# Table name: assignments
#
# id :integer(4) not null, primary key
# user_id :integer(4) default(0), not null
# task_id :integer(4) default(0), not null
# accepted :boolean(1)
# id :integer not null, primary key
# user_id :integer default(0), not null
# task_id :integer default(0), not null
# accepted :boolean
#
class Assignment < ActiveRecord::Base

View file

@ -2,8 +2,8 @@
#
# Table name: deliveries
#
# id :integer(4) not null, primary key
# supplier_id :integer(4)
# id :integer not null, primary key
# supplier_id :integer
# delivered_on :date
# created_at :datetime
#

View file

@ -2,11 +2,11 @@
#
# Table name: financial_transactions
#
# id :integer(4) not null, primary key
# ordergroup_id :integer(4) default(0), not null
# id :integer not null, primary key
# ordergroup_id :integer default(0), not null
# amount :decimal(8, 2) default(0.0), not null
# note :text default(""), not null
# user_id :integer(4) default(0), not null
# note :text not null
# user_id :integer default(0), not null
# created_on :datetime not null
#

View file

@ -2,28 +2,27 @@
#
# Table name: groups
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# type :string(255) default(""), not null
# name :string(255) default(""), not null
# description :string(255)
# account_balance :decimal(8, 2) default(0.0), not null
# account_balance :decimal(, ) default(0.0), not null
# account_updated :datetime
# created_on :datetime not null
# role_admin :boolean(1) not null
# role_suppliers :boolean(1) not null
# role_article_meta :boolean(1) not null
# role_finance :boolean(1) not null
# role_orders :boolean(1) not null
# weekly_task :boolean(1)
# weekday :integer(4)
# role_admin :boolean not null
# role_suppliers :boolean not null
# role_article_meta :boolean not null
# role_finance :boolean not null
# role_orders :boolean not null
# weekly_task :boolean
# weekday :integer
# task_name :string(255)
# task_description :string(255)
# task_required_users :integer(4) default(1)
# task_required_users :integer default(1)
# deleted_at :datetime
# contact_person :string(255)
# contact_phone :string(255)
# contact_address :string(255)
# stats :text
#
# Groups organize the User.

View file

@ -2,13 +2,13 @@
#
# Table name: group_orders
#
# id :integer(4) not null, primary key
# ordergroup_id :integer(4) default(0), not null
# order_id :integer(4) default(0), not null
# id :integer not null, primary key
# ordergroup_id :integer default(0), not null
# order_id :integer default(0), not null
# price :decimal(8, 2) default(0.0), not null
# lock_version :integer(4) default(0), not null
# lock_version :integer default(0), not null
# updated_on :datetime not null
# updated_by_user_id :integer(4)
# updated_by_user_id :integer
#
# A GroupOrder represents an Order placed by an Ordergroup.

View file

@ -2,13 +2,13 @@
#
# Table name: group_order_articles
#
# id :integer(4) not null, primary key
# group_order_id :integer(4) default(0), not null
# order_article_id :integer(4) default(0), not null
# quantity :integer(4) default(0), not null
# tolerance :integer(4) default(0), not null
# id :integer not null, primary key
# group_order_id :integer default(0), not null
# order_article_id :integer default(0), not null
# quantity :integer default(0), not null
# tolerance :integer default(0), not null
# updated_on :datetime not null
# result :decimal(8, 3)
# result :decimal(, )
#
# A GroupOrderArticle stores the sum of how many items of an OrderArticle are ordered as part of a GroupOrder.

View file

@ -2,10 +2,10 @@
#
# Table name: group_order_article_quantities
#
# id :integer(4) not null, primary key
# group_order_article_id :integer(4) default(0), not null
# quantity :integer(4) default(0)
# tolerance :integer(4) default(0)
# id :integer not null, primary key
# group_order_article_id :integer default(0), not null
# quantity :integer default(0)
# tolerance :integer default(0)
# created_on :datetime not null
#

View file

@ -2,11 +2,11 @@
#
# Table name: invites
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# token :string(255) default(""), not null
# expires_at :datetime not null
# group_id :integer(4) default(0), not null
# user_id :integer(4) default(0), not null
# group_id :integer default(0), not null
# user_id :integer default(0), not null
# email :string(255) default(""), not null
#

View file

@ -2,19 +2,19 @@
#
# Table name: invoices
#
# id :integer(4) not null, primary key
# supplier_id :integer(4)
# delivery_id :integer(4)
# order_id :integer(4)
# id :integer not null, primary key
# supplier_id :integer
# delivery_id :integer
# number :string(255)
# date :date
# paid_on :date
# note :text
# amount :decimal(8, 2) default(0.0), not null
# deposit :decimal(8, 2) default(0.0), not null
# deposit_credit :decimal(8, 2) default(0.0), not null
# created_at :datetime
# updated_at :datetime
# order_id :integer
# deposit :decimal(8, 2) default(0.0), not null
# deposit_credit :decimal(8, 2) default(0.0), not null
#
class Invoice < ActiveRecord::Base

View file

@ -2,9 +2,9 @@
#
# Table name: memberships
#
# id :integer(4) not null, primary key
# group_id :integer(4) default(0), not null
# user_id :integer(4) default(0), not null
# id :integer not null, primary key
# group_id :integer default(0), not null
# user_id :integer default(0), not null
#
class Membership < ActiveRecord::Base

View file

@ -2,13 +2,13 @@
#
# Table name: messages
#
# id :integer(4) not null, primary key
# sender_id :integer(4)
# id :integer not null, primary key
# sender_id :integer
# recipients_ids :text
# subject :string(255) not null
# body :text
# email_state :integer(4) default(0), not null
# private :boolean(1)
# email_state :integer default(0), not null
# private :boolean
# created_at :datetime
#

View file

@ -2,14 +2,14 @@
#
# Table name: orders
#
# id :integer(4) not null, primary key
# supplier_id :integer(4)
# id :integer not null, primary key
# supplier_id :integer
# note :text
# starts :datetime
# ends :datetime
# state :string(255) default("open")
# lock_version :integer(4) default(0), not null
# updated_by_user_id :integer(4)
# lock_version :integer default(0), not null
# updated_by_user_id :integer
# foodcoop_result :decimal(8, 2)
#

View file

@ -2,14 +2,14 @@
#
# Table name: order_articles
#
# id :integer(4) not null, primary key
# order_id :integer(4) default(0), not null
# article_id :integer(4) default(0), not null
# quantity :integer(4) default(0), not null
# tolerance :integer(4) default(0), not null
# units_to_order :integer(4) default(0), not null
# lock_version :integer(4) default(0), not null
# article_price_id :integer(4)
# id :integer not null, primary key
# order_id :integer default(0), not null
# article_id :integer default(0), not null
# quantity :integer default(0), not null
# tolerance :integer default(0), not null
# units_to_order :integer default(0), not null
# lock_version :integer default(0), not null
# article_price_id :integer
#
# An OrderArticle represents a single Article that is part of an Order.

View file

@ -2,9 +2,9 @@
#
# Table name: order_comments
#
# id :integer(4) not null, primary key
# order_id :integer(4)
# user_id :integer(4)
# id :integer not null, primary key
# order_id :integer
# user_id :integer
# text :text
# created_at :datetime
#

View file

@ -2,28 +2,27 @@
#
# Table name: groups
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# type :string(255) default(""), not null
# name :string(255) default(""), not null
# description :string(255)
# account_balance :decimal(8, 2) default(0.0), not null
# account_balance :decimal(, ) default(0.0), not null
# account_updated :datetime
# created_on :datetime not null
# role_admin :boolean(1) not null
# role_suppliers :boolean(1) not null
# role_article_meta :boolean(1) not null
# role_finance :boolean(1) not null
# role_orders :boolean(1) not null
# weekly_task :boolean(1)
# weekday :integer(4)
# role_admin :boolean not null
# role_suppliers :boolean not null
# role_article_meta :boolean not null
# role_finance :boolean not null
# role_orders :boolean not null
# weekly_task :boolean
# weekday :integer
# task_name :string(255)
# task_description :string(255)
# task_required_users :integer(4) default(1)
# task_required_users :integer default(1)
# deleted_at :datetime
# contact_person :string(255)
# contact_phone :string(255)
# contact_address :string(255)
# stats :text
#
# Ordergroups can order, they are "children" of the class Group

61
app/models/page.rb Normal file
View file

@ -0,0 +1,61 @@
# == Schema Information
#
# Table name: pages
#
# id :integer not null, primary key
# title :string(255)
# body :text
# permalink :string(255)
# lock_version :integer default(0)
# updated_by :integer
# redirect :integer
# created_at :datetime
# updated_at :datetime
#
class Page < ActiveRecord::Base
belongs_to :user, :foreign_key => 'updated_by'
acts_as_versioned :version_column => :lock_version
self.non_versioned_columns += ['permalink', 'created_at', 'title']
acts_as_tree :order => "title"
attr_accessor :old_title # Save title to create redirect page when editing title
validates_presence_of :title, :body
validates_uniqueness_of :permalink, :title
before_validation_on_create :set_permalink
before_validation_on_update :update_permalink
after_update :create_redirect
def self.permalink(title)
title.gsub(/[\/\.,;@\s]/, "_").gsub(/[\"\']/, "")
end
def set_permalink
unless title.blank?
self.permalink = Page.count == 0 ? "Home" : Page.permalink(title)
end
end
protected
def update_permalink
if changed.include?("title")
set_permalink
self.old_title = changes["title"].first # Save title for creating redirect
end
end
def create_redirect
unless old_title.blank?
Page.create :redirect => id,
:title => old_title,
:body => "Weiterleitung auf [[#{title}]]..",
:permalink => Page.permalink(old_title),
:updated_by => updated_by
end
end
end

View file

@ -2,26 +2,26 @@
#
# Table name: articles
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# name :string(255) default(""), not null
# supplier_id :integer(4) default(0), not null
# article_category_id :integer(4) default(0), not null
# supplier_id :integer default(0), not null
# article_category_id :integer default(0), not null
# unit :string(255) default(""), not null
# note :string(255)
# availability :boolean(1) default(TRUE), not null
# availability :boolean default(TRUE), not null
# manufacturer :string(255)
# origin :string(255)
# shared_updated_on :datetime
# price :decimal(8, 2)
# price :decimal(, )
# tax :float
# deposit :decimal(8, 2) default(0.0)
# unit_quantity :integer(4) default(1), not null
# deposit :decimal(, ) default(0.0)
# unit_quantity :integer default(1), not null
# order_number :string(255)
# created_at :datetime
# updated_at :datetime
# quantity :integer default(0)
# deleted_at :datetime
# type :string(255)
# quantity :integer(4) default(0)
#
class StockArticle < Article

View file

@ -2,13 +2,13 @@
#
# Table name: stock_changes
#
# id :integer(4) not null, primary key
# delivery_id :integer(4)
# order_id :integer(4)
# stock_article_id :integer(4)
# quantity :integer(4) default(0)
# id :integer not null, primary key
# delivery_id :integer
# order_id :integer
# stock_article_id :integer
# quantity :integer default(0)
# created_at :datetime
# stock_taking_id :integer(4)
# stock_taking_id :integer
#
class StockChange < ActiveRecord::Base

View file

@ -2,7 +2,7 @@
#
# Table name: stock_takings
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# date :date
# note :text
# created_at :datetime

View file

@ -2,7 +2,7 @@
#
# Table name: suppliers
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# name :string(255) default(""), not null
# address :string(255) default(""), not null
# phone :string(255) default(""), not null
@ -15,7 +15,7 @@
# delivery_days :string(255)
# order_howto :string(255)
# note :string(255)
# shared_supplier_id :integer(4)
# shared_supplier_id :integer
# min_order_quantity :string(255)
# deleted_at :datetime
#

View file

@ -2,16 +2,16 @@
#
# Table name: tasks
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# name :string(255) default(""), not null
# description :string(255)
# due_date :date
# done :boolean(1)
# workgroup_id :integer(4)
# assigned :boolean(1)
# done :boolean
# workgroup_id :integer
# assigned :boolean
# created_on :datetime not null
# updated_on :datetime not null
# required_users :integer(4) default(1)
# required_users :integer default(1)
#
class Task < ActiveRecord::Base

View file

@ -2,7 +2,7 @@
#
# Table name: users
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# nick :string(255) default(""), not null
# password_hash :string(255) default(""), not null
# password_salt :string(255) default(""), not null
@ -28,6 +28,7 @@ class User < ActiveRecord::Base
has_many :assignments, :dependent => :destroy
has_many :tasks, :through => :assignments
has_many :send_messages, :class_name => "Message", :foreign_key => "sender_id"
has_many :pages, :foreign_key => 'updated_by'
attr_accessor :password, :setting_attributes

View file

@ -2,28 +2,27 @@
#
# Table name: groups
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# type :string(255) default(""), not null
# name :string(255) default(""), not null
# description :string(255)
# account_balance :decimal(8, 2) default(0.0), not null
# account_balance :decimal(, ) default(0.0), not null
# account_updated :datetime
# created_on :datetime not null
# role_admin :boolean(1) not null
# role_suppliers :boolean(1) not null
# role_article_meta :boolean(1) not null
# role_finance :boolean(1) not null
# role_orders :boolean(1) not null
# weekly_task :boolean(1)
# weekday :integer(4)
# role_admin :boolean not null
# role_suppliers :boolean not null
# role_article_meta :boolean not null
# role_finance :boolean not null
# role_orders :boolean not null
# weekly_task :boolean
# weekday :integer
# task_name :string(255)
# task_description :string(255)
# task_required_users :integer(4) default(1)
# task_required_users :integer default(1)
# deleted_at :datetime
# contact_person :string(255)
# contact_phone :string(255)
# contact_address :string(255)
# stats :text
#
class Workgroup < Group

View file

@ -18,6 +18,12 @@
{ :name => "Aufgaben", :url => "/tasks"}
]
},
{ :name => "Wiki", :url => "/wiki", :active => ["pages", "wiki"],
:subnav => [
{ :name => "Startseite", :url => "/wiki" },
{ :name => "Alle Seiten", :url => "/pages/all" }
]
},
{ :name => "Bestellungen", :url => u.ordergroup ? "/ordering/" : "/orders",
:active => ["orders", "ordering"],
:subnav => [

View file

@ -30,7 +30,7 @@
- if flash[:error]
%h3.error#flashError= flash[:error]
#loader{:style => "display:none;"}= image_tag("loader.gif", :border => 0)
- if yield(:title)
- if show_title?
%h1= yield(:title)
= yield
#ajax_box(style="display:none")

View file

@ -0,0 +1,5 @@
- content = wikified_body @page.body, @page.title
//#toc
//%h2 Inhaltsverzeichnis
//= generate_toc @page.body
#wiki_content= content

View file

@ -0,0 +1,100 @@
- if params[:preview]
%h2 Vorschau
#preview= render :partial => 'body'
#wiki-syntax-help
%p
.box_title
%h2 Schnelle Formatierungshilfe
.column_content
%table(frame="void")
%tbody
%tr
%td(colspan=2)
%b Zeichenformatierung
%tr
%td
%i kursiv
%td
%pre
''kursiv''<br />
%tr
%td
%b fett
%td
%pre '''Fett'''<br />
%tr
%td
Keine Wiki-<br/>Formatierung
%td
%pre &lt;nowiki&gt;text&lt;/nowiki&gt;
%tr
%td(colspan=2)
%b Block-Formatierung
%tr
%td
Überschriften
%td
%pre
\== Ebene 1 ==
%pre
\=== Ebene 2 ===
%pre
\==== Ebene 3 ====
%tr
%td
Listen mit Punkten
%td
%pre
* Erster Punkt
%pre
** Zweiter Punkt
%tr
%td
Listen mit Zahlen
%td
%pre
1. Ersten Punkt
%pre
1. Zweiter Punkt
%tr
%td(colspan=2)
%b Link-Formatierung
%tr
%td
Wiki-Links
%td
%pre
[[Foodsoft Wiki Seite]]
%tr
%td
Externe Links
%td
%pre
[http://addresse.net Externe Seite]
%tr
%td(colspan=2)
%b Tabellenformatierung
%tr
%td
Siehe
= link_to "Tabellen", "http://www.mediawiki.org/wiki/Help:Tables", :target => '_blank'
- form_for @page do |f|
= f.error_messages
= f.hidden_field :lock_version
= f.hidden_field :parent_id
%p
%b Titel
%br/
= f.text_field :title
%p
%b Inhalt
%br/
= f.text_area :body, :size => "60x30"
%p
= f.submit "Vorschau", :name => 'preview'
|
= f.submit "Speichern"
|
= link_to "Abbrechen", :back

View file

@ -0,0 +1,12 @@
- title "Alle Wikiseiten"
%p= link_to "Neue Seite anlegen", new_page_path
%table
%tr
%th Titel
%th zuletzt aktualisiert
- for page in @pages
%tr
%td= link_to page.title, wiki_page_path(page.permalink)
%td= format_date page.updated_at

View file

@ -0,0 +1,3 @@
- title "#{@page.title} bearbeiten"
= render :partial => 'form'

View file

@ -0,0 +1,3 @@
- title "Neue Wikiseite anlegen"
= render :partial => 'form'

View file

@ -0,0 +1,36 @@
- title @page.title, false
%h1
%span#breadcrump
- for page in @page.ancestors.reverse
= link_to_wikipage(page)
&gt;&gt;
= @page.title
#sidebar
#page-versions
= link_to "Bearbeiten", edit_page_path(@page)
= link_to_function "Versionen (#{@page.versions.count})", "Element.toggle('versions')"
#versions{:style => "display:none"}
%ul
- for version in @page.versions.reverse
%li
= link_to I18n.l(version.updated_at, :format => "%d.%m.%y %H:%M"), version_page_path(@page, :version => version.lock_version)
= "(#{User.find(version.updated_by).nick})"
- unless @page.children.empty?
#subpages
%h2 Unterseiten
%ul
- for page in @page.children
%li= link_to_wikipage(page)
= render :partial => 'body'
%hr.clear/
%p
= link_to "Seite bearbeiten", edit_page_path(@page)
|
= link_to "Seite löschen", @page, :method => :delete, :confirm => "Achtung, auch alle Unterseiten werden gelöscht. Bist Du sicher?"
|
= "Zuletzt bearbeitet am #{format_datetime @page.updated_at} (#{@page.user.nick})"

View file

@ -0,0 +1,11 @@
- title @page.title
#sidebar
%b= "Version vom #{format_datetime @version.updated_at}"
%ul
%li= "Autor: #{User.find(@version.updated_by).nick}"
%li= link_to "Aktuelle Version sehen", wiki_page_path(:permalink => @page.permalink)
%li= link_to "Auf diese Version zurücksetzen", revert_page_path(@page, :version => @version.lock_version)
= wikified_body @version.body

View file

@ -1,4 +1,7 @@
ActionController::Routing::Routes.draw do |map|
map.resources :pages, :collection => { :all => :get }, :member => {:version => :get, :revert => :get}
map.wiki_page "/wiki/:permalink", :controller => 'pages', :action => 'show', :permalink => /[^\s]+/
map.wiki "/wiki", :controller => 'pages', :action => 'show', :permalink => 'Home'
map.logout '/logout', :controller => 'login', :action => 'logout'
map.my_profile '/home/profile', :controller => 'home', :action => 'profile'

View file

@ -0,0 +1,22 @@
class CreatePages < ActiveRecord::Migration
def self.up
create_table :pages do |t|
t.string :title
t.text :body
t.string :permalink
t.integer :lock_version, :default => 0
t.integer :updated_by
t.integer :redirect
t.integer :parent_id
t.timestamps
end
Page.create_versioned_table # Automaticly creates pages_versions table
end
def self.down
drop_table :pages
Page.drop_versioned_table
end
end

View file

@ -213,6 +213,30 @@ ActiveRecord::Schema.define(:version => 20090812110010) do
t.decimal "foodcoop_result", :precision => 8, :scale => 2
end
create_table "page_versions", :force => true do |t|
t.integer "page_id"
t.integer "lock_version"
t.text "body"
t.integer "updated_by"
t.integer "redirect"
t.integer "parent_id"
t.datetime "updated_at"
end
add_index "page_versions", ["page_id"], :name => "index_page_versions_on_page_id"
create_table "pages", :force => true do |t|
t.string "title"
t.text "body"
t.string "permalink"
t.integer "lock_version", :default => 0
t.integer "updated_by"
t.integer "redirect"
t.integer "parent_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "schema_info", :id => false, :force => true do |t|
t.integer "version"
end

23
lib/wikilink.rb Normal file
View file

@ -0,0 +1,23 @@
class Wikilink < WikiCloth::WikiLinkHandler
def url_for(page, parent = nil)
if parent
"/pages/new?title=#{page}&parent=#{parent}"
else
"/wiki/#{page}"
end
end
def link_attributes_for(page)
permalink = Page.permalink(page)
if Page.exists?(:permalink => permalink)
{ :href => url_for(permalink) }
else
{ :href => url_for(page, params[:referer]), :class => "new_wiki_link"}
end
end
def section_link(section)
""
end
end

View file

@ -441,6 +441,7 @@ ul.autocomplete .informal {
top: -1em;
left: 5%; }
.stats-bar {
height: 20px;
min-width: 10px;
@ -448,3 +449,37 @@ ul.autocomplete .informal {
background-color: #fff;
text-align: center;
margin: 0 10px 10px 0; }
a.new_wiki_link {
color: grey; }
#preview {
border: 1px dotted grey;
padding: 0 1em; }
#toc {
float: right; }
#breadcrump {
font-size: 0.5em; }
#breadcrump a {
color: #ed0606; }
#sidebar {
float: right;
width: 20%; }
#sidebar #page-versions {
margin-top: -4em;
text-align: right; }
#sidebar #subpages {
margin-top: 2em; }
#wiki_content h1, #wiki_content h2, #wiki_content h3, #wiki_content h4, #wiki_content h5 {
color: black; }
#wiki_content span.editsection {
display: none; }
#wiki-syntax-help {
float: right; }
#wiki-syntax-help table {
border-color: #78b74e; }

View file

@ -441,6 +441,7 @@ ul.autocomplete .informal {
top: -1em;
left: 5%; }
.stats-bar {
height: 20px;
min-width: 10px;
@ -449,6 +450,40 @@ ul.autocomplete .informal {
text-align: center;
margin: 0 10px 10px 0; }
a.new_wiki_link {
color: grey; }
#preview {
border: 1px dotted grey;
padding: 0 1em; }
#toc {
float: right; }
#breadcrump {
font-size: 0.5em; }
#breadcrump a {
color: #ed0606; }
#sidebar {
float: right;
width: 20%; }
#sidebar #page-versions {
margin-top: -4em;
text-align: right; }
#sidebar #subpages {
margin-top: 2em; }
#wiki_content h1, #wiki_content h2, #wiki_content h3, #wiki_content h4, #wiki_content h5 {
color: black; }
#wiki_content span.editsection {
display: none; }
#wiki-syntax-help {
float: right; }
#wiki-syntax-help table {
border-color: #78b74e; }
#logininfo, #header, #footer {
display: none; }

View file

@ -488,6 +488,7 @@ ul.autocomplete
top: -1em
left: 5%
<<<<<<< HEAD:public/stylesheets/sass/main.sass
// group stats
.stats-bar
height: 20px
@ -495,4 +496,38 @@ ul.autocomplete
border: 1px solid #DDDDDD
background-color: #fff
text-align: center
margin: 0 10px 10px 0
margin: 0 10px 10px 0
=======
// *** wiki
a.new_wiki_link
color: grey
#preview
border: 1px dotted grey
padding: 0 1em
#toc
float: right
#breadcrump
font-size: 0.5em
a
:color = !main_red
#sidebar
float: right
width: 20%
#page-versions
margin-top: -4em
text-align: right
#subpages
margin-top: 2em
#wiki_content
h1, h2, h3, h4, h5
color: black
span.editsection
display: none
#wiki-syntax-help
float: right
table
border-color: #78b74e
>>>>>>> wiki:public/stylesheets/sass/main.sass

View file

@ -2,7 +2,7 @@
#
# Table name: article_categories
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# name :string(255) default(""), not null
# description :string(255)
#

View file

@ -2,12 +2,12 @@
#
# Table name: article_prices
#
# id :integer(4) not null, primary key
# article_id :integer(4)
# id :integer not null, primary key
# article_id :integer
# price :decimal(8, 2) default(0.0), not null
# tax :decimal(8, 2) default(0.0), not null
# deposit :decimal(8, 2) default(0.0), not null
# unit_quantity :integer(4)
# unit_quantity :integer
# created_at :datetime
#

View file

@ -2,6 +2,36 @@
#
# Table name: articles
#
# id :integer not null, primary key
# name :string(255) default(""), not null
# supplier_id :integer default(0), not null
# article_category_id :integer default(0), not null
# unit :string(255) default(""), not null
# note :string(255)
# availability :boolean default(TRUE), not null
# manufacturer :string(255)
# origin :string(255)
# shared_updated_on :datetime
# price :decimal(, )
# tax :float
# deposit :decimal(, ) default(0.0)
# unit_quantity :integer default(1), not null
# order_number :string(255)
# created_at :datetime
# updated_at :datetime
# quantity :integer default(0)
# deleted_at :datetime
# type :string(255)
#
# == Schema Information
<<<<<<< HEAD:test/fixtures/articles.yml
=======
# Schema version: 20090325175756
>>>>>>> wiki:test/fixtures/articles.yml
#
# Table name: articles
#
# id :integer(4) not null, primary key
# name :string(255) default(""), not null
# supplier_id :integer(4) default(0), not null

View file

@ -2,10 +2,10 @@
#
# Table name: assignments
#
# id :integer(4) not null, primary key
# user_id :integer(4) default(0), not null
# task_id :integer(4) default(0), not null
# accepted :boolean(1)
# id :integer not null, primary key
# user_id :integer default(0), not null
# task_id :integer default(0), not null
# accepted :boolean
#
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html

View file

@ -2,11 +2,11 @@
#
# Table name: financial_transactions
#
# id :integer(4) not null, primary key
# ordergroup_id :integer(4) default(0), not null
# id :integer not null, primary key
# ordergroup_id :integer default(0), not null
# amount :decimal(8, 2) default(0.0), not null
# note :text default(""), not null
# user_id :integer(4) default(0), not null
# note :text not null
# user_id :integer default(0), not null
# created_on :datetime not null
#

View file

@ -2,10 +2,10 @@
#
# Table name: group_order_article_quantities
#
# id :integer(4) not null, primary key
# group_order_article_id :integer(4) default(0), not null
# quantity :integer(4) default(0)
# tolerance :integer(4) default(0)
# id :integer not null, primary key
# group_order_article_id :integer default(0), not null
# quantity :integer default(0)
# tolerance :integer default(0)
# created_on :datetime not null
#

View file

@ -2,28 +2,27 @@
#
# Table name: groups
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# type :string(255) default(""), not null
# name :string(255) default(""), not null
# description :string(255)
# account_balance :decimal(8, 2) default(0.0), not null
# account_balance :decimal(, ) default(0.0), not null
# account_updated :datetime
# created_on :datetime not null
# role_admin :boolean(1) not null
# role_suppliers :boolean(1) not null
# role_article_meta :boolean(1) not null
# role_finance :boolean(1) not null
# role_orders :boolean(1) not null
# weekly_task :boolean(1)
# weekday :integer(4)
# role_admin :boolean not null
# role_suppliers :boolean not null
# role_article_meta :boolean not null
# role_finance :boolean not null
# role_orders :boolean not null
# weekly_task :boolean
# weekday :integer
# task_name :string(255)
# task_description :string(255)
# task_required_users :integer(4) default(1)
# task_required_users :integer default(1)
# deleted_at :datetime
# contact_person :string(255)
# contact_phone :string(255)
# contact_address :string(255)
# stats :text
#
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html

View file

@ -2,9 +2,9 @@
#
# Table name: memberships
#
# id :integer(4) not null, primary key
# group_id :integer(4) default(0), not null
# user_id :integer(4) default(0), not null
# id :integer not null, primary key
# group_id :integer default(0), not null
# user_id :integer default(0), not null
#
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html

View file

@ -2,13 +2,13 @@
#
# Table name: messages
#
# id :integer(4) not null, primary key
# sender_id :integer(4)
# id :integer not null, primary key
# sender_id :integer
# recipients_ids :text
# subject :string(255) not null
# body :text
# email_state :integer(4) default(0), not null
# private :boolean(1)
# email_state :integer default(0), not null
# private :boolean
# created_at :datetime
#

View file

@ -2,9 +2,9 @@
#
# Table name: order_comments
#
# id :integer(4) not null, primary key
# order_id :integer(4)
# user_id :integer(4)
# id :integer not null, primary key
# order_id :integer
# user_id :integer
# text :text
# created_at :datetime
#

View file

@ -2,14 +2,14 @@
#
# Table name: orders
#
# id :integer(4) not null, primary key
# supplier_id :integer(4)
# id :integer not null, primary key
# supplier_id :integer
# note :text
# starts :datetime
# ends :datetime
# state :string(255) default("open")
# lock_version :integer(4) default(0), not null
# updated_by_user_id :integer(4)
# lock_version :integer default(0), not null
# updated_by_user_id :integer
# foodcoop_result :decimal(8, 2)
#

26
test/fixtures/pages.yml vendored Normal file
View file

@ -0,0 +1,26 @@
# == Schema Information
#
# Table name: pages
#
# id :integer not null, primary key
# title :string(255)
# body :text
# permalink :string(255)
# lock_version :integer default(0)
# updated_by :integer
# redirect :integer
# created_at :datetime
# updated_at :datetime
#
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
one:
title: MyString
body: MyText
permalink: MyString
two:
title: MyString
body: MyText
permalink: MyString

View file

@ -2,13 +2,13 @@
#
# Table name: stock_changes
#
# id :integer(4) not null, primary key
# delivery_id :integer(4)
# order_id :integer(4)
# stock_article_id :integer(4)
# quantity :integer(4) default(0)
# id :integer not null, primary key
# delivery_id :integer
# order_id :integer
# stock_article_id :integer
# quantity :integer default(0)
# created_at :datetime
# stock_taking_id :integer(4)
# stock_taking_id :integer
#
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html

View file

@ -2,7 +2,7 @@
#
# Table name: stock_takings
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# date :date
# note :text
# created_at :datetime

View file

@ -17,6 +17,29 @@
# lists :string(255)
#
# == Schema Information
<<<<<<< HEAD:test/fixtures/suppliers.yml
=======
# Schema version: 20090325175756
>>>>>>> wiki:test/fixtures/suppliers.yml
#
# Table name: suppliers
#
# id :integer(4) not null, primary key
# name :string(255) not null
# address :string(255) not null
# phone :string(255) not null
# phone2 :string(255)
# fax :string(255)
# email :string(255)
# url :string(255)
# delivery_days :string(255)
# note :string(255)
# created_on :datetime
# updated_on :datetime
# lists :string(255)
#
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
terra:
name: Terra

View file

@ -2,16 +2,16 @@
#
# Table name: tasks
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# name :string(255) default(""), not null
# description :string(255)
# due_date :date
# done :boolean(1)
# workgroup_id :integer(4)
# assigned :boolean(1)
# done :boolean
# workgroup_id :integer
# assigned :boolean
# created_on :datetime not null
# updated_on :datetime not null
# required_users :integer(4) default(1)
# required_users :integer default(1)
#
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html

View file

@ -2,7 +2,7 @@
#
# Table name: users
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# nick :string(255) default(""), not null
# password_hash :string(255) default(""), not null
# password_salt :string(255) default(""), not null

View file

@ -0,0 +1,45 @@
require 'test_helper'
class PagesControllerTest < ActionController::TestCase
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:pages)
end
test "should get new" do
get :new
assert_response :success
end
test "should create page" do
assert_difference('Page.count') do
post :create, :page => { }
end
assert_redirected_to page_path(assigns(:page))
end
test "should show page" do
get :show, :id => pages(:one).id
assert_response :success
end
test "should get edit" do
get :edit, :id => pages(:one).id
assert_response :success
end
test "should update page" do
put :update, :id => pages(:one).id, :page => { }
assert_redirected_to page_path(assigns(:page))
end
test "should destroy page" do
assert_difference('Page.count', -1) do
delete :destroy, :id => pages(:one).id
end
assert_redirected_to pages_path
end
end

View file

@ -2,7 +2,7 @@
#
# Table name: article_categories
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# name :string(255) default(""), not null
# description :string(255)
#

View file

@ -2,12 +2,12 @@
#
# Table name: article_prices
#
# id :integer(4) not null, primary key
# article_id :integer(4)
# id :integer not null, primary key
# article_id :integer
# price :decimal(8, 2) default(0.0), not null
# tax :decimal(8, 2) default(0.0), not null
# deposit :decimal(8, 2) default(0.0), not null
# unit_quantity :integer(4)
# unit_quantity :integer
# created_at :datetime
#

View file

@ -2,26 +2,26 @@
#
# Table name: articles
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# name :string(255) default(""), not null
# supplier_id :integer(4) default(0), not null
# article_category_id :integer(4) default(0), not null
# supplier_id :integer default(0), not null
# article_category_id :integer default(0), not null
# unit :string(255) default(""), not null
# note :string(255)
# availability :boolean(1) default(TRUE), not null
# availability :boolean default(TRUE), not null
# manufacturer :string(255)
# origin :string(255)
# shared_updated_on :datetime
# price :decimal(8, 2)
# price :decimal(, )
# tax :float
# deposit :decimal(8, 2) default(0.0)
# unit_quantity :integer(4) default(1), not null
# deposit :decimal(, ) default(0.0)
# unit_quantity :integer default(1), not null
# order_number :string(255)
# created_at :datetime
# updated_at :datetime
# quantity :integer default(0)
# deleted_at :datetime
# type :string(255)
# quantity :integer(4) default(0)
#
require File.dirname(__FILE__) + '/../test_helper'

View file

@ -2,10 +2,10 @@
#
# Table name: assignments
#
# id :integer(4) not null, primary key
# user_id :integer(4) default(0), not null
# task_id :integer(4) default(0), not null
# accepted :boolean(1)
# id :integer not null, primary key
# user_id :integer default(0), not null
# task_id :integer default(0), not null
# accepted :boolean
#
require File.dirname(__FILE__) + '/../test_helper'

View file

@ -2,11 +2,11 @@
#
# Table name: financial_transactions
#
# id :integer(4) not null, primary key
# ordergroup_id :integer(4) default(0), not null
# id :integer not null, primary key
# ordergroup_id :integer default(0), not null
# amount :decimal(8, 2) default(0.0), not null
# note :text default(""), not null
# user_id :integer(4) default(0), not null
# note :text not null
# user_id :integer default(0), not null
# created_on :datetime not null
#

View file

@ -2,10 +2,10 @@
#
# Table name: group_order_article_quantities
#
# id :integer(4) not null, primary key
# group_order_article_id :integer(4) default(0), not null
# quantity :integer(4) default(0)
# tolerance :integer(4) default(0)
# id :integer not null, primary key
# group_order_article_id :integer default(0), not null
# quantity :integer default(0)
# tolerance :integer default(0)
# created_on :datetime not null
#

View file

@ -2,28 +2,27 @@
#
# Table name: groups
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# type :string(255) default(""), not null
# name :string(255) default(""), not null
# description :string(255)
# account_balance :decimal(8, 2) default(0.0), not null
# account_balance :decimal(, ) default(0.0), not null
# account_updated :datetime
# created_on :datetime not null
# role_admin :boolean(1) not null
# role_suppliers :boolean(1) not null
# role_article_meta :boolean(1) not null
# role_finance :boolean(1) not null
# role_orders :boolean(1) not null
# weekly_task :boolean(1)
# weekday :integer(4)
# role_admin :boolean not null
# role_suppliers :boolean not null
# role_article_meta :boolean not null
# role_finance :boolean not null
# role_orders :boolean not null
# weekly_task :boolean
# weekday :integer
# task_name :string(255)
# task_description :string(255)
# task_required_users :integer(4) default(1)
# task_required_users :integer default(1)
# deleted_at :datetime
# contact_person :string(255)
# contact_phone :string(255)
# contact_address :string(255)
# stats :text
#
require File.dirname(__FILE__) + '/../test_helper'

View file

@ -2,9 +2,9 @@
#
# Table name: memberships
#
# id :integer(4) not null, primary key
# group_id :integer(4) default(0), not null
# user_id :integer(4) default(0), not null
# id :integer not null, primary key
# group_id :integer default(0), not null
# user_id :integer default(0), not null
#
require File.dirname(__FILE__) + '/../test_helper'

View file

@ -2,9 +2,9 @@
#
# Table name: order_comments
#
# id :integer(4) not null, primary key
# order_id :integer(4)
# user_id :integer(4)
# id :integer not null, primary key
# order_id :integer
# user_id :integer
# text :text
# created_at :datetime
#

View file

@ -2,14 +2,14 @@
#
# Table name: orders
#
# id :integer(4) not null, primary key
# supplier_id :integer(4)
# id :integer not null, primary key
# supplier_id :integer
# note :text
# starts :datetime
# ends :datetime
# state :string(255) default("open")
# lock_version :integer(4) default(0), not null
# updated_by_user_id :integer(4)
# lock_version :integer default(0), not null
# updated_by_user_id :integer
# foodcoop_result :decimal(8, 2)
#

23
test/unit/page_test.rb Normal file
View file

@ -0,0 +1,23 @@
# == Schema Information
#
# Table name: pages
#
# id :integer not null, primary key
# title :string(255)
# body :text
# permalink :string(255)
# lock_version :integer default(0)
# updated_by :integer
# redirect :integer
# created_at :datetime
# updated_at :datetime
#
require 'test_helper'
class PageTest < ActiveSupport::TestCase
# Replace this with your real tests.
test "the truth" do
assert true
end
end

View file

@ -2,26 +2,26 @@
#
# Table name: articles
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# name :string(255) default(""), not null
# supplier_id :integer(4) default(0), not null
# article_category_id :integer(4) default(0), not null
# supplier_id :integer default(0), not null
# article_category_id :integer default(0), not null
# unit :string(255) default(""), not null
# note :string(255)
# availability :boolean(1) default(TRUE), not null
# availability :boolean default(TRUE), not null
# manufacturer :string(255)
# origin :string(255)
# shared_updated_on :datetime
# price :decimal(8, 2)
# price :decimal(, )
# tax :float
# deposit :decimal(8, 2) default(0.0)
# unit_quantity :integer(4) default(1), not null
# deposit :decimal(, ) default(0.0)
# unit_quantity :integer default(1), not null
# order_number :string(255)
# created_at :datetime
# updated_at :datetime
# quantity :integer default(0)
# deleted_at :datetime
# type :string(255)
# quantity :integer(4) default(0)
#
require 'test_helper'

View file

@ -2,13 +2,13 @@
#
# Table name: stock_changes
#
# id :integer(4) not null, primary key
# delivery_id :integer(4)
# order_id :integer(4)
# stock_article_id :integer(4)
# quantity :integer(4) default(0)
# id :integer not null, primary key
# delivery_id :integer
# order_id :integer
# stock_article_id :integer
# quantity :integer default(0)
# created_at :datetime
# stock_taking_id :integer(4)
# stock_taking_id :integer
#
require 'test_helper'

View file

@ -2,7 +2,7 @@
#
# Table name: stock_takings
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# date :date
# note :text
# created_at :datetime

View file

@ -2,7 +2,7 @@
#
# Table name: suppliers
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# name :string(255) default(""), not null
# address :string(255) default(""), not null
# phone :string(255) default(""), not null
@ -15,7 +15,7 @@
# delivery_days :string(255)
# order_howto :string(255)
# note :string(255)
# shared_supplier_id :integer(4)
# shared_supplier_id :integer
# min_order_quantity :string(255)
# deleted_at :datetime
#

View file

@ -2,16 +2,16 @@
#
# Table name: tasks
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# name :string(255) default(""), not null
# description :string(255)
# due_date :date
# done :boolean(1)
# workgroup_id :integer(4)
# assigned :boolean(1)
# done :boolean
# workgroup_id :integer
# assigned :boolean
# created_on :datetime not null
# updated_on :datetime not null
# required_users :integer(4) default(1)
# required_users :integer default(1)
#
require File.dirname(__FILE__) + '/../test_helper'

View file

@ -2,7 +2,7 @@
#
# Table name: users
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# nick :string(255) default(""), not null
# password_hash :string(255) default(""), not null
# password_salt :string(255) default(""), not null

View file

@ -2,28 +2,27 @@
#
# Table name: groups
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# type :string(255) default(""), not null
# name :string(255) default(""), not null
# description :string(255)
# account_balance :decimal(8, 2) default(0.0), not null
# account_balance :decimal(, ) default(0.0), not null
# account_updated :datetime
# created_on :datetime not null
# role_admin :boolean(1) not null
# role_suppliers :boolean(1) not null
# role_article_meta :boolean(1) not null
# role_finance :boolean(1) not null
# role_orders :boolean(1) not null
# weekly_task :boolean(1)
# weekday :integer(4)
# role_admin :boolean not null
# role_suppliers :boolean not null
# role_article_meta :boolean not null
# role_finance :boolean not null
# role_orders :boolean not null
# weekly_task :boolean
# weekday :integer
# task_name :string(255)
# task_description :string(255)
# task_required_users :integer(4) default(1)
# task_required_users :integer default(1)
# deleted_at :datetime
# contact_person :string(255)
# contact_phone :string(255)
# contact_address :string(255)
# stats :text
#
require 'test_helper'

26
vendor/plugins/acts_as_tree/README vendored Normal file
View file

@ -0,0 +1,26 @@
acts_as_tree
============
Specify this +acts_as+ extension if you want to model a tree structure by providing a parent association and a children
association. This requires that you have a foreign key column, which by default is called +parent_id+.
class Category < ActiveRecord::Base
acts_as_tree :order => "name"
end
Example:
root
\_ child1
\_ subchild1
\_ subchild2
root = Category.create("name" => "root")
child1 = root.children.create("name" => "child1")
subchild1 = child1.children.create("name" => "subchild1")
root.parent # => nil
child1.parent # => root
root.children # => [child1]
root.children.first.children.first # => subchild1
Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license

22
vendor/plugins/acts_as_tree/Rakefile vendored Normal file
View file

@ -0,0 +1,22 @@
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
desc 'Default: run unit tests.'
task :default => :test
desc 'Test acts_as_tree plugin.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end
desc 'Generate documentation for acts_as_tree plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'acts_as_tree'
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('README')
rdoc.rdoc_files.include('lib/**/*.rb')
end

1
vendor/plugins/acts_as_tree/init.rb vendored Normal file
View file

@ -0,0 +1 @@
ActiveRecord::Base.send :include, ActiveRecord::Acts::Tree

View file

@ -0,0 +1,96 @@
module ActiveRecord
module Acts
module Tree
def self.included(base)
base.extend(ClassMethods)
end
# Specify this +acts_as+ extension if you want to model a tree structure by providing a parent association and a children
# association. This requires that you have a foreign key column, which by default is called +parent_id+.
#
# class Category < ActiveRecord::Base
# acts_as_tree :order => "name"
# end
#
# Example:
# root
# \_ child1
# \_ subchild1
# \_ subchild2
#
# root = Category.create("name" => "root")
# child1 = root.children.create("name" => "child1")
# subchild1 = child1.children.create("name" => "subchild1")
#
# root.parent # => nil
# child1.parent # => root
# root.children # => [child1]
# root.children.first.children.first # => subchild1
#
# In addition to the parent and children associations, the following instance methods are added to the class
# after calling <tt>acts_as_tree</tt>:
# * <tt>siblings</tt> - Returns all the children of the parent, excluding the current node (<tt>[subchild2]</tt> when called on <tt>subchild1</tt>)
# * <tt>self_and_siblings</tt> - Returns all the children of the parent, including the current node (<tt>[subchild1, subchild2]</tt> when called on <tt>subchild1</tt>)
# * <tt>ancestors</tt> - Returns all the ancestors of the current node (<tt>[child1, root]</tt> when called on <tt>subchild2</tt>)
# * <tt>root</tt> - Returns the root of the current node (<tt>root</tt> when called on <tt>subchild2</tt>)
module ClassMethods
# Configuration options are:
#
# * <tt>foreign_key</tt> - specifies the column name to use for tracking of the tree (default: +parent_id+)
# * <tt>order</tt> - makes it possible to sort the children according to this SQL snippet.
# * <tt>counter_cache</tt> - keeps a count in a +children_count+ column if set to +true+ (default: +false+).
def acts_as_tree(options = {})
configuration = { :foreign_key => "parent_id", :order => nil, :counter_cache => nil }
configuration.update(options) if options.is_a?(Hash)
belongs_to :parent, :class_name => name, :foreign_key => configuration[:foreign_key], :counter_cache => configuration[:counter_cache]
has_many :children, :class_name => name, :foreign_key => configuration[:foreign_key], :order => configuration[:order], :dependent => :destroy
class_eval <<-EOV
include ActiveRecord::Acts::Tree::InstanceMethods
def self.roots
find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
end
def self.root
find(:first, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
end
EOV
end
end
module InstanceMethods
# Returns list of ancestors, starting from parent until root.
#
# subchild1.ancestors # => [child1, root]
def ancestors
node, nodes = self, []
nodes << node = node.parent while node.parent
nodes
end
# Returns the root node of the tree.
def root
node = self
node = node.parent while node.parent
node
end
# Returns all siblings of the current node.
#
# subchild1.siblings # => [subchild2]
def siblings
self_and_siblings - [self]
end
# Returns all siblings and a reference to the current node.
#
# subchild1.self_and_siblings # => [subchild1, subchild2]
def self_and_siblings
parent ? parent.children : self.class.roots
end
end
end
end
end

View file

View file

@ -0,0 +1,219 @@
require 'test/unit'
require 'rubygems'
require 'active_record'
$:.unshift File.dirname(__FILE__) + '/../lib'
require File.dirname(__FILE__) + '/../init'
class Test::Unit::TestCase
def assert_queries(num = 1)
$query_count = 0
yield
ensure
assert_equal num, $query_count, "#{$query_count} instead of #{num} queries were executed."
end
def assert_no_queries(&block)
assert_queries(0, &block)
end
end
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
# AR keeps printing annoying schema statements
$stdout = StringIO.new
def setup_db
ActiveRecord::Base.logger
ActiveRecord::Schema.define(:version => 1) do
create_table :mixins do |t|
t.column :type, :string
t.column :parent_id, :integer
end
end
end
def teardown_db
ActiveRecord::Base.connection.tables.each do |table|
ActiveRecord::Base.connection.drop_table(table)
end
end
class Mixin < ActiveRecord::Base
end
class TreeMixin < Mixin
acts_as_tree :foreign_key => "parent_id", :order => "id"
end
class TreeMixinWithoutOrder < Mixin
acts_as_tree :foreign_key => "parent_id"
end
class RecursivelyCascadedTreeMixin < Mixin
acts_as_tree :foreign_key => "parent_id"
has_one :first_child, :class_name => 'RecursivelyCascadedTreeMixin', :foreign_key => :parent_id
end
class TreeTest < Test::Unit::TestCase
def setup
setup_db
@root1 = TreeMixin.create!
@root_child1 = TreeMixin.create! :parent_id => @root1.id
@child1_child = TreeMixin.create! :parent_id => @root_child1.id
@root_child2 = TreeMixin.create! :parent_id => @root1.id
@root2 = TreeMixin.create!
@root3 = TreeMixin.create!
end
def teardown
teardown_db
end
def test_children
assert_equal @root1.children, [@root_child1, @root_child2]
assert_equal @root_child1.children, [@child1_child]
assert_equal @child1_child.children, []
assert_equal @root_child2.children, []
end
def test_parent
assert_equal @root_child1.parent, @root1
assert_equal @root_child1.parent, @root_child2.parent
assert_nil @root1.parent
end
def test_delete
assert_equal 6, TreeMixin.count
@root1.destroy
assert_equal 2, TreeMixin.count
@root2.destroy
@root3.destroy
assert_equal 0, TreeMixin.count
end
def test_insert
@extra = @root1.children.create
assert @extra
assert_equal @extra.parent, @root1
assert_equal 3, @root1.children.size
assert @root1.children.include?(@extra)
assert @root1.children.include?(@root_child1)
assert @root1.children.include?(@root_child2)
end
def test_ancestors
assert_equal [], @root1.ancestors
assert_equal [@root1], @root_child1.ancestors
assert_equal [@root_child1, @root1], @child1_child.ancestors
assert_equal [@root1], @root_child2.ancestors
assert_equal [], @root2.ancestors
assert_equal [], @root3.ancestors
end
def test_root
assert_equal @root1, TreeMixin.root
assert_equal @root1, @root1.root
assert_equal @root1, @root_child1.root
assert_equal @root1, @child1_child.root
assert_equal @root1, @root_child2.root
assert_equal @root2, @root2.root
assert_equal @root3, @root3.root
end
def test_roots
assert_equal [@root1, @root2, @root3], TreeMixin.roots
end
def test_siblings
assert_equal [@root2, @root3], @root1.siblings
assert_equal [@root_child2], @root_child1.siblings
assert_equal [], @child1_child.siblings
assert_equal [@root_child1], @root_child2.siblings
assert_equal [@root1, @root3], @root2.siblings
assert_equal [@root1, @root2], @root3.siblings
end
def test_self_and_siblings
assert_equal [@root1, @root2, @root3], @root1.self_and_siblings
assert_equal [@root_child1, @root_child2], @root_child1.self_and_siblings
assert_equal [@child1_child], @child1_child.self_and_siblings
assert_equal [@root_child1, @root_child2], @root_child2.self_and_siblings
assert_equal [@root1, @root2, @root3], @root2.self_and_siblings
assert_equal [@root1, @root2, @root3], @root3.self_and_siblings
end
end
class TreeTestWithEagerLoading < Test::Unit::TestCase
def setup
teardown_db
setup_db
@root1 = TreeMixin.create!
@root_child1 = TreeMixin.create! :parent_id => @root1.id
@child1_child = TreeMixin.create! :parent_id => @root_child1.id
@root_child2 = TreeMixin.create! :parent_id => @root1.id
@root2 = TreeMixin.create!
@root3 = TreeMixin.create!
@rc1 = RecursivelyCascadedTreeMixin.create!
@rc2 = RecursivelyCascadedTreeMixin.create! :parent_id => @rc1.id
@rc3 = RecursivelyCascadedTreeMixin.create! :parent_id => @rc2.id
@rc4 = RecursivelyCascadedTreeMixin.create! :parent_id => @rc3.id
end
def teardown
teardown_db
end
def test_eager_association_loading
roots = TreeMixin.find(:all, :include => :children, :conditions => "mixins.parent_id IS NULL", :order => "mixins.id")
assert_equal [@root1, @root2, @root3], roots
assert_no_queries do
assert_equal 2, roots[0].children.size
assert_equal 0, roots[1].children.size
assert_equal 0, roots[2].children.size
end
end
def test_eager_association_loading_with_recursive_cascading_three_levels_has_many
root_node = RecursivelyCascadedTreeMixin.find(:first, :include => { :children => { :children => :children } }, :order => 'mixins.id')
assert_equal @rc4, assert_no_queries { root_node.children.first.children.first.children.first }
end
def test_eager_association_loading_with_recursive_cascading_three_levels_has_one
root_node = RecursivelyCascadedTreeMixin.find(:first, :include => { :first_child => { :first_child => :first_child } }, :order => 'mixins.id')
assert_equal @rc4, assert_no_queries { root_node.first_child.first_child.first_child }
end
def test_eager_association_loading_with_recursive_cascading_three_levels_belongs_to
leaf_node = RecursivelyCascadedTreeMixin.find(:first, :include => { :parent => { :parent => :parent } }, :order => 'mixins.id DESC')
assert_equal @rc1, assert_no_queries { leaf_node.parent.parent.parent }
end
end
class TreeTestWithoutOrder < Test::Unit::TestCase
def setup
setup_db
@root1 = TreeMixinWithoutOrder.create!
@root2 = TreeMixinWithoutOrder.create!
end
def teardown
teardown_db
end
def test_root
assert [@root1, @root2].include?(TreeMixinWithoutOrder.root)
end
def test_roots
assert_equal [], [@root1, @root2] - TreeMixinWithoutOrder.roots
end
end

View file

View file

View file

View file

View file

@ -0,0 +1,82 @@
*GIT* (version numbers are overrated)
* (16 Jun 2008) Backwards Compatibility is overrated (big updates for rails 2.1)
* Use ActiveRecord 2.1's dirty attribute checking instead [Asa Calow]
* Remove last traces of #non_versioned_fields
* Remove AR::Base.find_version and AR::Base.find_versions, rely on AR association proxies and named_scope
* Remove #versions_count, rely on AR association counter caching.
* Remove #versioned_attributes, basically the same as AR::Base.versioned_columns
* (5 Oct 2006) Allow customization of #versions association options [Dan Peterson]
*0.5.1*
* (8 Aug 2006) Versioned models now belong to the unversioned model. @article_version.article.class => Article [Aslak Hellesoy]
*0.5* # do versions even matter for plugins?
* (21 Apr 2006) Added without_locking and without_revision methods.
Foo.without_revision do
@foo.update_attributes ...
end
*0.4*
* (28 March 2006) Rename non_versioned_fields to non_versioned_columns (old one is kept for compatibility).
* (28 March 2006) Made explicit documentation note that string column names are required for non_versioned_columns.
*0.3.1*
* (7 Jan 2006) explicitly set :foreign_key option for the versioned model's belongs_to assocation for STI [Caged]
* (7 Jan 2006) added tests to prove has_many :through joins work
*0.3*
* (2 Jan 2006) added ability to share a mixin with versioned class
* (2 Jan 2006) changed the dynamic version model to MyModel::Version
*0.2.4*
* (27 Nov 2005) added note about possible destructive behavior of if_changed? [Michael Schuerig]
*0.2.3*
* (12 Nov 2005) fixed bug with old behavior of #blank? [Michael Schuerig]
* (12 Nov 2005) updated tests to use ActiveRecord Schema
*0.2.2*
* (3 Nov 2005) added documentation note to #acts_as_versioned [Martin Jul]
*0.2.1*
* (6 Oct 2005) renamed dirty? to changed? to keep it uniform. it was aliased to keep it backwards compatible.
*0.2*
* (6 Oct 2005) added find_versions and find_version class methods.
* (6 Oct 2005) removed transaction from create_versioned_table().
this way you can specify your own transaction around a group of operations.
* (30 Sep 2005) fixed bug where find_versions() would order by 'version' twice. (found by Joe Clark)
* (26 Sep 2005) added :sequence_name option to acts_as_versioned to set the sequence name on the versioned model
*0.1.3* (18 Sep 2005)
* First RubyForge release
*0.1.2*
* check if module is already included when acts_as_versioned is called
*0.1.1*
* Adding tests and rdocs
*0.1*
* Initial transfer from Rails ticket: http://dev.rubyonrails.com/ticket/1974

View file

@ -0,0 +1,20 @@
Copyright (c) 2005 Rick Olson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

28
vendor/plugins/acts_as_versioned/README vendored Normal file
View file

@ -0,0 +1,28 @@
= acts_as_versioned
This library adds simple versioning to an ActiveRecord module. ActiveRecord is required.
== Resources
Install
* gem install acts_as_versioned
Rubyforge project
* http://rubyforge.org/projects/ar-versioned
RDocs
* http://ar-versioned.rubyforge.org
Subversion
* http://techno-weenie.net/svn/projects/acts_as_versioned
Collaboa
* http://collaboa.techno-weenie.net/repository/browse/acts_as_versioned
Special thanks to Dreamer on ##rubyonrails for help in early testing. His ServerSideWiki (http://serversidewiki.com)
was the first project to use acts_as_versioned <em>in the wild</em>.

View file

@ -0,0 +1,41 @@
== Creating the test database
The default name for the test databases is "activerecord_versioned". If you
want to use another database name then be sure to update the connection
adapter setups you want to test with in test/connections/<your database>/connection.rb.
When you have the database online, you can import the fixture tables with
the test/fixtures/db_definitions/*.sql files.
Make sure that you create database objects with the same user that you specified in i
connection.rb otherwise (on Postgres, at least) tests for default values will fail.
== Running with Rake
The easiest way to run the unit tests is through Rake. The default task runs
the entire test suite for all the adapters. You can also run the suite on just
one adapter by using the tasks test_mysql_ruby, test_ruby_mysql, test_sqlite,
or test_postresql. For more information, checkout the full array of rake tasks with "rake -T"
Rake can be found at http://rake.rubyforge.org
== Running by hand
Unit tests are located in test directory. If you only want to run a single test suite,
or don't want to bother with Rake, you can do so with something like:
cd test; ruby -I "connections/native_mysql" base_test.rb
That'll run the base suite using the MySQL-Ruby adapter. Change the adapter
and test suite name as needed.
== Faster tests
If you are using a database that supports transactions, you can set the
"AR_TX_FIXTURES" environment variable to "yes" to use transactional fixtures.
This gives a very large speed boost. With rake:
rake AR_TX_FIXTURES=yes
Or, by hand:
AR_TX_FIXTURES=yes ruby -I connections/native_sqlite3 base_test.rb

View file

@ -0,0 +1,180 @@
require 'rubygems'
require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
require 'rake/testtask'
require 'rake/contrib/rubyforgepublisher'
PKG_NAME = 'acts_as_versioned'
PKG_VERSION = '0.3.1'
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
PROD_HOST = "technoweenie@bidwell.textdrive.com"
RUBY_FORGE_PROJECT = 'ar-versioned'
RUBY_FORGE_USER = 'technoweenie'
desc 'Default: run unit tests.'
task :default => :test
desc 'Test the calculations plugin.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end
desc 'Generate documentation for the calculations plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "#{PKG_NAME} -- Simple versioning with active record models"
rdoc.options << '--line-numbers --inline-source'
rdoc.rdoc_files.include('README', 'CHANGELOG', 'RUNNING_UNIT_TESTS')
rdoc.rdoc_files.include('lib/**/*.rb')
end
spec = Gem::Specification.new do |s|
s.name = PKG_NAME
s.version = PKG_VERSION
s.platform = Gem::Platform::RUBY
s.summary = "Simple versioning with active record models"
s.files = FileList["{lib,test}/**/*"].to_a + %w(README MIT-LICENSE CHANGELOG RUNNING_UNIT_TESTS)
s.files.delete "acts_as_versioned_plugin.sqlite.db"
s.files.delete "acts_as_versioned_plugin.sqlite3.db"
s.files.delete "test/debug.log"
s.require_path = 'lib'
s.autorequire = 'acts_as_versioned'
s.has_rdoc = true
s.test_files = Dir['test/**/*_test.rb']
s.add_dependency 'activerecord', '>= 1.10.1'
s.add_dependency 'activesupport', '>= 1.1.1'
s.author = "Rick Olson"
s.email = "technoweenie@gmail.com"
s.homepage = "http://techno-weenie.net"
end
Rake::GemPackageTask.new(spec) do |pkg|
pkg.need_tar = true
end
desc "Publish the API documentation"
task :pdoc => [:rdoc] do
Rake::RubyForgePublisher.new(RUBY_FORGE_PROJECT, RUBY_FORGE_USER).upload
end
desc 'Publish the gem and API docs'
task :publish => [:pdoc, :rubyforge_upload]
desc "Publish the release files to RubyForge."
task :rubyforge_upload => :package do
files = %w(gem tgz).map { |ext| "pkg/#{PKG_FILE_NAME}.#{ext}" }
if RUBY_FORGE_PROJECT then
require 'net/http'
require 'open-uri'
project_uri = "http://rubyforge.org/projects/#{RUBY_FORGE_PROJECT}/"
project_data = open(project_uri) { |data| data.read }
group_id = project_data[/[?&]group_id=(\d+)/, 1]
raise "Couldn't get group id" unless group_id
# This echos password to shell which is a bit sucky
if ENV["RUBY_FORGE_PASSWORD"]
password = ENV["RUBY_FORGE_PASSWORD"]
else
print "#{RUBY_FORGE_USER}@rubyforge.org's password: "
password = STDIN.gets.chomp
end
login_response = Net::HTTP.start("rubyforge.org", 80) do |http|
data = [
"login=1",
"form_loginname=#{RUBY_FORGE_USER}",
"form_pw=#{password}"
].join("&")
http.post("/account/login.php", data)
end
cookie = login_response["set-cookie"]
raise "Login failed" unless cookie
headers = { "Cookie" => cookie }
release_uri = "http://rubyforge.org/frs/admin/?group_id=#{group_id}"
release_data = open(release_uri, headers) { |data| data.read }
package_id = release_data[/[?&]package_id=(\d+)/, 1]
raise "Couldn't get package id" unless package_id
first_file = true
release_id = ""
files.each do |filename|
basename = File.basename(filename)
file_ext = File.extname(filename)
file_data = File.open(filename, "rb") { |file| file.read }
puts "Releasing #{basename}..."
release_response = Net::HTTP.start("rubyforge.org", 80) do |http|
release_date = Time.now.strftime("%Y-%m-%d %H:%M")
type_map = {
".zip" => "3000",
".tgz" => "3110",
".gz" => "3110",
".gem" => "1400"
}; type_map.default = "9999"
type = type_map[file_ext]
boundary = "rubyqMY6QN9bp6e4kS21H4y0zxcvoor"
query_hash = if first_file then
{
"group_id" => group_id,
"package_id" => package_id,
"release_name" => PKG_FILE_NAME,
"release_date" => release_date,
"type_id" => type,
"processor_id" => "8000", # Any
"release_notes" => "",
"release_changes" => "",
"preformatted" => "1",
"submit" => "1"
}
else
{
"group_id" => group_id,
"release_id" => release_id,
"package_id" => package_id,
"step2" => "1",
"type_id" => type,
"processor_id" => "8000", # Any
"submit" => "Add This File"
}
end
query = "?" + query_hash.map do |(name, value)|
[name, URI.encode(value)].join("=")
end.join("&")
data = [
"--" + boundary,
"Content-Disposition: form-data; name=\"userfile\"; filename=\"#{basename}\"",
"Content-Type: application/octet-stream",
"Content-Transfer-Encoding: binary",
"", file_data, ""
].join("\x0D\x0A")
release_headers = headers.merge(
"Content-Type" => "multipart/form-data; boundary=#{boundary}"
)
target = first_file ? "/frs/admin/qrs.php" : "/frs/admin/editrelease.php"
http.post(target + query, data, release_headers)
end
if first_file then
release_id = release_response.body[/release_id=(\d+)/, 1]
raise("Couldn't get release id") unless release_id
end
first_file = false
end
end
end

View file

@ -0,0 +1,4 @@
---
:patch: 2
:major: 0
:minor: 5

View file

@ -0,0 +1,29 @@
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = %q{acts_as_versioned}
s.version = "0.5.2"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["technoweenie"]
s.date = %q{2009-01-20}
s.description = %q{TODO}
s.email = %q{technoweenie@bidwell.textdrive.com}
s.files = ["VERSION.yml", "lib/acts_as_versioned.rb", "test/abstract_unit.rb", "test/database.yml", "test/fixtures", "test/fixtures/authors.yml", "test/fixtures/landmark.rb", "test/fixtures/landmark_versions.yml", "test/fixtures/landmarks.yml", "test/fixtures/locked_pages.yml", "test/fixtures/locked_pages_revisions.yml", "test/fixtures/migrations", "test/fixtures/migrations/1_add_versioned_tables.rb", "test/fixtures/page.rb", "test/fixtures/page_versions.yml", "test/fixtures/pages.yml", "test/fixtures/widget.rb", "test/migration_test.rb", "test/schema.rb", "test/versioned_test.rb"]
s.has_rdoc = true
s.homepage = %q{http://github.com/technoweenie/acts_as_versioned}
s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
s.require_paths = ["lib"]
s.rubygems_version = %q{1.3.1}
s.summary = %q{TODO}
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 2
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
else
end
else
end
end

View file

@ -0,0 +1 @@
require 'acts_as_versioned'

View file

@ -0,0 +1,486 @@
# Copyright (c) 2005 Rick Olson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
module ActiveRecord #:nodoc:
module Acts #:nodoc:
# Specify this act if you want to save a copy of the row in a versioned table. This assumes there is a
# versioned table ready and that your model has a version field. This works with optimistic locking if the lock_version
# column is present as well.
#
# The class for the versioned model is derived the first time it is seen. Therefore, if you change your database schema you have to restart
# your container for the changes to be reflected. In development mode this usually means restarting WEBrick.
#
# class Page < ActiveRecord::Base
# # assumes pages_versions table
# acts_as_versioned
# end
#
# Example:
#
# page = Page.create(:title => 'hello world!')
# page.version # => 1
#
# page.title = 'hello world'
# page.save
# page.version # => 2
# page.versions.size # => 2
#
# page.revert_to(1) # using version number
# page.title # => 'hello world!'
#
# page.revert_to(page.versions.last) # using versioned instance
# page.title # => 'hello world'
#
# page.versions.earliest # efficient query to find the first version
# page.versions.latest # efficient query to find the most recently created version
#
#
# Simple Queries to page between versions
#
# page.versions.before(version)
# page.versions.after(version)
#
# Access the previous/next versions from the versioned model itself
#
# version = page.versions.latest
# version.previous # go back one version
# version.next # go forward one version
#
# See ActiveRecord::Acts::Versioned::ClassMethods#acts_as_versioned for configuration options
module Versioned
CALLBACKS = [:set_new_version, :save_version, :save_version?]
def self.included(base) # :nodoc:
base.extend ClassMethods
end
module ClassMethods
# == Configuration options
#
# * <tt>class_name</tt> - versioned model class name (default: PageVersion in the above example)
# * <tt>table_name</tt> - versioned model table name (default: page_versions in the above example)
# * <tt>foreign_key</tt> - foreign key used to relate the versioned model to the original model (default: page_id in the above example)
# * <tt>inheritance_column</tt> - name of the column to save the model's inheritance_column value for STI. (default: versioned_type)
# * <tt>version_column</tt> - name of the column in the model that keeps the version number (default: version)
# * <tt>sequence_name</tt> - name of the custom sequence to be used by the versioned model.
# * <tt>limit</tt> - number of revisions to keep, defaults to unlimited
# * <tt>if</tt> - symbol of method to check before saving a new version. If this method returns false, a new version is not saved.
# For finer control, pass either a Proc or modify Model#version_condition_met?
#
# acts_as_versioned :if => Proc.new { |auction| !auction.expired? }
#
# or...
#
# class Auction
# def version_condition_met? # totally bypasses the <tt>:if</tt> option
# !expired?
# end
# end
#
# * <tt>if_changed</tt> - Simple way of specifying attributes that are required to be changed before saving a model. This takes
# either a symbol or array of symbols.
#
# * <tt>extend</tt> - Lets you specify a module to be mixed in both the original and versioned models. You can also just pass a block
# to create an anonymous mixin:
#
# class Auction
# acts_as_versioned do
# def started?
# !started_at.nil?
# end
# end
# end
#
# or...
#
# module AuctionExtension
# def started?
# !started_at.nil?
# end
# end
# class Auction
# acts_as_versioned :extend => AuctionExtension
# end
#
# Example code:
#
# @auction = Auction.find(1)
# @auction.started?
# @auction.versions.first.started?
#
# == Database Schema
#
# The model that you're versioning needs to have a 'version' attribute. The model is versioned
# into a table called #{model}_versions where the model name is singlular. The _versions table should
# contain all the fields you want versioned, the same version column, and a #{model}_id foreign key field.
#
# A lock_version field is also accepted if your model uses Optimistic Locking. If your table uses Single Table inheritance,
# then that field is reflected in the versioned model as 'versioned_type' by default.
#
# Acts_as_versioned comes prepared with the ActiveRecord::Acts::Versioned::ActMethods::ClassMethods#create_versioned_table
# method, perfect for a migration. It will also create the version column if the main model does not already have it.
#
# class AddVersions < ActiveRecord::Migration
# def self.up
# # create_versioned_table takes the same options hash
# # that create_table does
# Post.create_versioned_table
# end
#
# def self.down
# Post.drop_versioned_table
# end
# end
#
# == Changing What Fields Are Versioned
#
# By default, acts_as_versioned will version all but these fields:
#
# [self.primary_key, inheritance_column, 'version', 'lock_version', versioned_inheritance_column]
#
# You can add or change those by modifying #non_versioned_columns. Note that this takes strings and not symbols.
#
# class Post < ActiveRecord::Base
# acts_as_versioned
# self.non_versioned_columns << 'comments_count'
# end
#
def acts_as_versioned(options = {}, &extension)
# don't allow multiple calls
return if self.included_modules.include?(ActiveRecord::Acts::Versioned::ActMethods)
send :include, ActiveRecord::Acts::Versioned::ActMethods
cattr_accessor :versioned_class_name, :versioned_foreign_key, :versioned_table_name, :versioned_inheritance_column,
:version_column, :max_version_limit, :track_altered_attributes, :version_condition, :version_sequence_name, :non_versioned_columns,
:version_association_options, :version_if_changed
self.versioned_class_name = options[:class_name] || "Version"
self.versioned_foreign_key = options[:foreign_key] || self.to_s.foreign_key
self.versioned_table_name = options[:table_name] || "#{table_name_prefix}#{base_class.name.demodulize.underscore}_versions#{table_name_suffix}"
self.versioned_inheritance_column = options[:inheritance_column] || "versioned_#{inheritance_column}"
self.version_column = options[:version_column] || 'version'
self.version_sequence_name = options[:sequence_name]
self.max_version_limit = options[:limit].to_i
self.version_condition = options[:if] || true
self.non_versioned_columns = [self.primary_key, inheritance_column, self.version_column, 'lock_version', versioned_inheritance_column] + options[:non_versioned_columns].to_a.map(&:to_s)
self.version_association_options = {
:class_name => "#{self.to_s}::#{versioned_class_name}",
:foreign_key => versioned_foreign_key,
:dependent => :delete_all
}.merge(options[:association_options] || {})
if block_given?
extension_module_name = "#{versioned_class_name}Extension"
silence_warnings do
self.const_set(extension_module_name, Module.new(&extension))
end
options[:extend] = self.const_get(extension_module_name)
end
class_eval <<-CLASS_METHODS
has_many :versions, version_association_options do
# finds earliest version of this record
def earliest
@earliest ||= find(:first, :order => '#{version_column}')
end
# find latest version of this record
def latest
@latest ||= find(:first, :order => '#{version_column} desc')
end
end
before_save :set_new_version
after_save :save_version
after_save :clear_old_versions
unless options[:if_changed].nil?
self.track_altered_attributes = true
options[:if_changed] = [options[:if_changed]] unless options[:if_changed].is_a?(Array)
self.version_if_changed = options[:if_changed].map(&:to_s)
end
include options[:extend] if options[:extend].is_a?(Module)
CLASS_METHODS
# create the dynamic versioned model
const_set(versioned_class_name, Class.new(ActiveRecord::Base)).class_eval do
def self.reloadable? ; false ; end
# find first version before the given version
def self.before(version)
find :first, :order => 'version desc',
:conditions => ["#{original_class.versioned_foreign_key} = ? and version < ?", version.send(original_class.versioned_foreign_key), version.version]
end
# find first version after the given version.
def self.after(version)
find :first, :order => 'version',
:conditions => ["#{original_class.versioned_foreign_key} = ? and version > ?", version.send(original_class.versioned_foreign_key), version.version]
end
def previous
self.class.before(self)
end
def next
self.class.after(self)
end
def versions_count
page.version
end
end
versioned_class.cattr_accessor :original_class
versioned_class.original_class = self
versioned_class.set_table_name versioned_table_name
versioned_class.belongs_to self.to_s.demodulize.underscore.to_sym,
:class_name => "::#{self.to_s}",
:foreign_key => versioned_foreign_key
versioned_class.send :include, options[:extend] if options[:extend].is_a?(Module)
versioned_class.set_sequence_name version_sequence_name if version_sequence_name
end
end
module ActMethods
def self.included(base) # :nodoc:
base.extend ClassMethods
end
# Saves a version of the model in the versioned table. This is called in the after_save callback by default
def save_version
if @saving_version
@saving_version = nil
rev = self.class.versioned_class.new
clone_versioned_model(self, rev)
rev.send("#{self.class.version_column}=", send(self.class.version_column))
rev.send("#{self.class.versioned_foreign_key}=", id)
rev.save
end
end
# Clears old revisions if a limit is set with the :limit option in <tt>acts_as_versioned</tt>.
# Override this method to set your own criteria for clearing old versions.
def clear_old_versions
return if self.class.max_version_limit == 0
excess_baggage = send(self.class.version_column).to_i - self.class.max_version_limit
if excess_baggage > 0
self.class.versioned_class.delete_all ["#{self.class.version_column} <= ? and #{self.class.versioned_foreign_key} = ?", excess_baggage, id]
end
end
# Reverts a model to a given version. Takes either a version number or an instance of the versioned model
def revert_to(version)
if version.is_a?(self.class.versioned_class)
return false unless version.send(self.class.versioned_foreign_key) == id and !version.new_record?
else
return false unless version = versions.send("find_by_#{self.class.version_column}", version)
end
self.clone_versioned_model(version, self)
send("#{self.class.version_column}=", version.send(self.class.version_column))
true
end
# Reverts a model to a given version and saves the model.
# Takes either a version number or an instance of the versioned model
def revert_to!(version)
revert_to(version) ? save_without_revision : false
end
# Temporarily turns off Optimistic Locking while saving. Used when reverting so that a new version is not created.
def save_without_revision
save_without_revision!
true
rescue
false
end
def save_without_revision!
without_locking do
without_revision do
save!
end
end
end
def altered?
track_altered_attributes ? (version_if_changed - changed).length < version_if_changed.length : changed?
end
# Clones a model. Used when saving a new version or reverting a model's version.
def clone_versioned_model(orig_model, new_model)
self.class.versioned_columns.each do |col|
new_model.send("#{col.name}=", orig_model.send(col.name)) if orig_model.has_attribute?(col.name)
end
if orig_model.is_a?(self.class.versioned_class)
new_model[new_model.class.inheritance_column] = orig_model[self.class.versioned_inheritance_column]
elsif new_model.is_a?(self.class.versioned_class)
new_model[self.class.versioned_inheritance_column] = orig_model[orig_model.class.inheritance_column]
end
end
# Checks whether a new version shall be saved or not. Calls <tt>version_condition_met?</tt> and <tt>changed?</tt>.
def save_version?
version_condition_met? && altered?
end
# Checks condition set in the :if option to check whether a revision should be created or not. Override this for
# custom version condition checking.
def version_condition_met?
case
when version_condition.is_a?(Symbol)
send(version_condition)
when version_condition.respond_to?(:call) && (version_condition.arity == 1 || version_condition.arity == -1)
version_condition.call(self)
else
version_condition
end
end
# Executes the block with the versioning callbacks disabled.
#
# @foo.without_revision do
# @foo.save
# end
#
def without_revision(&block)
self.class.without_revision(&block)
end
# Turns off optimistic locking for the duration of the block
#
# @foo.without_locking do
# @foo.save
# end
#
def without_locking(&block)
self.class.without_locking(&block)
end
def empty_callback() end #:nodoc:
protected
# sets the new version before saving, unless you're using optimistic locking. In that case, let it take care of the version.
def set_new_version
@saving_version = new_record? || save_version?
self.send("#{self.class.version_column}=", next_version) if new_record? || (!locking_enabled? && save_version?)
end
# Gets the next available version for the current record, or 1 for a new record
def next_version
(new_record? ? 0 : versions.calculate(:max, version_column).to_i) + 1
end
module ClassMethods
# Returns an array of columns that are versioned. See non_versioned_columns
def versioned_columns
@versioned_columns ||= columns.select { |c| !non_versioned_columns.include?(c.name) }
end
# Returns an instance of the dynamic versioned model
def versioned_class
const_get versioned_class_name
end
# Rake migration task to create the versioned table using options passed to acts_as_versioned
def create_versioned_table(create_table_options = {})
# create version column in main table if it does not exist
if !self.content_columns.find { |c| [version_column.to_s, 'lock_version'].include? c.name }
self.connection.add_column table_name, version_column, :integer
self.reset_column_information
end
return if connection.table_exists?(versioned_table_name)
self.connection.create_table(versioned_table_name, create_table_options) do |t|
t.column versioned_foreign_key, :integer
t.column version_column, :integer
end
self.versioned_columns.each do |col|
self.connection.add_column versioned_table_name, col.name, col.type,
:limit => col.limit,
:default => col.default,
:scale => col.scale,
:precision => col.precision
end
if type_col = self.columns_hash[inheritance_column]
self.connection.add_column versioned_table_name, versioned_inheritance_column, type_col.type,
:limit => type_col.limit,
:default => type_col.default,
:scale => type_col.scale,
:precision => type_col.precision
end
self.connection.add_index versioned_table_name, versioned_foreign_key
end
# Rake migration task to drop the versioned table
def drop_versioned_table
self.connection.drop_table versioned_table_name
end
# Executes the block with the versioning callbacks disabled.
#
# Foo.without_revision do
# @foo.save
# end
#
def without_revision(&block)
class_eval do
CALLBACKS.each do |attr_name|
alias_method "orig_#{attr_name}".to_sym, attr_name
alias_method attr_name, :empty_callback
end
end
block.call
ensure
class_eval do
CALLBACKS.each do |attr_name|
alias_method attr_name, "orig_#{attr_name}".to_sym
end
end
end
# Turns off optimistic locking for the duration of the block
#
# Foo.without_locking do
# @foo.save
# end
#
def without_locking(&block)
current = ActiveRecord::Base.lock_optimistically
ActiveRecord::Base.lock_optimistically = false if current
begin
block.call
ensure
ActiveRecord::Base.lock_optimistically = true if current
end
end
end
end
end
end
end
ActiveRecord::Base.send :include, ActiveRecord::Acts::Versioned

Some files were not shown because too many files have changed in this diff Show more