`
fantaxy025025
  • 浏览: 1327935 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类

如何在rails里打开gem里面的类_为何有时候不生效

 
阅读更多

 

在rails里面打开某个gem的类,补充一下等等,也是常见的,虽然不太欢迎这么做。

 

为何有时候不生效?

如果gem里面已经登记了autoload  :ConstName "some_path"则ruby就可以找到了,不会走rails的机制了。(原因看上一篇里的解释)

 

在rails代码里,手动require也不起作用?

ruby的autoload只是登记作用,并不加载登记的文件。

只有当使用这个常量的时候,才加载该文件。

  如果已经使用了该常量并促发了加载该文件,则再次require的时候,因为名字是一样的,由require的机制可以得知不会再执行了。

 

如何才能生效?

  知道了原理是最好的,也就知道了不能起作用的一万种原因。

  如何才能打开gem中使用了autoload机制的类。(如果gem没有使用autoload,则不会有问题,想想为何?提示:还是require的机制)

#一般我们的目的是改写,而不是完全重写,所以需要起一个不同的名字,否则require将不起作用

  但这样很不好,会破坏rails的规则。

 

参考的这篇http://blog.yorkxin.org/posts/2014/02/10/autoload-in-ruby-autoload-paths-in-rails-and-module-reopening/文章说了一个方法:“如果該 class / module 已經在 Gem 裡面載入,則要在 Rails 裡面 reopen 它,就必須放在 autoload_paths 以外的地方,並且手動 require 之

 

  这个说法其实还是有很多问题的,虽然这么做是可以实现结果的。

  为什么必须放在autoload_paths 以外的地方?其实放在以内也是可以的,只要换个文件名字,但缺点也说了,会破坏rails的命名和规则。 

  如果命名规则符合rails的规范,放在了autoload_paths 以外的地方以外的地方,就一定行么?

  不一定。如果别的地方正好在ruby的load_paths里也不一定行。只不过一般在rails项目里,rails已经做过了处理,把autoload_paths放入了ruby的load_paths了。

 

我们的类会优先执行么?也就是担心改写名弄成反而把原来的类给改了,导致很多莫名其妙的问题

不会的。

看看这个代码,猜猜执行结果。

 

#autoload_lib/user.rb
class User
  puts "====autoload_lib/user loaded"
end
 

 

 

#autoload_lib2/user.rb
class User
  puts "====autoload_lib2/user loaded"
end
 

 

 

#main
autoload :User, "autoload_lib/user"

require "autoload_lib2/user"
user = User.new


puts "END"
 ====autoload_lib/user loaded
====autoload_lib2/user loaded
END

 

上面的运行结果,可以得知,autoload在没有改写的情况下,只要碰到了这个常量,就去加载对应的文件了。不错啊,ruby这个机制还是很科学滴。

 

能不能改写autoload呢? 

居然能,我的天,这个可别乱用吧。

看代码:

autoload :User, "autoload_lib/user"

autoload :User, "autoload_lib2/user"
# require "autoload_lib2/user" #没作用了
user = User.new


puts "END"

 运行结果:

====autoload_lib2/user loaded

END

居然真改写了。你觉得呢?

团队都这么用,管技术的就累死了。

 

prefer:http://blog.yorkxin.org/posts/2014/02/10/autoload-in-ruby-autoload-paths-in-rails-and-module-reopening/ 有改动和补充

 

为了防止丢失,复制到这里吧。

關於 Ruby 的 autoload 與 Rails 的 autoload_paths 以及 reopen module / class

February 10, 2014 · 4 Comments

最近在實作一個特別的需求,做了一個 gem 搞這種事:

  • 在 Gem 裡面, lib/models/post.rb 定義 Post < ActiveRecord::Base
  • 在 App 裡面, app/models/post.rb 打開 class Post 多寫一些 app-specific methods

然後就搞了三天搞不定。

具體的現象是:

  • 在 Gem 裡面,不論是使用 Kernel#autoload 還是 Rails 的 config.autoload_paths << 來做到自動載入,都無法在 App 改寫 Post class 。
  • 如果在 Gem 裡面不做 autoloading ,則 Rails 會去抓 App 裡面的 app/models/post.rb, which is not inherited from ActiveRecord::Base 。

之後試了繼承(很難搞)和 module ,最後是用 ActiveSupport::Concern 包了 module ,把 association 之類的東西寫在 included do 裡面,解決。

今天讀到這篇文章 Rails autoloading — how it works, and when it doesn't ,對於 Ruby 和 Rails 的 "autoload" 有粗略的瞭解了。簡單整理如下:

  • Ruby 的 Kernel#autoload 是告訴 Ruby runtime 「要找某個 constant 的時候,可以載入某檔案」,比較像是「登記」,在登記之後, Ruby runtime 若發現程式裡面有要用某個 const ,但沒有定義,就會載入該檔案,這是發生在「第一次使用」的時候,用第二次就不會觸發。
  • Rails 的 autoloading 跟 Ruby 的 Kernel#autoload 完全不一樣,實作方式是用 Module#const_missing :抓不到(const 在 runtime 沒定義)的時候才自動根據 constant 找檔名,例如Taiwan::Taipei::SungShan 就是會找 taiwan/taipei/sung_shan.rb 。
  • 承上,「要去哪裡找檔案」這件事,是在 config.autoload_paths 設定的,這個 array 就是「要自動從檔案載入缺失的 const 的時候,就去依序搜尋哪些路徑」,類似 shell 的 $PATH 。如果檔案不存在,就會 raise NameError ;如果檔案存在,但 const name 跟所要找的不同,就會出現「Expected app/models/user.rb to define User」這種錯誤。
  • 承上,第一次載入完成以後,就可以在 Runtime 裡面找到,所以不會再度觸發 const_missing 來自動搜尋。

所以:

  • Kernel#autoload 不應跟 Rails 的 autoload_paths 混淆,要視為兩個完全不同的功能
  • 誰第一次載入誰算數, Rails 只在找不到該 const 的時候才會去 autoload_paths 搜尋
  • 所以,如果某個 const (class / module) 已經在 runtime 裡面定義了,那麼要在 Rails 裡面 reopen 它,就必須確定它一定會執行,例如 initializers 裡面,或是手動 require 它。如果是放在某個 autoload paths 裡面,例如 app/models/ ,則 Rails 並不會執行之,因為同名的 const 已經在 Runtime 裡面了。

這也就是為什麼會有「在 gem 和在 app 裡面,同名的 model class 是 mutually-exclusive,除非手動 require 才能改寫其內容」。也就是說,想要在 gem 裡面定義一個 model ,然後在 rails app 裡面 reopen 它,是不可能的,必須要手動載入它的 reopening。

說得更 general 一點就是:如果該 class / module 已經在 Gem 裡面載入,則要在 Rails 裡面 reopen 它,就必須放在 autoload_paths 以外的地方,並且手動 require 之。


該文很推薦一讀,除了詳細說明了 Ruby 和 Rails 的 autloading 機制,還提到一些陷阱,例如說 Rails 的 autoloading 其實不會理 Module.nesting (lexical context of current line) ,這樣子某些情況下會變成「第一次可以成功 autoload ,但第二次卻說 NameError 找不到 const」這種問題。

By Yu-Cheng Chuang February 10, 2014  ruby  rails

 

+

+

+

=
-

-

-

 

分享到:
评论

相关推荐

    rails_admin_flatly_theme:RailsAdmin 的 Bootstrap 2 Flatly 主题

    gem 'rails_admin_flatly_theme' , github : 'konjoot/rails_admin_flatly_theme' 在config/application.rb ,就在Bundler.require之后: ENV [ 'RAILS_ADMIN_THEME' ] = 'flatly_theme' 然后运行bundle , ...

    centOS Rails3环境搭建

    ### CentOS环境下Rails 3开发环境搭建详解 #### 一、准备工作与环境配置 在开始部署Rails 3开发环境之前,我们需要确保系统上已经安装了一些基本的软件包和工具。这一步骤对于后续的Ruby和Rails安装至关重要。 ##...

    Ruby-RailsFootnotes在每一个Rails页脚展示应用程序的相关信息方便调试

    Ruby on Rails是一款强大的Web开发框架,它以简洁和生产力为导向,深受开发者喜爱。在开发过程中,调试是必不可少的一部分,为了帮助开发者更有效地定位问题,Rails社区创建了多种辅助工具,其中之一便是Rails ...

    log_analyzer:Rails日志分析器(查看视图呈现的速度)

    LogAnalyzer 查看Ruby on Rails应用程序中渲染的速度... 将此行添加到您的应用程序的Gemfile中: gem 'log_analyzer' 然后执行: $ bundle或将其自己安装为: $ gem install log_analyzer用法安装后,在控制台命令log_

    rvm nginx passenger rails配置服务器.docx

    《使用RVM、Nginx和Passenger配置Rails服务器详解》 配置Rails服务器是一个关键步骤,特别是对于那些希望部署Web应用程序的人来说。在这个过程中,RVM(Ruby Version Manager)、Nginx和Passenger扮演着核心角色。...

    ruby on rails操作流程.pdf

    在本文中,我们将深入探讨在Ubuntu环境下搭建Ruby on Rails开发环境的详细步骤。 首先,我们需要在VirtualBox上安装Ubuntu操作系统。在这个过程中可能会遇到一些常见问题,例如终端软件的使用。确保使用启动器打开...

    Ruby中的gem包管理的使用及gem源搭建教程

    通过以上步骤,你不仅可以熟练地管理和使用RubyGems,还能创建并分享自己的gem包,为Ruby社区贡献一份力量。记得保持RubyGems和相关库的更新,以获取最新的功能和安全修复。同时,良好的文档和版本控制对于gem的使用...

    Linux系统平台上安装和配置Ruby on Rails

    在Linux系统上安装和配置Ruby on Rails是一个常见的任务,特别是在开发Web应用程序时。Ruby on Rails(RoR)是一个基于Ruby语言的开源Web应用框架,它遵循模型-视图-控制器(MVC)架构模式,强调简洁和生产力。在...

    mac及linux下搭建ruby+rails环境

    在Mac和Linux操作系统中搭建Ruby on Rails开发环境是一项常见的任务,尤其对于从事Web开发的人员来说至关重要。Ruby on Rails(简称Rails)是一个基于Ruby语言的开源Web应用框架,它遵循模型-视图-控制器(MVC)架构...

    fitvids-rails:fitvids.js的Rails包装器

    **fitvids-rails** 是一个专门为Rails框架设计的组件,它将JavaScript库 **FitVids.js** 集成到了Rails 3.1及更高版本的资产管道(Asset Pipeline)中。FitVids.js是一个轻量级的解决方案,旨在自动调整网页中嵌入...

    RVM配置ROR

    - 检查是否有足够的磁盘空间来存储Ruby、Rails以及相关依赖库。 - 如果遇到任何问题或错误,请查阅RVM官方文档或寻求社区帮助。 #### 五、总结 通过上述步骤,我们可以在Ubuntu环境下顺利地配置出适合开发Ruby on ...

    linux安装redmine_1.2.1.pdf

    有了Ruby、RubyGems和Rails之后,可以通过Gem安装Redmine。首先,创建一个新的数据库并配置数据库连接信息(如MySQL或PostgreSQL)。然后,使用`gem install redmine`命令安装Redmine。安装过程中,系统会自动处理...

    redmine安装方法

    最后,通过 RubyGems 安装 Redmine,命令为 `gem install redmine`。安装完成后,按照 Redmine 文档的指示配置数据库连接、创建数据库、初始化项目和启动服务器。 整个过程中,可能会遇到依赖问题、权限问题或者...

    redmine在centos下配置文档

    下载 Redmine 的源代码,解压后,在目录中执行 `bundle install --without development test` 来安装 Redmine 所需的 Gem。配置数据库连接信息(如数据库名、用户名、密码)在 config/database.yml 文件中,然后执行...

    ruby在unbuntu的安装版本

    Ruby是一种强大的、面向对象的脚本语言,尤其适合Web开发,如使用Rails框架。在Ubuntu操作系统上安装Ruby,可以让你利用其丰富的库和工具来构建高效的应用程序。本指南将详细讲解如何在Ubuntu系统上安装Ruby 2.1.5这...

    linux下的redmine搭建借鉴.pdf

    在Linux环境下搭建Redmine是一个涉及多个步骤的过程,主要包括Ruby、RubyGems、Rails、i18n、MySQL驱动以及Redmine本身的安装与配置。以下是对这些步骤的详细说明: 1. **Ruby的安装**: - 从Ruby官方网站下载稳定...

    linux安装redmine1.2.1.pdf

    在本文中,我们将详细探讨如何在Linux环境下安装Redmine 1.2.1。Redmine是一个开源项目管理软件,基于Ruby on Rails框架构建,适用于跟踪任务、管理版本控制和协调团队工作。 首先,我们需要安装Ruby运行环境。...

    利用Unicorn和Nginx部署Redmine

    标题中的“利用Unicorn和Nginx部署Redmine”指的是在服务器上安装并配置Redmine项目管理工具,通过Unicorn作为应用服务器,Nginx作为反向代理和负载均衡器,来提供高效、稳定的服务。这是一个常见的Web应用程序部署...

    passenger-3.0.11.tar.gz

    在Redmine的部署中,Passenger可以作为一个高效的解决方案,因为它能够快速响应用户的请求,并且与Rails应用有很好的兼容性。不过,需要注意的是,3.0.11版本相对较旧,可能不支持一些较新的Rails特性或安全更新。...

Global site tag (gtag.js) - Google Analytics