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

agilejzl · 2018年01月26日 · 最后由 nuanshuidai 回复于 2018年03月21日 · 3524 次阅读
本帖已被设为精华帖!

当你使用轻量级 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'

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

共收到 31 条回复

赞,看完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楼 已删除

引入 ActiveRecord 其实就可以考虑不要上 Sinatra 了,ActiveRecord 还依赖 ActiveSupport,一通下来,已经半个 Rails 都导入进来了。除了写路由的方法发生了变化,总的来说没有区别。特别是加个 WebSocket 还要自己处理集群平衡。如果使用 Redis (Ohm) 做数据库,那感觉还是挺爽的,明显会变快不少。重写一个 ActiveRecord 可以考虑直接上 Sequel 吧,基本满足需求。

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

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基础上优化或许才是靠谱的方案。

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