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 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
@@ -0,0 +1,4 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new
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,5 @@
1
+ require "swee/version"
2
+ require "swee/engine"
3
+
4
+ # module Swee
5
+ # end
@@ -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