Rails 从 0 开始制作一个 Rails

icepoint1999 · 2016年11月14日 · 最后由 academus 回复于 2016年11月14日 · 2570 次阅读

看了一下之前有一本 rebuilding rails 的书籍。但是由于是外文的并且售价略昂贵。所以我在阅读完之后整理了一番。不知道这样算不算侵权。没有抄袭。对书中知识进行了一遍整理。现在贴出整理好的一部分。如有侵权嫌疑 还请管理员大大尽快删除。谢谢!

打造你自己的 Rails

*捡起你的知识并且把他们拼凑成你自己的 rails 吧!

第一章

为什么要打造自己的 rails

  了解一个框架越深,就越能让你掌握它,并且使用它,如果你能够自己打造一个自己的‘rails’,岂不是一件非常值得去做的事情吗?    当然,RAILS 是一个非常顽固的框架,甚至他的制作团队也是这么说的。(约束>配置)但是现在你有机会自己制作一个你认定的约束框架,并且在这基础之上能够添加一些小插件。

谁适合看这份文档(书籍)

  如果你觉得自己能够熟练使用 rails,但是对内部一些机制不大了解,阅读本分文档会对你有一些很大的帮助。

开始吧!

以下环境 (mac os) 是你应该搭建好的
  • Ruby 2.0 或者更高版本
  • 一个编辑器(ruby mine / atom)
  • 一个终端工具(iterm2)

以上工具如在其他环境 可以找合适的替代品。在此不多做赘述。

让他开始工作

想一个名字是很难的,暂时就以 Shuriken 作为我们的"框架"的名字 首先在你的终端创建一个 gem

bundle gem Shuriken
create  rulers/Gemfile
          create  shuriken/Rakefile
          create  shuriken/LICENSE.txt
          create  shuriken/README.md
          create  shuriken/.gitignore
          create  shuriken/shuriken.gemspec
          create  shuriken/lib/shuriken.rb
          create  shuriken/lib/shuriken/shuriken.rb
    Initializating git repo in shuriken

其实 rails 也是一个 gem,我们创建了一个叫 Shuriken(手里剑)的 gem。并且目前的依赖都在 shuriken.gemspec 中

#shuriken.gemspec
    lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'Shuriken/version'

Gem::Specification.new do |spec|
  spec.name          = "Shuriken"
  spec.version       = Shuriken::VERSION
  spec.authors       = ["icepoint0"]
  spec.email         = ["[email protected]"]

  spec.summary       = "RUBY FRAME WORK"
  spec.description   = "Gay BING"
  spec.homepage      = "http://thunderjava.com"
  spec.license       = "MIT"

  # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
  # delete this section to allow pushing this gem to any host.
  if spec.respond_to?(:metadata)
    spec.metadata['allowed_push_host'] = "TODO: Set to 'http://github.com/icepoint0'"
  else
    raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
  end

  spec.files         = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
  spec.bindir        = "exe"
  spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
  spec.require_paths = ["lib"]
  spec.add_development_dependency "bundler", "~> 1.11"
  spec.add_dependency "rake", "~> 10.0"
  spec.add_development_dependency "rspec", "~> 3.0"
  spec.add_dependency "rack"
  spec.add_dependency "erubis"

并且我们加入了 rack, erubis 等依赖。这个后续会提到作用。

####创建我们的应用项目   现在我们的 gem 已经创建好了。接下来就是创建我们的应用项目来使用这个 gem。

在终端创建一个文件夹

> mkdir shuriken-demo
> cd shuriken-demo
> mkdir config
> mkdir app
> touch Gemfile #创建gemfile

在你的 Gemfile 里加上我吗现在要用的这个 gem

#shuriken-demo/Gemfile
source 'https://rubygems.org'
gem 'Shuriken', path: '~code/Shuriken'

gem 之后的 path 为 本地 gem 路径 由于还在开发阶段 所以暂不上传到你的 git / ruby gems 网站

然后执行 bundle

> cd shuriken-demo
> bundle

学过 java 的同学都知道。java 有 servelt , 那么 ruby 有什么呢?对了。是 rack。

我们现在暂时要打造的是一个很小的基于 rack 的框架 因此少不了 rack。

在 shuriken-demo 里创建 config.ru 并且添加如下内容

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

Rack 的启动代表着 对于每个请求执行这个脚本,在这个例子里所有的请求都返回 hello shuriken 的 200 返回 html

shuriken-demo 项目目录下 启动他

rackup -p 3001

打开你的浏览器 输入 localhost:3001 就可以看到 hello shuriken 的字样了!

为你的 Rack 请求加上规则

  进入你的 Shuriken 路径下 打开 lib/Shuriken.rb

# Shuriken/lib/Shuriken.rb
    require 'Shuriken/version'
    module Shuriken
      class Application
        def call(env)
             [200, {'Content-Type' => 'text/html'},
            ["Hello from Ruby on Shuriken!"]
         end
       end
     end    

然后在你的 shuriken-demo 项目里 创建 config/application.rb 并且添加

require 'Shuriken'
    module ShurikenDemo 
       class Application < Shuriken::Application
       end
    end

然后修改你的 conifg.ru

#shuriken-demo/config.ru
require './config/application'
run ShurikenDemo::Application.new

然后执行 rack up -p 3001 就可以看到"Hello from ruby on shuriken"的字样了----第一章结束 -2016.11.14

##第二章

你的第一个控制器

首先,我们梳理一下思路,路由从浏览器进入。到我们的 rack run env 里。这些路由带着参数。带着特有的 path_info . 所以我们首先要拿到 path_info 并且去进行解析。然后去和我们的 action 以及 controller 进行匹配。此外,我们还要能确保 controller 能够自动加载。不用手动去 require。还要能够渲染页面模板。而不是单纯的渲染 html 字符串。并且能够像 rails 那样去使用 action 里 的实例变量。让我们一步一步来吧。

#####rails 里的自动加载

首先我们要理解自动加载,通常我们用 const_get() 去获取 contstant。但是如果没有 require 进来则会抛出异常。现在我们在 lib/Shuriken/loader 里加入如下代码

class Object
  def self.const_missing(c)
    require Shuriken.to_underscore(c.to_s)
    Object.const_get(c)
  end
end

然后在我们的 lib/Shuriken.rb 里 require 进去

# lib/Shuriken.rb
require 'Shuriken/loader'

然后我们现在要根据路由去执行对应的 controller 以及 action

还是修改这个文件

#lib/Shuriken.rb
module Rulers
      class Application
        def call(env)
          klass, act = get_controller_and_action(env)
          controller = klass.new(env)
          text = controller.send(act)
          [200, {'Content-Type' => 'text/html'},
              [text]]

         end
       end
 end

然后还是在 Application class 里添加 get_controller_and_action 这个方法


def get_controller_and_action(env)
    _, cont, action, after =
     env["PATH_INFO"].split('/', 4)
    cont = cont.capitalize # "User"
    cont += "Controller" # "UserController"
    [Object.const_get(cont), action]
end

我们需要一个基础 controller 来让我们的应用进行继承,所以创建 lib/Shuriken/controller.rb 并且在lib/Shuriken.rb require 进去

#lib/Shuriken/controller.rb
    module Shuirken
     class Controller
       def initialize(env)
       end
     end
    end 

打开我们的 shuriken-demo 创建我们的控制器 **app/controllers/user_controller

class UserController < Shuriken::Controller
  def hello
  'hello'
  end
end

现在我们的控制器加好了 自动加载的代码也写好了。那么怎么像 rails 那样自动加载这个 controller 呢。你需要在config/application.rb 下加这些内容

#config/application.rb
#在 require 'Shuirken'之后添加以下代码
  $LOAD_PATH << File.join(File.dirname(__FILE__),
"..", "app","controllers")

然后执行 rackup -p 3001

打开你的浏览器 输入 "localhost:3001/user/hello" 你会发现一个错误:

NameError: wrong constant name
    Favicon.icoController
      .../gems/rulers-0.0.3/lib/rulers/routing.rb:9:in
    `const_get'
      .../gems/rulers-0.0.3/lib/rulers/routing.rb:9:in
    `get_controller_and_action'
      .../gems/rulers-0.0.3/lib/rulers.rb:7:in `call'
      .../gems/rack-1.4.1/lib/rack/lint.rb:48:in
    `_call'
      (...more lines...)

是的。我们有一个站点图标请求忘记了。马上补上: 打开 Shuriken 项目 里的 lib/Shuriken.rb

# rulers/lib/Shuriken.rb
   module Rulers
     class Application
       def call(env)
         if env['PATH_INFO'] == '/favicon.ico'
           return [404,
             {'Content-Type' => 'text/html'}, []]
         end
         klass, act = get_controller_and_action(env)
         controller = klass.new(env)
         text = controller.send(act)
         [200, {'Content-Type' => 'text/html'},[text]]
       end
    end
  end

也许你会觉得这是一个糟糕的设定。不过最后我们会修复它。至少现在让他可以先工作起来。

挺有意思的啊 内部的运作原理,希望一直更新下去

如果觉得 Rails 顽固不如不用,Sinatra 就挺好,深度可配置,Rails 的组件 ActtiveRecord、ActiveSupport 什么的拿来用就好了,详见Sinatra + ActiveRecord = Really Lean Startup?

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