Gem 有考虑使用 Sinatra + ActiveRecord 替换你的 Rails 项目吗

agilejzl · 2018年01月26日 · 最后由 crazyphage 回复于 2018年05月14日 · 7658 次阅读
本帖已被管理员设置为精华贴

当你使用轻量级 Sinatra 做项目主框架,比如简单的 API 项目、MicroService,然后为了数据库模型操作引入的 Rails,你该怀疑了。

打开你的 Gemfile.lock 文件,找到以下类似代码

rails (5.0.1)
      actioncable (= 5.0.1)
      actionmailer (= 5.0.1)
      actionpack (= 5.0.1)
      actionview (= 5.0.1)
      activejob (= 5.0.1)
      activemodel (= 5.0.1)
      activerecord (= 5.0.1)
      activesupport (= 5.0.1)
      bundler (>= 1.3.0, < 2.0)
      railties (= 5.0.1)
      sprockets-rails (>= 2.0.0)

其实只需引入 activerecord 就可以。

最近在重温《Ruby 元编程》一书,有一章节讲到 attr_accessor 是怎么实现的。

User < ActiveRecord::Base

你不觉得这是件神奇的事吗?为什么 Model 继承了 ActiveRecord::Base,就可以使用 attr_accessor、各种 ORM 操作。 当然 源代码能告诉你为什么。

然后自己用 Sinatra 做控制器层,自己尝试写个简单版 gem mini_active_record来实现, 项目的目录结构基本与 Rails 保持一致,如下

一步一步实现下来,attr_accessor、validates、establish_connection、attribute_names、attributes 以及各种 CRUD 的操作, 有遇到实现问题时去看看官方 activerecord 的源代码,顿然明朗了。比如:

validates 用define_method "#{attribute}=" (PS:官方不是这么实现的啦~~),那就跟 attr_accessor 有重复定义attribute=方法了, 那么在 Model 里使用 attr_accessor、validates 有先后顺序依赖。

establish_connection 的单例模式实现,以及如何加载数据库的配置参数?

模型对象关系查询问题,如 User.find_by(id: 1) 转换成对应 SQL,使用 gem arel 解决。

CRUD 的操作需要数据库适配、持久化,需要保持已修改的模型数据 和 上次与数据库同步的数据,这样 Model.persisted?、changed?等方法才能正常高效的运作。

说了这么多,总结一句就是 Sinatra 项目 mini_active_record

gem 'activerecord' # gem 'mini_active_record'

加入这行代码,项目也能正常运作。

赞,看完 Ruby 元编程之后尝试看了点 activerecord,被各种方法调用来调用去的看晕了,估计功力还不够深把。最近正在看sinatra的源码,感觉简单很多。

hanluner 回复

逗我吧 rails + activerecord?

哈哈 activerecord 是经典之作,值得看

agilejzl 回复

为了对应你的话,打错,是用 rails。但 rails 可以没有 activerecord。

hanluner 回复

原来为啥用 Sinatra?现在为什么要换?

hanluner 回复

那有什么不错的方案替换 activerecord 吗?

请问用 Rails 遇到性能瓶颈了吗?如果只是学习还是值得鼓励的。

agilejzl 回复

代替的可能有,超越 ActiveRecord 的 应该没有吧

迟早会换回去的,除非你的项目真是个玩具,不打算长期维护😂

rainchen 回复

感谢你的总结,越实现越觉得庞大,就没继续了,结果是验证了 ActiveRecord 实现的很好。

你的吐槽点待统一处理。

huacnlee 回复

就是实验性的模仿实现 ActiveRecord,使用语法保持跟 ActiveRecord 一致

rainchen 回复

Sequel 这点比较好,只有 44 个 modules

Sequel.constants.sort.each{ |m| puts m if Sequel.const_get(m).is_a?(Module) }
ASTTransformer
AdapterNotFound
BasicObject
(irb):7: warning: constant Sequel::BeforeHookFailed is deprecated
BeforeHookFailed
CheckConstraintViolation
ConnectionPool
ConstraintViolation
Database
DatabaseConnectionError
DatabaseDisconnectError
DatabaseError
Dataset
DeprecatedIdentifierMangling
Deprecation
Error
ForeignKeyConstraintViolation
HookFailed
Inflections
InvalidOperation
InvalidValue
LiteralString
MassAssignmentRestriction
Mock
Model
NoExistingObject
NoMatchingRow
NotNullConstraintViolation
Plugins
PoolTimeout
Qualifier
Rollback
SQL
SQLTime
Schema
SerializationFailure
ThreadedConnectionPool
Timezones
(irb):7: warning: constant Sequel::UnbindDuplicate is deprecated
UnbindDuplicate
(irb):7: warning: constant Sequel::Unbinder is deprecated
Unbinder
UndefinedAssociation
UniqueConstraintViolation
UnmodifiedIdentifiers
VIRTUAL_ROW
ValidationFailed
agilejzl 回复

我吐槽的是 rails 的代码组织和命名:)

rainchen 回复

为啥?初看看不懂,调来调去的,但是 ruby 的组织方式,语言特性。感觉这么写是有道理的

我们原来是用 sinatra + ar,后来还是用回了 rails。rails 真的很成熟了。如果要用到某些个 gem,也对 rails 支持很好,否则还要自己配置适配

20 楼 已删除
21 楼 已删除

听大家一讨论,又学到了新东西。

agilejzl 回复

ActiveRecord 最大的问题是,架构上没有做分层,导致理解他和扩展他的难度非常高,看上去各用途功能很好的放在不同的 module 里,实际上内部的代码是相互耦合的

AR 太重了,Sequel 轻量很多。

但是 Sequel 里一些 module 实现和文档并不一致,而且载入多个的话要注意顺序... 坑度感觉和配置 postcss 差不多。

不过大部分 module 你都不需要,某些 DSL 也是白费劲,Sequel.lit 就可以了。

huacnlee 将本帖设为了精华贴。 02月07日 00:23

回帖加精

正在用 modori.rb 做 api 哈哈

放弃吧,rails 已是 best practice

抛砖引玉,很高心看到大家的经验分享,没有一一的回复了

dsh0416 回复

我是最近做的项目服务拆分成多个小项目,不使用成熟的 Rails,需要一套完善的轻量级的架构方案

jasl 回复

嗯呢 在参考 ActiveRecord 源码实现时,有明显感觉

不用 rails 时,习惯 Sinatra + DataMapper。 ActiveRecord 依赖拉起来确实太大了。不过作为百科全书式的库,总是在其它库缺少某个功能需要抄段代码时,让你放心。copy-paste编程必备

到头来你们自己做的哪一个有 Rails 的全面和完整?或者维护的有 Rails 这么正常?解决 BUG 有 Rails 来的及时?Rails 是多少人验证过的。所以放弃吧,好好的在 Rails 基础上优化或许才是靠谱的方案。

nuanshuidai 回复

并不这么觉得,jQuery 也是优化过的,前端很多人还是各走各的路。此条回复。

目前在项目中大量使用 Cuba+Sequel,的确有些东西不如 AR 方便,这个体会到了,ActiveSupport 等等缺失,需要自己零时去打 patch。
当然啦,你如果 monkey patch 都不用自己打的程序,写起来就不 Ruby 啦。

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