anise 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.ruby +41 -0
- data/APACHE2.txt +204 -0
- data/COPYING.rdoc +17 -0
- data/HISTORY.rdoc +37 -0
- data/{README → README.rdoc} +31 -15
- data/VERSION +1 -1
- data/lib/anise.rb +37 -15
- data/lib/anise.yml +41 -0
- data/lib/anise/annotation.rb +94 -108
- data/lib/anise/annotator.rb +42 -46
- data/lib/anise/attribute.rb +58 -107
- data/lib/anise/module.rb +19 -0
- data/{spec/01_annotations.rd → qed/01_annotations.qed} +1 -0
- data/{spec/04_callbacks.rd → qed/02_annotation_added.rdoc} +1 -1
- data/qed/03_attributes.rdoc +16 -0
- data/qed/04_annotator.rdoc +49 -0
- data/qed/applique/ae.rb +1 -0
- data/qed/toplevel/01_annotations.qed +30 -0
- data/qed/toplevel/03_attributes.rdoc +20 -0
- data/test/test_anise.rb +4 -6
- data/test/test_anise_toplevel.rb +4 -6
- data/test/test_annotator.rb +3 -5
- data/test/test_annotator_toplevel.rb +3 -5
- data/test/test_attribute.rb +1 -1
- data/test/test_attribute_toplevel.rb +2 -2
- metadata +78 -67
- data/COPYING +0 -166
- data/HISTORY +0 -22
- data/MANIFEST +0 -35
- data/RELEASE +0 -13
- data/meta/abstract +0 -1
- data/meta/authors +0 -1
- data/meta/contact +0 -1
- data/meta/created +0 -1
- data/meta/homepage +0 -1
- data/meta/license +0 -1
- data/meta/require +0 -1
- data/meta/summary +0 -1
data/lib/anise.yml
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
---
|
2
|
+
spec_version: 1.0.0
|
3
|
+
replaces: []
|
4
|
+
|
5
|
+
loadpath:
|
6
|
+
- lib
|
7
|
+
name: anise
|
8
|
+
repositories: {}
|
9
|
+
|
10
|
+
conflicts: []
|
11
|
+
|
12
|
+
engine_check: []
|
13
|
+
|
14
|
+
title: Anise
|
15
|
+
contact: trans <transfire@gmail.com>
|
16
|
+
resources:
|
17
|
+
code: http://github.com/rubyworks/anise
|
18
|
+
mail: http://groups.google.com/group/rubyworks-mailinglist
|
19
|
+
home: http://rubyworks.github.com/anise
|
20
|
+
maintainers: []
|
21
|
+
|
22
|
+
requires:
|
23
|
+
- group:
|
24
|
+
- test
|
25
|
+
name: qed
|
26
|
+
version: 0+
|
27
|
+
- group:
|
28
|
+
- build
|
29
|
+
name: syckle
|
30
|
+
version: 0+
|
31
|
+
manifest: MANIFEST
|
32
|
+
version: 0.5.0
|
33
|
+
licenses:
|
34
|
+
- Apache 2.0
|
35
|
+
copyright: Copyright (c) 2008 Thomas Sawyer
|
36
|
+
authors:
|
37
|
+
- Thomas Sawyer
|
38
|
+
organization: Rubyworks
|
39
|
+
description: Anise is an Annotation System for the Ruby programming language. Unlike most other annotations systems it is not a comment-based or macro-based system that sits over-and-above the rest of the code. Rather, Anise is a dynamic annotations system operating at runtime.
|
40
|
+
summary: Dynamic Annotation System
|
41
|
+
created: "2008-02-21"
|
data/lib/anise/annotation.rb
CHANGED
@@ -1,14 +1,11 @@
|
|
1
1
|
module Anise
|
2
2
|
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# They are simply data. But you can put them to use. For instance
|
10
|
-
# an attribute validator might check for an annotation called
|
11
|
-
# :valid and test against it.
|
3
|
+
# The Annotate module is the core of the Anise system. It provides the
|
4
|
+
# framework for annotating class or module related objects, typically
|
5
|
+
# symbols representing methods, with arbitrary metadata. These annotations
|
6
|
+
# do not do anything in themselves. They are simply data. But they can be
|
7
|
+
# put to good use. For instance an attribute validator might check for an
|
8
|
+
# annotation called :valid and test against it.
|
12
9
|
#
|
13
10
|
# == Synopsis
|
14
11
|
#
|
@@ -48,12 +45,12 @@ module Anise
|
|
48
45
|
# Or, we could even annotate the class itself.
|
49
46
|
#
|
50
47
|
# class X
|
51
|
-
# include Anise::
|
48
|
+
# include Anise::Annotate
|
52
49
|
#
|
53
50
|
# ann self, :valid => lambda{ |x| x.is_a?(Enumerable) }
|
54
51
|
# end
|
55
52
|
#
|
56
|
-
#
|
53
|
+
# Although annotations are arbitrary they are tied to the class or
|
57
54
|
# module they are defined against.
|
58
55
|
#
|
59
56
|
#--
|
@@ -66,125 +63,114 @@ module Anise
|
|
66
63
|
#
|
67
64
|
# TODO: The ann(x).name notation is kind of nice. Would like to add that
|
68
65
|
# back-in if reasonable. This would require @annotations to be an
|
69
|
-
# OpenHash or OpenObject rather than just a Hash.
|
66
|
+
# OpenHash or OpenObject rather than just a Hash though.
|
70
67
|
#++
|
71
68
|
module Annotation
|
72
69
|
|
70
|
+
#
|
71
|
+
|
73
72
|
def self.append_features(base)
|
74
|
-
|
75
|
-
|
76
|
-
elsif base == ::Module
|
77
|
-
unless ::Module < Annotation
|
78
|
-
super
|
79
|
-
end
|
80
|
-
else
|
81
|
-
base.extend self
|
82
|
-
end
|
73
|
+
base.extend ClassMethods
|
74
|
+
super(base)
|
83
75
|
end
|
84
76
|
|
85
|
-
#
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
77
|
+
# Anise::Annotations Domain Language.
|
78
|
+
|
79
|
+
module ClassMethods
|
80
|
+
|
81
|
+
# Lookup an annotation. Unlike +annotations[ref]+
|
82
|
+
# this provides a complete annotation <i>heritage</i>,
|
83
|
+
# pulling annotations of the same reference name
|
84
|
+
# from ancestor classes and modules.
|
85
|
+
|
86
|
+
def annotation(ref=nil)
|
87
|
+
return(@annotations ||= {}) if ref.nil?
|
88
|
+
|
89
|
+
ref = ref.to_sym
|
90
|
+
ann = {}
|
91
|
+
ancestors.reverse_each do |anc|
|
92
|
+
next unless anc < Annotation
|
93
|
+
if h = anc.annotations[ref]
|
94
|
+
ann.merge!(h)
|
95
|
+
end
|
100
96
|
end
|
97
|
+
return ann
|
101
98
|
end
|
102
|
-
return ann
|
103
|
-
#ancs = ancestors.select{ |a| a.is_a?(Annotations) }
|
104
|
-
#ancs.inject({}) do |memo, ancestor|
|
105
|
-
# ancestor.annotations[ref] ||= {}
|
106
|
-
# ancestor.annotations[ref].merge(memo)
|
107
|
-
#end
|
108
|
-
end
|
109
99
|
|
110
|
-
|
111
|
-
alias_method :annotations, :annotation
|
100
|
+
# Plural alias for #annotation.
|
112
101
|
|
113
|
-
|
114
|
-
#
|
115
|
-
#def annotations
|
116
|
-
# #$annotations[self]
|
117
|
-
# @annotations ||= {}
|
118
|
-
#end
|
102
|
+
alias_method :annotations, :annotation
|
119
103
|
|
120
|
-
|
121
|
-
#
|
122
|
-
def ann(ref, keys_or_class=nil, keys=nil)
|
123
|
-
return annotation(ref) unless keys_or_class or keys
|
124
|
-
|
125
|
-
if Class === keys_or_class
|
126
|
-
keys ||= {}
|
127
|
-
keys[:class] = keys_or_class
|
128
|
-
else
|
129
|
-
keys = keys_or_class
|
130
|
-
end
|
104
|
+
# Set or read annotations.
|
131
105
|
|
132
|
-
|
133
|
-
ref
|
134
|
-
keys = keys.inject({}){ |h,(k,v)| h[k.to_sym] = v; h} #rekey
|
135
|
-
annotations[ref] ||= {}
|
136
|
-
annotations[ref].update(keys)
|
137
|
-
# callback
|
138
|
-
annotation_added(ref)
|
139
|
-
else
|
140
|
-
key = keys.to_sym
|
141
|
-
annotation(ref)[key]
|
142
|
-
end
|
143
|
-
end
|
106
|
+
def ann(ref, keys_or_class=nil, keys=nil)
|
107
|
+
return annotation(ref) unless keys_or_class or keys
|
144
108
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
unless keys_or_class or keys
|
152
|
-
return annotations[ref] ||= {}
|
153
|
-
end
|
109
|
+
if Class === keys_or_class
|
110
|
+
keys ||= {}
|
111
|
+
keys[:class] = keys_or_class
|
112
|
+
else
|
113
|
+
keys = keys_or_class
|
114
|
+
end
|
154
115
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
116
|
+
if Hash === keys
|
117
|
+
ref = ref.to_sym
|
118
|
+
keys = keys.inject({}){ |h,(k,v)| h[k.to_sym] = v; h} #rekey
|
119
|
+
annotations[ref] ||= {}
|
120
|
+
annotations[ref].update(keys)
|
121
|
+
# callback
|
122
|
+
annotation_added(ref) #if method_defined?(:annotation_added)
|
123
|
+
else
|
124
|
+
key = keys.to_sym
|
125
|
+
annotation(ref)[key]
|
126
|
+
end
|
160
127
|
end
|
161
128
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
#
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
129
|
+
# To change an annotation's value in place for a given class or module
|
130
|
+
# it first must be duplicated, otherwise the change may effect annotations
|
131
|
+
# in the class or module's ancestors.
|
132
|
+
|
133
|
+
def ann!(ref, keys_or_class=nil, keys=nil)
|
134
|
+
#return annotation(ref) unless keys_or_class or keys
|
135
|
+
unless keys_or_class or keys
|
136
|
+
return annotations[ref] ||= {}
|
137
|
+
end
|
138
|
+
|
139
|
+
if Class === keys_or_class
|
140
|
+
keys ||= {}
|
141
|
+
keys[:class] = keys_or_class
|
142
|
+
else
|
143
|
+
keys = keys_or_class
|
144
|
+
end
|
145
|
+
|
146
|
+
if Hash === keys
|
147
|
+
ref = ref.to_sym
|
148
|
+
keys = keys.inject({}){ |h,(k,v)| h[k.to_sym] = v; h} #rekey
|
149
|
+
annotations[ref] ||= {}
|
150
|
+
annotations[ref].update(keys)
|
151
|
+
# callback
|
152
|
+
annotation_added(ref) #if method_defined?(:annotation_added)
|
153
|
+
else
|
154
|
+
key = keys.to_sym
|
155
|
+
annotations[ref] ||= {}
|
156
|
+
begin
|
157
|
+
annotations[ref][key] = annotation(ref)[key].dup
|
158
|
+
rescue TypeError
|
159
|
+
annotations[ref][key] = annotation(ref)[key]
|
160
|
+
end
|
176
161
|
end
|
177
162
|
end
|
178
|
-
end
|
179
163
|
|
180
|
-
|
181
|
-
|
182
|
-
|
164
|
+
# Callback method. This method is called for each new annotation.
|
165
|
+
|
166
|
+
def annotation_added(name)
|
167
|
+
super if defined?(super)
|
168
|
+
end
|
169
|
+
|
183
170
|
end
|
184
171
|
|
185
172
|
end
|
186
173
|
|
187
174
|
end
|
188
175
|
|
189
|
-
# 2006-11-07
|
190
|
-
# Copyright (c) 2005, 2008 TigerOps
|
176
|
+
# Copyright (c) 2006-11-07 Thomas Sawyer
|
data/lib/anise/annotator.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
module Anise
|
2
|
-
require 'anise/annotation
|
2
|
+
require 'anise/annotation'
|
3
3
|
|
4
4
|
# = Annotator
|
5
5
|
#
|
6
|
-
# Annotator allows for the creation of
|
7
|
-
#
|
6
|
+
# The Annotator module allows for the creation of <i>method annotations</i>
|
7
|
+
# which attach to the next method defined.
|
8
|
+
#
|
9
|
+
# This idiom of annotator-before-definition was popularized by
|
10
|
+
# Rake's desc/task pair. The Annotator module makes it very easy
|
11
|
+
# to add similar capabilites to any program.
|
8
12
|
#
|
9
13
|
# require 'anise/annotator'
|
10
14
|
#
|
@@ -22,66 +26,58 @@ module Anise
|
|
22
26
|
#
|
23
27
|
# X.ann(:see, :doc) #=> "See what I mean?"
|
24
28
|
#
|
25
|
-
#
|
26
|
-
# Rake's desc/task pair. Annotator makes it very easy to add
|
27
|
-
# similar capabilites to any program.
|
28
|
-
#
|
29
|
-
# The library uses the #method_added callback, so be sure to
|
29
|
+
# Note that the library uses the #method_added callback, so be sure to
|
30
30
|
# respect good practices of calling +super+ if you need to override
|
31
31
|
# this method while using Annotator.
|
32
32
|
#
|
33
|
-
|
33
|
+
#--
|
34
|
+
# TODO: Allow annotators to be inherited via module mixins.
|
34
35
|
#
|
36
|
+
# TODO: Ensure thread-safety of <code>@_pending_annotations</code> variable.
|
37
|
+
#++
|
35
38
|
module Annotator
|
36
39
|
|
40
|
+
#
|
37
41
|
def self.append_features(base)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
52
|
-
@pending_annotations = []
|
53
|
-
#super if defined?(super)
|
54
|
-
end
|
42
|
+
Annotation.append_features(base) #unless base.is_a?(Annotation)
|
43
|
+
base.extend ClassMethods
|
44
|
+
super(base)
|
45
|
+
end
|
46
|
+
|
47
|
+
module ClassMethods
|
48
|
+
|
49
|
+
# Define an annotator.
|
50
|
+
def annotator(name, &block)
|
51
|
+
(class << self; self; end).module_eval do
|
52
|
+
define_method(name) do |*args|
|
53
|
+
@_pending_annotations ||= []
|
54
|
+
@_pending_annotations << [name, args, block]
|
55
55
|
end
|
56
|
-
super
|
57
56
|
end
|
58
|
-
else
|
59
|
-
base.extend Annotation #unless base.is_a?(Annotation)
|
60
|
-
base.extend self
|
61
57
|
end
|
62
|
-
end
|
63
58
|
|
64
|
-
|
65
|
-
(
|
66
|
-
|
67
|
-
|
68
|
-
|
59
|
+
# When a method is added, run all pending annotations.
|
60
|
+
def method_added(sym)
|
61
|
+
@_pending_annotations ||= []
|
62
|
+
@_pending_annotations.each do |name, args, block|
|
63
|
+
if block
|
64
|
+
block.call(sym, *args)
|
65
|
+
else
|
66
|
+
if args.size == 1
|
67
|
+
ann(sym, name=>args.first)
|
68
|
+
else
|
69
|
+
ann(sym, name=>args)
|
70
|
+
end
|
71
|
+
end
|
69
72
|
end
|
73
|
+
@_pending_annotations = []
|
74
|
+
super if defined?(super)
|
70
75
|
end
|
71
|
-
end
|
72
76
|
|
73
|
-
def method_added(sym)
|
74
|
-
@pending_annotations ||= []
|
75
|
-
@pending_annotations.each do |name, args|
|
76
|
-
ann sym, name => args
|
77
|
-
end
|
78
|
-
@pending_annotations = []
|
79
|
-
super if defined?(super)
|
80
77
|
end
|
81
78
|
|
82
79
|
end
|
83
80
|
|
84
81
|
end
|
85
82
|
|
86
|
-
# Copyright (c) 2005,
|
87
|
-
|
83
|
+
# Copyright (c) 2005,2011 Thomas Sawyer
|
data/lib/anise/attribute.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
#require 'facets/inheritor' # remove dependency
|
2
|
-
|
3
1
|
module Anise
|
2
|
+
#require 'facets/inheritor' # removed dependency
|
4
3
|
require 'anise/annotation'
|
4
|
+
require 'anise/module'
|
5
5
|
|
6
6
|
# = Annotated Attributes
|
7
7
|
#
|
@@ -22,35 +22,20 @@ module Anise
|
|
22
22
|
# See annotation.rb for more information.
|
23
23
|
#
|
24
24
|
# NOTE: This library was designed to be backward compatible with
|
25
|
-
#
|
25
|
+
# the standard versions of the same methods.
|
26
26
|
#
|
27
27
|
module Attribute
|
28
28
|
|
29
|
+
#
|
29
30
|
def self.append_features(base)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
annotatable_attribute_method_for_module(:attr)
|
39
|
-
annotatable_attribute_method_for_module(:attr_reader)
|
40
|
-
annotatable_attribute_method_for_module(:attr_writer)
|
41
|
-
annotatable_attribute_method_for_module(:attr_accessor)
|
42
|
-
annotatable_attribute_method_for_module(:attr_setter) if defined?(attr_setter)
|
43
|
-
end
|
44
|
-
else
|
45
|
-
base.extend Annotation
|
46
|
-
base.extend Attribute
|
47
|
-
base = (class << base; self; end)
|
48
|
-
#inheritor :instance_attributes, [], :|
|
49
|
-
annotatable_attribute_method(base, :attr)
|
50
|
-
annotatable_attribute_method(base, :attr_reader)
|
51
|
-
annotatable_attribute_method(base, :attr_writer)
|
52
|
-
annotatable_attribute_method(base, :attr_accessor)
|
53
|
-
annotatable_attribute_method(base, :attr_setter) if defined?(attr_setter)
|
31
|
+
super(base)
|
32
|
+
Annotation.append_features(base)
|
33
|
+
base.extend ClassMethods #Attribute
|
34
|
+
|
35
|
+
#inheritor :instance_attributes, [], :|
|
36
|
+
base_class = (class << base; self; end)
|
37
|
+
base_class.attribute_methods.each do |attr_method|
|
38
|
+
annotatable_attribute_method(base_class, attr_method)
|
54
39
|
end
|
55
40
|
end
|
56
41
|
|
@@ -90,99 +75,65 @@ module Anise
|
|
90
75
|
end
|
91
76
|
end
|
92
77
|
|
93
|
-
#
|
94
|
-
|
95
|
-
::Module.module_eval do
|
96
|
-
alias_method "__#{attr_method_name}", attr_method_name
|
97
|
-
|
98
|
-
define_method(attr_method_name) do |*args|
|
99
|
-
|
100
|
-
args.flatten!
|
101
|
-
|
102
|
-
harg={}; while args.last.is_a?(Hash)
|
103
|
-
harg.update(args.pop)
|
104
|
-
end
|
105
|
-
|
106
|
-
raise ArgumentError if args.empty? and harg.empty?
|
107
|
-
|
108
|
-
if args.empty? # hash mode
|
109
|
-
harg.each { |a,h| __send__(attr_method_name,a,h) }
|
110
|
-
else
|
111
|
-
klass = harg[:class] = args.pop if args.last.is_a?(Class)
|
112
|
-
|
113
|
-
__send__("__#{attr_method_name}", *args)
|
114
|
-
|
115
|
-
args.each{|a| ann(a.to_sym,harg)}
|
116
|
-
|
117
|
-
instance_attributes!.concat(args) #merge!
|
118
|
-
|
119
|
-
# Use this callback to customize for your needs.
|
120
|
-
if respond_to?(:attr_callback)
|
121
|
-
attr_callback(self, args, harg)
|
122
|
-
end
|
78
|
+
# Anise::Attributes Doman Language.
|
79
|
+
module ClassMethods
|
123
80
|
|
124
|
-
|
125
|
-
|
81
|
+
# Instance attributes, including inherited attributes.
|
82
|
+
def instance_attributes
|
83
|
+
a = []
|
84
|
+
ancestors.each do |anc|
|
85
|
+
next unless anc < Attribute
|
86
|
+
if x = anc.instance_attributes!
|
87
|
+
a |= x
|
126
88
|
end
|
127
89
|
end
|
90
|
+
return a
|
128
91
|
end
|
129
|
-
end
|
130
92
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
ancestors.each do |anc|
|
135
|
-
next unless anc.is_a?(Attribute)
|
136
|
-
if x = anc.instance_attributes!
|
137
|
-
a |= x
|
138
|
-
end
|
93
|
+
# Local instance attributes.
|
94
|
+
def instance_attributes!
|
95
|
+
@instance_attributes ||= []
|
139
96
|
end
|
140
|
-
return a
|
141
|
-
end
|
142
97
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
#
|
159
|
-
def classified_attributes
|
160
|
-
instance_attributes.find_all do |a|
|
161
|
-
self.ann(a, :class)
|
98
|
+
# Return list of attributes that have a :class annotation.
|
99
|
+
#
|
100
|
+
# class MyClass
|
101
|
+
# attr_accessor :test
|
102
|
+
# attr_accessor :name, String, :doc => 'Hello'
|
103
|
+
# attr_accessor :age, Fixnum
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# MyClass.instance_attributes # => [:test, :name, :age, :body]
|
107
|
+
# MyClass.classified_attributes # => [:name, :age]
|
108
|
+
#
|
109
|
+
def classified_attributes
|
110
|
+
instance_attributes.find_all do |a|
|
111
|
+
self.ann(a, :class)
|
112
|
+
end
|
162
113
|
end
|
163
|
-
end
|
164
114
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
115
|
+
# This define a simple adjustment to #attr to allow it to handle the boolean argument and
|
116
|
+
# to be able to accept attributes. It's backward compatible and is not needed for Ruby 1.9
|
117
|
+
# which gets rid of the secondary argument.
|
118
|
+
#
|
119
|
+
def attr(*args)
|
120
|
+
args.flatten!
|
121
|
+
case args.last
|
122
|
+
when TrueClass
|
123
|
+
args.pop
|
124
|
+
attr_accessor(*args)
|
125
|
+
when FalseClass, NilClass
|
126
|
+
args.pop
|
127
|
+
attr_reader(*args)
|
128
|
+
else
|
129
|
+
attr_reader(*args)
|
130
|
+
end
|
180
131
|
end
|
132
|
+
|
181
133
|
end
|
182
134
|
|
183
135
|
end
|
184
136
|
|
185
137
|
end
|
186
138
|
|
187
|
-
# Copyright (c) 2005, 2008
|
188
|
-
|
139
|
+
# Copyright (c) 2005, 2008 Thomas Sawyer
|