kindergarten 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,3 @@
1
+ module Kindergarten
2
+ VERSION = "0.0.5"
3
+ end
@@ -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