kindergarten 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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,42 @@
|
|
1
|
+
module Kindergarten::ORM
|
2
|
+
module ActiveRecord
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
def update_attributes(hash)
|
8
|
+
self.class.check(:update_attributes, hash)
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def create(*args)
|
13
|
+
check(:create, *args)
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def new(*args)
|
18
|
+
check(:new, *args)
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def check(method, *args)
|
23
|
+
required = self.force_rinsed? ?
|
24
|
+
Kindergarten::RinsedHash :
|
25
|
+
Kindergarten::ScrubbedHash
|
26
|
+
|
27
|
+
if args[0].is_a?(Array)
|
28
|
+
args.each do |input|
|
29
|
+
raise Unscrubbed unless input.is_a?(required)
|
30
|
+
end
|
31
|
+
|
32
|
+
elsif args[0].is_a?(Hash)
|
33
|
+
raise Unscrubbed unless args[0].is_a?(required)
|
34
|
+
|
35
|
+
elsif args.any?
|
36
|
+
warn "WARNING: #{self.name}.#{method} called with unkown signature"
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Kindergarten
|
2
|
+
module ORM
|
3
|
+
|
4
|
+
# An exception to throw when unscrubbed input is provided
|
5
|
+
class Unscrubbed < ArgumentError
|
6
|
+
def initialize(msg=nil)
|
7
|
+
@msg = msg || "Unscrubbed input provided"
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
@msg
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Provide ORM tidyness
|
16
|
+
module Governess
|
17
|
+
def self.included(base)
|
18
|
+
if base.ancestors.include?(::ActiveRecord::Base)
|
19
|
+
base.send(:include, Kindergarten::ORM::ActiveRecord)
|
20
|
+
else
|
21
|
+
raise "Your ORM #{base.superclass} for #{base} is not supported (yet)"
|
22
|
+
end
|
23
|
+
|
24
|
+
base.extend Modes
|
25
|
+
end
|
26
|
+
|
27
|
+
module Modes
|
28
|
+
def force_rinsed
|
29
|
+
@force_rinsed = true
|
30
|
+
end
|
31
|
+
|
32
|
+
def force_rinsed?
|
33
|
+
@force_rinsed == true ? true : false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
require 'kindergarten/orm/active_record'
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module Kindergarten
|
2
|
+
# A Perimeter is used to define the places where the child can play.
|
3
|
+
#
|
4
|
+
# @example
|
5
|
+
# class ExamplePerimeter < Kindergarten::Perimeter
|
6
|
+
# purpose :books
|
7
|
+
#
|
8
|
+
# govern do |child|
|
9
|
+
# can :read, Book do |book|
|
10
|
+
# book.level <= 2
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# def read(book)
|
15
|
+
# guard(:read, book)
|
16
|
+
# book.read
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# sandbox :read
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
class Perimeter
|
23
|
+
class << self
|
24
|
+
attr_reader :sandboxed_methods, :govern_proc
|
25
|
+
|
26
|
+
# Define a list of sandbox methods
|
27
|
+
def sandbox(*list)
|
28
|
+
@sandboxed_methods ||= []
|
29
|
+
@sandboxed_methods |= list
|
30
|
+
end
|
31
|
+
|
32
|
+
# Instruct the Governess how to govern this perimeter
|
33
|
+
def govern(&proc)
|
34
|
+
@govern_proc = proc
|
35
|
+
end
|
36
|
+
|
37
|
+
# Get/set the purpose of the perimeter
|
38
|
+
def purpose(*purpose)
|
39
|
+
purpose.any? ? @purpose = purpose[0] : @purpose
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get/set the governess of the perimeter
|
43
|
+
def governess(*klass)
|
44
|
+
klass.any? ? @governess = klass[0] : @governess
|
45
|
+
end
|
46
|
+
|
47
|
+
# Subscribe to an event from a given purpose
|
48
|
+
# @param [Symbol] purpose Listen to other perimeters that have this
|
49
|
+
# purpose
|
50
|
+
# @param [Symbol] event Listen for events with this name
|
51
|
+
# @param [Proc,Symbol] block Invoke this on the event
|
52
|
+
# @example Symbol form
|
53
|
+
# subscribe :users, :create, :user_created
|
54
|
+
#
|
55
|
+
# def user_created(event)
|
56
|
+
# # ...
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# @example Block form
|
60
|
+
# subscribe :users, :update do |event|
|
61
|
+
# # ...
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
def subscribe(purpose, event, block)
|
65
|
+
@callbacks ||= {}
|
66
|
+
@callbacks[purpose] ||= {}
|
67
|
+
@callbacks[purpose][event] ||= []
|
68
|
+
@callbacks[purpose][event] << block
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
attr_reader :child, :governess
|
73
|
+
|
74
|
+
# Obtain an un-sandboxed instance for testing purposes
|
75
|
+
#
|
76
|
+
# @return [Perimeter] with the given child and/or governess
|
77
|
+
#
|
78
|
+
def self.instance(child=nil, governess=nil)
|
79
|
+
self.new(child, governess)
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize(child, governess)
|
83
|
+
@child = child
|
84
|
+
@governess = governess
|
85
|
+
|
86
|
+
unless @governess.nil? || self.class.govern_proc.nil?
|
87
|
+
@governess.instance_eval(&self.class.govern_proc)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# @return [Array] List of sandbox methods
|
92
|
+
def sandbox_methods
|
93
|
+
self.class.sandboxed_methods
|
94
|
+
end
|
95
|
+
|
96
|
+
# @see Governess#scrub
|
97
|
+
def scrub(*args)
|
98
|
+
self.governess.scrub(*args)
|
99
|
+
end
|
100
|
+
|
101
|
+
# @see Governess#rinse
|
102
|
+
def rinse(*args)
|
103
|
+
self.governess.rinse(*args)
|
104
|
+
end
|
105
|
+
|
106
|
+
# @see Governess#guard
|
107
|
+
def guard(action, target)
|
108
|
+
self.governess.guard(action, target)
|
109
|
+
end
|
110
|
+
|
111
|
+
# @see Governess#unguarded
|
112
|
+
def unguarded(&block)
|
113
|
+
self.governess.unguarded(&block)
|
114
|
+
end
|
115
|
+
|
116
|
+
def governed(method, unguarded=false, &block)
|
117
|
+
if unguarded == true
|
118
|
+
self.governess.unguarded do
|
119
|
+
self.governess.governed(method, &block)
|
120
|
+
end
|
121
|
+
|
122
|
+
else
|
123
|
+
self.governess.governed(method, &block)
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Kindergarten
|
2
|
+
class Sandbox
|
3
|
+
attr_reader :child, :governess, :perimeter
|
4
|
+
|
5
|
+
def initialize(child)
|
6
|
+
@child = child
|
7
|
+
@governess = Kindergarten::HeadGoverness.new(child)
|
8
|
+
|
9
|
+
@perimeter = []
|
10
|
+
def @perimeter.include?(other)
|
11
|
+
(self.collect(&:class) & [ other.class ]).any?
|
12
|
+
end
|
13
|
+
|
14
|
+
@unguarded = false
|
15
|
+
end
|
16
|
+
|
17
|
+
def extend_perimeter(*perimeter_classes)
|
18
|
+
perimeter_classes.each do |perimeter_class|
|
19
|
+
# if the perimeter specifies a governess, use that - otherwise appoint
|
20
|
+
# the head governess
|
21
|
+
child = self.child
|
22
|
+
governess = perimeter_class.governess ?
|
23
|
+
perimeter_class.governess.new(child) :
|
24
|
+
self.governess
|
25
|
+
|
26
|
+
perimeter = perimeter_class.new(child, governess)
|
27
|
+
|
28
|
+
# the head governess must know all the rules
|
29
|
+
unless governess == self.governess || perimeter_class.govern_proc.nil?
|
30
|
+
self.governess.instance_eval(&perimeter_class.govern_proc)
|
31
|
+
end
|
32
|
+
|
33
|
+
raise ArgumentError.new(
|
34
|
+
"Module must inherit from Kindergarten::Perimeter"
|
35
|
+
) unless perimeter.kind_of?(Kindergarten::Perimeter)
|
36
|
+
|
37
|
+
@perimeter << perimeter unless @perimeter.include?(perimeter)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
alias_method :load_perimeter, :extend_perimeter
|
41
|
+
alias_method :load_module, :extend_perimeter
|
42
|
+
|
43
|
+
def unguarded(&block)
|
44
|
+
@unguarded = true
|
45
|
+
yield
|
46
|
+
@unguarded = false
|
47
|
+
end
|
48
|
+
|
49
|
+
def allows?(action, target)
|
50
|
+
governess.can?(action, target)
|
51
|
+
end
|
52
|
+
alias_method :allowed?, :allows?
|
53
|
+
|
54
|
+
def disallows?(action, target)
|
55
|
+
governess.cannot?(action, target)
|
56
|
+
end
|
57
|
+
alias_method :disallowed?, :disallows?
|
58
|
+
|
59
|
+
def method_missing(name, *args, &block)
|
60
|
+
super
|
61
|
+
rescue NoMethodError => ex
|
62
|
+
@perimeter.each do |perimeter|
|
63
|
+
if perimeter.sandbox_methods.include?(name)
|
64
|
+
return perimeter.governed(name, @unguarded) do
|
65
|
+
perimeter.send(name, *args, &block)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# still here? then there is no part of the perimeter that provides method
|
71
|
+
raise ex
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Kindergarten::HeadGoverness do
|
4
|
+
before(:each) do
|
5
|
+
@governess = Kindergarten::HeadGoverness.new("child")
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should include CanCan ability" do
|
9
|
+
@governess.should be_kind_of(CanCan::Ability)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe :governing do
|
13
|
+
it "should guard the child" do
|
14
|
+
expect {
|
15
|
+
@governess.guard(:free, "Willy")
|
16
|
+
}.to raise_error(Kindergarten::AccessDenied)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should keep a closed eye" do
|
20
|
+
expect {
|
21
|
+
@governess.unguarded do
|
22
|
+
@governess.guard(:free, "Willy")
|
23
|
+
end
|
24
|
+
}.to_not raise_error(Kindergarten::AccessDenied)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe :washing do
|
29
|
+
it "should scrub attributes" do
|
30
|
+
attr = { a: 1, b: 2, c: 3 }
|
31
|
+
|
32
|
+
scrubbed = @governess.scrub(attr, :a, :c)
|
33
|
+
scrubbed.should_not be_has_key(:b)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should return a ScrubbedHash after scrubbing" do
|
37
|
+
attr = { a: 1, b: 2, c: 3 }
|
38
|
+
|
39
|
+
scrubbed = @governess.scrub(attr, :a, :c)
|
40
|
+
scrubbed.should be_kind_of(Kindergarten::ScrubbedHash)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should rinse attributes" do
|
44
|
+
attr = { a: "1", b: "2a", c: "3" }
|
45
|
+
rinsed = @governess.rinse(attr, a: /(\d+)/, b: /(\D+)/)
|
46
|
+
|
47
|
+
rinsed.should_not be_has_key(:c)
|
48
|
+
rinsed[:a].should eq "1"
|
49
|
+
rinsed[:b].should eq "a"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should pass attributes" do
|
53
|
+
attr = { a: "1", b: "2a", c: "3" }
|
54
|
+
rinsed = @governess.rinse(attr, a: :pass, c: :pass)
|
55
|
+
|
56
|
+
rinsed.should_not be_has_key(:b)
|
57
|
+
rinsed[:a].should eq "1"
|
58
|
+
rinsed[:c].should eq "3"
|
59
|
+
end
|
60
|
+
it "should return a RinsedHash after rinsing" do
|
61
|
+
attr = { a: "1", b: "2a", c: "3" }
|
62
|
+
rinsed = @governess.rinse(attr, a: /(\d+)/, b: /(\d+)/)
|
63
|
+
|
64
|
+
rinsed.should be_kind_of(Kindergarten::RinsedHash)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Kindergarten::Perimeter do
|
4
|
+
describe :class do
|
5
|
+
it "should have a :sandbox method" do
|
6
|
+
SpecPerimeter.should respond_to(:sandbox)
|
7
|
+
SpecPerimeter.should respond_to(:sandboxed_methods)
|
8
|
+
end
|
9
|
+
it "should return sandboxed methods" do
|
10
|
+
SpecPerimeter.sandboxed_methods.should_not be_empty
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should have a :govern method" do
|
14
|
+
SpecPerimeter.should respond_to(:govern)
|
15
|
+
SpecPerimeter.should respond_to(:govern_proc)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should return a govern proc" do
|
19
|
+
SpecPerimeter.govern_proc.should be_kind_of(Proc)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe :instance do
|
24
|
+
it "should have an initialize method with 2 arguments" do
|
25
|
+
SpecPerimeter.instance.method(:initialize).arity.should == 2
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should have a :guard method" do
|
29
|
+
SpecPerimeter.instance.should respond_to(:guard)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should have an :unguarded method" do
|
33
|
+
SpecPerimeter.instance.should respond_to(:unguarded)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should have a :scrub method" do
|
37
|
+
SpecPerimeter.instance.should respond_to(:scrub)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should have a :rinse method" do
|
41
|
+
SpecPerimeter.instance.should respond_to(:rinse)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should have a :sandbox_methods method" do
|
45
|
+
SpecPerimeter.instance.should respond_to(:sandbox_methods)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe :sandbox do
|
50
|
+
before(:each) do
|
51
|
+
@sandbox = Kindergarten.sandbox("child")
|
52
|
+
@sandbox.extend_perimeter(SpecPerimeter)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should have the SpecPerimeter" do
|
56
|
+
@sandbox.perimeter.collect(&:class).should include(SpecPerimeter)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should fill the governess" do
|
60
|
+
@sandbox.governess.should_not be_empty
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should have the sandboxed method" do
|
64
|
+
@sandbox.sandboxed.should eq "child"
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should have the guarded method" do
|
68
|
+
expect {
|
69
|
+
@sandbox.guarded
|
70
|
+
}.to raise_error(Kindergarten::AccessDenied)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should not have the unboxed method" do
|
74
|
+
expect {
|
75
|
+
@sanbox.unboxed
|
76
|
+
}.to raise_error(NoMethodError)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should have the not_guarded method" do
|
80
|
+
@sandbox.not_guarded.should eq "OK"
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should have the unsafe method" do
|
84
|
+
expect {
|
85
|
+
@sandbox.unsafe
|
86
|
+
}.to raise_error(Kindergarten::Perimeter::Unguarded)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe :unguarded do
|
91
|
+
before(:each) do
|
92
|
+
@sandbox = Kindergarten.sandbox("child")
|
93
|
+
@sandbox.extend_perimeter(SpecPerimeter)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should allow the unsafe method" do
|
97
|
+
expect {
|
98
|
+
@sandbox.unguarded do
|
99
|
+
@sandbox.unsafe
|
100
|
+
end
|
101
|
+
}.to_not raise_error(Kindergarten::Perimeter::Unguarded)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should allow the not_guarded method" do
|
105
|
+
expect {
|
106
|
+
@sandbox.unguarded do
|
107
|
+
@sandbox.not_guarded
|
108
|
+
end
|
109
|
+
}.to_not raise_error(Kindergarten::Perimeter::Unguarded)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|