pakada 0.0.0 → 0.0.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.
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