看隔壁於 ORM 的 Ruby/Rails 的都被其他言走了,但我得至少 RSpec 在 JS 世界有替代。以下是一用 RSpec 的例子:
require 'rspec' require 'matrix' RSpec.describe 'numbers' do # 描述加法的性 shared_examples 'addition' do # 加法足交律 # a 和 b 在的 context 注入 it 'has commutativity' do expect(a + b).to eq(b + a) end end context 'for number' do # 令 a 和 b ,用加法的用例 let(:a) { Random.rand } let(:b) { Random.rand } it_behaves_like 'addition' end context 'for vector' do # a 和 b 可以是向量,也足加法的性 let(:a) { Vector[Random.rand, Random.rand] } let(:b) { Vector[Random.rand, Random.rand] } it_behaves_like 'addition' end end
如果要用 js ,至少所有的 a 和 b 都要成似context.a
context.b
,因 ruby 有 self ,而 js 只有法作用域,致 js 的 dsl 表力很受限。
另外在 ruby 也可以很易做 mock ,例如例子修改了Date.today
返回我用的日期:
require 'rspec' require 'date' RSpec.describe 'Date' do # 定一用的日期,一般使用的 let(:date) { Date.parse('1970-01-01') } before do # Date.today 本身是定好的,但我可以覆它返回我的日期 allow(Date).to receive(:today).and_return(date) end describe '.today' do it 'is the mocked date' do # Date.today 在是我 mock 的日期 1970-01-01 expect(Date.today).to eq(date) end end end
![]() | 1 Ketteiron 50 天前 ![]() ror 最优秀的地方在于极致地实现约定大于配置,以及魔法般的 DSL 。 有什么好处?就是极致地少写代码。 不同人有不同的喜好,就像我追求的是接近完美的类型安全,多写代码无所谓,反正现在大部分活是 AI 干的。而类型安全让我几乎杜绝各种傻逼运行时错误,可以安心地睡觉。你要说人菜那我无话可说,我本来就是一名普通的程序员,一定会犯错误,我只能希望能少犯错误。Ruby 很优秀与 Ruby 不适合团队协作并不冲突。 |
![]() | 2 ericguo 50 天前 ![]() @dssxzuxc 多写代码费 token 啊。。。能少写一点总是好的,只要没有歧义,我觉得 ruby 甚至可以当作以后所有其他语言的 LLM 转换的源语言用。没有明面上的类型干扰,更加容易让 LLM 关注逻辑,从逻辑推断出各个语言应该使用的类型或者需要导入的库。 |
![]() | 4 NineTree 49 天前 看不懂,但是感觉很厉害 |
![]() | 5 qiumaoyuan 49 天前 @dssxzuxc 我觉得不是适合不适合团队合作,而是适合什么样的团队合作。我还是那句话,程序员写代码时的思维应该是主动的、清醒的、活跃的,而不是被动的、怠惰的。当然如果你习惯把一个方法的代码写得超过 10 行,那确实大脑容易超载。 |
6 visper 49 天前 ruby 都已经没人用了吧。 |
7 dddd1919 49 天前 ![]() @dssxzuxc #1 除此之外觉得还有一点就是 all in one 的思路,通过这种 add-in 可以在一个工程内解决各种各样的问题。测试除了 Rspec ,还用过 Capybara 处理模拟 web 的测试,用 factory girl 构造铺底数据,用 waiter 去做自动化灰盒,这一切都是一个工程统统搞定,到现在其他生态很难找到这么完整的工具生态 |
![]() | 8 catamaran 49 天前 @qiumaoyuan 如果你的代码每个方法都不超过 10 行,我想可以称为神人了 |
9 kakki 49 天前 这种手工造 DSL 的语言根本不适合有一定规模的团队合作,对于代码民工来说,架构设计为做填空题就行,无论谁来拉翔都破坏力有限,Ruby 比较适合小规模专家级团队,尤其是独立开发. |
![]() | 10 qiumaoyuan 49 天前 @catamaran 不要自己吓自己,也不要限制自己。我觉得主要是很多人遇到问题习惯性的选择应付,而不是死磕。看看多数人对“屎山”的态度,几乎没有人选择去清理干净,增强自己能力的,而是能跑就行。在“屎山”的基础上,遇到 bug 的态度是兵来将挡水来土掩,不考虑怎么系统性的解决,除非实在应付不了,才会逼自己努力思考一下。 |
![]() | 11 zpvip 49 天前 @kakki 我一直搞不明白很多团队的代码合并流程, 团队成员发 pull request 时不同时提交测试吗? pull request 合并前没有人审核代码吗? 如果新代码能通过各种测试, 有两人以上 approved, 能有什么破坏力? |
12 Configuration 49 天前 实际上 RSpec 在 ruby 社区争议挺大的,喜欢的人很喜欢,讨厌的人很讨厌 |
![]() | 13 cloudzhou 49 天前 说起这个测试用例,接触 ai 后,平时补充代码不让我惊讶 ai 来写测试代码,那真是又稳又细,修修改改就可以用了 所以 ai 目前还没有到创造性的时候,擅长于已有的数据下,分析进行后续处理 |
![]() | 15 cloudzhou 49 天前 @zpvip #11 按照我经历过的公司,没有哪家真的测试用例 80% 以上的,都是主流程走一走,甚至依靠一些白盒测试,而人日压缩越发严重,压力很大,每次接需求就是填坑而已 之前我从 Java 世界到 Django ,简直蜜月期 后来为什么去掉幻想呢?从我需要大批量修改某个变量开始 比如说有一个广泛大量使用的表,我因为业务需求,需要字段重命名 nameXXX -> nameYYY 如果是静态语言,那么 ide -> refactor/rename 等,一把搞定 同理 看某个全局变量哪里引用,对应修改逻辑 我不知道这么多年,脚本语言是否改进了,在当时的话,是依靠 grep + 人肉 但是,我修改了好多次,发现总是漏了一些地方 起码 Python 来说,是运行时解析,到对应代码,才抛出错误 ror 不知道是否完善一些 |
![]() | 16 msg7086 49 天前 |
17 lithium4010 49 天前 jest-plugin-set |
![]() | 18 zpvip 49 天前 @cloudzhou #15 Rails 的标准 CRUD 的 DB Migration, Controller, Model, View 还有 RSpec / Minitest 都是命令行生成的, 如果就真是标准的 Restful, 那测试就是 100% 覆盖且通过, 此时你还没有写一行代码, 只运行了一行命令, 例如: bin/rails generate scaffold Post title:string body:text 具体什么过程, 可以去这看视频: https://kamal-deploy.org, 从 9:54 开始. 我一般会把 View 模板选做好, 不像 DHH 演示那样阳春. 当然实际情况不是这么简单, 各种一对多, 多对多关系, Post 嵌套 Comments 之类的, 补充一些测试用例也非常简单, 另外会提前安装测试相关的 Gems, 比如: gem "capybara" gem "selenium-webdriver" gem 'webmock', '~> 3.25' gem 'shoulda-matchers' gem 'rspec-rails' gem "factory_bot_rails", gem "faker", "~> 3.5" 现在有了 AI, 它自己会发现这些 Gems 并用这些 Gems 去写 RSpecs. 所以覆盖率 95% 以上是不难的. 一般 Rails 程序员都会有个启动模板, 也可以用别人现成的. https://github.com/nickjj/docker-rails-example 字段重命名的事, IDE 批量替换就可以了吧, 如果测试覆盖率好, 一下就可以找出漏网之鱼吧. 当然, 最简单的方法就是不改旧代码, 加个别名就可以了, 李白, 字太白, 号诗仙, 几个名字同时可用, 以后有心情了也可以一天改一处, 发现一处改一处, 天天提交 Git commit 还可以混 KPI. class User < ApplicationRecord alias_attribute :name_老字段名, :name_新改的字段名 end u = User.new u.name_老字段名 = "Alice" u.name_老字段名 # => "Alice" u.name_新改的字段名 # => "Alice" u.name_新改的字段名 = "Bob" u.name_老字段名 # => "Bob" 我以前做了十多年 C++, 副业也用 PHP 开发过流量很大的系统, 后来尝试换成 Ruby on Rails, 发现这才是网站开发的正确方式, 再后来发现全职 RoR 岂不是更爽, C++ 又不会给更多钱. 年纪大了为了能更适应就业市场, 也尝试用 Go/C#/Java/PHP/JS (React, NodeJs), 越用越气, 相比 Ruby on Rails 真是太麻烦了, 稍写两下就感叹, 在 Ruby on Rails 不就一句话的事吗, 各种测试写得头痛, 难怪覆盖率上不去. 语法方面, Java 的 Lamda 把我气笑了, 只有一个抽象方法的接口?! 真是老气横秋, 感觉有人按着我的头写代码, 的确是很好的防呆设计. AI 时代, 现在程序员一个顶十个, 全栈工程师应该更吃香, 希望我还能再战几年后再去卖红薯. |
![]() | 19 razertory 49 天前 是不是用 Scala 写出来也优雅 import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import org.scalatest.{OneInstancePerTest, Outcome} import org.scalatest.wordspec.AnyWordSpec import scala.util.Random // 共享测试 trait AdditionBehaviors { this: AnyWordSpec with Matchers => // 用 def 而不是 val ,让子类可以延迟绑定 def a: Double def b: Double def additionBehavior(): Unit = { "addition" should { "be commutative" in { (a + b) shouldEqual (b + a) } } } } // 针对 Double 的测试 class DoubleAdditionSpec extends AnyWordSpec with Matchers with AdditionBehaviors { override def a: Double = Random.nextDouble() override def b: Double = Random.nextDouble() "Double" should { behave like additionBehavior() } } // 针对 Vector[Double] 的测试 import breeze.linalg.{DenseVector => Vec} class VectorAdditionSpec extends AnyWordSpec with Matchers with AdditionBehaviors { override def a: Double = Random.nextDouble() override def b: Double = Random.nextDouble() // 这里把 a 、b 换成向量 private val v1 = Vec(a, a) private val v2 = Vec(b, b) "Vector" should { behave like additionBehavior() } // 重新实现加法行为,因为这里是向量 override def additionBehavior(): Unit = { "addition" should { "be commutative" in { (v1 + v2) shouldEqual (v2 + v1) } } } } |
20 charles0 49 天前 QuickCheck 、Hypothesis 是不是都能实现这个? |
![]() | 21 cloudzhou 49 天前 @zpvip 别名就算了,那肯定不是解决之道,清晰是第一位 我在想,是否有个语言,有 ror 的开发舒适度,同时具备静态编译过程(可选),然后还能编译成为一个独立 bin (类似 Go ) 也就是你在开发阶段,ror 现有开发方式;又是严格类型和语法,ide 完全可解析,比如变量名方法名错误及时提示(加个运行参数) 那就完美了 |
22 xgdgsc 49 天前 via Android @cloudzhou https://github.com/erikedin/Behavior.jl?tab=readme-ov-file 可以试试 Julia ,就是目前编译独立 bin 在实现进程中,编译出比较大的一坨 dll 目前就可以,1.12rc 在测试编译出小体积的 dll 了。 |
![]() | 23 doraemon0711 49 天前 ![]() 我是力挺 rspec 的一派,因为我认为 rspec 完全发挥了 ruby 的高度定制 DSL 的能力,让写测试就像写文章一样顺畅 但我也充分理解一个对 ruby 不熟悉的人,或者母语非英语的人看到这些代码会一脸懵的情况,因为我刚开始用 rspec 是就是这样的 说回 ruby ,我认为其最显著的特点,是用**最简洁**的方式把面向对象发挥到了极致,当意识到 ruby 中任何定义的 class 都是 Class 的实例、`1+1`实际上是`1.+(1)`时,我才意识到什么叫完全面向对象的编程语言,你或许可以指责 ruby 性能不行,但了解过 ruby 的人一定不应该指责其语法不优雅 |
![]() | 24 flyqie 49 天前 via Android 说起来,有点好奇,ruby 很多设计确实不错,但现在用的似乎并不算多,为什么呢? |
![]() | 25 mizuhashi OP @flyqie 我得者是有的,像 react 的非常落伍但是用的人很多。人的角度我只知道自己很喜 ruby ,14 年的候我要在 ruby python node 面一,然後看了作者 matz 的,很欣人,就定使用 ruby 了,後也遇到了很多用 ruby 的有趣的人(在 qq 群的老是一位 rpgmaker 本的)。我得技背後的人/人文是很重要的,如果要用的人少找原因,大概就是有似想法的人不是多 |
![]() | 26 mizuhashi OP @razertory 是的,在 ruby 的方式是,定一代,如 { a },我可以自由定 a 的解方式。上 a 代表的 self.a ,然後 ruby 可以在行每代的候指定 self 是什,再找到的 a 方法,只要具似的特性就能差不多的西 |
28 kneo 48 天前 via Android 有没有可能,别人没有,是因为嫌弃? |
![]() | 29 AV1 48 天前 @mizuhashi a 自动绑定到 self.a ,JS 曾经有类似的语法,也就是 with 块。 动态绑定 self ,在 JS 里也有类似的 call/apply/bind 。 还有 eval 、new Function ,可以玩各种很骚的操作。 但这些特性在 JS 里,通常不被视为优秀,而是糟粕,遭到唾弃。 或许这就是从“精致的小众玩意”转变到“流水线的大众工具”的代价吧 |
![]() | 30 qiumaoyuan 48 天前 @flyqie 语言优秀不优秀,跟用的人多不多没有太大关系。我觉得以逻辑严谨著称的群体,应该要看得清楚这点。 很多时候反而设计得很糟糕的东西更流行,每当有人谈到排行榜、流行程度,我就总是联想到 MooTools 和 jQuery 。MooTools 设计得十分优秀,无论是源码(喏,你真要看每个函数/方法 10 行左右的代码,MooTools 相当多时候都是),还是暴露出来的接口,都简洁得不像话,但受众就是少得可怜。 而 jQuery 满世界闻名,流行程度我就不用多说了。但如果你想要复用代码,它就一个插件机制可选,写出来的代码总是又臭又长。反观 MooTools 本身基于对象的设计,OOP 当中那么多的复用手段,它都完全可以利用。 jQuery 流行的原因是啥?我觉得就是看起来上手简单。但工具这东西,上手难度和往深了用之后的趁手程度往往是两回事。 另外,如果看排行榜的话,以前 C 和 C++ 一直是老大,这两年 AI 火了,Python 一下窜到第一,所以 Python 突然变成最优秀的语言了吗?明显不是这个原因,对吧? |
![]() | 33 mizuhashi OP ![]() @qiumaoyuan rspec 起很快,跑起也很快,免的快 |