Ruby 如何创建自己的 Mini MVC 框架(如何无框架写个 Web App)

rocLv · 2022年11月30日 · 最后由 amonlei 回复于 2022年12月02日 · 600 次阅读

我们都知道 Rails、Grape 等等都是 Rack App 框架。

所谓 Rack App 是指遵循了 Rack 协议的 App,其中最主要的,或者最容易理解的一点就是,他们有共同的入口点:即 config.ru 文件。

其中,对于 config.ru 的规定是:

  1. run 命令后面加一个可以响应 call 方法的一个实例
  2. 这个 call 方法需要返回一个数组,其中,第一个为 HTTP 状态码,第二个为 Hash 组成的,代表 HTTP 协议中的 Headers,第三个是个字符串数组,数据个数为 1。 示例:[200, { 'Content-Type' => 'application/json'}, ["I am html body"]]

对于 Ruby 来说,具有call 这个方法的默认的就有 Proc, proc, lambda 等等,当然也可以自己声明。

依据上面这些基础知识,我们可以实现一个所谓的 Web App:

# config.ru
run proc {
      [200, {'Content-Type' => 'text/html'},
        ["Hello, world!"]]
}

这时候我们在这个文件所在的目录下运行:

$ rackup -p 300

我们就能在浏览器里面查看了: http://localhost:3000

到目前来说,我们算是可以实现一个简单的 Web APP 了。

那么我们如何实现一个 MVC 分层的 Web App 呢?

首先,我们可以按照 Rails 框架的思路,做一个分层: 数据库我们用 Sqlite 3,

# app/model/user.rb

class User
  attr_accessor :name, :email

  def initialize(name, email)
    @name = name
    @email = email
  end

  def self.all
    @users = []
    db = SQLite3::Database.new "toyapp.db"
    db.execute('select * from users where 1 = 1 limit 1;') do |row|
      @users << self.new(row[1], row[2])
    end
    @users
  end

  #method_missing
end

View 层我们用 ERB 来实现:

# index.html.erb
<div>
  name: <%= @user.name %>
  <br>
  email: <%= @user.email %>
</div>

Controller 层,我们简单的模仿 Rails:

class UsersController
  def index
    @user = User.all[0]
    puts @user
    template = File.open("./index.html.erb").read
    template = ERB.new(template)
    template.result(binding)  # 通过 binding 切换作用域
  end
end

我们通过 Application 可以把这些串起来:

class Application
  def call(env)   # env 参数是 Rack 默认会传过来的,其中包含了一个请求常见的信息
    [ 200,
      {'Content-Type'=>'text/html'},
      [UsersController.new.index]
    ]
  end
end

然后我们通过 Rack 来运行我们的 App:

# config.ru
run Application.new

现在,我们可以来试试我们的 Web APP

$ rackup -p 3000

源码地址:https://github.com/rocLv/no-framework-app.git

感兴趣的也可以找我来索要视频录像,视频剪辑好以后会发布在这。

这是我们全栈程序员 的其中一节课,目前还有 4 个名额开放,有兴趣的可以赶快来报名吧。

我们希望这次训练营结束以后,可以覆盖平常常见的开发场景。

有时候觉得 Rails 给的太多了,Sinatra 又给的太少,于是把两者混合起来,写了这个示例代码,分享给大家:

https://github.com/neweos/spaceship

.
├── app
│   ├── controllers
│   │   ├── application_controller.rb
│   │   └── spaces_controller.rb
│   ├── models
│   │   ├── space.rb
│   │   └── user.rb
│   └── serializers
│       ├── space_serializer.rb
│       └── user_serializer.rb
├── config
│   ├── application.rb
│   ├── database.yml
│   ├── frameworks.rb
│   ├── plugins
│   │   ├── model.rb
│   │   └── resource.rb
│   ├── puma.rb
│   └── setup.rb
├── config.ru
├── db
│   ├── migrate
│   │   ├── 20210325015514_create_users.rb
│   │   └── 20210621080357_create_spaces.rb
│   └── schema.rb
├── Gemfile
├── Gemfile.lock
├── lib
│   ├── spaceship
│   │   ├── knock
│   │   │   ├── authenticable.rb
│   │   │   └── auth_token.rb
│   │   ├── knock.rb
│   │   ├── setup.rb
│   │   └── version.rb
│   └── spaceship.rb
├── Procfile
├── Rakefile
└── test
    └── test_helper.rb

代码是去年写的,应该还能跑。

flask 回复

rails new app --minimal

piecehealth 回复

API only 的功能出来之后,我习惯用以下参数

--skip-active-storage
--skip-action-mailbox
--skip-action-cable
--skip-action-text
--skip-sprockets
--skip-spring
--skip-javascript
--skip-turbolinks
--skip-jbuilder
--skip-bundle
--api

写了一个示例 https://github.com/neweos/applause

piecehealth 回复

这个我试了一下,没少多少

写着写着就越来越像 rails 了

需要 登录 后方可回复, 如果你还没有账号请 注册新账号