Eisen's Blog

© 2024. All rights reserved.

CSS 实现的小三角

2013 February-13

在家待了快两周了,又进入了半死的状态。来写篇 blog 振奋下精神吧。

昨天在做一个类似与 weibo 上评论的东西,在点击评论按钮后会显示评论框。而这个评论框在它的右上角会有一个小三角与评论按钮相呼应。

之前在一本书里面见过,(这本书似乎是台湾人写的,当时觉得它讲的特别相近,不过有些年头了,都忘记它叫什么了,刚才在豆瓣上用 CSS 的关键词找了下,也没找到。),是采用 0 宽度, 0 高度用厚边框挤压出来的三角形。就是说,当对一个 0 空间的元素给予一个很厚的边框的时候,那么四个方向(top left right bottom)的边框会相互挤压,变成三角形!那么,对三个方向的边框给予透明 transparent,而给予另一个方向的边框一个可见色,那三角形就大功告成了!

代码:JS Bin

<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
  <div class="arrow-up"></div>
  <div class="content">
  </div>
</body>
</html>
[/code]
[code lang="css"]
.arrow-up {
  position: relative;
  left: 95%;

  height: 0;
  width: 0;
  border-width: 7px;
  border-color: transparent;
  border-style: solid;
  border-bottom-color: #f1f1f1;
}

.content {
  height: 100px;
  background-color: #f1f1f1;
}

不过这里有个问题,可以看的出来,在微博里面,人家的那个小三角是有边框的!

加上边框,就瞎了吧。这里,在 bootstrap popover 的代码来看到了它的神方法:用两个小三角叠加出边框的效果!一个三角为边框颜色,另一个三角为背景颜色覆盖在上面。且第二个三角的位置稍微向下,一方面可以显示出三角形的边框,另一方面可以覆盖三角下面矩形的边框部分。

代码:JS Bin

.arrow-up, .arrow-up:after {
  height: 0;
  width: 0;
  border-width: 7px;
  border-color: transparent;
  border-style: solid;
}

.arrow-up {
  position: relative;
  left: 93%;

  border-bottom-color: gray; /* border-color */
}

.arrow-up:after {
  content: "";
  display: block;
  /* overlay */
  position: absolute;
  left: -7px;
  top: -6px; /* and show 1px */
  border-bottom-color: #f1f1f1; /* background-color */
}

.content {
  height: 100px;
  background-color: #f1f1f1;
  border: 1px solid gray;
}

最后附上一个用 CSS 做小三角的网站 https://apps.eky.hk/css-triangle-generator/


rails -- factory_girl and faker

2013 January-29

昨晚写 high 了,本来想写写 factory_girl 和 faker 这两个 gem,结果一字未提。这里再写一篇好了。

factory_girl 与 faker 都是在那本在线的 rails tutorials 里面看到的。factory_girl 用于替换 fixtures 而 faker 用于生成各种随机的内容,这两个东西一个提供生成东西的框架,一个提供具体生产出来的内容,一起使用就显得非常合理了。刚刚纠结的在写相关的内容,反复的打开这两个 gem 的 github 页面翻来翻去,翻的好烦,决定总结一下!这里结合 rails tutorials 写一下自己感觉比较实用的内容。

刚接触 rails 时看到 fixture 这个东西,感觉还是挺体贴的。不过,尽管是可以配合 erb 方式嵌入代码,但依然不够灵活。于是就有人(thoughtbot)做了个写 code 生成 fixture 的东西,叫做 factory_girl。用处和 fixture 是一样的,只是把 yaml 换成了 ruby code。既然,它的名字里面有 factory,那么我就把它认为是用于为每个类生成很多实例的。这里先给第一个例子。

FactoryGirl.define do
  factory :user do
    email '[email protected]'
    password '00000000'
    password_confirmation '000000000'
  end
end

这是一个等价于 fixture 的例子,用于生成一个静态的 user 记录。 不过 factory_girl 很灵活,不像 fixture 直接放进数据库,还可以有其他的方式。

# Returns a User instance that's not saved
user = FactoryGirl.build(:user)

# Returns a saved User instance
user = FactoryGirl.create(:user)

# Returns a hash of attributes that can be used to build a User instance
attrs = FactoryGirl.attributes_for(:user)

# Returns an object with all defined attributes stubbed out
stub = FactoryGirl.build_stubbed(:user)

build create attributes_for 都比较容易理解,而最后这个 build_stubbed 就诡异了。我 search 了一下,发现了 thoughtbot 自己写的一篇文章 Use Factory Girl’s build_stubbed for a Faster Test Suite,文章中提到

build_stubbed is the younger, more hip sibling to build; it instantiates and assigns attributes just like build, but that’s where the similarities end. It makes objects look look like they’ve been persisted, creates associations with the build_stubbed strategy (whereas build still uses create), and stubs out a handful of methods that interact with the database and raises if you call them. This leads to much faster tests and reduces your test dependency on a database.

差不多是说 很多测试的时候,虽然数据保存到了数据库,但是并不会和数据库打交道,你要的内容已经在这个对象里面了。那么,为了加速测试,你其实不用真的把数据写到数据库里面。build_stubbed 假装已经把数据保存到数据库了(伪装的create)。然后你在继续别的测试就行了。当然,既然是假装保存到数据库里面了,那么如果你真的用到保存后的信息,它会报错的。不过到目前为止,我并没有实用过这个方法。

[update]学到老活到老,写这个的时候居然都不知道 stub 是什么,现在知道了。看了 mocha 就什么都知道了。

额,刚才我还想写我对于 attributes_for 的实用呢,因为我以为 user model 保存之后,其 password 属性就不复存在了呢,不过刚才我尝试了一下,它依然建在,那么我之前为了得到原有的 password 而实用 attributes_for 就是徒劳的了。这么说来,这个方法就没什么用处了 o_o。因为 factory_girl 支持属性的重写。

rails g scaffold comment content:text user_id:integer post_id:integer

FactoryGirl.define do
  factory :comment do
    content 'bla bla'
  end
end

@comment = FactoryGirl.create :comment, user_id: user_id, post_id: post_id

我仅仅为 comment 的 content 提供内容,在真正生产 comment 的时候,用重写的方式把 user_id post_id 补全即可。

到目前为止,factory_girl 差不多只是 fixture 等价功能的加强版,下面就要讲述它作为 工厂 的特性了。同时,有了工厂,就要有材料,Faker 就要登场了。

在 factory_girl 的 readme 中 有一个章节叫做 Lazy Attributes,是说一些属性在对象生成的时候才能定义,而不是用静态的定义。就比如 user,静态的 email 由于需要是唯一的,因此不能用于反复生成。需要我们提供一个方法可以生成不同的 email。那么 sequence 与 generate 就要登场了。

sequence 与 generate 方法是配合使用的。

# Defines a new sequence
FactoryGirl.define do
  sequence :email do |n|
    "person#{n}@example.com"
  end
end

FactoryGirl.generate :email
# => "[email protected]"

FactoryGirl.generate :email
# => "[email protected]"

如果用 python 的模式来解释 sequence 像是一个生成器,而 generate 像是一个迭代器,这样配合实用可以生成无限多的 email 了。

上面的例子是 官方 readme 给出的。而我则把 sequence 与 faker 一起使用,就可每次生成随机的内容了。这部分内容在 rails tutorial 里面也有提及(sample microposts)。我的做法比较类似。

FactoryGirl.define do
  sequence(:random_title) {|n| Faker::Lorem.words().join(' ')}

  sequence(:random_content) do |n|
    Faker::Lorem.paragraphs().map {|elem| "<p>#{elem}</p>"}.join("\n")
  end

  factory :post do
    title { generate :random_title }
    content { generate :random_content }
  end
end

faker 确实是个很方便的东西,而且没有什么难度,想要看具体的用法,直接来看 docs 就好了。

最后,我结合这些,做了一个类似于 rails tutorials 里面的 populate task 的东西,用于生成初始数据。

namespace :db do
  desc "Fill database with sample data"
  task populate: :environment do
    email1 = "[email protected]"
    email2 = "[email protected]"
    email3 = "[email protected]"
    email4 = "[email protected]"

    user1 = make_user(email1)
    user2 = make_user(email2)
    user3 = make_admin(email3)
    user4 = make_moderator(email4)

    2.times do
      post = make_posts(user1)
      [user1, user2, user3, user4].each do |u|
        FactoryGirl.create :comment, post_id: post.id, user_id: u.id
      end
    end

    2.times do
      make_posts(user2)
    end

  end

  def make_user(email)
    User.create!(
      email: email,
      password: "00000000",
      password_confirmation: "00000000"
    )
  end

  def make_admin(email)
    user = make_user(email)
    user.role = "admin"
    user.save!
    user
  end

  def make_moderator(email)
    user = make_user(email)
    user.role = "moderator"
    user.save!
    user
  end

  def make_posts(user)
    content = Faker::Lorem.paragraphs().map {|item| "<p>#{item}</p>"}.join("\n")
    user.posts.create!(title: Faker::Lorem.sentence(), content: content)
  end
end

每次需要用命令

rake db:reset && rake db:populate

而且,前提是 seeds.rb 为空,因为 rake db:reset 重新跑所有的 migration 然后跑 seeds.rb。这让我觉得是不是这些 sample 数据放在 seeds.rb 会更好一些呢?单独的 task 粘和性并不是很好的样子。

(更新)我已经这么做了,把 populate 这个 task 干掉,把里面的代码稍作修改直接放到 seeds.rb 然后每次 只需

rake db:reset

即可。

最后眼馋 factory_girl 里面 sequence generate 方法,自己写了一个 naive 版本的,挂在这里好了。

class Factory
  @@generators = {}

  def self.sequence(name, &proc)
    @@generators[name] = {
      seq: 0,
      proc: proc
    }
  end

  def self.generate(name)
    @@generators[name][:seq] += 1
    @@generators[name][:proc].call(@@generators[name][:seq])
  end
end

Factory.sequence :email do |n|
  "example#{n}@exmple.com"
end

puts Factory.generate :email
# [email protected]
puts Factory.generate :email
# [email protected]

factory_girl 到目前为止,用的还是比较浅的,不过作为 fixture 的替代品,以后应该有很多打交道的时候。


On rails way

2013 January-28

好久好久没有写过 public 的 blog 了。虽然是有写一些东西,但是没有公开,那么,写的再多,自己的 blog 上也什么都体现不出来,何况,写的也不多。而且,质量也很有问题,这也是没有公开的原因之一吧。写的烂,不好意思放出来。而且,我发现人真的是很放松的时候才会去写 blog 的,如果你平时很压抑,很烦,即便是空转,白白浪费时间,也没有办法静下心来,去写一点东西。哦,或者更进一步,是首先平时的工作生活相对比较顺心,有了身心都空闲的时间,然后才会去做自己喜欢做的事情(当然,最理想的情况是工作的内容就是很喜欢的事情),然后才会从喜欢做的事情里面感悟出点什么东西,然后才会想起来写点东西表达出来的。勉强是写不出东西来的,上次快元旦的时候发现自己好久没有写东西,以刷数据的心态勉强去写,结果胡乱写了些东西放在草稿箱里,估计很难见得了人了。不过幸好,这次的应该可以。

从去年八月份比较闲的时候我在又一个揪心的项目之后再次开始了寻找靠谱的开发框架的道路。这次,我决定跨越语言的界限,去尝试一下大名鼎鼎的 ruby on rails。说到语言的障碍,我又想先歪个楼,谈谈每个人对于语言的偏好。

虽说自己接触了很多的编程语言,但是我觉得真正熟练的没有几个。真正让我说比较拿得出手的,我可能会说 js 一个。python 一瓶子不满,半瓶子晃荡。c c++ java php 都是什么时候用什么时候学,虽然有些语言在用的时候也会觉得得心应手(其实我说的是 python 还有 php)用完了就忘记了,一点都不会写(哦,当然 python 还是写的多一些,一直会呢 o_0,但是依然不深入)。这也是我很拙急的地方。一直这样晃荡下去可不是个长久之计,一定要很深刻的掌握一门通用的语言。python 是我一直比较看好的语言,有很强大的社区,自然的语法,整体来说比较理想。但是,可悲的是,python 在 web 开发方面的能力,我个人感觉并不是最佳方案。尤其是在看了 rails 之后发现 django 简直就是 rails 的翻版,而且翻的似乎不太理想。但是我有一直觉得 web 开发一定是重头戏,因为 web 开发有很多重复无聊的东西,需要一个可以 DRY 的框架来帮你摆脱那些无聊的东西。出于对 python 的失望,也同时在那段时间看了本书 37-signal 的书 rework。我决定尝试一下大名鼎鼎的 ruby on rails。

跨语言一直我是比较忌讳但是有每次不得不去做的事情。前面有提到,由于工作环境的问题,我会不得不选择工作环境所共用的语言。这也是导致我使用了这么语言的原因。当然 python 是我自发学习的,没有任何一个环境是逼迫我实用 python 的,所以我一直对 python 很有好感。然后 js 作为前端唯一的语言,毋庸置疑的被我经常实用而导致对其比较熟练。很遗憾,一直在后端没有什么太大的建树,所以 c++ c java 都很没有信心。不过在前一阵子经历 java refactory 的历练之后,似乎对 java 也有些上道了,这也多亏了钱师傅的指导,没有他的指导,我觉得我要死在电脑桌前了o_o。所以说,其实每个经历都会有所收获的,即便是非常烂的经历。如果下次再遇到类似的问题,我想,我就不会像这次这么纠结了。起码,我是这么觉得。同时对后端似乎也有些开窍了,以前毫无概念的内存泄漏,起码知道是有理由担心的: 程序都是没昼没夜的跑的,错之毫厘谬之千里。而且性能也是非常值得考虑的问题,当数据量到了百万级别后,O(n^2)也是不小的工作量,甚至内存也是问题了。

虽说我和一些人有探讨过语言到底是不是障碍的问题。一个观点是说,语言不应该是个障碍。有一定经验的工程师都会实用过好多种的语言,但是不论什么语言,真正重要的东西是那种连伪代码都可以表示的算法或者说是逻辑。我曾经一度比较认同这种观点,因为我也是经历了很多语言过来的。在百度写 php 的时候,我也不会觉得说 php 有什么问题,每种语言该有的东西它都是具备的。基本的 web 开发要做的事情在 python java php ruby 里面其实都会反复出现了,它们只不过是围着 http 协议转罢了。但之后我发现,其实每个人对于语言是有各自的钟爱的。就单拿 web 开发来说,虽然都是围着 http 协议转,但是每个语言转的方式各有区别。尤其是在经历了这些语言做类似的事情之后,我觉得,它们绕的水平还是有区别的,或者说我对于怎么绕比较好是有自己的喜好的。虽然经历了很多语言,但是在用一个不太喜欢的语言的时候总会有一种凑合的想法,就是感觉就用这么一阵子,反正不会一直用的,于是也不会太深入的去研究。现在想想,这种凑合的心态是很危险的,会浪费很多的时间。但是,其实这差不多就是在做自己不喜欢的事情,兴趣和动力都相对小一些,有些时间还是去折腾自己感兴趣的东西去了。更惨的情况就是干不喜欢又不得不去做的事情,心理消耗过大,导致空转,精力白白消耗,白天干不好,晚上不精神干别的。而且,说来说去,到真正做事情的时候,大家不都是还实用自己用的最顺手的语言的时候最有生产效率么。后台之前是 python,后来换做了别的语言是因为人换了吧。他们不会考虑说用之前的 python 代码而是换做了 java。这应该是目前核心人员最顺手的语言是 java 吧。R 和 clojure 大行其道也是因为类似的原因吧。那么,语言不是问题这种说法,就比较扯了。

在对所了解的语言比较失望同时看到 37-signal 这个奇葩的时候(哦,我喜欢这样的奇葩),我觉得尝试一下这朵奇葩做出来的框架。看了本 agile rails 看了本 rails tutorial 感觉目前绝大多数的框架都是在学 rails 似的(当然,不排除 rails 跟风其他框架的可能)。不过 rails 的 generator 做的确实让人很顺心(对,就是 generator,一开是就觉得这样的设计很棒),同时 ruby 这个语言也确实有胜过 python 的地方,我对其还是有一些好感的。不过在去年 8 9 月份对于 ruby rails 还不是非常的有感觉。同时又有了新的事情要去做,这些东西就搁下了。

跑来写 java 之后,发现钱师傅对 ruby 很是喜欢,于是多了些对 ruby 的讨论,这才重新开始尝试 rails。果然,做过的事情都是有意义的,虽然八月份干的活当时不太明白,但是起码建立起了索引,这次上手就快了许多。而且,这次聪明了写,开始写些没有意义的项目来加深印象。现在看来,效果还是不错的。

之前和钱师傅讨论过有关语言的问题,钱师傅说,语言应该掌握两种,一种是非常动态,可以快速写出来的语言,一种是很底层的,可以保证执行效率的语言。那么,现在看来,其中一个是 ruby,另一个是 C 或者 C++。我比较偏爱 C,不过现在的偏爱是没有根据的,因为我没有做过。不过,这个想法是有了的,希望以后也有机会写一些很底层的 C 的 code。不过,目前,我已经走在了 rails 的道路上,现在先是 rails,以后用 ruby 做更多的事情吧。