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.
@@ -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