pakada 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ - LICENSE ROADMAP.md
data/Gemfile CHANGED
@@ -4,3 +4,6 @@ gemspec
4
4
 
5
5
  gem "awesome_print"
6
6
  gem "rake"
7
+
8
+ gem "fakefs", :git => "https://github.com/lgierth/fakefs",
9
+ :branch => "fix_infinite_loop"
data/README.md CHANGED
@@ -1,4 +1,15 @@
1
- Very Extensible HTTP Container For Ruby
2
- =======================================
1
+ Modularization contract for web applications
2
+ ============================================
3
+
4
+ Pakada is about modular web applications: how should they look like? How should
5
+ modules interact with each other to fit a certain idea?
6
+
7
+ TODO
8
+ ----
9
+
10
+ - Thread and Fiber safety
11
+ - Documentation
12
+ - Basic CLI for creating projects/modules
13
+ - Create instances via CLI
14
+ - Project templates/profiles (e.g. Blog, Forum, etc.)
3
15
 
4
- TODO: Write documentation
data/lib/pakada/module.rb CHANGED
@@ -9,13 +9,13 @@ class Pakada
9
9
  klass.send :include, Hooked
10
10
 
11
11
  class << klass
12
- attr_reader :module_name, :path
12
+ attr_reader :pakada_name, :path, :dependencies
13
13
  end
14
14
 
15
- unless klass.module_name
16
- klass.instance_variable_set :@module_name, detect_name(klass.to_s)
15
+ unless klass.pakada_name
16
+ klass.instance_variable_set :@pakada_name, detect_name(klass.to_s)
17
17
  end
18
- unless klass.module_name
18
+ unless klass.pakada_name
19
19
  raise "Could not detect name for module #{klass}"
20
20
  end
21
21
 
@@ -25,6 +25,10 @@ class Pakada
25
25
  klass.instance_variable_set :@path, detect_path(file)
26
26
  end
27
27
 
28
+ unless klass.dependencies
29
+ klass.instance_variable_set :@dependencies, []
30
+ end
31
+
28
32
  @descendants << klass
29
33
  end
30
34
 
@@ -56,14 +60,18 @@ class Pakada
56
60
  end
57
61
  end
58
62
 
59
- def module_name
60
- self.class.module_name
63
+ def pakada_name
64
+ self.class.pakada_name
61
65
  end
62
66
 
63
67
  def path
64
68
  self.class.path
65
69
  end
66
70
 
71
+ def dependencies
72
+ self.class.dependencies
73
+ end
74
+
67
75
  def boot; end
68
76
 
69
77
  def hooks; end
@@ -1,3 +1,3 @@
1
1
  class Pakada
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/pakada.rb CHANGED
@@ -1,88 +1,155 @@
1
- require "rack"
2
1
  require "hooked"
2
+ require "pathname"
3
+ require "rack"
3
4
 
4
5
  require "pakada/module"
5
6
  require "pakada/version"
6
7
 
7
8
  class Pakada
8
- DEFAULT_APP = proc {|req_env| Pakada.request req_env }
9
-
10
- attr_reader :env, :modules, :middleware
11
- attr_accessor :app
9
+ include Hooked
12
10
 
13
- def initialize
14
- @middleware = []
11
+ @instances = {}
12
+ @stack = []
13
+
14
+ class << self
15
+ attr_reader :instances, :stack
16
+ end
17
+
18
+ def self.instance(name = nil, &block)
19
+ return @stack.last unless name
20
+ return instances[name] unless block
21
+
22
+ unless instances[name]
23
+ dsl = Struct.new(:urls, :modules, :path).new
24
+ ret = block.call(dsl)
25
+
26
+ config = Hash[*dsl.members.map {|m| m.to_sym }.zip(dsl.values).flatten(1)]
27
+ instances[name] = new(name, config)
28
+ else
29
+ @stack << instances[name]
30
+ begin
31
+ ret = block.call(instances[name])
32
+ ensure
33
+ @stack.pop
34
+ end
35
+ end
36
+
37
+ ret
15
38
  end
16
39
 
17
- def development?
40
+ def self.boot
41
+ instance(:default) {|ins| ins.urls = ["/"] } if instances.empty?
42
+
43
+ @urlmap = Rack::URLMap.new instances.values.inject({}) {|map, ins|
44
+ ins.urls.each {|url|
45
+ map[url] = proc {|env|
46
+ instance(ins.name) { ins.call env }
47
+ }
48
+ }
49
+ map
50
+ }
51
+
52
+ instances.values.each {|ins|
53
+ instance(ins.name) { ins.boot }
54
+ }
55
+ end
56
+
57
+ def self.call(env)
58
+ @urlmap.call env
59
+ end
60
+
61
+ def self.env
62
+ (ENV["RACK_ENV"] || :development).to_sym
63
+ end
64
+
65
+ def self.development?
18
66
  env == :development
19
67
  end
20
68
 
21
- def testing?
69
+ def self.testing?
22
70
  env == :testing
23
71
  end
24
72
 
25
- def production?
73
+ def self.staging?
74
+ env == :staging
75
+ end
76
+
77
+ def self.production?
26
78
  env == :production
27
79
  end
28
80
 
29
- def [](name)
30
- modules[name]
81
+ def self.modules
82
+ instance.modules if instance
31
83
  end
32
84
 
33
- def boot
34
- load_env
35
- load_modules
36
- load_hooks
37
-
38
- modules.each_value {|m| m.boot } unless testing?
85
+ def self.[](pakada_name)
86
+ modules[pakada_name] if modules
87
+ end
88
+
89
+ def self.middleware
90
+ instance.middleware if instance
91
+ end
92
+
93
+ def self.urls
94
+ instance.urls if instance
39
95
  end
40
96
 
41
- def load_env
42
- @env = (ENV["RACK_ENV"] || :development).to_sym
97
+ def self.path
98
+ instance.path if instance
43
99
  end
44
100
 
45
- def load_modules
46
- @modules = Pakada::Module.descendants.inject({}) do |h, m|
47
- h[m.module_name] = m.new; h
101
+ attr_reader :name, :urls, :path
102
+ attr_reader :modules, :middleware
103
+
104
+ def initialize(name, config = {})
105
+ @name = name
106
+ config[:urls] ||= []
107
+ config[:modules] ||= Module.descendants
108
+ config[:path] ||= name == :default ? "" : "instances/#{name}"
109
+
110
+ modules, resolved = Array(config[:modules]).dup, false
111
+ until resolved
112
+ j ||= modules.size
113
+ i, j = j, 0
114
+
115
+ modules.reverse[0...i].map {|mod|
116
+ mod.dependencies
117
+ }.flatten.each {|dep|
118
+ unless modules.include? dep
119
+ modules << dep
120
+ j += 1
121
+ end
122
+ }
123
+
124
+ resolved = true if j == 0
48
125
  end
126
+ @modules = modules.inject({}) {|hash, mod|
127
+ hash[mod.pakada_name] = mod.new; hash
128
+ }
129
+
130
+ path = File.expand_path(config[:path])
131
+ @urls, @path = Array(config[:urls]), Pathname.new(path)
132
+ @middleware = []
49
133
  end
50
134
 
51
- def load_hooks
52
- modules.each_value {|m| m.hooks } unless testing?
135
+ def boot
136
+ [:hooks, :boot].each {|m|
137
+ modules.each_value {|mod| mod.send m }
138
+ } unless Pakada.testing?
53
139
  end
54
140
 
55
141
  def call(req_env)
56
- to_app.call req_env
142
+ to_app.call req_env
57
143
  end
58
144
 
59
145
  def to_app
60
- builder = Rack::Builder.new
61
- middleware.each {|mw| builder.use *(Array === mw ? mw : [mw]) }
62
- builder.run app || DEFAULT_APP
63
- builder.to_app
146
+ Rack::Builder.new.tap {|b|
147
+ middleware.each {|mw| b.use *(Array === mw ? mw : [mw]) }
148
+ b.run proc {|req_env| request req_env }
149
+ }.to_app
64
150
  end
65
151
 
66
152
  def request(req_env)
67
153
  Rack::Response.new("Hi, I'm Pakada #{VERSION}").finish
68
154
  end
69
-
70
- class << self
71
- def instance
72
- @instance ||= new
73
- end
74
-
75
- def reset_instance
76
- @instance = nil
77
- end
78
-
79
- def method_missing(m, *args, &block)
80
- super(m, *args, &block) unless instance.respond_to? m
81
- instance.send m, *args, &block
82
- end
83
-
84
- def respond_to?(m, private = false)
85
- super(m, private) || instance.respond_to?(m, private)
86
- end
87
- end
88
155
  end
data/pakada.gemspec CHANGED
@@ -9,17 +9,19 @@ Gem::Specification.new do |s|
9
9
  s.authors = ["Lars Gierth"]
10
10
  s.email = ["lars.gierth@gmail.com"]
11
11
  s.homepage = "https://rubygems.org/gems/pakada"
12
- s.summary = %q{Very Extensible HTTP Container For Ruby}
12
+ s.summary = %q{Modularization contract for web applications}
13
13
  #s.description = %q{TODO: Write a gem description}
14
14
 
15
15
  s.add_dependency "rack"
16
- s.add_dependency "hooked", "~> 0.2"
16
+ s.add_dependency "hooked"
17
17
 
18
18
  s.add_development_dependency "rspec"
19
19
  s.add_development_dependency "fakefs"
20
20
  s.add_development_dependency "fakefs-require"
21
+ s.add_development_dependency "yard"
22
+ s.add_development_dependency "rdiscount"
21
23
 
22
- s.files = `git ls-files`.split("\n") - [".gitignore", ".rvmrc", "config.ru"]
24
+ s.files = `git ls-files`.split("\n") - [".gitignore", ".travis.yml", "config.ru"]
23
25
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
26
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
27
  s.require_paths = ["lib"]
data/spec/module_spec.rb CHANGED
@@ -67,7 +67,7 @@ describe Pakada::Module do
67
67
  klass = Class.new
68
68
  Pakada::Module.should_receive(:detect_name).with(klass.to_s).once { :some_module }
69
69
  klass.send(:include, Pakada::Module)
70
- klass.module_name.should == :some_module
70
+ klass.pakada_name.should == :some_module
71
71
  end
72
72
 
73
73
  it "crashes if it can't detect a name" do
@@ -79,10 +79,10 @@ describe Pakada::Module do
79
79
  it "respects an already set name" do
80
80
  Pakada::Module.should_not_receive(:detect_name)
81
81
  klass = Class.new do
82
- @module_name = :some_module
82
+ @pakada_name = :some_module
83
83
  include Pakada::Module
84
84
  end
85
- klass.module_name.should == :some_module
85
+ klass.pakada_name.should == :some_module
86
86
  end
87
87
 
88
88
  it "tries to determine the module's path" do
@@ -97,7 +97,7 @@ describe Pakada::Module do
97
97
 
98
98
  Pakada::Module.should_receive(:detect_path).with(file).once { path }
99
99
  klass = Class.new do
100
- @module_name = :some_module
100
+ @pakada_name = :some_module
101
101
  include Pakada::Module
102
102
  end
103
103
  klass.path.should == path
@@ -108,7 +108,7 @@ describe Pakada::Module do
108
108
  it "respects an already set path" do
109
109
  Pakada::Module.should_not_receive(:detect_path)
110
110
  klass = Class.new do
111
- @module_name = :some_module
111
+ @pakada_name = :some_module
112
112
  @path = "/my/path"
113
113
  include Pakada::Module
114
114
  end
@@ -117,7 +117,7 @@ describe Pakada::Module do
117
117
 
118
118
  it "adds the module to the list of descendants" do
119
119
  klass = Class.new do
120
- @module_name = :some_module
120
+ @pakada_name = :some_module
121
121
  include Pakada::Module
122
122
  end
123
123
  Pakada::Module.descendants.should include(klass)
@@ -128,7 +128,7 @@ end
128
128
  describe "SomeModule" do
129
129
  before :each do
130
130
  @klass = Class.new do
131
- @module_name = :foo
131
+ @pakada_name = :foo
132
132
  @path = "/opt/pakada"
133
133
  include Pakada::Module
134
134
  end
@@ -147,13 +147,13 @@ describe "SomeModule" do
147
147
  @obj.should respond_to(:hooked)
148
148
  end
149
149
 
150
- describe ".module_name" do
150
+ describe ".pakada_name" do
151
151
  it "contains the module's name" do
152
- @klass.module_name.should == :foo
152
+ @klass.pakada_name.should == :foo
153
153
  end
154
154
 
155
155
  it "is read-only" do
156
- proc { @klass.module_name = :bar }.should raise_error(NoMethodError)
156
+ proc { @klass.pakada_name = :bar }.should raise_error(NoMethodError)
157
157
  end
158
158
  end
159
159
 
@@ -167,9 +167,9 @@ describe "SomeModule" do
167
167
  end
168
168
  end
169
169
 
170
- describe "#module_name" do
171
- it "is a shortcut for ::module_name" do
172
- @obj.module_name.should == @klass.module_name
170
+ describe "#pakada_name" do
171
+ it "is a shortcut for ::pakada_name" do
172
+ @obj.pakada_name.should == @klass.pakada_name
173
173
  end
174
174
  end
175
175
 
data/spec/pakada_spec.rb CHANGED
@@ -1,235 +1,339 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Pakada do
4
- subject { Pakada.instance }
5
-
6
- it "forwards all messages to the Singleton instance" do
7
- Pakada.instance.should_receive(:message)
8
- Pakada.message
9
-
10
- proc { Pakada.other_message }.should raise_error(NoMethodError, /`other_message'/)
11
- end
12
-
13
- it "forwards .respond_to? to the Singleton instance" do
14
- Pakada.instance.should_receive(:respond_to?).with(:message, true) { true }
15
- Pakada.respond_to?(:message, true).should be_true
16
-
17
- Pakada.instance.should_not_receive(:respond_to?).with(:instance)
18
- Pakada.respond_to?(:instance)
19
- end
20
-
21
- describe ".instance" do
22
- it "creates a new Singleton instance and returns it" do
23
- Pakada.stub(:new) { double "a Pakada instance" }
24
- Pakada.instance.should == Pakada.instance
4
+ context ".instance" do
5
+ it "returns the instances stack's top element" do
6
+ Pakada.instance.should be_nil
7
+
8
+ Pakada.instances[:foo] = stub("instance")
9
+ Pakada.instance(:foo) {|ins|
10
+ Pakada.instance.should equal(ins)
11
+ }
12
+
13
+ Pakada.instance.should be_nil
25
14
  end
26
15
  end
27
16
 
28
- describe ".reset_instance" do
29
- it "unsets the Singleton instance" do
30
- instance = Pakada.instance
31
- Pakada.reset_instance
32
- Pakada.instance.should_not == instance
17
+ context ".instance(name)" do
18
+ let(:ins) { stub "instance" }
19
+
20
+ it "returns the specified instance" do
21
+ Pakada.instance(:i_dont_exist).should be_nil
22
+
23
+ Pakada.instances[:foo] = ins
24
+ Pakada.instance(:foo).should equal(ins)
33
25
  end
34
26
  end
35
27
 
36
- describe ".boot" do
37
- before :each do
38
- subject.stub :load_env
39
- subject.stub :load_modules
40
- subject.stub :load_hooks
41
-
42
- modules = [:foo, :bar, :baz].inject({}) do |modules, name|
43
- modules[name] = stub "#{name} module", :boot => nil
44
- modules
45
- end
46
- subject.stub(:modules).and_return modules
28
+ context ".instance(name, &block)" do
29
+ before do
30
+ Pakada.stub :instances => {
31
+ :foo => stub("instance: foo"),
32
+ :bar => stub("instance: bar")
33
+ }
47
34
  end
48
35
 
49
- it "calls .load_env" do
50
- subject.should_receive :load_env
51
- subject.boot
52
- end
53
-
54
- it "calls .load_modules" do
55
- subject.should_receive :load_modules
56
- subject.boot
57
- end
36
+ let(:foo) { Pakada.instances[:foo] }
37
+ let(:bar) { Pakada.instances[:bar] }
58
38
 
59
- it "calls .load_hooks" do
60
- subject.should_receive :load_hooks
61
- subject.boot
39
+ it "executes the block in the specified instance's context" do
40
+ Pakada.instance(:foo) {|ins|
41
+ ins.should equal(foo)
42
+ Pakada.instance.should equal(foo)
43
+
44
+ Pakada.instance(:bar) {|ins|
45
+ ins.should equal(bar)
46
+ Pakada.instance.should equal(bar)
47
+ }
48
+
49
+ Pakada.instance.should equal(foo)
50
+ }
62
51
  end
63
52
 
64
- it "calls #boot on each module" do
65
- subject.modules.each_value {|m| m.should_receive :boot }
66
- subject.boot
67
- end
53
+ let(:create) { stub "instance: create_me" }
54
+ let(:urls) { stub "urls" }
55
+ let(:modules) { stub "modules" }
56
+ let(:path) { stub "path" }
68
57
 
69
- it "doesn't call #boot in testing environments" do
70
- subject.stub(:env).and_return :testing
71
- subject.modules.each_value {|m| m.should_not_receive :boot }
72
- subject.boot
58
+ it "creates the specified instance if it doesn't exist" do
59
+ Pakada.should_receive(:new).with(:create_me,
60
+ :urls => urls, :modules => modules, :path => path
61
+ ).and_return create
62
+
63
+ Pakada.instance(:create_me) {|dsl|
64
+ dsl.urls, dsl.modules, dsl.path = urls, modules, path
65
+ }
66
+
67
+ Pakada.instances[:create_me].should equal(create)
73
68
  end
74
69
  end
75
70
 
76
- describe ".load_env" do
77
- before :each do
78
- @env = ENV["RACK_ENV"]
71
+ context ".boot" do
72
+ before do
73
+ Pakada.instance(:foo) {|ins|
74
+ ins.urls = "/", "/foo"
75
+ }
76
+ Pakada.instance(:bar) {}
79
77
  end
80
78
 
81
- after :each do
82
- ENV["RACK_ENV"] = @env
79
+ let(:foo) { Pakada.instance :foo }
80
+ let(:bar) { Pakada.instance :bar }
81
+ let(:env) { stub "env" }
82
+ let(:response) { stub "response" }
83
+
84
+ it "creates a Rack::URLMap with end-points for each instance" do
85
+ foo.should_receive(:call).twice {|e|
86
+ Pakada.instance.should equal(foo)
87
+ env.should equal(e)
88
+ response
89
+ }
90
+
91
+ Rack::URLMap.should_receive(:new) {|map|
92
+ map.each {|url, app|
93
+ foo.urls.should include(url)
94
+ app.call(env).should equal(response)
95
+ }
96
+ }
97
+
98
+ Pakada.boot
83
99
  end
84
100
 
85
- it "uses RACK_ENV to determine the runtime environment" do
86
- ENV["RACK_ENV"] = "something"
87
- subject.load_env
88
- subject.env.should == :something
101
+ it "calls #boot on each instance" do
102
+ foo.should_receive :boot
103
+ bar.should_receive :boot
104
+ Pakada.boot
89
105
  end
90
106
 
91
- it "is uses 'development' as the default environment" do
92
- ENV.delete "RACK_ENV"
93
- subject.load_env
94
- subject.env.should == :development
107
+ let(:default) { Pakada.instance :default }
108
+
109
+ it "creates a default instance if there are no others" do
110
+ Pakada.instances.clear
111
+
112
+ Rack::URLMap.should_receive(:new) {|map|
113
+ default.should_receive(:call) {
114
+ Pakada.instance.should == default
115
+ }
116
+ map["/"].call({})
117
+ }
118
+
119
+ Pakada.boot
95
120
  end
96
121
  end
97
122
 
98
- describe ".load_modules!" do
99
- it "loads the modules" do
100
- mod = double "module"
101
- modcls = double "module class", :module_name => :foo
102
- modcls.should_receive(:new) { mod }
103
- Pakada::Module.stub(:descendants) { [modcls] }
104
-
105
- subject.load_modules
106
- subject.modules[:foo].should == mod
123
+ context ".call(env)" do
124
+ let(:urlmap) { stub "url map" }
125
+ let(:env) { stub "env" }
126
+ let(:response) { stub "response" }
127
+
128
+ before do
129
+ Rack::URLMap.stub :new => urlmap
130
+ Pakada.boot
131
+ end
132
+
133
+ it "forwards to the URL map" do
134
+ urlmap.should_receive(:call) {|e|
135
+ e.should equal(env)
136
+ response
137
+ }
138
+ Pakada.call(env).should equal(response)
107
139
  end
108
140
  end
109
141
 
110
- describe ".load_hooks" do
111
- before :each do
112
- subject.stub(:modules).and_return({})
113
- [:foo, :bar].each do |m|
114
- subject.modules[m] = stub("#{m} module")
115
- end
116
- end
142
+ context ".env" do
143
+ after { ENV.delete "RACK_ENV" }
117
144
 
118
- it "calls #hooks on each module" do
119
- subject.modules.each_value {|m| m.should_receive :hooks }
120
- subject.load_hooks
145
+ it "reflects the runtime environment" do
146
+ ENV["RACK_ENV"] = "envenvenv"
147
+ Pakada.env.should equal(:envenvenv)
121
148
  end
122
149
 
123
- it "doesn't call #hooks in testing environments" do
124
- subject.stub(:env).and_return :testing
125
- subject.modules.each_value {|m| m.should_not_receive :hooks }
126
- subject.load_hooks
150
+ it "defaults to :development" do
151
+ Pakada.env.should equal(:development)
127
152
  end
128
153
  end
129
154
 
130
- describe ".[]" do
131
- it "forwards to .modules" do
132
- subject.stub(:modules).and_return :some_module => stub("module")
133
- subject[:some_module].should == subject.modules[:some_module]
155
+ context ".development?" do
156
+ it "is true if env == :development" do
157
+ Pakada.stub :env => :development
158
+ Pakada.development?.should be_true
134
159
  end
135
160
  end
136
161
 
137
- describe ".development?" do
138
- it "is a shortcut for .env == :development" do
139
- subject.stub(:env) { :development }
140
- subject.development?.should be_true
141
-
142
- subject.stub(:env) { :something }
143
- subject.development?.should be_false
162
+ context ".testing?" do
163
+ it "is true if env == :testing" do
164
+ Pakada.stub :env => :testing
165
+ Pakada.testing?.should be_true
144
166
  end
145
167
  end
146
168
 
147
- describe ".testing?" do
148
- it "is a shortcut for .env == :testing" do
149
- subject.stub(:env) { :testing }
150
- subject.testing?.should be_true
151
-
152
- subject.stub(:env) { :something }
153
- subject.testing?.should be_false
169
+ context ".staging?" do
170
+ it "is true if env == :staging" do
171
+ Pakada.stub :env => :staging
172
+ Pakada.staging?.should be_true
154
173
  end
155
174
  end
156
175
 
157
- describe ".production?" do
158
- it "is a shortcut for .env == :production" do
159
- subject.stub(:env) { :production }
160
- subject.production?.should be_true
161
-
162
- subject.stub(:env) { :something }
163
- subject.production?.should be_false
176
+ context ".production?" do
177
+ it "is true if env == :production" do
178
+ Pakada.stub :env => :production
179
+ Pakada.production?.should be_true
164
180
  end
165
181
  end
166
182
 
167
- describe ".middleware" do
168
- it "is a simple Array" do
169
- Array.should === subject.middleware
183
+ context "#initialize(name, config)" do
184
+ let(:module1) do
185
+ stub :new => stub, :pakada_name => :module1, :dependencies => [module2]
186
+ end
187
+ let(:module2) do
188
+ stub :new => stub, :pakada_name => :module2, :dependencies => [module3]
189
+ end
190
+ let(:module3) do
191
+ stub :new => stub, :pakada_name => :module3, :dependencies => []
192
+ end
193
+ let(:instance) { Pakada.new :foo, :modules => [module1] }
194
+
195
+ it "sets the instance name" do
196
+ instance.name.should equal(:foo)
197
+ end
198
+
199
+ it "resolves and initializes the modules" do
200
+ instance.modules[:module1].should equal(module1.new)
201
+ instance.modules[:module2].should equal(module2.new)
202
+ instance.modules[:module3].should equal(module3.new)
203
+ end
204
+
205
+ it "sets the URL list" do
206
+ ins = Pakada.new(:foo, :urls => "http://example.org")
207
+ ins.urls.should == ["http://example.org"]
170
208
  end
171
209
 
172
- it "is empty initially" do
173
- subject.middleware.should be_empty
210
+ it "expands the path" do
211
+ ins = Pakada.new(:foo, :path => "bar/foo")
212
+ ins.path.to_s.should == File.join(Dir.pwd, "bar/foo")
213
+ ins.path.absolute?.should be_true
174
214
  end
175
215
  end
176
216
 
177
- describe ".to_app" do
178
- before :each do
179
- @builder = stub "builder"
180
- Rack::Builder.stub(:new).and_return @builder
217
+ context "#initialize(name)" do
218
+ let(:instance) { Pakada.new :foo }
219
+
220
+ before do
221
+ 1.upto(5) {|i|
222
+ Pakada::Module.descendants << stub({
223
+ :new => stub("module#{i}"),
224
+ :pakada_name => :"module#{i}",
225
+ :dependencies => []
226
+ })
227
+ }
181
228
  end
182
-
183
- it "builds a Rack endpoint" do
184
- mw = proc do |pos|
185
- Class.new do
186
- attr_reader :app, :pos
187
- def initialize(app); @app, @pos = app, pos; end
188
- end
189
- end
190
- subject.stub(:middleware).and_return [mw.call(0), mw.call(1)]
191
-
192
- pending "what about middleware insertion order?"
229
+
230
+ it "uses an empty URL list" do
231
+ instance.urls.should be_empty
193
232
  end
194
233
 
195
- it "handles middleware arguments" do
196
- @builder.stub(:run)
197
- @builder.stub(:to_app)
234
+ it "uses all available modules" do
235
+ instance.should have(5).modules
236
+ end
237
+
238
+ it "uses instances/<name> as path" do
239
+ instance.path.to_s.should == File.join(Dir.pwd, "instances", "foo")
240
+ end
241
+ end
242
+
243
+ context "#initialize(name = :default)" do
244
+ it "uses the current directory as path" do
245
+ instance = Pakada.new(:default)
246
+ instance.path.to_s.should == Dir.pwd
247
+ end
248
+ end
249
+
250
+ context "#boot" do
251
+ let(:foo) { instance.modules[:foo] }
252
+ let(:bar) { instance.modules[:bar] }
253
+ let(:instance) { Pakada.instance :instance }
254
+
255
+ before do
256
+ Pakada.instance(:instance) {}
257
+ instance.stub :modules => {
258
+ :foo => stub("foo module"),
259
+ :bar => stub("bar module")
260
+ }
261
+ end
262
+
263
+ it "calls #hooks and #boot on each module" do
264
+ foo.should_receive :hooks
265
+ foo.should_receive :boot
266
+ bar.should_receive :hooks
267
+ bar.should_receive :boot
198
268
 
199
- mw = [
200
- [stub("mw#0"), stub("mw arg#0"), stub("mw arg#1")],
201
- stub("mw#1")
202
- ]
203
- subject.stub(:middleware).and_return mw
269
+ instance.boot
270
+ end
271
+
272
+ it "does nothing if env == :testing" do
273
+ Pakada.stub :env => :testing
274
+ foo.should_not_receive :hooks
275
+ foo.should_not_receive :boot
276
+ bar.should_not_receive :hooks
277
+ bar.should_not_receive :boot
204
278
 
205
- @builder.should_receive(:use).with *mw[0]
206
- @builder.should_receive(:use).with mw[1]
207
- subject.to_app
279
+ instance.boot
208
280
  end
209
281
  end
210
282
 
211
- describe ".call" do
212
- it "forwards to .to_app.call" do
213
- req, resp = stub("request"), stub("response")
214
- subject.stub(:to_app).and_return stub("app")
283
+ context "#call(env)" do
284
+ let(:instance) { Pakada.new :foo }
285
+ let(:env) { stub "env" }
286
+ let(:response) { stub "response" }
287
+
288
+ before { instance.stub :to_app => stub("#to_app") }
289
+
290
+ it "forwards to #to_app.call" do
291
+ instance.to_app.should_receive(:call) {|e|
292
+ e.should equal(env)
293
+ response
294
+ }
215
295
 
216
- subject.to_app.should_receive(:call).with(req).and_return resp
217
- subject.call(req).should == resp
296
+ instance.call(env).should equal(response)
218
297
  end
219
298
  end
220
299
 
221
- describe "::DEFAULT_APP" do
222
- it "calls .request" do
223
- req, resp = stub("request"), stub("response")
224
- Pakada.should_receive(:request).with(req).and_return resp
300
+ context "#to_app" do
301
+ let(:instance) { Pakada.new :foo }
302
+ let(:mw1) { stub "middleware#1" }
303
+ let(:mw2) { [stub("middleware#2"), stub("arg")] }
304
+ let(:app) { stub "app" }
305
+ let(:builder) { stub "builder" }
306
+ let(:env) { stub "env" }
307
+ let(:response) { stub "response" }
308
+
309
+ before { Rack::Builder.stub :new => builder }
310
+
311
+ it "builds a Rack app of middleware and endpoint" do
312
+ builder.should_receive(:use).with mw1
313
+ builder.should_receive(:use).with *mw2
314
+ builder.should_receive(:run) {|endpoint|
315
+ instance.should_receive(:request) {|e|
316
+ e.should equal(env)
317
+ response
318
+ }
319
+ endpoint.call(env).should equal(response)
320
+ }
321
+ builder.should_receive(:to_app) { app }
225
322
 
226
- Pakada::DEFAULT_APP.call(req).should == resp
323
+ instance.middleware << mw1 << mw2
324
+ instance.to_app.should equal(app)
227
325
  end
228
326
  end
229
327
 
230
- describe ".request" do
231
- it "prints a tender welcome greeting" do
232
- Pakada.request({})[2].body.to_s.should =~ /Pakada #{Pakada::VERSION}/
328
+ context "#request(env)" do
329
+ let(:response) { Pakada.instance(:default).request({}) }
330
+
331
+ before { Pakada.boot }
332
+
333
+ it "returns a tender greeting" do
334
+ response[0].should equal(200)
335
+ response[1]["Content-Type"].should == "text/html"
336
+ response[2].body.join.should match(/Pakada #{Pakada::VERSION}/)
233
337
  end
234
338
  end
235
339
  end
data/spec/spec_helper.rb CHANGED
@@ -11,5 +11,8 @@ begin
11
11
  rescue LoadError; end
12
12
 
13
13
  RSpec.configure do |config|
14
- config.before(:each) { Pakada.reset_instance }
14
+ config.before(:each) {
15
+ Pakada.instances.clear
16
+ Pakada::Module.descendants.clear
17
+ }
15
18
  end
metadata CHANGED
@@ -1,83 +1,102 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: pakada
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
4
5
  prerelease:
5
- version: 0.2.1
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Lars Gierth
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-05-06 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
12
+ date: 2011-08-01 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
16
15
  name: rack
17
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &80222130 !ruby/object:Gem::Requirement
18
17
  none: false
19
- requirements:
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
23
22
  type: :runtime
24
23
  prerelease: false
25
- version_requirements: *id001
26
- - !ruby/object:Gem::Dependency
24
+ version_requirements: *80222130
25
+ - !ruby/object:Gem::Dependency
27
26
  name: hooked
28
- requirement: &id002 !ruby/object:Gem::Requirement
27
+ requirement: &80221440 !ruby/object:Gem::Requirement
29
28
  none: false
30
- requirements:
31
- - - ~>
32
- - !ruby/object:Gem::Version
33
- version: "0.2"
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
34
33
  type: :runtime
35
34
  prerelease: false
36
- version_requirements: *id002
37
- - !ruby/object:Gem::Dependency
35
+ version_requirements: *80221440
36
+ - !ruby/object:Gem::Dependency
38
37
  name: rspec
39
- requirement: &id003 !ruby/object:Gem::Requirement
38
+ requirement: &80220860 !ruby/object:Gem::Requirement
40
39
  none: false
41
- requirements:
42
- - - ">="
43
- - !ruby/object:Gem::Version
44
- version: "0"
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
45
44
  type: :development
46
45
  prerelease: false
47
- version_requirements: *id003
48
- - !ruby/object:Gem::Dependency
46
+ version_requirements: *80220860
47
+ - !ruby/object:Gem::Dependency
49
48
  name: fakefs
50
- requirement: &id004 !ruby/object:Gem::Requirement
49
+ requirement: &80220160 !ruby/object:Gem::Requirement
51
50
  none: false
52
- requirements:
53
- - - ">="
54
- - !ruby/object:Gem::Version
55
- version: "0"
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
56
55
  type: :development
57
56
  prerelease: false
58
- version_requirements: *id004
59
- - !ruby/object:Gem::Dependency
57
+ version_requirements: *80220160
58
+ - !ruby/object:Gem::Dependency
60
59
  name: fakefs-require
61
- requirement: &id005 !ruby/object:Gem::Requirement
60
+ requirement: &80219690 !ruby/object:Gem::Requirement
62
61
  none: false
63
- requirements:
64
- - - ">="
65
- - !ruby/object:Gem::Version
66
- version: "0"
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
67
66
  type: :development
68
67
  prerelease: false
69
- version_requirements: *id005
68
+ version_requirements: *80219690
69
+ - !ruby/object:Gem::Dependency
70
+ name: yard
71
+ requirement: &80219040 !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: *80219040
80
+ - !ruby/object:Gem::Dependency
81
+ name: rdiscount
82
+ requirement: &80218590 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *80218590
70
91
  description:
71
- email:
92
+ email:
72
93
  - lars.gierth@gmail.com
73
94
  executables: []
74
-
75
95
  extensions: []
76
-
77
96
  extra_rdoc_files: []
78
-
79
- files:
97
+ files:
80
98
  - .rspec
99
+ - .yardopts
81
100
  - Gemfile
82
101
  - LICENSE
83
102
  - README.md
@@ -91,36 +110,26 @@ files:
91
110
  - spec/spec_helper.rb
92
111
  homepage: https://rubygems.org/gems/pakada
93
112
  licenses: []
94
-
95
113
  post_install_message:
96
114
  rdoc_options: []
97
-
98
- require_paths:
115
+ require_paths:
99
116
  - lib
100
- required_ruby_version: !ruby/object:Gem::Requirement
117
+ required_ruby_version: !ruby/object:Gem::Requirement
101
118
  none: false
102
- requirements:
103
- - - ">="
104
- - !ruby/object:Gem::Version
105
- hash: -121495153
106
- segments:
107
- - 0
108
- version: "0"
109
- required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ! '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
124
  none: false
111
- requirements:
112
- - - ">="
113
- - !ruby/object:Gem::Version
114
- hash: -121495153
115
- segments:
116
- - 0
117
- version: "0"
125
+ requirements:
126
+ - - ! '>='
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
118
129
  requirements: []
119
-
120
130
  rubyforge_project:
121
- rubygems_version: 1.8.1
131
+ rubygems_version: 1.8.6
122
132
  signing_key:
123
133
  specification_version: 3
124
- summary: Very Extensible HTTP Container For Ruby
134
+ summary: Modularization contract for web applications
125
135
  test_files: []
126
-