- 浏览: 829818 次
- 性别:
- 来自: 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 1374Rails 3,目前流行Web开发框架Rails的一个升级版 ... -
MerbAdmin:Merb数据管理好帮手
2010-01-11 09:43 881Merb中要加入类似Django的Admin功能早有传闻,如今 ... -
rails cms
2009-12-28 20:29 1640Rails CMS alternatives ======= ... -
Generating Thousands of PDFs on EC2 with Ruby
2009-12-24 18:01 994The Problem For about two mont ... -
Shrink your JavaScript with the Google Compiler Rails Plugin
2009-11-16 11:27 895Like it or not, JavaScript has ... -
Thank you, Rails
2009-11-06 18:21 541It’s fashionable, or perhaps in ... -
Top 50 Ruby on Rails Websites
2009-10-31 15:18 915We’re big fans of Ruby on Rails ... -
Let a human test your app, not (just) unit tests
2009-10-31 09:26 821I’m a big believer in unit test ... -
Heroku Gets Add-Ons: Serious Ruby Webapp Hosting Made Easy
2009-10-30 07:37 885Heroku is a Ruby webapp hosti ... -
Rails + Google Analytics = easy goal tracking
2009-10-29 20:38 865Google Analytics is an indis ... -
Integrating Flickr into your rails website
2009-10-29 20:37 1031In this post I’m going to show ... -
Ruby on Rails Roadshow in Austin Thursday
2009-10-29 14:25 781Justin Britten founded Prefine ... -
Ruby on Rails and the importance of being stupid
2009-10-21 08:13 773A tale of two servers… Server ... -
How a 1-Engineer Rails Site Scaled to 10 Million Requests Per Day
2009-10-20 14:49 737Ravelry is an online knitting ... -
Installing Rails on CentOS 5
2009-10-20 14:24 1157Note: Since this post origina ... -
CentOS配置lighttpd和rails
2009-10-20 14:22 1099lighttpd版本:1.4.18 fastcgi版本: ... -
Cells:将组件开发带入Ruby2.3
2009-10-20 09:17 1084cells "将使得面向组 ... -
High Quality Ruby on Rails Example Applications
2009-10-15 16:34 1431Sometimes to best way to get ... -
Install Passenger on Ubuntu
2009-10-07 10:17 771Phusion Passenger is one of the ... -
Installing Ruby on Rails with Apache on Ubuntu 9.04 (Jaunty)
2009-10-07 10:00 980Installing 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的对象,可以是对象、...
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...
在Sinatra中使用Rails资产 这是一个最小的演示应用程序,展示了如何在 Sinatra 应用程序中使用 。 Rails Assets 将与基于的应用程序集成在一起。 一体化 文件 此应用程序使用将 Sinatra 与 Sprockets 集成的 ...
Sinatra::Synchrony是 Sinatra的一个小扩展,它动态提升了Sinatra网络应用的并发性。由于EventMachine和EM-Synchrony的支持,当你有很多 传输和低速IO请求时(如向外部APIs发送的HTTP请求),它增加了你的应用每个...
jump star sinatra sinatra教程
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)...
与今天一样,这里有用于Locaweb,使用git pull的Rails 3和使用git push的Rails 3的模板。 默认情况下,它与Passenger一起使用,但是您可以指定其他服务器,例如Mongrel,Thin和Unicorn。 您可以从远程计算机或...
网JavaPythonPHP节点JS待定待定待定待定怎么运行的这只是Ruby on Rails应用程序的准系统。只要有可能,我们都应该使用它。但是,如果您使用的是诸如Sinatra之类的其他框架,这些框架具有其自己的标准化应用程序结构...
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' 有关...
Rails电影浏览器 入门 这些说明将为您提供在本地计算机上运行并运行的项目的副本,以进行开发和测试。 正在安装 将此存储库克隆到您的计算机: git clone https://github.com/eclectic-coding/sinatra-todo-app ...