pakada 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile CHANGED
@@ -1,12 +1,5 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- group :pakada do
4
- gem "pakada", :path => "."
5
- end
6
-
7
- group :testing do
8
- gem "mocha"
9
- gem "fakefs"
10
- gem "fakefs-require"
11
- end
3
+ gemspec
12
4
 
5
+ gem "awesome_print"
data/Rakefile CHANGED
@@ -1,4 +1,9 @@
1
1
  require "bundler"
2
- Bundler.setup(:pakada)
2
+ Bundler.setup :development
3
3
 
4
- require "pakada/tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ task :default => :spec
7
+ RSpec::Core::RakeTask.new :spec
8
+
9
+ Bundler::GemHelper.install_tasks
data/lib/pakada/module.rb CHANGED
@@ -1,81 +1,65 @@
1
1
  class Pakada
2
2
  module Module
3
- @descendants = Dictionary.new
3
+ @descendants = {}
4
4
 
5
5
  class << self
6
6
  attr_reader :descendants
7
7
 
8
- def prepare
9
- specs = []
10
- Gem.loaded_specs.each do |name, spec|
11
- next unless name =~ Regexp.new("^#{gem_prefix}")
12
- i = $LOAD_PATH.find_index(spec.load_paths.first)
13
- specs[i] = spec
14
- end
15
- specs.delete(nil)
8
+ def included(klass)
9
+ klass.send :include, Hooked::Container
10
+
11
+ klass.singleton_class.class_eval { attr_reader :module_name, :path }
16
12
 
17
- specs.reverse.each do |spec|
18
- name = spec.name[gem_prefix.length..-1].to_sym
19
- @descendants[name] = {
20
- :gemspec => spec,
21
- :klass => nil,
22
- :path => spec.full_gem_path,
23
- } unless @descendants[name]
13
+ unless klass.module_name
14
+ klass.instance_variable_set :@module_name, detect_name(klass.to_s)
15
+ end
16
+ unless klass.module_name
17
+ raise "Could not detect name for module #{klass}"
24
18
  end
25
- end
26
-
27
- def load
28
- prepare
29
19
 
30
- @descendants.each do |name, mod|
31
- next if mod[:klass]
32
- require(mod[:gemspec].full_gem_path + "/module")
20
+ unless klass.path
21
+ # use Kernel.caller instead of self.caller so Mocha doesn't explode...
22
+ file = Kernel.caller[0].split(":")[0]
23
+ klass.instance_variable_set :@path, detect_path(file)
33
24
  end
25
+
26
+ @descendants[klass.module_name] = klass
34
27
  end
35
28
 
36
- def gem_prefix
37
- @gem_prefix ||= "pakada" + Pakada::VERSION.split(".")[0] + "-"
29
+ def detect_name(klass)
30
+ name = klass.gsub(/^Pakada::/, "").
31
+ gsub(/([A-Z]+)([A-Z][a-z])/, "\\1_\\2").
32
+ gsub(/([a-z])([A-Z])/, "\\1_\\2").
33
+ gsub(/::/, "_").
34
+ downcase
35
+ return name.to_sym if name.match /^[a-z0-9_]+$/
38
36
  end
39
37
 
40
- def included(klass)
41
- klass.class_eval { @hooks = {} }
42
- klass.extend(ClassMethods)
38
+ def detect_path(file)
39
+ filepath = nil
40
+ filepath = file if file =~ /^\// && File.exists?(file)
41
+ $LOAD_PATH.each do |dir|
42
+ x = File.join dir, file
43
+ if File.exists? x
44
+ filepath = File.expand_path x
45
+ break
46
+ end
47
+ end unless filepath
48
+ return unless filepath
43
49
 
44
- name = klass.to_s.underscore.split("_")[0..-2].join("_")
45
- @descendants[name.to_sym][:klass] = klass
50
+ segments = File.dirname(filepath).split("/").reverse
51
+ pos = segments.find_index("lib") || -1
52
+
53
+ segments[(pos + 1)..-1].reverse.join "/"
46
54
  end
47
55
  end
48
56
 
49
- def hook(name, *args)
50
- hooks = self.class.class_eval { @hooks }
51
- hooks[name].each {|h| instance_exec(*args, &h) } if hooks[name]
52
- end
53
-
54
- def version
55
- return self.class.version
57
+ def module_name
58
+ self.class.module_name
56
59
  end
57
60
 
58
61
  def path
59
- return self.class.path
60
- end
61
-
62
- module ClassMethods
63
- def hook(name, &block)
64
- @hooks[name] ||= []
65
- @hooks[name] << block
66
- end
67
-
68
- def name
69
- return @name ||= Pakada::Module.descendants.find {|m| m[1][:klass] == self }[0]
70
- end
71
-
72
- def version
73
- return Pakada::Module.descendants[name][:gemspec].version.to_s
74
- end
75
-
76
- def path
77
- return Pakada::Module.descendants[name][:path]
78
- end
62
+ self.class.path
79
63
  end
80
64
  end
81
65
  end
@@ -1,3 +1,3 @@
1
1
  class Pakada
2
- VERSION = Gem.loaded_specs["pakada"].version.to_s
2
+ VERSION = "0.0.1"
3
3
  end
data/lib/pakada.rb CHANGED
@@ -1,113 +1,97 @@
1
1
  require "rack"
2
- require "pakada/support"
2
+ require "hooked"
3
3
 
4
- require "pakada/application"
5
4
  require "pakada/module"
6
5
  require "pakada/version"
7
6
 
8
7
  class Pakada
9
- class << self
10
- attr_reader :modules, :middleware
11
- attr_writer :env
12
-
13
- def boot(env = nil)
14
- @env = env.to_sym if env
15
-
16
- load_middleware
17
- load_modules if @modules.empty?
18
-
19
- hook(:boot) unless testing?
20
- end
21
-
22
- def load_middleware
23
- config = YAML.load_file("config/middleware.yml") rescue {}
24
- @middleware = Rack::Builder.new
25
- config[env].each do |klass|
26
- args = []
27
- klass, args = klass.to_a[0] if klass.is_a?(Hash)
28
- if args.is_a?(Array)
29
- @middleware.use(klass.constantize, *args)
30
- else
31
- @middleware.use(klass.constantize, args)
32
- end
33
- end if config[env]
34
- end
35
-
36
- def load_modules
37
- Module.load
38
- @modules = Dictionary.new
39
- @hooks = {}
40
- Module.descendants.each do |name, mod|
41
- @modules[name] = mod[:klass].new
42
- @modules[name].hook(:activate) unless testing?
43
- end
44
- end
45
-
46
- def [](name)
47
- return modules[name]
48
- end
49
-
50
- def []=(name, mod)
51
- modules[name] = mod
52
- end
53
-
54
- def hook(name, *args)
55
- catch(:halt) { @modules.each_value {|mod| mod.hook(name, *args) } }
56
- end
57
-
58
- def hook_reverse(name, *args)
59
- catch(:halt) { @modules.reverse.each_value {|mod| mod.hook(name, *args) } }
60
- end
61
-
62
- def app
63
- unless @app
64
- @middleware.run(Application)
65
- @app = @middleware.to_app
66
- end
67
- return @app
68
- end
69
-
70
- def request
71
- return Application.request
72
- end
73
-
74
- def response
75
- return Application.response
76
- end
77
-
78
- def reset
79
- @env = nil
80
- @middleware = Rack::Builder.new
81
- @modules = Dictionary.new
82
- @app = nil
83
- end
84
-
85
- def shutdown
86
- hook_reverse(:shutdown) unless testing?
87
-
88
- reset
89
- end
8
+ include Hooked::Container
9
+
10
+ DEFAULT_APP = proc do |req_env|
11
+ Rack::Response.new("Hi, I'm Pakada #{VERSION}").finish
12
+ end
13
+
14
+ attr_reader :env, :modules, :hooks, :middleware
15
+ attr_accessor :app
16
+
17
+ def initialize
18
+ @middleware = []
19
+ end
20
+
21
+ def development?
22
+ env == :development
23
+ end
24
+
25
+ def testing?
26
+ env == :testing
27
+ end
28
+
29
+ def production?
30
+ env == :production
31
+ end
32
+
33
+ def boot
34
+ load_env
35
+ load_modules
36
+ load_hooks
90
37
 
91
- def env
92
- return @env ||= (ENV["RACK_ENV"] || :development).to_sym
38
+ hooks.invoke(:boot) unless testing?
39
+ end
40
+
41
+ def load_env
42
+ @env = (ENV["RACK_ENV"] || :development).to_sym
43
+ end
44
+
45
+ def load_modules
46
+ @modules = Pakada::Module.descendants.values.inject({}) do |h, m|
47
+ h[m.module_name] = m.new; h
93
48
  end
94
-
95
- def development?
96
- return env == :development
49
+ end
50
+
51
+ def load_hooks
52
+ raise "modules have not been loaded" unless modules.respond_to? :values
53
+ @hooks = Hooked::Controller.new self, *modules.values
54
+ end
55
+
56
+ def call(req_env)
57
+ to_app.call(req_env)
58
+ end
59
+
60
+ def to_app
61
+ builder = Rack::Builder.new
62
+ middleware.each {|mw| builder.use *(Array === mw ? mw : [mw]) }
63
+ builder.run proc {|req_env| handle_request(req_env) }
64
+ builder
65
+ end
66
+
67
+ def handle_request(req_env)
68
+ req = Rack::Request.new req_env
69
+ hooks.invoke(:request, req).finish
70
+ end
71
+
72
+ hookable :boot do |nothing|; end
73
+
74
+ hookable :request do |ctx|
75
+ resp = (app || DEFAULT_APP).call ctx.args.env
76
+ ctx.result = Rack::Response.new resp[2], *resp[0..1]
77
+ end
78
+
79
+ class << self
80
+ def instance
81
+ @instance ||= new
97
82
  end
98
83
 
99
- def testing?
100
- return env == :testing
84
+ def reset_instance
85
+ @instance = nil
101
86
  end
102
87
 
103
- def staging?
104
- return env == :staging
88
+ def method_missing(m, *args, &block)
89
+ super(m, *args, &block) unless instance.respond_to? m
90
+ instance.send m, *args, &block
105
91
  end
106
92
 
107
- def production?
108
- return env == :production
93
+ def respond_to?(m)
94
+ super(m) || instance.respond_to?(m)
109
95
  end
110
96
  end
111
-
112
- reset
113
97
  end
data/pakada.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "pakada/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "pakada"
7
+ s.version = Pakada::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Lars Gierth"]
10
+ s.email = ["lars.gierth@gmail.com"]
11
+ s.homepage = "http://rubygems.org/gems/pakada"
12
+ s.summary = %q{Very Extensible HTTP Container For Ruby}
13
+ #s.description = %q{TODO: Write a gem description}
14
+
15
+ s.add_dependency "rack", "~> 1.2.1"
16
+ s.add_dependency "hooked", "~> 0.1.0"
17
+
18
+ s.add_development_dependency "rspec", "~> 2.3"
19
+ s.add_development_dependency "mocha", "~> 0.9.10"
20
+ s.add_development_dependency "fakefs", "~> 0.3.1"
21
+ s.add_development_dependency "fakefs-require", "~> 0.2.1"
22
+
23
+ s.files = `git ls-files`.split("\n") - [".gitignore", ".rvmrc", "config.ru"]
24
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
26
+ s.require_paths = ["lib"]
27
+ end
@@ -0,0 +1,180 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require "spec_helper"
4
+
5
+ describe Pakada::Module do
6
+ describe ".descendants" do
7
+ it "keeps a list containing its descendants" do
8
+ Pakada::Module.descendants.should == {}
9
+ end
10
+ end
11
+
12
+ describe ".detect_path" do
13
+ before :each do
14
+ FakeFS.activate!
15
+ FakeFS::Require.activate! :fallback => true
16
+ end
17
+
18
+ after :each do
19
+ FakeFS::Require.deactivate!
20
+ FakeFS.deactivate!
21
+ FakeFS::FileSystem.clear
22
+ end
23
+
24
+ it "loops through the load path to find a matching path" do
25
+ m = Pakada::Module
26
+
27
+ path = "/my/path"
28
+ FileUtils.mkdir_p path + "/lib"
29
+ FileUtils.touch path + "/lib/some_module.rb"
30
+ $LOAD_PATH << path + "/lib"
31
+
32
+ m.detect_path("some_module.rb").should == path
33
+ m.detect_path(path + "/lib/some_module.rb").should == path
34
+ $LOAD_PATH.delete path + "/lib"
35
+
36
+ m.detect_path("config.ru").should be_nil
37
+ end
38
+ end
39
+
40
+ describe ".detect_name" do
41
+ it "strips off Pakada:: and normalizes the klass name" do
42
+ m = Pakada::Module
43
+
44
+ m.detect_name("Pakada::SomeModule::FOO").should == :some_module_foo
45
+ m.detect_name("My_Module").should == :my_module
46
+
47
+ m.detect_name("L337").should == :l337
48
+ m.detect_name("Läö7").should be_nil
49
+ end
50
+ end
51
+
52
+ describe ".included hook" do
53
+ before :each do
54
+ FakeFS.activate!
55
+ FakeFS::Require.activate! :fallback => true
56
+ end
57
+
58
+ after :each do
59
+ Pakada::Module.descendants.clear
60
+
61
+ FakeFS::Require.deactivate!
62
+ FakeFS.deactivate!
63
+ FakeFS::FileSystem.clear
64
+ end
65
+
66
+ it "tries to determine the module's name" do
67
+ klass = Class.new
68
+ Pakada::Module.should_receive(:detect_name).with(klass.to_s).once { :some_module }
69
+ klass.send(:include, Pakada::Module)
70
+ klass.module_name.should == :some_module
71
+ end
72
+
73
+ it "crashes if it can't detect a name" do
74
+ klass = Class.new
75
+ Pakada::Module.should_receive(:detect_name).with(klass.to_s).once { nil }
76
+ proc { klass.send(:include, Pakada::Module) }.should raise_error(RuntimeError)
77
+ end
78
+
79
+ it "respects an already set name" do
80
+ Pakada::Module.should_not_receive(:detect_name)
81
+ klass = Class.new do
82
+ @module_name = :some_module
83
+ include Pakada::Module
84
+ end
85
+ klass.module_name.should == :some_module
86
+ end
87
+
88
+ it "tries to determine the module's path" do
89
+ path = "/my/path"
90
+ file = path + "/lib/some_module.rb"
91
+
92
+ Kernel.should_receive(:mock_caller).once { [file + ":1: in `something'"] }
93
+ Kernel.instance_eval do
94
+ alias orig_caller caller
95
+ alias caller mock_caller
96
+ end
97
+
98
+ Pakada::Module.should_receive(:detect_path).with(file).once { path }
99
+ klass = Class.new do
100
+ @module_name = :some_module
101
+ include Pakada::Module
102
+ end
103
+ klass.path.should == path
104
+
105
+ Kernel.instance_eval { alias caller orig_caller }
106
+ end
107
+
108
+ it "respects an already set path" do
109
+ Pakada::Module.should_not_receive(:detect_path)
110
+ klass = Class.new do
111
+ @module_name = :some_module
112
+ @path = "/my/path"
113
+ include Pakada::Module
114
+ end
115
+ klass.path.should == "/my/path"
116
+ end
117
+
118
+ it "adds the module to the list of descendants" do
119
+ klass = Class.new do
120
+ @module_name = :some_module
121
+ include Pakada::Module
122
+ end
123
+ Pakada::Module.descendants[:some_module].should == klass
124
+ end
125
+ end
126
+ end
127
+
128
+ describe "SomeModule" do
129
+ before :each do
130
+ @klass = Class.new do
131
+ @module_name = :foo
132
+ @path = "/opt/pakada"
133
+ include Pakada::Module
134
+ end
135
+ @obj = @klass.new
136
+ end
137
+
138
+ after :each do
139
+ @klass, @obj = nil
140
+ Pakada::Module.descendants.clear
141
+ end
142
+
143
+ it "is a hooking container" do
144
+ @obj.should respond_to(:hooked)
145
+ @obj.should respond_to(:hooked=)
146
+ @obj.should respond_to(:hookable)
147
+ end
148
+
149
+ describe ".module_name" do
150
+ it "contains the module's name" do
151
+ @klass.module_name.should == :foo
152
+ end
153
+
154
+ it "is read-only" do
155
+ proc { @klass.module_name = :bar }.should raise_error(NoMethodError)
156
+ end
157
+ end
158
+
159
+ describe ".path" do
160
+ it "contains the module's base path" do
161
+ @klass.path.should == "/opt/pakada"
162
+ end
163
+
164
+ it "is read-only" do
165
+ proc { @klass.path = "/tmp/pkd" }.should raise_error(NoMethodError)
166
+ end
167
+ end
168
+
169
+ describe "#module_name" do
170
+ it "is a shortcut for ::module_name" do
171
+ @obj.module_name.should == @klass.module_name
172
+ end
173
+ end
174
+
175
+ describe "#path" do
176
+ it "is a shortcut for ::path" do
177
+ @obj.path.should == @klass.path
178
+ end
179
+ end
180
+ end