Rails 想募集 ActiveModel::Errors 的業務需求,打造一个新的 Errors API

lulalala · 2017年04月19日 · 最后由 jasl 回复于 2017年04月21日 · 2170 次阅读

嗨各位,是这样的,我在工作需求上偶尔会碰到觉得 ActiveModel::Errors 不太好用的时候, 比如说 error message 有时不想要 prefix attribute name 之类的需求。 虽然最近有新的 error details api 能用,但是感觉只是叠床架屋。 所以想说我们是否能搜集大家平常碰过的 api 设计需求, 等到搜集一定程度以后,可以来设计符合这些需求的 errors API,然后来实作。

我开了一个 repo,想说可以更新 wiki,或是发 issue 来搜集需求:

https://github.com/lulalala/rails_error_api_redesign/

要是你工作上正好碰到 errors api 的局限,欢迎来这里把你的 use case 加进来。

1 楼 已删除

不是 exception 相關,而是跟 model 的 validation errors 之類相關

3 楼 已删除
  1. 动态设置验证 根据数据自定义 validation,把 validation 设计成数据库存储。 例如:

    company1.configure_validation_on :User, :email, :present
    company2.configure_validation_on :User, :state, :present
    

    那么当一个 User create 的时候,如果 user 关联的是 company1,那么久要验证 email。如果 user 关联的是 company2 那么验证 state. 这些 validation 需要写在数据库里,而不是代码里。那么定义一个 validate: custom_validation 是最简单的,但是无法简单重用 rails 的 ActiveModel::Errors, 用 meta programming 也不合适,因为 validation 被设计成一种 callback。

  2. 除了 Errors 之外多一层 Warnings 老板希望能让用户创建数据时不强制验证,但是创建完后给出 warning,以及在页面上能显示 warning。 那么如果能类似 record.errors 多一层 record.warnings 会很不错。

yakjuly 回复

第一点我可以提供一个思路(我最近刚好为公司做了类似金数据的动态表单系统):

  • validates 是公开的类方法,也就是说允许类似 User.validates :name, presence: true 的写法为模型增加验证(其实这条是废话)

  • 所以可以在运行时通过这种手法为模型增加新的行为(验证、虚拟字段等等咯)

  • 但是 models 里的我们定义的模型的生命周期是跟 APP 的生命周期一致的,所以不可以直接操作他们

  • 所以需要一种手法来动态创建类型,参见 Class.new,于是有

new_user_class = Class.new(User)
new_user_class.validates :name, presence: true

new_user = new_user_class.new user_params
new_user.validate
  • 但其实这样是雅蠛蝶的,因为会被 AR 内部的一些细节坑,比如 STI

  • ActiveType ! 他甚至贴心的为你提供了 class SignUp < ActiveType::Record[User] 的这种方法

  • 两者结合,问题解决,前端不需要做任何改动~

一般场景 ActiveType 足够用了(虽然我就在原型阶段用了,而后就用自己写的换掉了,因为 AT 不太满足某些复杂需求的需要吧)。

第二点,模型上的验证器是把数据库的约束挪到应用层面做的,引入 warning 就违背验证器的精神了,但其实变通一下就可以用 AR 本身的机制解决, validate 操作是可以提供 context 的,参考 http://blog.arkency.com/2014/04/mastering-rails-validations-contexts/ (链接我随便 google 的,我自己没总结过文章)

yakjuly 回复

感恩,不过这想要注重在 .errors 物件上,也就是 validation 过程中把错误讯息加入 errors 的流程,前面怎样 validate 在范围外 :P

lulalala 回复

好多项目用 simple_form ,没记错的话错误信息提示的处理它管了,这个应该会降低改进这块 API 的价值,可能需要先了解下视图层的 FormHelper 类 gem 是如何跟模型的 Error 对象互动的,然后在 Error 对象上做文章,使得他方便定制,也能让那些 FormHelper 库利用上你的改进

当然你提出的问题确实是普遍需求,我经手过的项目一般都通过写特定的 helper 来搞定了

jasl 回复

目前一条可以走的办法是,建立一个 AR#luerrors(暂)的方法,包装现行的 errors API,来提供想要的新 API 功能。这样才能让既有的 validation 能无缝接轨。而 simple_form 这类因为还是呼叫既有 API 所以不会坏。感觉这是最保守安全的方式。

lulalala 回复

只接打开类来做应该也 ok 的,这块设计挺 oo 的

jasl 回复

第一点:我还没想过 Class.new(User) 可以这么用,很 cool,ActiveType 没看过,看起来可以一试。 第二点,我考虑过使用 context,但是不想把 errors object 搞混淆了,同一个页面需要同时显示 warnings 和 errors 的时候,我不能执行一下 valid?(context),然后再执行一下 valid。

总体问题就是 ActiveRecord 不能简单的给单个实例 动态添加验证。必须依赖 callbacks 体系,在 Class 上提前定义好验证。

其实还有些验证问题和关联表验证有关,我都是用 FormObject 重写验证,这体验挺糟糕的。

yakjuly 回复

Class.new 有个缺点是不会有类名,(可能)会导致 Model 的类成员 model_name 属性构造失败,用父类的就可以了,这算是个坑吧

这个方法的好处是你可以通过写标准的模型验证器(Validator)来做数据的验证工作。关联表的验证可以封装逻辑在里面

lulalala 加强 model error 的 AdequateErrors 0.1 版公开 提及了此话题。 11月15日 10:54
lulalala Rails 6.1 的 ActiveModel#errors 翻新 提及了此话题。 06月29日 09:18
需要 登录 后方可回复, 如果你还没有账号请 注册新账号