pakada 0.2.1 → 0.3.0

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