# rubyframework **Repository Path**: frech/rubyframework ## Basic Information - **Project Name**: rubyframework - **Description**: 《Rebuilding Rails》一书中的示例以及该书的章后练习。 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2017-10-20 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #### 该项目为《Rebuilding Rails》一书中的示例并完成该书的章后练习。 目的:旨在了解元编程以及 Rails 的架构。 #### 元编程示例: 1. Rails 要求文件命名为下划线,类命名为驼峰,这样才会自动加载相应文件的原因:使用了 `const_missing` 和 `const_get` 方法。当调用了一个类名时,因为类名在ruby中是属于常量(大写),而又没有引进该文件的时候就会调用 `const_missing` 方法。 ``` class Object def self.const_missing(c) return nil if @calling_const_missing @calling_const_missing = true require Rubyframework.to_underscore(c.to_s) klass = Object.const_get(c) @calling_const_missing = false klass end end ``` 此外,在通过路由地址获取controller和action的时候也使用到了`const_get`方法。 ``` def get_controller_and_action(env) _, cont, action, after = env['PATH_INFO'].split('/', 4) cont = cont.capitalize cont += 'Controller' [Object.const_get(cont), action] end ``` 2. 因为几乎所有的web框架都使用了rack作为App服务器,此书例子也不例外。所以需要定义一个Proc或者命名为call的方法。在输入地址后,rack就会调用call方法去处理逻辑。我们通过上面方法获取了controller和action后,怎么根据不同的controller和action去处理不同的逻辑呢?`send`方法搞定!书中给出的例子如下: ``` klass, act = get_controller_and_action(env) controller = klass.new(env) text = controller.send(act) ``` 3. ORM中在查询数据库时常常会使用 `find_by_xxx()` 的方法。在此书使用文件的json格式作为储存数据一节中,最后提到的练习题让你实现一个类似 ActiveRecord 的 `find_all_by_xxx()` 方法。我使用了 `method_missing` 并用正则匹配方法名,匹配了就遍历文件寻找。没有匹配就调用 `super` 方法使其抛异常。 ``` def self.method_missing(method_sym, *arguments, &block) if method_sym.to_s =~ /^find_all_by_(.*)/ array = [] FileModel.all.each do |hash| if !hash[$1].nil? && hash[$1] == arguments[0] array << hash end end array else super end end ``` 4. 在渲染模版的时候,为了使在controller定义的实例变量能在模版里使用。我通过 `instance_variables` 和 `instance_variable_get` 方法获取所有实例变量并传到模版中。 ``` def get_instance_variables hash = {} self.instance_variables.each do |attribute| hash[attribute] = self.instance_variable_get(attribute) end hash end ``` 5. 第七章练习题中,原来的hash是使用形式如`hash['key']`来获取值的,但是Rails中是通过调用方法,即hash.key来获取值。此时可以使用 `define_method` 或者 `method_missing` 来实现。 ``` # define_method 实现方式 @hash.each do |key, value| define_method("#{key}") do value end end ``` ``` #method_missing 实现方式 def method_missing(name, *args, &block) return @hash[name.to_s] if @hash[name.to_s] super end ``` 6. 最后一章适配路由中有个用到 `instance_eval` 方法的例子。我们知道这个方法会在一个对象的上下文执行一个块,把这个块称为 Context Probe。就像是一个深入到对象中的代码片段。 - 在测试到web应用中,就像使用rails到路由一样写了如下路由。 ``` app = BestQuotes::Application.new use Rack::ContentType app.route do match "", "quotes#index" match "sub-app", proc { [200, {}, ["Hello, sub-app!"]] } # default routes match ":controller/:id/:action" match ":controller/:id", default: { "action" => "show" } match ":controller", default: { "action" => "index" } end ``` - 而在routing.rb里,使用了 `instance_eval` 去调用match方法,此处就相当于 `@route_obj.match("", "quotes#index")`,并依次调用 ``` def route(&block) @route_obj ||= RouteObject.new @route_obj.instance_eval(&block) end ```