The error
1 | $ rvm install 3.1.2 |
The keyword to search
“__rvm_log_dotted:23: permission denied:”
The solution
https://github.com/rvm/rvm/issues/5055#issuecomment-815036429
1 | rvm install 3.1.2 --with-gcc=clang |
1 | $ rvm install 3.1.2 |
“__rvm_log_dotted:23: permission denied:”
https://github.com/rvm/rvm/issues/5055#issuecomment-815036429
1 | rvm install 3.1.2 --with-gcc=clang |
Actually, this is a general trick to understand any code: Find a concrete question -> get source code -> add breakpoints -> mess around -> observe the stacktrace…
rubocop codebase is huge, what if I want to know how it works by observing one cop?
follow the steps bellow:
bundle install
require 'pry'; binding.pry
to lib/rubocop/cop/lint/shadowed_exception.rbexe/rubocop
exe/rubocop --only Lint/ShadowedException tmp/shawdowed_exception.rb
and mess around with that breakpointcaller.grep /rubocop/
to see stacktracebtw, go ahead to see spec/tests is also a good option.
Have fun reading source code
use exe/rubocop --cache false --only Lint/ShadowedException tmp/shawdowed_exception.rb
to avoid odd behaviors while debugging
第一次知道”rubocop”这个关键字并且开始用它, 是在一次分享”exception handling”后
当时分享的主题是, ruby里的exception类有的继承关系, 最常被捕获的异常应该是StandError及它的子类, 不该捕获Exception类 参考
最后的结论是, 希望大家了解一下 Exception相关类的继承关系, 捕获该被捕获的异常
分享的最后, 有人提到了rubocop, 我了解了一下, 才意识到, 这件事其实可以不用写代码的人主动去检查和注意, 完全可以交给lint程序自己检查
be rubocop --only Lint/RescueException
系统变复杂后, 代码里几乎不可避免的出现了一大团异常处理的代码
尤其是在代码入口处, 一个个的rescue
看得头大, 有一次还因为没注意到一个更具体(specific)的异常被一个更通用(genral)的异常给遮蔽(shadowed 这个描述是后来知道的, 最初没有这个准确的概念)掉了, 好一阵debug才发现原因
这时候我想这种检查是不是也可以自动完成呢?
问题 -> 需求 -> 自己实现? -> 查查已有工具 -> 找到已有工具 -> 验证 -> 有问题 -> 尝试解决/求助 -> ...
第一反应还是自己写个工具检查
但是多了个心眼, 先查查有没有已有的工具已经做了这件事?
事实证明这个问题早有人遇到过, 并且有了解决办法: be rubocop --only Lint/ShadowedException
既然已经有了工具, 那试着用用呗
结果不理想, 没法识别出restclient
里被遮蔽的异常, 试了下看rubocop的代码, 一下看懵了, 暂时放弃
于是换了个更直接的方式: 给rubocop提issue: Lint/ShadowedException cop not working as expected for RestClient::RequestFailed exception
作者很快做了回复:
RuboCop is a static analysis tool, it doesn’t know inheritance relationships at runtime. It has false negatives for unknown inheritance relationships.
这里出现了一个不了解的概念: “static analysis tool”
经过 rubocop -> parser -> ast -> wiki: Abstract syntax tree -> wiki: Program analysis
才算意识到 “Program analysis can be performed without executing the program (static program analysis), during runtime (dynamic program analysis) or in a combination of both.”
而rubocop是一个静态程序分析工具, RestClient::RequestFailed
相关的异常只能在运行时确定(需要 require 'rest-client'
)
这下算是搞明白了作者回复的含义
coding -> problem -> analysis: can be automated? -> tool available?
rubocop 里的 ShadowedException cop, 没法检查出需要运行时才能判断出的继承关系
但是他可以检测出 ruby里已有的一些异常(TODO 验证一下)
处理我的这个需求有两种方式
自己实现一个新的工具, 专门做这件事
把这个能力集成进ShadowedException
里面, 这里涉及到比较难的问题是
ref: https://ruby-doc.org/core-3.0.0/Encoding.html#class-Encoding-label-Script+encoding
终于知道了ruby script里encoding
这个magic comment
是什么作用了
这个magic comment
不是被编辑器解析的, 而是由ruby解释器解析的(not 100% sure)
All Ruby script code has an associated Encoding which any String literal created in the source code will be associated to.
The default script encoding is Encoding::UTF_8 after v2.0, but it can be changed by a magic comment on the first line of the source code file (or second line, if there is a shebang line on the first).
The comment must contain the word coding or encoding, followed by a colon, space and the Encoding name or alias:
The
__ENCODING__
keyword returns the script encoding of the file which the keyword is written:
file1.rb
1 | # encoding: ASCII-8BIT |
file2.rb
1 | # encoding: UTF-8 |
见识真的很重要, 如果我早点意识到”Error Handing”的正确做法是使用”Error Service”, 可以省下不少时间, 减少很多没有意义的成就感, 获得真正的提高
在做AMG时, 经常在苦恼错误的处理:
error_logs
表, 在rescue里记录错误信息, 并且专门给它写了个index页和show页面用于定位问题(当时还洋洋自得…)Exception
类增加monkey patch
, 在每个rescue里, 打印或者记录e.to_hash
1 | class Exception |
error_handler
, 里面打印错误信息和记录error_log
, 这步倒问题不大https://www.mikeperham.com/2013/08/25/please-use-an-error-service/
error handling这个问题的解决方案是: 使用 bugsnag 之类的Error Service
应用里不该捕获所有的unknown exceptions
, rescue StandardError => e
甚至 rescue Exception => e
都是不可取的
错误应该被暴露出去, 然后及时修复; 由于错误被吞掉导致的问题很让人恼火, 而且有时候会很难排查
grep error
了对于难发现定时任务的报错, 其实也有解决方案: 使用sidekiq的插件 sidekiq-scheduler, 放弃linux的cron服务
有什么好处呢?
最近在项目代码里看到了 prepend
的用法, 读了文档, 基本明白了它的用法; 但是对于说它是为了解决之前 alias_methods
的问题的这部分讨论还没看明白(可能是我没怎么用过alias_method)
在我的理解中这几点比较关键:
inherited
included
…)类似, prepend 也有自己的hook: prepended
1 | class Service |
prepend
一个module ServiceDebugger
, 里面定义一个同名的方法, 这个模块里的方法定义会覆盖 Service
里的方法定义1 | module ServiceDebugger |
ServiceDebugger
里面可以调用super
方法, 调用原始定义的方法, 类似 around_hook
里的 yield
1 | module ServiceDebugger |
可以用这种机制, 在不修改原有代码的情况下, 增强原有的方法, 例如 增加日志; super前的代码类似于 before_hook
, 之后的代码类似于after_hook
此外, 可以在被prepend的模块里定义prepended
, 里面实现一些自动触发的行为
1 | module ServiceDebugger |
是魔法吗? 不确定; 不知道其他语言有没有这么多hook 和灵活的用法
2022-07-09 09:47:43 update
不是魔法, hook的本质大概是在模块/类的生命周期中 提前定义好”如果存在则会被调用的代码块”, 从外部看来就是hook了
用这种视角来看, 其他语言肯定也可以实现
核心大概是: “生命周期” 和 “封装”
很好例子就是 activerecord 里的 “The Object Life Cycle”
清晰的定义 配合良好的封装, 使得activerecord的hook使用起来很方便
昨天加今天, 基本搞明白了一个困扰我很久的问题, 有很多收获, 在这里记录一下
CharDetector
这个应用很简单, 核心代码就只有10行左右, 它完成的事情很简单, 它的实现很简单, 这是它简单的方面PS. 越写越长可不是好习惯!
WIP
Have fun.
最近捣鼓出一个小工具, 用来分析某个git仓库里所有人的commit, 看看某个人在这个仓库里做过多少提交
虽然统计结果不可能完全准确, 但是足够满足好奇心了
核心:
git shortlog --summary --numbered --email --all
gem install youplot
vim $ABS_PATH/analyse-git-info.rb
1 | require 'pry' |
# cd to any git repo
ruby $ABS_PATH/analyse-git-info.rb | uplot bar -o -d, -t "Git commit count of user"
记录一下使用RestClient这个Gem时遇到的一个坑.
版本: rest-client (2.1.0)
如果直接使用封装过的 RestClient.get
/RestClient.put
/RestClient.post
等方法, 当遇到异常的响应时(比如400 bad request), 得不到任何有用的信息
应该使用带块的方式调用, 用块参数接收 response, request 和 result, 不使用块会导致异常时丢失信息
用rails准备一个简单的接口, 响应 400, 并返回错误的信息
1 | # config/routes.rb |
可以看到, 用块的这个可以获取到响应里返回的详细信息
这个区别是在调用JIRA7和企业微信的API时发现的, 现象是: 用restclient调用api只返回了400, 用postman调试却能得到错误信息, 使用net/http调试, 也能得到错误信息
经过调试和阅读文档才意识到, 信息是被RestClient给吞了
看源码可以看到, 4XX和5XX的状态吗, 如果响应里有信息, RestClient是不会解析和返回的, 只会包装一个对应的异常
看这注释的意思, 这是个feature, 不是个bug, 但是假如确实有人在 4XX 响应里返回了信息(就像JIRA7和企业微信机器人接口那样), 那使用RestClient, 就得小心了…
1 | # lib/restclient/abstract_response.rb |
Request.execute(:method => :get, :url => url, :headers => headers, &block)
封装自己的请求RestClient.get
等封装后的方法, 并使用块示例:
1 | def make_qywxrobot_request(url, payload) |
目前没看出RestClient有什么优势…(可能是没遇到很复杂的http请求的场景)
又仔细读一下文档, 发现文档里还是提供了些头绪的, 只是遇到问题时没读懂
这个”信息被吞”的问题, 和Error Handling有点像, 很多时候异常被吞掉是很恼人的事情, 一定要想清楚再决定是否 rescue
rescue Exception => e
和 rescue => e
的区别, 多数情况下前者是万万不可的Lint/RescueException
to your .rubocop.yml and run bundle exec rubocop
没必要时, 可以不考虑使用lib(例入写gem时, 要尽量少的引入依赖)
使用开源lib时, 最好能先了解它, 不要拿来就用, 不然遇到了奇怪的问题时会很头疼; 如果有安全问题也会很麻烦的, 甚至会有巨大的损失