kindergarten 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +19 -0
- data/.rspec +1 -0
- data/.travis.yml +12 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +89 -0
- data/Rakefile +7 -0
- data/TODO.md +6 -0
- data/kindergarten.gemspec +25 -0
- data/lib/kindergarten.rb +17 -0
- data/lib/kindergarten/exceptions.rb +19 -0
- data/lib/kindergarten/governesses.rb +19 -0
- data/lib/kindergarten/governesses/easy_governess.rb +11 -0
- data/lib/kindergarten/governesses/head_governess.rb +144 -0
- data/lib/kindergarten/governesses/strict_governess.rb +48 -0
- data/lib/kindergarten/orm/active_record.rb +42 -0
- data/lib/kindergarten/orm/governess.rb +41 -0
- data/lib/kindergarten/perimeter.rb +129 -0
- data/lib/kindergarten/sandbox.rb +76 -0
- data/lib/kindergarten/version.rb +3 -0
- data/spec/kindergarten/governess_spec.rb +67 -0
- data/spec/kindergarten/perimeter_spec.rb +112 -0
- data/spec/kindergarten/sandbox_spec.rb +52 -0
- data/spec/kindergarten_spec.rb +12 -0
- data/spec/orm/active_record_spec.rb +125 -0
- data/spec/orm_helper.rb +15 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/support/01_rspec_helper.rb +9 -0
- data/spec/support/db/.gitkeep +0 -0
- data/spec/support/dining_perimeter.rb +14 -0
- data/spec/support/drinking_perimeter.rb +39 -0
- data/spec/support/log/.gitkeep +0 -0
- data/spec/support/puppet_perimeter.rb +19 -0
- data/spec/support/spec_perimeter.rb +35 -0
- metadata +146 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Kindergarten::Sandbox do
|
|
4
|
+
it "should allow an instance for any child" do
|
|
5
|
+
expect {
|
|
6
|
+
Kindergarten::Sandbox.new(:any)
|
|
7
|
+
}.to_not raise_error
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "should know it's child" do
|
|
11
|
+
sandbox = Kindergarten::Sandbox.new(:its)
|
|
12
|
+
sandbox.child.should eq :its
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should create an empty governess for the object" do
|
|
16
|
+
sandbox = Kindergarten::Sandbox.new(:object)
|
|
17
|
+
sandbox.governess.should be_kind_of(Kindergarten::HeadGoverness)
|
|
18
|
+
sandbox.governess.should be_empty
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should define an empty perimeter" do
|
|
22
|
+
sandbox = Kindergarten::Sandbox.new(:child)
|
|
23
|
+
sandbox.perimeter.should be_empty
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should provide a extend_perimeter function" do
|
|
27
|
+
sandbox = Kindergarten::Sandbox.new(:child)
|
|
28
|
+
sandbox.should respond_to(:extend_perimeter)
|
|
29
|
+
|
|
30
|
+
expect {
|
|
31
|
+
sandbox.extend_perimeter(SpecPerimeter)
|
|
32
|
+
}.to change {
|
|
33
|
+
sandbox.perimeter.empty?
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe :HeadGoverness do
|
|
38
|
+
before(:each) do
|
|
39
|
+
@sandbox = Kindergarten::Sandbox.new(:child)
|
|
40
|
+
@sandbox.extend_perimeter(SpecPerimeter, PuppetPerimeter)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should tell the outside what is allowed" do
|
|
44
|
+
@sandbox.should be_allowed(:view, "string")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should know the rules accross perimeters" do
|
|
48
|
+
puppet = @sandbox.grab_puppet
|
|
49
|
+
@sandbox.should be_disallowed(:bbq, puppet)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Kindergarten do
|
|
4
|
+
it "should have a version constant" do
|
|
5
|
+
Kindergarten::VERSION.should_not be_nil
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it "should have a sandbox method" do
|
|
9
|
+
res = Kindergarten.sandbox("string")
|
|
10
|
+
res.should be_kind_of(Kindergarten::Sandbox)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'orm_helper'
|
|
3
|
+
|
|
4
|
+
class Migration < ActiveRecord::Migration
|
|
5
|
+
def up
|
|
6
|
+
create_table :bars do |t|
|
|
7
|
+
t.string :name
|
|
8
|
+
t.text :description
|
|
9
|
+
t.string :city
|
|
10
|
+
t.string :street
|
|
11
|
+
t.string :type
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
create_table :restaurants do |t|
|
|
15
|
+
t.string :name
|
|
16
|
+
t.text :description
|
|
17
|
+
t.integer :stars
|
|
18
|
+
t.decimal :avg_meal_price, :precision => 5, :scale => 2
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class Bar < ActiveRecord::Base
|
|
24
|
+
include Kindergarten::ORM::Governess
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class Joint < Bar
|
|
28
|
+
# for STI tests
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class Restaurant < ActiveRecord::Base
|
|
32
|
+
include Kindergarten::ORM::Governess
|
|
33
|
+
|
|
34
|
+
force_rinsed
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe Kindergarten::ORM::ActiveRecord do
|
|
38
|
+
before(:all) do
|
|
39
|
+
begin
|
|
40
|
+
mig = Migration.new
|
|
41
|
+
mig.suppress_messages do
|
|
42
|
+
mig.up
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
rescue => ex
|
|
46
|
+
unless ex.message =~ /already exists/
|
|
47
|
+
$stderr.puts "!!! #{ex.class}: #{ex.message}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe :Drinking do
|
|
53
|
+
before(:each) do
|
|
54
|
+
@sandbox = Kindergarten.sandbox(:child)
|
|
55
|
+
@sandbox.extend_perimeter(DrinkingPerimeter)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "should create a bar" do
|
|
59
|
+
bar = nil
|
|
60
|
+
expect {
|
|
61
|
+
bar = @sandbox.create_bar( name: "foo", decription: "bar" )
|
|
62
|
+
}.to_not raise_error
|
|
63
|
+
|
|
64
|
+
bar.should be_kind_of(Bar)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "should not create a bar w/o scrubbing" do
|
|
68
|
+
expect {
|
|
69
|
+
@sandbox.create_bar_wo( name: "foo", decription: "bar" )
|
|
70
|
+
}.to raise_error(Kindergarten::ORM::Unscrubbed)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "should protect the Joint" do
|
|
74
|
+
expect {
|
|
75
|
+
@sandbox.create_joint( name: "foo", decription: "bar" )
|
|
76
|
+
}.to raise_error(Kindergarten::ORM::Unscrubbed)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "should build a clean joint" do
|
|
80
|
+
expect {
|
|
81
|
+
@sandbox.build_joint(name: "clean")
|
|
82
|
+
}.to_not raise_error(Kindergarten::ORM::Unscrubbed)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "should not build a dirty joint" do
|
|
86
|
+
expect {
|
|
87
|
+
@sandbox.build_dirty_joint
|
|
88
|
+
}.to raise_error(Kindergarten::ORM::Unscrubbed)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "should update a bar" do
|
|
92
|
+
bar = @sandbox.create_bar(name: "Whiskey Inn")
|
|
93
|
+
expect {
|
|
94
|
+
@sandbox.update_bar(bar, name: "Whiskey Out")
|
|
95
|
+
}.to_not raise_error(Kindergarten::ORM::Unscrubbed)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "should not update a dirty bar" do
|
|
99
|
+
bar = @sandbox.create_bar(name: "Whiskey Inn")
|
|
100
|
+
expect {
|
|
101
|
+
@sandbox.update_bar_dirty(bar, name: "Whiskey Out")
|
|
102
|
+
}.to raise_error(Kindergarten::ORM::Unscrubbed)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
describe :Dining do
|
|
107
|
+
before(:each) do
|
|
108
|
+
@sandbox = Kindergarten.sandbox(:child)
|
|
109
|
+
@sandbox.extend_perimeter(DiningPerimeter)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it "should not create a scrubbed restaurant" do
|
|
113
|
+
expect {
|
|
114
|
+
@sandbox.create_restaurant_scrubbed(name: "China Wok")
|
|
115
|
+
}.to raise_error(Kindergarten::ORM::Unscrubbed)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "should create a rinsed restaurant" do
|
|
119
|
+
expect {
|
|
120
|
+
@sandbox.create_restaurant(name: "Lobster Inn")
|
|
121
|
+
}.to_not raise_error(Kindergarten::ORM::Unscrubbed)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
end
|
data/spec/orm_helper.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'sqlite3'
|
|
2
|
+
require 'active_record'
|
|
3
|
+
require "kindergarten/orm/governess"
|
|
4
|
+
|
|
5
|
+
logger = Logger.new File.expand_path( "../support/log/test.log", __FILE__)
|
|
6
|
+
logger.formatter = proc { |severity, datetime, progname, msg|
|
|
7
|
+
"[#{severity}] #{msg}\n"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
ActiveRecord::Base.logger = logger
|
|
11
|
+
ActiveRecord::Base.establish_connection(
|
|
12
|
+
:adapter => 'sqlite3',
|
|
13
|
+
:database => File.expand_path( "../support/db/test.db", __FILE__),
|
|
14
|
+
)
|
|
15
|
+
|
data/spec/spec_helper.rb
ADDED
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class DiningPerimeter < Kindergarten::Perimeter
|
|
2
|
+
# should raise Unscrubbed
|
|
3
|
+
def create_restaurant_scrubbed(args)
|
|
4
|
+
Restaurant.create(scrub(args, :name, :description))
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# should work
|
|
8
|
+
def create_restaurant(args)
|
|
9
|
+
safe = rinse(args, :name => /([\w\s\-]+)/, :description => :pass)
|
|
10
|
+
Restaurant.create(safe)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
sandbox :create_restaurant_scrubbed, :create_restaurant
|
|
14
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
class DrinkingPerimeter < Kindergarten::Perimeter
|
|
2
|
+
# supposed to work
|
|
3
|
+
def create_bar(args)
|
|
4
|
+
Bar.create(scrub(args, :name, :description))
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# should raise Unscrubbed
|
|
8
|
+
def create_bar_wo(args)
|
|
9
|
+
Bar.create(args)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# should raise Unscrubbed
|
|
13
|
+
def create_joint(args)
|
|
14
|
+
Joint.create(args)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# should not raise Unscrubbed
|
|
18
|
+
def build_joint(args)
|
|
19
|
+
Joint.new(scrub(args, :name))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# should raise Unscrubbed
|
|
23
|
+
def build_dirty_joint
|
|
24
|
+
Joint.new(name: "dirty")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# should not raise Unscrubbed
|
|
28
|
+
def update_bar(bar, args)
|
|
29
|
+
bar.update_attributes(scrub(args, :name, :city, :street))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# should raise
|
|
33
|
+
def update_bar_dirty(bar, args)
|
|
34
|
+
bar.update_attributes(args)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
sandbox :create_bar, :create_bar_wo, :create_joint, :build_joint,
|
|
38
|
+
:build_dirty_joint, :update_bar, :update_bar_dirty
|
|
39
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class PuppetPerimeter < Kindergarten::Perimeter
|
|
2
|
+
class Puppet
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
govern do |child|
|
|
6
|
+
can [:play_with,:dress,:nappy_change], Puppet
|
|
7
|
+
cannot [:tear, :bbq], Puppet
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def grab_puppet
|
|
11
|
+
guard(:play_with, Puppet.new)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def play_puppet(puppet, action)
|
|
15
|
+
guard(:action, puppet)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
sandbox :grab_puppet, :play_puppet
|
|
19
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
class SpecPerimeter < Kindergarten::Perimeter
|
|
2
|
+
governess Kindergarten::StrictGoverness
|
|
3
|
+
|
|
4
|
+
govern do |child|
|
|
5
|
+
can :view, String
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# should pass and return the child
|
|
9
|
+
def sandboxed
|
|
10
|
+
guard(:view, child)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# should raise AccessDenied
|
|
14
|
+
def guarded
|
|
15
|
+
guard(:somethings, child)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# should not be accessible
|
|
19
|
+
def unboxed
|
|
20
|
+
$stderr.puts "I should never happen"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# should return "OK"
|
|
24
|
+
def not_guarded
|
|
25
|
+
unguarded
|
|
26
|
+
"OK"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# should raise Unguarded
|
|
30
|
+
def unsafe
|
|
31
|
+
return child.reverse
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
sandbox :sandboxed, :not_guarded, :guarded, :unsafe
|
|
35
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: kindergarten
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.5
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Hartog C. de Mik
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2012-10-24 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: cancan
|
|
16
|
+
requirement: &29385360 !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ~>
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: 1.6.8
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: *29385360
|
|
25
|
+
- !ruby/object:Gem::Dependency
|
|
26
|
+
name: activesupport
|
|
27
|
+
requirement: &29381580 !ruby/object:Gem::Requirement
|
|
28
|
+
none: false
|
|
29
|
+
requirements:
|
|
30
|
+
- - ~>
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '3.2'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: *29381580
|
|
36
|
+
- !ruby/object:Gem::Dependency
|
|
37
|
+
name: rake
|
|
38
|
+
requirement: &29380560 !ruby/object:Gem::Requirement
|
|
39
|
+
none: false
|
|
40
|
+
requirements:
|
|
41
|
+
- - ! '>='
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '0'
|
|
44
|
+
type: :development
|
|
45
|
+
prerelease: false
|
|
46
|
+
version_requirements: *29380560
|
|
47
|
+
- !ruby/object:Gem::Dependency
|
|
48
|
+
name: rspec
|
|
49
|
+
requirement: &29379620 !ruby/object:Gem::Requirement
|
|
50
|
+
none: false
|
|
51
|
+
requirements:
|
|
52
|
+
- - ~>
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '2.10'
|
|
55
|
+
type: :development
|
|
56
|
+
prerelease: false
|
|
57
|
+
version_requirements: *29379620
|
|
58
|
+
- !ruby/object:Gem::Dependency
|
|
59
|
+
name: activerecord
|
|
60
|
+
requirement: &29378540 !ruby/object:Gem::Requirement
|
|
61
|
+
none: false
|
|
62
|
+
requirements:
|
|
63
|
+
- - ~>
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: '3.2'
|
|
66
|
+
type: :development
|
|
67
|
+
prerelease: false
|
|
68
|
+
version_requirements: *29378540
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: sqlite3
|
|
71
|
+
requirement: &29377500 !ruby/object:Gem::Requirement
|
|
72
|
+
none: false
|
|
73
|
+
requirements:
|
|
74
|
+
- - ! '>='
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '0'
|
|
77
|
+
type: :development
|
|
78
|
+
prerelease: false
|
|
79
|
+
version_requirements: *29377500
|
|
80
|
+
description: A kindergarten with a perimeter, a governess and a sandbox
|
|
81
|
+
email:
|
|
82
|
+
- hartog@organisedminds.com
|
|
83
|
+
executables: []
|
|
84
|
+
extensions: []
|
|
85
|
+
extra_rdoc_files: []
|
|
86
|
+
files:
|
|
87
|
+
- .gitignore
|
|
88
|
+
- .rspec
|
|
89
|
+
- .travis.yml
|
|
90
|
+
- Gemfile
|
|
91
|
+
- LICENSE
|
|
92
|
+
- README.md
|
|
93
|
+
- Rakefile
|
|
94
|
+
- TODO.md
|
|
95
|
+
- kindergarten.gemspec
|
|
96
|
+
- lib/kindergarten.rb
|
|
97
|
+
- lib/kindergarten/exceptions.rb
|
|
98
|
+
- lib/kindergarten/governesses.rb
|
|
99
|
+
- lib/kindergarten/governesses/easy_governess.rb
|
|
100
|
+
- lib/kindergarten/governesses/head_governess.rb
|
|
101
|
+
- lib/kindergarten/governesses/strict_governess.rb
|
|
102
|
+
- lib/kindergarten/orm/active_record.rb
|
|
103
|
+
- lib/kindergarten/orm/governess.rb
|
|
104
|
+
- lib/kindergarten/perimeter.rb
|
|
105
|
+
- lib/kindergarten/sandbox.rb
|
|
106
|
+
- lib/kindergarten/version.rb
|
|
107
|
+
- spec/kindergarten/governess_spec.rb
|
|
108
|
+
- spec/kindergarten/perimeter_spec.rb
|
|
109
|
+
- spec/kindergarten/sandbox_spec.rb
|
|
110
|
+
- spec/kindergarten_spec.rb
|
|
111
|
+
- spec/orm/active_record_spec.rb
|
|
112
|
+
- spec/orm_helper.rb
|
|
113
|
+
- spec/spec_helper.rb
|
|
114
|
+
- spec/support/01_rspec_helper.rb
|
|
115
|
+
- spec/support/db/.gitkeep
|
|
116
|
+
- spec/support/dining_perimeter.rb
|
|
117
|
+
- spec/support/drinking_perimeter.rb
|
|
118
|
+
- spec/support/log/.gitkeep
|
|
119
|
+
- spec/support/puppet_perimeter.rb
|
|
120
|
+
- spec/support/spec_perimeter.rb
|
|
121
|
+
homepage: ''
|
|
122
|
+
licenses: []
|
|
123
|
+
post_install_message:
|
|
124
|
+
rdoc_options: []
|
|
125
|
+
require_paths:
|
|
126
|
+
- lib
|
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
128
|
+
none: false
|
|
129
|
+
requirements:
|
|
130
|
+
- - ! '>='
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: '0'
|
|
133
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
|
+
none: false
|
|
135
|
+
requirements:
|
|
136
|
+
- - ! '>='
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
139
|
+
requirements: []
|
|
140
|
+
rubyforge_project:
|
|
141
|
+
rubygems_version: 1.8.11
|
|
142
|
+
signing_key:
|
|
143
|
+
specification_version: 3
|
|
144
|
+
summary: Provide a kindergarten for your code to play in
|
|
145
|
+
test_files: []
|
|
146
|
+
has_rdoc:
|