playhouse 0.1.1

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,76 @@
1
+ module Playhouse
2
+ class ContextUsageError < ArgumentError
3
+ end
4
+
5
+ class ActorKeyError < ContextUsageError
6
+ def initialize(context_name, actor_name)
7
+ @context_name = context_name
8
+ @actor_name = actor_name
9
+ end
10
+
11
+ def description
12
+ message
13
+ end
14
+ end
15
+
16
+ class InvalidActorKeyError < ActorKeyError
17
+ def message
18
+ "Actor key #{@actor_name.inspect} is not a symbol, for #{@context_name}"
19
+ end
20
+ end
21
+
22
+ class UnknownActorKeyError < ActorKeyError
23
+ def message
24
+ "Unknown actor #{@actor_name.inspect} for #{@context_name}"
25
+ end
26
+ end
27
+
28
+ class ContextValidationError < Exception
29
+ def initialize(*params)
30
+ @part_errors = {}
31
+ end
32
+
33
+ def for_part(part_name)
34
+ @part_errors[part_name] || []
35
+ end
36
+
37
+ def add_to_part(part_name, error)
38
+ @part_errors[part_name] ||= []
39
+ @part_errors[part_name] << error
40
+ end
41
+
42
+ def part_errors
43
+ @part_errors
44
+ end
45
+
46
+ def message
47
+ part_messages.join("\n")
48
+ end
49
+
50
+ private
51
+
52
+ def part_messages
53
+ @part_errors.map do |part_name, errors|
54
+ [part_name, errors.map(&:message).join(', ')].join(': ')
55
+ end
56
+ end
57
+ end
58
+
59
+ class ActorValidationError < Exception
60
+ def initialize(options)
61
+ @part_name = options[:part_name]
62
+ end
63
+
64
+ attr_reader :part_name
65
+ end
66
+
67
+ class RequiredActorMissing < ActorValidationError
68
+ def message
69
+ "Missing actor #{@part_name}"
70
+ end
71
+
72
+ def description
73
+ "You must specify one of these"
74
+ end
75
+ end
76
+ end
data/playhouse.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "playhouse"
3
+ s.version = '0.1.1'
4
+ s.authors = ["Craig Ambrose", "Joshua Vial"]
5
+ s.email = ["craig@enspiral.com", "joshua@enspiral.com"]
6
+ s.homepage = "https://github.com/enspiral/economatic"
7
+ s.summary = "A DCI framework"
8
+ s.description = "Provides one possible way of implementing a DCI architecture in a ruby app"
9
+
10
+ s.require_paths = %w(lib)
11
+
12
+ s.files = `git ls-files`.split($/)
13
+ s.test_files = s.files.grep(%r{^(spec)/})
14
+
15
+ s.required_ruby_version = '>= 1.9.2'
16
+
17
+ s.add_dependency 'rake'
18
+ s.add_dependency 'activesupport'
19
+ s.add_dependency 'activerecord'
20
+ end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+ require 'playhouse/context'
3
+
4
+ module Playhouse
5
+ describe Context do
6
+ before do
7
+ class ExampleContext < Context
8
+ attr_accessor :performed
9
+
10
+ def perform
11
+ self.performed = true
12
+ end
13
+ end
14
+ end
15
+
16
+ after do
17
+ Playhouse.class_eval{remove_const :ExampleContext}
18
+ end
19
+
20
+ describe 'constructing' do
21
+ it 'does not allow an unspecified actor to be stored' do
22
+ expect { ExampleContext.new(foobar: 'value') }.to raise_error(ArgumentError)
23
+ end
24
+ end
25
+
26
+ describe '.actor' do
27
+ let(:role) { double(:role) }
28
+
29
+ it 'allows the actor to be passed into the constructor and stored' do
30
+ ExampleContext.actor :foobar
31
+ subject = ExampleContext.new(foobar: 'value')
32
+ subject.foobar.should == 'value'
33
+ end
34
+
35
+ it 'extends the actor by the specified role' do
36
+ ExampleContext.actor :foobar, role: role
37
+ role.should_receive(:cast_actor).with('value').and_return('cast value')
38
+ subject = ExampleContext.new(foobar: 'value')
39
+ subject.call
40
+ subject.foobar.should == 'cast value'
41
+ end
42
+ end
43
+
44
+ describe '#inherit_actors_from' do
45
+ before do
46
+ class ParentContext < Context
47
+ actor :current_user
48
+ actor :other_actor
49
+ end
50
+ @parent = ParentContext.new current_user: 'user', other_actor: 'other'
51
+ ExampleContext.actor :current_user
52
+ end
53
+
54
+ it 'loads actors that are unset' do
55
+ subject = ExampleContext.new
56
+ subject.inherit_actors_from @parent
57
+ expect(subject.current_user).to eq 'user'
58
+ end
59
+
60
+ it 'does not load actors that are already set' do
61
+ subject = ExampleContext.new current_user: 'user override'
62
+ subject.inherit_actors_from @parent
63
+ expect(subject.current_user).to eq 'user override'
64
+ end
65
+
66
+ it 'does not load actors for which it has no part' do
67
+ subject = ExampleContext.new
68
+ subject.inherit_actors_from @parent
69
+ expect{subject.other_actor}.to raise_error
70
+ end
71
+ end
72
+
73
+ describe '#call' do
74
+ it 'calls perform' do
75
+ subject = ExampleContext.new
76
+ subject.call
77
+
78
+ subject.performed.should be_true
79
+ end
80
+
81
+ it 'does not raise an error if an optional actor is not supplied' do
82
+ ExampleContext.actor :foobar, optional: true
83
+ subject = ExampleContext.new
84
+ subject.call
85
+ end
86
+
87
+ it 'raises an error if a required actor is not supplied' do
88
+ ExampleContext.actor :foobar
89
+ subject = ExampleContext.new
90
+ expect {
91
+ subject.call
92
+ }.to raise_error
93
+ end
94
+ end
95
+
96
+ describe 'http_method' do
97
+ it 'defaults to :get' do
98
+ expect(ExampleContext.http_methods).to include(:get)
99
+ end
100
+ it 'can be set one at a time' do
101
+ ExampleContext.http_method :post
102
+ expect(ExampleContext.http_methods).to include(:post)
103
+ end
104
+ it 'can be set with an array' do
105
+ ExampleContext.http_method [:get, :post]
106
+ expect(ExampleContext.http_methods).to include(:get)
107
+ expect(ExampleContext.http_methods).to include(:post)
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+ require 'playhouse/part'
3
+
4
+ module Playhouse
5
+ describe Part do
6
+ subject { Part.new(:amount) }
7
+
8
+ describe "#validators" do
9
+ it "is empty for an optional part" do
10
+ subject.optional = true
11
+ subject.validators.should == []
12
+ end
13
+
14
+ it "includes a required actor validator if not optional" do
15
+ subject.validators.first.should be_a(RequiredActorValidator)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+ require 'playhouse/context'
3
+ require 'playhouse/play'
4
+
5
+ module Playhouse
6
+ describe Play do
7
+ before do
8
+ class CalculateTax < Context
9
+ end
10
+
11
+ class ExampleAPI < Play
12
+ context CalculateTax
13
+ end
14
+ end
15
+
16
+ after do
17
+ Playhouse.class_eval{remove_const :ExampleAPI}
18
+ Playhouse.class_eval{remove_const :CalculateTax}
19
+ end
20
+
21
+ context 'when instantiated' do
22
+ subject { ExampleAPI.new }
23
+ let(:context) { double(:context) }
24
+
25
+ it 'presents contexts as callable methods' do
26
+ CalculateTax.actor :taxable_income
27
+ CalculateTax.should_receive(:new).with(taxable_income: 123).and_return(context)
28
+ context.should_receive(:call)
29
+
30
+ #subject.respond_to?(:calculate_tax).should be_true
31
+ subject.calculate_tax taxable_income: 123
32
+ end
33
+
34
+ it 'presents callable methods with parent' do
35
+ parent = double(:context)
36
+ expect(subject).to receive(:execute_context_with_parent)
37
+
38
+ subject.calculate_tax_with_parent parent, {}
39
+ end
40
+
41
+ it 'has a name' do
42
+ expect(subject.name).to eq('example_api')
43
+ end
44
+ end
45
+
46
+ describe 'resource' do
47
+ before do
48
+ module ExampleResource
49
+ class Context1 < Context
50
+ end
51
+ class Context2 < Context
52
+ end
53
+ end
54
+ end
55
+ it 'loads all contexts inside a module' do
56
+ ExampleAPI.stub(:context)
57
+ expect(ExampleAPI).to receive(:context).with(ExampleResource::Context1)
58
+ expect(ExampleAPI).to receive(:context).with(ExampleResource::Context2)
59
+ ExampleAPI.contexts_for ExampleResource
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+ require 'playhouse/production'
3
+
4
+ module Playhouse
5
+ describe Production do
6
+ let(:theatre) { double(:theatre) }
7
+ let(:interface) { double(:interface, build: instance) }
8
+ let(:instance) { double(:interface_instance) }
9
+
10
+ describe "#run" do
11
+ it "requires a theatre and an interface" do
12
+ expect { subject.run(theatre: theatre) }.to raise_error(ArgumentError)
13
+ expect { subject.run(interface: interface) }.to raise_error(ArgumentError)
14
+ end
15
+
16
+ it "opens the theatre" do
17
+ theatre.should_receive :while_open
18
+ subject.run(theatre: theatre, interface: interface)
19
+ end
20
+
21
+ it "runs the interface" do
22
+ theatre.stub(:while_open).and_yield
23
+ interface.should_receive(:build).with(subject).and_return(instance)
24
+ instance.should_receive(:run)
25
+
26
+ subject.run(theatre: theatre, interface: interface)
27
+ end
28
+
29
+ it "passes optional interface_args parameter through to interface" do
30
+ theatre.stub(:while_open).and_yield
31
+ instance.should_receive(:run).with("args")
32
+
33
+ subject.run(theatre: theatre, interface: interface, interface_args: "args")
34
+ end
35
+ end
36
+
37
+ describe "#plays" do
38
+ let(:play_class) { double(:play_class, new: play) }
39
+ let(:play) { double(:play) }
40
+
41
+ it "has a collection of plays which can be added" do
42
+ subject.add_play play_class
43
+ subject.plays.should == [play]
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+ require 'playhouse/role'
3
+
4
+ module Playhouse
5
+ describe Role do
6
+ module ExampleRole
7
+ include Role
8
+
9
+ actor_dependency :dingbat
10
+
11
+ def foobar
12
+ 'awesome!'
13
+ end
14
+ end
15
+
16
+ class DingbatActor
17
+ def dingbat
18
+ end
19
+ end
20
+
21
+ subject { ExampleRole }
22
+ let(:valid_actor) { DingbatActor.new }
23
+ let(:invalid_actor) { Object.new }
24
+
25
+ context "when casting as" do
26
+ it "adds foobar method to actor" do
27
+ player = subject.cast_actor(valid_actor)
28
+ player.foobar.should == 'awesome!'
29
+ end
30
+
31
+ context "without a default role" do
32
+ it "raises exception if an actor dependency is not met" do
33
+ expect {
34
+ subject.cast_actor(invalid_actor)
35
+ }.to raise_error
36
+ end
37
+ end
38
+
39
+ context "with a default role" do
40
+ subject { ExampleRoleWithDefault }
41
+
42
+ module DingbatProviderRole
43
+ include Role
44
+ def dingbat
45
+ end
46
+ end
47
+ module ExampleRoleWithDefault
48
+ include Role
49
+
50
+ actor_dependency :dingbat, default_role: DingbatProviderRole
51
+
52
+ def foobar
53
+ 'awesome!'
54
+ end
55
+ end
56
+
57
+ it "auto casts actor with default role" do
58
+ player = subject.cast_actor(invalid_actor)
59
+ player.foobar
60
+ player.dingbat
61
+ end
62
+ end
63
+ end
64
+
65
+ context "when casting an enumerable as" do
66
+ it "extends each member of the enumerable" do
67
+ players = subject.cast_all([valid_actor])
68
+ players.first.foobar.should == 'awesome!'
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ require 'playhouse/support/default_hash_values'
3
+
4
+ module Playhouse
5
+ module Support
6
+ describe DefaultHashValues do
7
+ def subject_with(data)
8
+ data.extend(DefaultHashValues)
9
+ end
10
+
11
+ describe "#value_or_default" do
12
+ it "returns the value if it is not nil" do
13
+ subject_with(key: 1).value_or_default(:key, 'fish').should == 1
14
+ subject_with(key: false).value_or_default(:key, 'fish').should == false
15
+ end
16
+
17
+ it "returns the default if the value is nil" do
18
+ subject_with(key: nil).value_or_default(:key, 'fish').should == 'fish'
19
+ end
20
+ end
21
+
22
+ describe "#value_or_error" do
23
+ it "returns the value if it is not nil" do
24
+ subject_with(key: 1).value_or_error(:key, 'fish').should == 1
25
+ subject_with(key: false).value_or_error(:key, 'fish').should == false
26
+ end
27
+
28
+ it "raises the error if the value is nil" do
29
+ expect { subject_with(key: nil).value_or_error(:key, 'fish') }.to raise_error('fish')
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end