cannon 0.0.2
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.
- checksums.yaml +7 -0
- data/.bundle/config +2 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/Gemfile +5 -0
- data/README +3 -0
- data/Rakefile +20 -0
- data/bin/bundler +16 -0
- data/bin/coderay +16 -0
- data/bin/htmldiff +16 -0
- data/bin/ldiff +16 -0
- data/bin/mustache +16 -0
- data/bin/pry +16 -0
- data/bin/rspec +16 -0
- data/cannon.gemspec +29 -0
- data/lib/cannon.rb +16 -0
- data/lib/cannon/app.rb +196 -0
- data/lib/cannon/concerns/path_cache.rb +48 -0
- data/lib/cannon/concerns/signature.rb +14 -0
- data/lib/cannon/config.rb +58 -0
- data/lib/cannon/cookie_jar.rb +99 -0
- data/lib/cannon/handler.rb +25 -0
- data/lib/cannon/middleware.rb +47 -0
- data/lib/cannon/middleware/content_type.rb +15 -0
- data/lib/cannon/middleware/cookies.rb +18 -0
- data/lib/cannon/middleware/files.rb +33 -0
- data/lib/cannon/middleware/flush_and_benchmark.rb +20 -0
- data/lib/cannon/middleware/request_logger.rb +13 -0
- data/lib/cannon/middleware/router.rb +19 -0
- data/lib/cannon/request.rb +36 -0
- data/lib/cannon/response.rb +165 -0
- data/lib/cannon/route.rb +80 -0
- data/lib/cannon/route_action.rb +92 -0
- data/lib/cannon/version.rb +3 -0
- data/lib/cannon/views.rb +28 -0
- data/spec/app_spec.rb +20 -0
- data/spec/config_spec.rb +41 -0
- data/spec/environments_spec.rb +68 -0
- data/spec/features/action_types_spec.rb +154 -0
- data/spec/features/cookies_spec.rb +62 -0
- data/spec/features/files_spec.rb +17 -0
- data/spec/features/method_types_spec.rb +104 -0
- data/spec/features/requests_spec.rb +59 -0
- data/spec/features/views_spec.rb +31 -0
- data/spec/fixtures/public/background.jpg +0 -0
- data/spec/fixtures/views/render_test.html +1 -0
- data/spec/fixtures/views/test.html +1 -0
- data/spec/spec_helper.rb +98 -0
- data/spec/support/cannon_test.rb +108 -0
- metadata +219 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 66079c33280a0b8397bb8e889127200e3afef3a9
|
4
|
+
data.tar.gz: c43167297db90ba74f2dbacbad97e16b0ce663e8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d9b3c0a33b211e8ca0480f197e6edb8d0a45f9bf31bdf01bcea9b6cd7bc095a51ef6435770286c961e83ed1c48ce9e9d5f4d18ade2f3974127b830dc682d6baf
|
7
|
+
data.tar.gz: ef901f8d5be2112104a7167cfa87e2a26884f4bbd6ee74133103148b609047b6f1d387815179fed3567e520b3e7d75f9ff3d92d52f0d52f93233d61b6b118082
|
data/.bundle/config
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'rubygems'
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'bundler'
|
10
|
+
Bundler::GemHelper.install_tasks
|
11
|
+
|
12
|
+
require 'rake'
|
13
|
+
require 'rspec/core/rake_task'
|
14
|
+
|
15
|
+
desc "Run all examples"
|
16
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
17
|
+
t.rspec_opts = %w[--color]
|
18
|
+
end
|
19
|
+
|
20
|
+
task :default => [:spec]
|
data/bin/bundler
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'bundler' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require "pathname"
|
10
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require "rubygems"
|
14
|
+
require "bundler/setup"
|
15
|
+
|
16
|
+
load Gem.bin_path("bundler", "bundler")
|
data/bin/coderay
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'coderay' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require "pathname"
|
10
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require "rubygems"
|
14
|
+
require "bundler/setup"
|
15
|
+
|
16
|
+
load Gem.bin_path("coderay", "coderay")
|
data/bin/htmldiff
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'htmldiff' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require "pathname"
|
10
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require "rubygems"
|
14
|
+
require "bundler/setup"
|
15
|
+
|
16
|
+
load Gem.bin_path("diff-lcs", "htmldiff")
|
data/bin/ldiff
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'ldiff' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require "pathname"
|
10
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require "rubygems"
|
14
|
+
require "bundler/setup"
|
15
|
+
|
16
|
+
load Gem.bin_path("diff-lcs", "ldiff")
|
data/bin/mustache
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'mustache' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require "pathname"
|
10
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require "rubygems"
|
14
|
+
require "bundler/setup"
|
15
|
+
|
16
|
+
load Gem.bin_path("mustache", "mustache")
|
data/bin/pry
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'pry' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require "pathname"
|
10
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require "rubygems"
|
14
|
+
require "bundler/setup"
|
15
|
+
|
16
|
+
load Gem.bin_path("pry", "pry")
|
data/bin/rspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rspec' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require "pathname"
|
10
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require "rubygems"
|
14
|
+
require "bundler/setup"
|
15
|
+
|
16
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/cannon.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cannon/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'cannon'
|
8
|
+
spec.version = Cannon::VERSION
|
9
|
+
spec.authors = ['Joe Osburn']
|
10
|
+
spec.email = ['joe@jnodev.com']
|
11
|
+
|
12
|
+
spec.summary = %q{Cannon is a fast web framework}
|
13
|
+
spec.homepage = 'https://github.com/joeosburn/cannon'
|
14
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
15
|
+
spec.bindir = 'exe'
|
16
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
17
|
+
spec.require_paths = ['lib']
|
18
|
+
spec.test_files = Dir['spec/**/*']
|
19
|
+
|
20
|
+
spec.add_dependency 'eventmachine', '~> 1.0.8'
|
21
|
+
spec.add_dependency 'eventmachine_httpserver'
|
22
|
+
spec.add_dependency 'mime-types', '~> 2.6.2'
|
23
|
+
spec.add_dependency 'mustache', '~> 1.0.2'
|
24
|
+
spec.add_dependency 'pry', '~> 0.10.3'
|
25
|
+
spec.add_dependency 'msgpack', '~> 0.7.1'
|
26
|
+
|
27
|
+
spec.add_development_dependency 'rspec', '~> 3.3.0'
|
28
|
+
spec.add_development_dependency 'http-cookie', '~> 1.0.2'
|
29
|
+
end
|
data/lib/cannon.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'evma_httpserver'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
require 'cannon/concerns/path_cache'
|
6
|
+
require 'cannon/concerns/signature'
|
7
|
+
require 'cannon/config'
|
8
|
+
require 'cannon/views'
|
9
|
+
require 'cannon/request'
|
10
|
+
require 'cannon/cookie_jar'
|
11
|
+
require 'cannon/response'
|
12
|
+
require 'cannon/middleware'
|
13
|
+
require 'cannon/route_action'
|
14
|
+
require 'cannon/route'
|
15
|
+
require 'cannon/handler'
|
16
|
+
require 'cannon/app'
|
data/lib/cannon/app.rb
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
require 'mime/types'
|
2
|
+
require 'pry'
|
3
|
+
|
4
|
+
module Cannon
|
5
|
+
class AlreadyListening < StandardError; end
|
6
|
+
|
7
|
+
class App
|
8
|
+
attr_reader :routes, :app_binding, :cache
|
9
|
+
|
10
|
+
def initialize(app_binding, port: nil, ip_address: nil, &block)
|
11
|
+
@app_binding = app_binding
|
12
|
+
@routes = []
|
13
|
+
@load_environment = block
|
14
|
+
@cache = {}
|
15
|
+
|
16
|
+
config.port = port unless port.nil?
|
17
|
+
config.ip_address = ip_address unless ip_address.nil?
|
18
|
+
|
19
|
+
define_cannon_environment
|
20
|
+
define_cannon_root
|
21
|
+
define_cannon_mime_type
|
22
|
+
define_cannon_config
|
23
|
+
define_cannon_configure_method
|
24
|
+
define_cannon_logger
|
25
|
+
define_cannon_cache
|
26
|
+
end
|
27
|
+
|
28
|
+
%w{get post put patch delete head all}.each do |http_method|
|
29
|
+
define_method(http_method) do |path, action: nil, actions: nil, redirect: nil, &block|
|
30
|
+
add_route(path, method: http_method.to_sym, action: action, actions: actions, redirect: redirect, &block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def listen(port: config.port, ip_address: config.ip_address, async: false)
|
35
|
+
cannon_app = self
|
36
|
+
|
37
|
+
if ENV['CONSOLE']
|
38
|
+
command_set = Pry::CommandSet.new {}
|
39
|
+
Pry.start binding, commands: command_set
|
40
|
+
exit
|
41
|
+
end
|
42
|
+
|
43
|
+
raise AlreadyListening, 'App is currently listening' unless @server_thread.nil?
|
44
|
+
|
45
|
+
Cannon::Handler.define_singleton_method(:app) { cannon_app }
|
46
|
+
|
47
|
+
$LOAD_PATH << Cannon.root
|
48
|
+
reload_environment unless config.reload_on_request
|
49
|
+
|
50
|
+
server_block = ->(notifier) do
|
51
|
+
EventMachine::run {
|
52
|
+
server = EventMachine::start_server(ip_address, port, Cannon::Handler)
|
53
|
+
notifier << server unless notifier.nil? # notify the calling thread that the server started if async
|
54
|
+
Cannon.logger.info "Cannon listening on port #{port}..."
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
if async
|
59
|
+
notification = Queue.new
|
60
|
+
Thread.abort_on_exception = true
|
61
|
+
@server_thread = Thread.new { server_block.call(notification) }
|
62
|
+
notification.pop
|
63
|
+
else
|
64
|
+
server_block.call(nil)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def stop
|
69
|
+
return if @server_thread.nil?
|
70
|
+
EventMachine::stop_event_loop
|
71
|
+
@server_thread.join(10)
|
72
|
+
@server_thread.kill unless @server_thread.stop?
|
73
|
+
Thread.abort_on_exception = false
|
74
|
+
@server_thread = nil
|
75
|
+
Cannon.logger.info "Cannon no longer listening"
|
76
|
+
end
|
77
|
+
|
78
|
+
def reload_environment
|
79
|
+
@load_environment.call unless @load_environment.nil?
|
80
|
+
end
|
81
|
+
|
82
|
+
def configure(*environments, &block)
|
83
|
+
environments.each do |environment|
|
84
|
+
define_env_helper(environment)
|
85
|
+
yield config if Cannon.env == environment.to_s
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def config
|
90
|
+
@config ||= Config.new
|
91
|
+
end
|
92
|
+
|
93
|
+
def env
|
94
|
+
@env ||= detect_env
|
95
|
+
end
|
96
|
+
|
97
|
+
def middleware_runner
|
98
|
+
@middleware_runner ||= build_middleware_runner(prepared_middleware_stack)
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def prepared_middleware_stack
|
104
|
+
stack = config.middleware.dup
|
105
|
+
stack << 'FlushAndBenchmark'
|
106
|
+
end
|
107
|
+
|
108
|
+
def build_middleware_runner(middleware, callback: nil)
|
109
|
+
return callback if middleware.size < 1
|
110
|
+
|
111
|
+
middleware_runner = MiddlewareRunner.new(middleware.pop, callback: callback, app: self)
|
112
|
+
build_middleware_runner(middleware, callback: middleware_runner)
|
113
|
+
end
|
114
|
+
|
115
|
+
def add_route(path, method:, action:, actions:, redirect:, &block)
|
116
|
+
route = Route.new(self, method: method, path: path, actions: [block, action, actions].flatten.compact, redirect: redirect)
|
117
|
+
routes << route
|
118
|
+
extra_router(route)
|
119
|
+
end
|
120
|
+
|
121
|
+
def detect_env
|
122
|
+
ENV['CANNON_ENV'] ? ENV['CANNON_ENV'].dup : 'development'
|
123
|
+
end
|
124
|
+
|
125
|
+
def define_cannon_environment
|
126
|
+
cannon_method(:env, self.env)
|
127
|
+
define_env_helper(self.env)
|
128
|
+
end
|
129
|
+
|
130
|
+
def define_env_helper(env)
|
131
|
+
helper_method = "#{env}?"
|
132
|
+
return if Cannon.env.respond_to?(helper_method)
|
133
|
+
Cannon.env.singleton_class.send(:define_method, helper_method) { Cannon.env == env }
|
134
|
+
end
|
135
|
+
|
136
|
+
def define_cannon_config
|
137
|
+
cannon_method(:config, self.config)
|
138
|
+
end
|
139
|
+
|
140
|
+
def define_cannon_configure_method
|
141
|
+
app = self
|
142
|
+
Cannon.send(:define_method, :configure, ->(*environments, &block) { app.configure(*environments, &block) })
|
143
|
+
Cannon.send(:module_function, :configure)
|
144
|
+
end
|
145
|
+
|
146
|
+
def define_cannon_root
|
147
|
+
cannon_method(:root, @app_binding.eval('File.expand_path(File.dirname(__FILE__))'))
|
148
|
+
end
|
149
|
+
|
150
|
+
def define_cannon_cache
|
151
|
+
cannon_method(:cache, self.cache)
|
152
|
+
end
|
153
|
+
|
154
|
+
def define_cannon_logger
|
155
|
+
app = self
|
156
|
+
Cannon.send(:define_method, :logger, -> { app.config.logger })
|
157
|
+
Cannon.send(:module_function, :logger)
|
158
|
+
end
|
159
|
+
|
160
|
+
def define_cannon_mime_type
|
161
|
+
Cannon.send(:define_method, :mime_type, ->(filepath) { MIME::Types.type_for(filepath.split('/').last).first })
|
162
|
+
Cannon.send(:module_function, :mime_type)
|
163
|
+
end
|
164
|
+
|
165
|
+
def cannon_method(name, value)
|
166
|
+
Cannon.send(:define_method, name.to_sym, -> { value })
|
167
|
+
Cannon.send(:module_function, name.to_sym)
|
168
|
+
end
|
169
|
+
|
170
|
+
def extra_router(route)
|
171
|
+
ExtraRouter.new(self, route)
|
172
|
+
end
|
173
|
+
|
174
|
+
class ExtraRouter
|
175
|
+
def initialize(app, route)
|
176
|
+
@app = app
|
177
|
+
@route = route
|
178
|
+
end
|
179
|
+
|
180
|
+
def handle(&block)
|
181
|
+
@route.add_route_action(block)
|
182
|
+
self
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
module Kernel
|
189
|
+
def reload(lib)
|
190
|
+
if old = $LOADED_FEATURES.find{ |path| path=~/#{Regexp.escape lib}(\.rb)?\z/ }
|
191
|
+
load old
|
192
|
+
else
|
193
|
+
require lib
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module PathCache
|
2
|
+
def self.included(base)
|
3
|
+
base.send(:attr_accessor, :base_path, :cache)
|
4
|
+
base.send(:attr_reader, :path_array)
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def file_and_content_type(filepath)
|
10
|
+
if @app.cache[cache].include?(filepath)
|
11
|
+
@app.cache[cache][filepath]
|
12
|
+
else
|
13
|
+
@app.cache[cache][filepath] = read_file_and_content_type(filepath)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def read_file_and_content_type(filepath)
|
18
|
+
[IO.binread(filepath), Cannon.mime_type(filepath)]
|
19
|
+
end
|
20
|
+
|
21
|
+
def outdated_cache?
|
22
|
+
if @app.config.reload_on_request
|
23
|
+
@last_path_signature != current_path_signature
|
24
|
+
else
|
25
|
+
@last_path_signature.nil?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def reload_cache
|
30
|
+
@last_path_signature = current_path_signature
|
31
|
+
@path_array = build_path_array
|
32
|
+
@app.cache[cache] = {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def current_path_signature
|
36
|
+
Dir.glob("#{base_path}/**/*").map do |name|
|
37
|
+
[name, File.mtime(name)].to_s
|
38
|
+
end.inject(Digest::SHA512.new) do |digest, x|
|
39
|
+
digest.update x
|
40
|
+
end.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_path_array
|
44
|
+
Dir.glob("#{base_path}/**/*").reject { |file| File.directory?(file) }.collect do |name|
|
45
|
+
name.gsub(/^#{base_path}/, '')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|