- 浏览: 849813 次
- 性别:
- 来自: lanzhou
文章分类
最新评论
-
liu346435400:
楼主讲了实话啊,中国程序员的现状,也是只见中国程序员拼死拼活的 ...
中国的程序员为什么这么辛苦 -
qw8226718:
国内ASP.NET下功能比较完善,优化比较好的Spacebui ...
国内外开源sns源码大全 -
dotjar:
敢问兰州的大哥,Prism 现在在12.04LTS上可用么?我 ...
最佳 Ubuntu 下 WebQQ 聊天体验 -
coralsea:
兄弟,卫星通信不是这么简单的,单向接收卫星广播信号不需要太大的 ...
Google 上网 -
txin0814:
我成功安装chrome frame后 在IE地址栏前加上cf: ...
IE中使用Google Chrome Frame运行HTML 5
n Ruby, we have the great fortune to have one major framework (Rails) and a number of minor frameworks that drive innovation forward. One of the great minor frameworks which has been getting a lot of traction recently is Sinatra, primarily because it exposes a great DSL for writing small, single-purpose apps.
Here’s an example of a simple Sinatra application.
class MyApp < Sinatra::Base
set :views, File.dirname(__FILE__)
enable :sessions
before do
halt if session[:fail] == true
end
get "/hello" do
"Hello world"
end
get "/world" do
@name = "Carl"
erb :awesomesauce
end
get "/fail" do
session[:fail] = true
"You failed"
end
end
There’s a lot of functionality packed into this little package. You can declare some code to be run before all actions, declare actions and the URL they should be routed from, use rendering semantics, and even use sessions.
We’ve been saying that Rails 3 is flexible enough to use as a framework toolkit–let’s prove it by using Rails to build the subset of the Sinatra DSL described above.
Let’s start with a very tiny subset of the DSL:
class MyApp < Sinatra::Base
get "/hello" do
"HELLO World"
end
post "/world" do
"Hello WORLD"
end
end
The first step is to declare the Sinatra base class:
module Sinatra
class Base < ActionController::Metal
include ActionController::RackConvenience
end
end
We start off by making Sinatra::Base a subclass of the bare metal ActionController implementation, which provides just enough infrastructure to get going. We also include the RackConvenience module, which provides request and response and handles some basic Rack tasks for us.
Next, let’s add support for the GET and POST method:
class Sinatra::Base
def self.inherited(klass)
klass.class_eval { @_routes = [] }
end
class << self
def get(uri, options = {}, &block) route(:get, uri, options, &block) end
def post(uri, options = {}, &block) route(:post, uri, options, &block) end
def route(http_method, uri, options, &block)
action_name = "[#{http_method}] #{uri}"
@_routes << {:method => http_method.to_s.upcase, :uri => uri,
:action => action_name,ptions => options}
define_method(action_name, &block)
end
end
end
We’ve simply defined some class methods on the Sinatra::Base to store off routing details for the get and post methods, and creating a new method named [GET] /hello. This is a bit of an interesting Ruby trick; while the def keyword has strict semantics for method names, define_method allows any string.
Now we need to wire up the actual routing. There are a number of options, including the Rails router (rack-mount, rack-router, and usher are all new, working Rails-like routers). We’ll use Usher, a fast Rails-like router written by Josh Hull.
class << Sinatra::Base
def to_app
routes, controller = @_routes, self
Usher::Interface.for(:rack) do
routes.each do |route|
add(route[:uri], :conditions => {:method => route[:method]}.merge(route[:options])).
to(controller.action(route[:action]))
end
end
end
end
Here, we define to_app, which is used by Rack to convert a parameter to run into a valid Rack application. We create a new Usher interface, and add a route for each route created by Sinatra. Because Usher::Interface.for uses instance_eval for its DSL, we store off the routes and controller in local variables that will still be available in the closure.
One little detail here: In Rails 3, each action in a controller is a valid rack endpoint. You get the endpoint by doing ControllerName.action(method_name). Here, we’re simply pulling out the action named “[GET] /hello” that we created in route.
The final piece of the puzzle is covering the action processing in the controller itself. For this, we will mostly reuse the default action processing, with a small change:
class Sinatra::Base
def process_action(*)
self.response_body = super
end
end
What’s happening here is that Rails does not treat the return value of the action as significant, instead expecting it to be set using render, but Sinatra treats the returned string as significant. As a result, we set the response_body to the return value of the action.
Next, let’s add session support.
class << Sinatra::Base
def set(name, value)
send("_set_#{name}", value)
end
def enable(name)
set(name, true)
end
def _set_sessions(value)
@_sessions = value
include ActionController::Session if value
end
def to_app
routes, controller = @_routes, self
app = Usher::Interface.for(:rack) do
routes.each do |route|
add(route[:uri], :conditions => {:method => route[:method]}.merge(route[:options])).
to(controller.action(route[:action]))
end
end
if @_sessions
app = ActionDispatch::Session::CookieStore.new(app, {:key => "_secret_key",
:secret => Digest::SHA2.hexdigest(Time.now.to_s + rand(100).to_s)})
end
app
end
end
There’s a few things going on here. First, Sinatra provides an API for setting options: setption, :value. In Sinatra, enableption is equivalent to setption, true. To simplify adding new options, we just delegate set :whatever, value to a call to _set_whatever(value).
We then implement _set_sessions(value) to include ActionController::Session, which provides the session helper. In to_app, we wrap the original application in an ActionDispatch::Session::CookieStore if sessions were set.
Next, we want to add in support for callbacks (before do). It’s only a few lines:
class Sinatra::Base
include AbstractController::Callbacks
end
class << Sinatra::Base
alias before before_filter
end
Basically, we pull in the normal Rails callback code, and then rename before_filter to before and we’re good to go.
Finally, let’s dig into rendering.
class Sinatra::Base
include ActionController::RenderingController
def sinatra_render_file(name)
render :template => name.to_s
end
def sinatra_render_inline(string, type)
render :inline => string, :type => type
end
%w(haml erb builder).each do |type|
define_method(type) do |thing|
return sinatra_render_inline(thing, type) if thing.is_a?(String)
return sinatra_render_file(thing)
end
end
end
class << Sinatra::Base
alias _set_views append_view_path
end
We include the RenderController module, which provides rendering support. Sinatra supports a few different syntaxes for rendering. It supports erb :template_name which renders the ERB template named template_name. It also supports erb "Some String", which renders the string uses the ERB engine.
Rails supports both of those via render :template and render :inline, so we simply defer to that functionality in each case. We also handle Sinatra’s set :views, view_path by delegating to append_view_path.
You can check out the full repository at https://github.com/wycats/railsnatra/
So there you have it, a large subset of the Sinatra DSL written in Rails in under 100 lines of code. And if you want to add in more advanced Rails features, like layouts, flash, respond_to, file streaming, or conditional get support, it’s just a simple module inclusion away.
Here’s an example of a simple Sinatra application.
class MyApp < Sinatra::Base
set :views, File.dirname(__FILE__)
enable :sessions
before do
halt if session[:fail] == true
end
get "/hello" do
"Hello world"
end
get "/world" do
@name = "Carl"
erb :awesomesauce
end
get "/fail" do
session[:fail] = true
"You failed"
end
end
There’s a lot of functionality packed into this little package. You can declare some code to be run before all actions, declare actions and the URL they should be routed from, use rendering semantics, and even use sessions.
We’ve been saying that Rails 3 is flexible enough to use as a framework toolkit–let’s prove it by using Rails to build the subset of the Sinatra DSL described above.
Let’s start with a very tiny subset of the DSL:
class MyApp < Sinatra::Base
get "/hello" do
"HELLO World"
end
post "/world" do
"Hello WORLD"
end
end
The first step is to declare the Sinatra base class:
module Sinatra
class Base < ActionController::Metal
include ActionController::RackConvenience
end
end
We start off by making Sinatra::Base a subclass of the bare metal ActionController implementation, which provides just enough infrastructure to get going. We also include the RackConvenience module, which provides request and response and handles some basic Rack tasks for us.
Next, let’s add support for the GET and POST method:
class Sinatra::Base
def self.inherited(klass)
klass.class_eval { @_routes = [] }
end
class << self
def get(uri, options = {}, &block) route(:get, uri, options, &block) end
def post(uri, options = {}, &block) route(:post, uri, options, &block) end
def route(http_method, uri, options, &block)
action_name = "[#{http_method}] #{uri}"
@_routes << {:method => http_method.to_s.upcase, :uri => uri,
:action => action_name,ptions => options}
define_method(action_name, &block)
end
end
end
We’ve simply defined some class methods on the Sinatra::Base to store off routing details for the get and post methods, and creating a new method named [GET] /hello. This is a bit of an interesting Ruby trick; while the def keyword has strict semantics for method names, define_method allows any string.
Now we need to wire up the actual routing. There are a number of options, including the Rails router (rack-mount, rack-router, and usher are all new, working Rails-like routers). We’ll use Usher, a fast Rails-like router written by Josh Hull.
class << Sinatra::Base
def to_app
routes, controller = @_routes, self
Usher::Interface.for(:rack) do
routes.each do |route|
add(route[:uri], :conditions => {:method => route[:method]}.merge(route[:options])).
to(controller.action(route[:action]))
end
end
end
end
Here, we define to_app, which is used by Rack to convert a parameter to run into a valid Rack application. We create a new Usher interface, and add a route for each route created by Sinatra. Because Usher::Interface.for uses instance_eval for its DSL, we store off the routes and controller in local variables that will still be available in the closure.
One little detail here: In Rails 3, each action in a controller is a valid rack endpoint. You get the endpoint by doing ControllerName.action(method_name). Here, we’re simply pulling out the action named “[GET] /hello” that we created in route.
The final piece of the puzzle is covering the action processing in the controller itself. For this, we will mostly reuse the default action processing, with a small change:
class Sinatra::Base
def process_action(*)
self.response_body = super
end
end
What’s happening here is that Rails does not treat the return value of the action as significant, instead expecting it to be set using render, but Sinatra treats the returned string as significant. As a result, we set the response_body to the return value of the action.
Next, let’s add session support.
class << Sinatra::Base
def set(name, value)
send("_set_#{name}", value)
end
def enable(name)
set(name, true)
end
def _set_sessions(value)
@_sessions = value
include ActionController::Session if value
end
def to_app
routes, controller = @_routes, self
app = Usher::Interface.for(:rack) do
routes.each do |route|
add(route[:uri], :conditions => {:method => route[:method]}.merge(route[:options])).
to(controller.action(route[:action]))
end
end
if @_sessions
app = ActionDispatch::Session::CookieStore.new(app, {:key => "_secret_key",
:secret => Digest::SHA2.hexdigest(Time.now.to_s + rand(100).to_s)})
end
app
end
end
There’s a few things going on here. First, Sinatra provides an API for setting options: setption, :value. In Sinatra, enableption is equivalent to setption, true. To simplify adding new options, we just delegate set :whatever, value to a call to _set_whatever(value).
We then implement _set_sessions(value) to include ActionController::Session, which provides the session helper. In to_app, we wrap the original application in an ActionDispatch::Session::CookieStore if sessions were set.
Next, we want to add in support for callbacks (before do). It’s only a few lines:
class Sinatra::Base
include AbstractController::Callbacks
end
class << Sinatra::Base
alias before before_filter
end
Basically, we pull in the normal Rails callback code, and then rename before_filter to before and we’re good to go.
Finally, let’s dig into rendering.
class Sinatra::Base
include ActionController::RenderingController
def sinatra_render_file(name)
render :template => name.to_s
end
def sinatra_render_inline(string, type)
render :inline => string, :type => type
end
%w(haml erb builder).each do |type|
define_method(type) do |thing|
return sinatra_render_inline(thing, type) if thing.is_a?(String)
return sinatra_render_file(thing)
end
end
end
class << Sinatra::Base
alias _set_views append_view_path
end
We include the RenderController module, which provides rendering support. Sinatra supports a few different syntaxes for rendering. It supports erb :template_name which renders the ERB template named template_name. It also supports erb "Some String", which renders the string uses the ERB engine.
Rails supports both of those via render :template and render :inline, so we simply defer to that functionality in each case. We also handle Sinatra’s set :views, view_path by delegating to append_view_path.
You can check out the full repository at https://github.com/wycats/railsnatra/
So there you have it, a large subset of the Sinatra DSL written in Rails in under 100 lines of code. And if you want to add in more advanced Rails features, like layouts, flash, respond_to, file streaming, or conditional get support, it’s just a simple module inclusion away.
发表评论
-
Rails 3 Beta版本月将出 Merb融合带来选择
2010-01-11 09:48 1419Rails 3,目前流行Web开发框架Rails的一个升级版 ... -
MerbAdmin:Merb数据管理好帮手
2010-01-11 09:43 907Merb中要加入类似Django的Admin功能早有传闻,如今 ... -
rails cms
2009-12-28 20:29 1669Rails CMS alternatives ======= ... -
Generating Thousands of PDFs on EC2 with Ruby
2009-12-24 18:01 1038The Problem For about two mont ... -
Shrink your JavaScript with the Google Compiler Rails Plugin
2009-11-16 11:27 933Like it or not, JavaScript has ... -
Thank you, Rails
2009-11-06 18:21 567It’s fashionable, or perhaps in ... -
Top 50 Ruby on Rails Websites
2009-10-31 15:18 944We’re big fans of Ruby on Rails ... -
Let a human test your app, not (just) unit tests
2009-10-31 09:26 853I’m a big believer in unit test ... -
Heroku Gets Add-Ons: Serious Ruby Webapp Hosting Made Easy
2009-10-30 07:37 913Heroku is a Ruby webapp hosti ... -
Rails + Google Analytics = easy goal tracking
2009-10-29 20:38 892Google Analytics is an indis ... -
Integrating Flickr into your rails website
2009-10-29 20:37 1067In this post I’m going to show ... -
Ruby on Rails Roadshow in Austin Thursday
2009-10-29 14:25 809Justin Britten founded Prefine ... -
Ruby on Rails and the importance of being stupid
2009-10-21 08:13 806A tale of two servers… Server ... -
How a 1-Engineer Rails Site Scaled to 10 Million Requests Per Day
2009-10-20 14:49 775Ravelry is an online knitting ... -
Installing Rails on CentOS 5
2009-10-20 14:24 1191Note: Since this post origina ... -
CentOS配置lighttpd和rails
2009-10-20 14:22 1123lighttpd版本:1.4.18 fastcgi版本: ... -
Cells:将组件开发带入Ruby2.3
2009-10-20 09:17 1118cells "将使得面向组 ... -
High Quality Ruby on Rails Example Applications
2009-10-15 16:34 1461Sometimes to best way to get ... -
Install Passenger on Ubuntu
2009-10-07 10:17 806Phusion Passenger is one of the ... -
Installing Ruby on Rails with Apache on Ubuntu 9.04 (Jaunty)
2009-10-07 10:00 1015Installing Passenger and Depe ...
相关推荐
在Sinatra中提供对内置Web服务支持的response_to样式Rails块 功能/问题: 根据提供的内容处理内容类型的设置 自动可以调整XMLHttpRequests返回Javascript 根据HTTP_ACCEPT标头解析识别请求。 优先级的顺序是...
Chapter 13: Two Web Application Approaches: Rails and Sinatra Chapter 14: Ruby and the Internet Chapter 15: Networking and Sockets Chapter 16: Useful Ruby Libraries Appendix A: Ruby Primer and Review...
这应该在 Sinatra 或 Rails 应用程序之上工作,以提供广泛的用户群和大量的社交互动,同时允许自托管、分散和复制内容。 有关更多信息,请参阅。 该项目是在上述Nelumba项目的基础上建立的。 基础项目只是不可变...
按localhost:3000 / twitter加载已安装的异步Sinatra应用程序(报告最新的Rails 3条推文) Howto /示例提交: 修改您的config.ru以包括Rack :: FiberPool中间件: 配置ActiveRecord以使用异
在Ubuntu 14.04 LTS上使用Passenger 5 / Nginx部署Sinatra / Rails 4应用程序的命令列表。 我还在博客上写了一篇文章(针对初学者),详细说明了每个步骤-http: 如果您遇到困难并需要任何帮助,请在该帖子上发表...
rack是ruby服务器和rack应用程序之间的一个框架,rails,sinatra都是基于rack构建的,都属于rack应用程序。 rack提供了一个标准的接口,用于与服务器进行交互。标准的rack程序是一个可以响应call的对象,可以是对象、...
Darren 从事编程和网站建设已经超过十年,在 2007 年发现了 Ruby 和 Rails,之后又发现了 Sinatra 并深深地被它的优雅所吸引。2010 年初,他创建了“IDidItMyWay”博客来记录自己学习 Sinatra 的过程,并在同年利用 ...
bugsnag-ruby, Rails Sinatra rack 和 ruby的Bugsnag错误监视 ruby的 Bugsnag异常报告器 ruby 异常报告器提供了从你的 Rails Sinatra/英镑/或者英镑的普通 ruby 应用程序中抛出的异常通知。 任何未捕获的异常都会...
安装:##用于Rails 3 +,Sinatra和Merb的Gemfile gem'will_paginate','〜> will_paginate will_paginate是与Ruby on Rails,Sinatra,Hanami :: View,Merb,DataMapper和Sequel集成的分页库。 安装:##用于Rails 3...
3. **Rack-based executable**: Rails应用本质上是Rack应用,因此你可以创建一个Rack服务器,如Sinatra,然后包装Rails作为其一部分。这样可以生成一个独立的可执行文件,但可能需要额外的配置来处理Rails的依赖。 ...
在Sinatra中使用Rails资产 这是一个最小的演示应用程序,展示了如何在 Sinatra 应用程序中使用 。 Rails Assets 将与基于的应用程序集成在一起。 一体化 文件 此应用程序使用将 Sinatra 与 Sprockets 集成的 ...
Rails,全称Ruby on Rails,是一款基于Ruby编程语言的开源Web应用程序框架,它遵循MVC(模型-视图-控制器)架构模式,旨在简化Web应用的开发过程,提高开发效率,同时强调“DRY(Don't Repeat Yourself)”原则和...
Sinatra::Synchrony是 Sinatra的一个小扩展,它动态提升了Sinatra网络应用的并发性。由于EventMachine和EM-Synchrony的支持,当你有很多 传输和低速IO请求时(如向外部APIs发送的HTTP请求),它增加了你的应用每个...
2. **Passenger 5**: Passenger(又名Phusion Passenger)是一个流行的Web服务器模块,专门用于Ruby on Rails和Sinatra等Web框架。Passenger 5支持多进程和多线程模型,可以有效地管理Rails应用的实例,提高并发处理...
sinatra-pubsub, 为 Sinatra 推送&流 Sinatra::PubSubPubSub是对 Sinatra的扩展,它增加了使用HTML5服务器发送事件的基本发布/订阅流。例如客户端可以订阅以下事件:var es = new EventSource('/subscribe
Owasp Ruby on Rails速查表也有一些检查。 总体介绍 当您在代码上运行awnsscanner时,它将解析项目Gemfile.lock以查找所使用的gem,并尝试检测您正在使用的ruby解释器版本,或者您在最喜欢的ruby版本管理工具(RVM...
almost-sinatra, Sinatra 重构,现在只有六个行 比一双袜子更受欢迎 几乎 Sinatra "until programmers stop acting like obfuscation is morally hazardous,they're not artists, just kid
它与基于Rack的Web应用程序(例如Ruby on Rails)一起很好地工作。 信息RDoc文档av CarrierWave该gem提供了一种简单且极为灵活的方式来从Ruby应用程序上载文件。 它与基于Rack的Web应用程序(例如Ruby on Rails)...
will_paginate will_paginate是一个分页库,与Ruby on Rails,Sinatra,Hanami :: View,Merb,DataMapper和Sequel集成。 安装: ## Gemfile for Rails 3+, Sinatra, and Merbgem 'will_paginate' , '~> 3.1.0' 有关...