swee 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.
- checksums.yaml +7 -0
- data/README.md +212 -0
- data/Rakefile +4 -0
- data/bin/swee +38 -0
- data/doc/tmp.rb +77 -0
- data/lib/swee.rb +5 -0
- data/lib/swee/app_executor.rb +152 -0
- data/lib/swee/config.rb +118 -0
- data/lib/swee/connection.rb +48 -0
- data/lib/swee/controller.rb +158 -0
- data/lib/swee/controller_filter.rb +67 -0
- data/lib/swee/daemonize.rb +17 -0
- data/lib/swee/engine.rb +201 -0
- data/lib/swee/exception.rb +2 -0
- data/lib/swee/helper.rb +29 -0
- data/lib/swee/installer.rb +58 -0
- data/lib/swee/lodder.rb +92 -0
- data/lib/swee/middlewaves/common_logger.rb +29 -0
- data/lib/swee/middlewaves/content_length.rb +21 -0
- data/lib/swee/middlewaves/reloader.rb +24 -0
- data/lib/swee/patches/logger.rb +15 -0
- data/lib/swee/routes.rb +46 -0
- data/lib/swee/server.rb +298 -0
- data/lib/swee/support.rb +79 -0
- data/lib/swee/swee_logger.rb +59 -0
- data/lib/swee/thin/headers.rb +40 -0
- data/lib/swee/thin/request.rb +162 -0
- data/lib/swee/thin/response.rb +177 -0
- data/lib/swee/version.rb +3 -0
- data/lib/swee/view.rb +36 -0
- data/lib/template/config.rb +38 -0
- data/lib/template/controllers/HomeController.rb +11 -0
- data/lib/template/public/404.html +66 -0
- data/lib/template/routes.rb +11 -0
- data/lib/template/todo_config.rb +75 -0
- data/lib/template/views/home/index.erb +2 -0
- metadata +164 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c6d3fb00b524dff4a95f163d99ce8de1499007de
|
4
|
+
data.tar.gz: 64cf3eb2bfe632c48dcf730daf801516590cdfbc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1d61382816067f99614e37f005f91df500e50381323de38e8c469157b05d809ab01d8f62e61d8f0bf636d6b702ac8f3c4686c88a9bfe21dbe7ed15c3a60fd537
|
7
|
+
data.tar.gz: af69221ebecf0aec1211765ec80ec7bfc782c2f2f64e6367e7bfff21316a68c46e65b1693e2f34b39af276aca84c0b2d3cf5767103cdcc1acac38710dbfba8a9
|
data/README.md
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
# Swee
|
2
|
+
|
3
|
+
Swee 是一个轻量级的 ruby web 框架, 包含一个http服务器。
|
4
|
+
目前还是demo阶段, 供学习和参考使用
|
5
|
+
|
6
|
+
## 特性 和 实现方式
|
7
|
+
|
8
|
+
1. 底层接受http请求 基于 EventMachine 的 event loop
|
9
|
+
2. 内部一些特性都是基于 event loop 如: 服务器重启, 以及代码reload等特性
|
10
|
+
3. 处理请求并包装为 request 结构暂时使用的是 Thin 的 http_parser
|
11
|
+
4. 应用层轻量级包装 实现 route 和 controller 目前支持 rails 的一些特性
|
12
|
+
5. 使用部分 rack 的 middlewaves 和 一些自己实现的 middlewaves
|
13
|
+
|
14
|
+
## 安装
|
15
|
+
|
16
|
+
请使用ruby2.1.0或以上版本, 然后安装 swee 的Gem
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem 'swee'
|
20
|
+
```
|
21
|
+
|
22
|
+
## 使用方法
|
23
|
+
|
24
|
+
生成项目
|
25
|
+
生成以后会创建一个项目目录和一些项目必须的文件
|
26
|
+
随后会贴心的自动开启一个服务器
|
27
|
+
打开您最喜欢的浏览器输入 http://localhost:3000
|
28
|
+
可以看到一个 hello 的欢迎页面
|
29
|
+
|
30
|
+
```console
|
31
|
+
swee new myproj
|
32
|
+
```
|
33
|
+
|
34
|
+
运行swee命令行
|
35
|
+
|
36
|
+
```console
|
37
|
+
swee c
|
38
|
+
```
|
39
|
+
|
40
|
+
启动服务器
|
41
|
+
|
42
|
+
```console
|
43
|
+
swee s
|
44
|
+
```
|
45
|
+
|
46
|
+
端口和环境参数
|
47
|
+
|
48
|
+
```console
|
49
|
+
swee s -p 8080 -e production
|
50
|
+
```
|
51
|
+
|
52
|
+
## 配置服务器
|
53
|
+
|
54
|
+
打开项目下的 config.rb 并配置它
|
55
|
+
|
56
|
+
先配置 server_config 部分
|
57
|
+
|
58
|
+
配置日志文件格式: [日志文件位置,文件保留数量,每个文件的大小]
|
59
|
+
```ruby
|
60
|
+
cfg.log_file = [ "./logs/development.log", 10, 10240000 ]
|
61
|
+
```
|
62
|
+
|
63
|
+
配置日志等级
|
64
|
+
```ruby
|
65
|
+
cfg.logger_level = :debug
|
66
|
+
```
|
67
|
+
|
68
|
+
配置重启模式
|
69
|
+
:touch => 请配置 touch_file, 并指定 restart.txt 文件位置
|
70
|
+
:pid => 请配置 pid_file, 并制定 pid 文件位置
|
71
|
+
```ruby
|
72
|
+
cfg.restart_mode = :touch
|
73
|
+
cfg.touch_file = "./tmp/restart.txt"
|
74
|
+
cfg.pid_file = "./tmp/pid"
|
75
|
+
```
|
76
|
+
|
77
|
+
是否改变代码立刻reload(重载)
|
78
|
+
```ruby
|
79
|
+
cfg.code_reload = true
|
80
|
+
```
|
81
|
+
|
82
|
+
最大连接数
|
83
|
+
```ruby
|
84
|
+
cfg.max_connections = 1024
|
85
|
+
```
|
86
|
+
|
87
|
+
是否后台运行(守护进程模式)
|
88
|
+
```ruby
|
89
|
+
cfg.run_background = false
|
90
|
+
```
|
91
|
+
|
92
|
+
重启服务器
|
93
|
+
仅支持后台运行的服务器
|
94
|
+
|
95
|
+
touch 方式
|
96
|
+
```console
|
97
|
+
touch tmp/restart.txt
|
98
|
+
```
|
99
|
+
|
100
|
+
pid 方式
|
101
|
+
```console
|
102
|
+
kill -USR2 `cat tmp/pid`
|
103
|
+
```
|
104
|
+
|
105
|
+
## 配置路由
|
106
|
+
暂时支持 rails 方式的路由 path_info 映射为 controller#action
|
107
|
+
|
108
|
+
打开项目下的 routes.rb 并配置它
|
109
|
+
|
110
|
+
get 请求路径为/, 对应 controllers 目录下的 HomeController 已经 index 方法 (rails里的action)
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
get "/", "home#index"
|
114
|
+
```
|
115
|
+
|
116
|
+
post 请求路径为/items/buy, 对应 controllers 目录下的 ItemsController 已经 buy 方法 (rails里的action)
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
post "/items/buy", "items#buy"
|
120
|
+
```
|
121
|
+
|
122
|
+
match 请求路径为/items, 对应 controllers 目录下的 ItemsController 已经 new 方法 (rails里的action)
|
123
|
+
via 配置为 请求方法, 目前可用 :get, :post 两种
|
124
|
+
```ruby
|
125
|
+
match "/items", "items#new", via: [:get, :post]
|
126
|
+
```
|
127
|
+
|
128
|
+
## controller 特性
|
129
|
+
|
130
|
+
过滤器
|
131
|
+
支持 rails 方式的过滤器
|
132
|
+
before_filter, after_filter 和 round_filter
|
133
|
+
接受参数类型 :only 和 :except
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
before_filter :set_variable, :only => [:index]
|
137
|
+
before_filter :set_variable, :except => [:index,:create]
|
138
|
+
round_filter :set_variable, :only => [:index]
|
139
|
+
```
|
140
|
+
|
141
|
+
request 方法
|
142
|
+
会获取一个 对浏览器请求的request包装对象
|
143
|
+
```ruby
|
144
|
+
request.url
|
145
|
+
request.method
|
146
|
+
....
|
147
|
+
```
|
148
|
+
params 方法
|
149
|
+
得到一个 Hash 对象,包含请求的参数以 和 controller , action 参数
|
150
|
+
```ruby
|
151
|
+
params[:controller]
|
152
|
+
params[:action]
|
153
|
+
....
|
154
|
+
```
|
155
|
+
|
156
|
+
render 方法
|
157
|
+
支持 rails 的 render 方式
|
158
|
+
|
159
|
+
render方法(可省略)
|
160
|
+
默认会寻找当前 controller#action 所对应寻找好 views目录下 controller 目录 action 的erb文件
|
161
|
+
|
162
|
+
渲染一个text文本
|
163
|
+
```ruby
|
164
|
+
render :text => "foobar"
|
165
|
+
```
|
166
|
+
|
167
|
+
渲染一个json
|
168
|
+
```ruby
|
169
|
+
render :json => { foo: "bar" }
|
170
|
+
```
|
171
|
+
|
172
|
+
## 关于 Gemfile
|
173
|
+
|
174
|
+
关于 Gemfile
|
175
|
+
|
176
|
+
用户可以自行安装所需的gem
|
177
|
+
|
178
|
+
先安装 bundler
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
gem install bundler
|
182
|
+
```
|
183
|
+
然后在项目目录下创建一个 Gemfile
|
184
|
+
|
185
|
+
添加您所需要的 gem 然后执行
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
bundle install
|
189
|
+
```
|
190
|
+
|
191
|
+
## 关于model 和 数据库
|
192
|
+
|
193
|
+
当前版本还未完成 model 的封装
|
194
|
+
暂时推荐用户自己选择, 推荐使用 active_record 或者 mongoid
|
195
|
+
自行添加对应的gem 到 Gemfile 中
|
196
|
+
|
197
|
+
## 测试
|
198
|
+
|
199
|
+
执行 rspec 测试
|
200
|
+
暂时测试覆盖不全
|
201
|
+
|
202
|
+
```ruby
|
203
|
+
rspec spec/*
|
204
|
+
```
|
205
|
+
|
206
|
+
## 贡献代码
|
207
|
+
|
208
|
+
1. Fork it ( https://github.com/[my-github-username]/swee/fork )
|
209
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
210
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
211
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
212
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/swee
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# argv = ARGV
|
4
|
+
|
5
|
+
# _cmd = argv.shift
|
6
|
+
|
7
|
+
# # 简单解析命令行参数 启动服务 和 设置环境
|
8
|
+
|
9
|
+
# ENV["start_port"] = "3000"
|
10
|
+
# ENV["app_env"] = "development"
|
11
|
+
|
12
|
+
# case _cmd
|
13
|
+
# when "s"
|
14
|
+
# while !argv.empty?
|
15
|
+
# _cmd = argv.shift
|
16
|
+
# s = argv.shift
|
17
|
+
# raise "命令#{_cmd}缺少参数" if s.nil?
|
18
|
+
# case _cmd
|
19
|
+
# when "-p"
|
20
|
+
# ENV["start_port"] = s if s =~ /[0-9]+/
|
21
|
+
# when "-e"
|
22
|
+
# ENV["app_env"] = s if s =~ /[a-z]+/
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
# require 'swee'
|
26
|
+
# Swee::Server.boot!
|
27
|
+
# when "c"
|
28
|
+
# require 'irb'
|
29
|
+
# require 'swee'
|
30
|
+
|
31
|
+
# IRB.start
|
32
|
+
# else
|
33
|
+
# p "help"
|
34
|
+
# end
|
35
|
+
|
36
|
+
require 'swee'
|
37
|
+
|
38
|
+
Swee::Engine.boot! ARGV
|
data/doc/tmp.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
|
2
|
+
# attr_accessor :config
|
3
|
+
# def initialize
|
4
|
+
# @port,@env = ENV["start_port"], ENV["app_env"]
|
5
|
+
# @config = Swee::Application.config
|
6
|
+
# @middlewares = []
|
7
|
+
# end
|
8
|
+
|
9
|
+
# def use(middleware_class,*options, &block)
|
10
|
+
# @middlewares << lambda {|app| middleware_class.new(app,*options, &block)}
|
11
|
+
# end
|
12
|
+
|
13
|
+
# def use_lambda _lambda
|
14
|
+
# @middlewares << _lambda
|
15
|
+
# end
|
16
|
+
|
17
|
+
# def run(app)
|
18
|
+
# @app = app
|
19
|
+
# end
|
20
|
+
|
21
|
+
# def to_app
|
22
|
+
# @middlewares.reverse.inject(@app) { |app, middleware| middleware.call(app)}
|
23
|
+
# end
|
24
|
+
|
25
|
+
# def load_default_middlewares
|
26
|
+
# use Rack::ContentLength
|
27
|
+
# # use Rack::ContentType,"text/plain"
|
28
|
+
# use Rack::CommonLogger, $stder # => 记录日志
|
29
|
+
|
30
|
+
# use Rack::Chunked # => 分块传输 (很多服务器自动use了该中间件)
|
31
|
+
|
32
|
+
# use Rack::ConditionalGet # => get资源缓存控制
|
33
|
+
# # use Rack::Etag # => 判断计算url对象是否改变 和 ConditionalGet 中间件一起使用
|
34
|
+
# use Rack::Deflater # => http 传输内容编码(压缩)
|
35
|
+
|
36
|
+
# # use Rack::Head # => RFC2616要求HEAD请求的响应体必须为空这就是Rack::HEAD所做的事情
|
37
|
+
|
38
|
+
# use Rack::MethodOverride # => 用 post 模拟 put 和 delete
|
39
|
+
# # => 需要表单中加入了一个隐含字段“_method” 把它的值设为“put 或 delete“
|
40
|
+
# # use Rack::Lint # => 检查请求和响应是否符合Rack规格
|
41
|
+
|
42
|
+
# use Rack::Reloader, 2 # => 开发环境用 修改代码不重启服务器
|
43
|
+
# # use Rack::Runtime # => header中加入一个 请求的处理时间 X-Runtime
|
44
|
+
|
45
|
+
# # use Rack::Sendfile # => 传送文件
|
46
|
+
|
47
|
+
# # apps = [lambda {|env| [404, {}, ["File doesn't exists"]]}, lambda {|env| [200, {}, ["I'm ok"]]}]
|
48
|
+
# # use Rack::Cascade # => 挂载多个应用程序 它会尝试所有这些应用程序,直到某一个应用程序返回的代码不是404
|
49
|
+
|
50
|
+
# # use Rack::Lock # => 不支持多线程框架 将互锁
|
51
|
+
|
52
|
+
# # use Rack::Session::Cookie, :key => 'rack.session', # => key, 缺省为 rack.session
|
53
|
+
# # :domain => 'example.com', # => 域名
|
54
|
+
# # :path => '/', # => 路径
|
55
|
+
# # :expire_after => 2592000, # => 过期时间
|
56
|
+
# # :secret => 'any_secret_key' # => cookie 加密
|
57
|
+
|
58
|
+
# # default_options = {
|
59
|
+
# # :path => '/',
|
60
|
+
# # :domain => nil,
|
61
|
+
# # :expire_after => nil,
|
62
|
+
# # :secure => false,
|
63
|
+
# # :httponly => true,
|
64
|
+
# # :defer => false, # => 如果设置defer为true,那么响应头中将不会设置cookie(暂时还不知道有什么用处)
|
65
|
+
# # :renew => false, # => 如果设置此选项为true,那么在具体的会话管理实现中不应该把原先客户端通过请求发送的session_id,
|
66
|
+
# # # => 而是每次生成一个新的session_id,并把原先session_id对应的会话数据和这个新的session_id对应。
|
67
|
+
# # # 注意renew的优先级高于defer,也就是即使defer设置为true,
|
68
|
+
# # # 只要设置了renew为true,那么cookie也会被写入到响应头中
|
69
|
+
# # :sidbits => 128 # => 生成的session_id长度为多少个bit, ID类提供了一个实用的generate_sid方法可以供你的具体实现使用
|
70
|
+
# # }
|
71
|
+
# # use Rack::Session::Abstract::ID,default_options
|
72
|
+
|
73
|
+
# # default_options = Rack::Session::Abstract::ID::DEFAULT_OPTIONS.merge ({
|
74
|
+
# # :namespace => 'rack:session', :memcache_server => 'localhost:11211'
|
75
|
+
# # })
|
76
|
+
# # use Rack::Session::Memcache, DEFAULT_OPTIONS
|
77
|
+
# end
|
data/lib/swee.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
module Swee
|
2
|
+
class AppExecutor
|
3
|
+
|
4
|
+
class << self
|
5
|
+
# 启动 app 渲染器
|
6
|
+
# app_path 转路由
|
7
|
+
# 执行 app controller 方法
|
8
|
+
# 渲染 filter (:before, :after, :round)
|
9
|
+
# 生成 View
|
10
|
+
def run env
|
11
|
+
route = path_to_route(env)
|
12
|
+
request_method = env["REQUEST_METHOD"].downcase.to_sym
|
13
|
+
if route.nil? || !route.request_methods.include?(request_method)
|
14
|
+
return View.render_404
|
15
|
+
end
|
16
|
+
controller_intance = route_to_controller route, env
|
17
|
+
render_view controller_intance, route
|
18
|
+
end
|
19
|
+
|
20
|
+
# app_path转路由
|
21
|
+
# 获取 路由结构
|
22
|
+
def path_to_route env
|
23
|
+
_path_info = env["PATH_INFO"]
|
24
|
+
Routes.tables[_path_info.to_s]
|
25
|
+
end
|
26
|
+
|
27
|
+
# 执行 app controller 方法
|
28
|
+
def route_to_controller route, env
|
29
|
+
controller = route.controller
|
30
|
+
controller_intance = route.create_controller_instance
|
31
|
+
controller_intance.warp_request env,route
|
32
|
+
controller_intance
|
33
|
+
end
|
34
|
+
|
35
|
+
# 渲染视图
|
36
|
+
# 渲染 -> 前置 后置 环绕控制器
|
37
|
+
def render_view controller_intance, route
|
38
|
+
execute_before_filter controller_intance, route
|
39
|
+
result = execute_controller controller_intance, route.action
|
40
|
+
execute_after_filter controller_intance, route
|
41
|
+
return !rack_responsed?(result) ? controller_intance.render : result
|
42
|
+
end
|
43
|
+
|
44
|
+
# 执行前置过滤器
|
45
|
+
def execute_before_filter controller_intance,route
|
46
|
+
execute_filter :before, controller_intance, route
|
47
|
+
end
|
48
|
+
|
49
|
+
# 执行后置过滤器
|
50
|
+
def execute_after_filter controller_intance,route
|
51
|
+
execute_filter :after, controller_intance, route
|
52
|
+
end
|
53
|
+
|
54
|
+
# 执行控制器
|
55
|
+
def execute_controller controller_intance,action
|
56
|
+
result = controller_intance.send(action)
|
57
|
+
result.freeze
|
58
|
+
result
|
59
|
+
end
|
60
|
+
|
61
|
+
# 执行过滤器
|
62
|
+
def execute_filter _type, controller_intance,route
|
63
|
+
_filter_actions = Controller.find_filter_methods route.controller_name,_type,route.action
|
64
|
+
_filter_actions.each do |_action|
|
65
|
+
controller_intance.send _action
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# 检测是否为 rack 格式得响应
|
70
|
+
# rack标准格式: [status,headers,[body]]
|
71
|
+
def rack_responsed? result
|
72
|
+
if result.is_a?(Array) && result.size == 3
|
73
|
+
status = result[0]
|
74
|
+
headers = result[1]
|
75
|
+
body = result[2]
|
76
|
+
if status.is_a?(Integer) && headers.is_a?(Hash) && body.respond_to?(:each)
|
77
|
+
return true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
return false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
DEFAULT_THTEADS = 20
|
85
|
+
|
86
|
+
# TODO: 多线程处理控制器和路由
|
87
|
+
class ExeThread
|
88
|
+
DEFAULT_SLEEP = 0.5
|
89
|
+
@t = nil
|
90
|
+
def initialize
|
91
|
+
@mission_queue = []
|
92
|
+
end
|
93
|
+
|
94
|
+
def run
|
95
|
+
while true
|
96
|
+
if @mission_queue.empty?
|
97
|
+
wait
|
98
|
+
else
|
99
|
+
@mission_queue.shift.call()
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def create!
|
105
|
+
@t = Thread.new { run }
|
106
|
+
end
|
107
|
+
|
108
|
+
def busy?
|
109
|
+
!@mission_queue.empty?
|
110
|
+
end
|
111
|
+
|
112
|
+
def << mission
|
113
|
+
@mission_queue << mission
|
114
|
+
create! if @t.dead?
|
115
|
+
end
|
116
|
+
|
117
|
+
def wait
|
118
|
+
sleep DEFAULT_SLEEP
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def initialize
|
123
|
+
@queue = []
|
124
|
+
@threadpoll = Array.new
|
125
|
+
DEFAULT_THTEADS.times do
|
126
|
+
@threadpoll << ExeThread.new
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def current_thread
|
131
|
+
_current_thread = @threadpoll.select { |t| !t.busy? }
|
132
|
+
end
|
133
|
+
|
134
|
+
def run
|
135
|
+
f = Fiber.new do
|
136
|
+
while true
|
137
|
+
Fiber.yield
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def wait
|
143
|
+
while exist_alive_thread?
|
144
|
+
sleep 0.1
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def <<()
|
149
|
+
@queue.push()
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|