开源项目 PlainSite:A Truly Hackable Static Site Generator!

jex · 2014年11月26日 · 最后由 jex 回复于 2014年11月26日 · 4516 次阅读

PlainSite:A Simple but Truly Hackable Static Site Generator https://github.com/CJex/PlainSite

这个项目其实是去年做的,当时没时间推广。之前看到某Python静态站点生成器还特地强调不仅是生成博客而是 Site ,我就低调不下去了。 PlainSite 虽然功能简单,但却具有无限的扩展性,理论上可以生成分类任意复杂的静态站点(只要你会写Ruby)。没人用太可惜。

下载运行也和 Jekyll差不多,gem install PlainSiteplainsite initplainsite build。支持Markdown,也用了FrontMatter格式。 我先列举下它的亮点:

  1. 配合Git,可以只生成更新的文章对应的页面,而不用每次都重新生成整个站点。 我记得以前谁说用Octpress要是本地build话,硬盘卡卡响。PlainSite可以通过git status读取哪些文件被修改过,只生成修改过的Post相关的HTML页面,不会每次都重新生成整个站点。(plainsite build -a可以重新生成整个)
  2. 采用LazyLoad,仅读取需要用到的数据文件。进一步减少硬盘文件读取。
  3. 可以使输出页面中只包含相对路径的URL,这样站点可以不需要Web服务器直接用File协议浏览。执行plainsite build -l就生成一个local站点,直接在文件管理器中打开就能浏览。当然PlainSite也内置了一个实时预览用的WebServer,修改文件不需要重启。
  4. 自动清除已被删除文章相应的页面。PlainSite需要把所有不由它管理的静态文件放到_src/assets下面,这样它就可以自动删除孤立的文章页面。你删除了某文章,生成的页面也能自动清理。
  5. 分页实在太简单了,下面提到API时再说。另外分面列表页面的文件名也作了优化,当有新文章时只需重新生成首页。

好了,上面的亮点其实是次要的。最主要的是,PlainSite是一个Framework,提供了一套API,而且这套API和MVC框架很相近。首先是数据管理,在站点_src/data目录下,相当一个文件数据库,一个目录表示一个Category,类似于数据库中的Table,一个Markdown或HTML文件表示一个Post,相应于数据库中一条记录。 如何查询呢?Ruby DSL的强大体现出来了。 读取news目录下的所有Post,相当于SQL:SELECT * FROM news

$site.data / :news / '*'  # 返回PlainSite::Data::PostList 对象

你要文章置顶功能?

($site.data / :news / '*' ).order_by &:top

对应在_src/data/news/目录下的Post文件中就写:

---
title: Today Top News-Wall down
top: true
---
这里是正文内容

你要分页?好,按每页5条分页

$site.data / :news / '*'  / 5  # 返回 [PlainSite::Data::PostListPage]

可这数据拿出来又怎么使用呢?对应于MVC中的URL Router,在_src/data/routes.rb

$site.route(
  url_pattern: "/{date}-top-news-{title}.html", 
  data: $site.data / :news / '*' ,
  template: 'news-list.html'  
)

上面的代码其实就相当于以PostList中每篇Post为Context去Render news-list.html这个模板,URL模式中用Post的属性值去替换。

那接下来就是模板,PlainSite使用ERB,扩展支持include和layout,例如:

---
layout: base.html   # 相对于当前模板文件的路径
---
<% content_for :page_title do %>
  <%=title%> - <%=site.name %>
<% end %>
<% content_for :page_content do %>
  <h1><%=title%></h1>
  <p>Date:<%=date%></p>
  <%=content%>
  <hr />

  Use site.url_for to get url,so it can be affected by 'plainsite build --local',results in relative url.
  <%=site.url_for 'essays/hello' %>

  Also support includes.
  <%=include 'footer.html' %>
<% end %>

base.html 文件内容:

<html>
<head>
  <title><%=yield :page_title%></title>
</head>
<body>
  <%=yield :page_content%>
</body>
</html>

好了,MVC齐全了。但强大在什么地方呢?强大的不是PlainSite而是你会Ruby!因为routes.rb就是一个Ruby Script。所以:

# 给每个分类都生成一个列表页面
# $site.data.subs 是子目录,返回 Category[]
$site.data.subs.each do |category|
  $site.route(
    url_pattern: "#{category.name}/{slug}.html",
    # category.posts/5 means category.posts.paginate(page_size:5)
    # return PostListPage[]
    data: category.posts/5, # category.posts is same as category / '*'  .
    template: 'list.html'
  )
end

# 要RSS?
$site.route(
  url_pattern: 'rss.xml',
  data: { posts: $site.data/'**' }, # RubyChina代码高亮补丁:/
  template: 'rss.erb'  # rss.erb在PlainSite中已经内置,`plainsite init`会自动生成这个文件
)

#你要给每个分类单独生成一个RSS?
$site.data.subs.each do |category|
  $site.route(
    url_pattern: "#{category.name}/{slug}-rss.xml",
    data:category,
    template: 'category-rss.html'
  )
end

而且模板是ERB,你想怎么搞就怎么搞:

# routes.rb 中写
$site.route(
   url_pattern:'jobs',
   data: {jobs:($site.data / :jobs / '*').order_by &:pay_money },  #按付的钱排序
   template: 'jobs.html'
)

然后jobs.html:

<%jobs.each do |job|%>
   <li><%=job.title%></li>
<%end%>

好了,明白了吧,意思就是只要你会写代码,那它扩展性是无限的。 Who choosed PlainSite?好吧,其实只有我自己一个人在用:https://jex.im/

另外我告诉你们一个网站:https://staticsitegenerators.net/ ,想用哪个随便挑

共收到 18 条回复

如果gem install plain_site不成功的话请告知我(Windows需要安装DevKit),因为我好不容易跨墙gem push上去,然后就没法再安装下来了,总是timeout,这边墙太高了。(可以换 https://ruby.taobao.org/ 试试)

还有好像gem不解决ruby headers的问题,你需要安装好ruby-dev(或ruby-devel什么的)。


如果是自己build,我不知道bundle和bundler有什么区别,自从我敲错了哪个 bundle(r) build bundle(r) install 就有一个幽灵plainsite命令被安装进去却运行不起来也删不掉

本想集成到 githis.com 。。。

#2楼 @moliliang 这个跑在托管服务器上还真比较麻烦,因为允许执行任意代码

在 win 下安装成功

感觉很熟悉,原来半年前就关注了~

#4楼 @ywjno 出乎意料。是不是以前已经装过yaml、pygments了?之前我看windows下装起来好麻烦

#6楼 @jex 那些是什么东西完全不认识,就配置了一个 DevKit 就行了

#5楼 @small_fish__ 我是因为不想hack Jekyll的代码所以自己写了,估计别人也跟我一样不喜欢去hack 已有的东西都纷纷自己造轮子

#8楼 @jex 不是,我是偶然看到你博客,然后发现的,所以。。。

#9楼 @small_fish__ 想用哪个随你挑:https://staticsitegenerators.net/

总共三百多个静态站点生成器!

#11楼 @xfstart07 我看了感觉它的Dynamic Pages思路跟我的有些相似,但我用 routes.rb 一个raw文件解决了Dynamic Pages、sitemap、local data、Pretty URLs等多个问题,而且我发现它好像没有类似PlainSite提供的直接用类似SQL的DSL读取数据的接口。

#10楼 @jex 看了你的代码,与jekyll比起来,清爽许多. 貌似没有看到tag的功能,只有category 吗?

#13楼 @small_fish__

要tag?其实DIY起来很简单!在post中front matter header中写

---
tags: [Ruby,Rails,PlainSite]
---

然后给每种tag都生成一个列表页面?routes.rb

tagMap=Hash.new
($site.data['**'] ).each do |post|
    post.tags.each do |tag|
        tagMap[tag]=tagMap[tag] || []
        tagMap[tag].push post
    end
end

tagMap.each do |tag,posts|
   # 来个分页吧
   posts=PlainSite::Data::PostList.new posts,$site
   $site.route(
      url_patten: '/tag/#{tag}',
      data: posts / 10 ,  # 高亮补丁/
      template: 'your-tag-list-tpl.html'
   )
end

然后your-tag-list-tpl.html可以照抄list.html

只要会Ruby,DIY就是这么简单!(好坑爹,不过也正是因为我偷懒,所以我才把它设计的尽量灵活啊 ^O^)

PS:@lgn1st 下面的语法高亮不对,不知道有没有办法FIX:

$site.data / '**'  
# 这里被当成正则表达式了可能

#14楼 @jex 是的,灵活点好, 代码也够简单,只是你的代码有些貌似需要格式化一下。

另外貌似 博客标题如果有 !就会报错( mac 环境)

~/my/plain_test:$ plainsite newpost post-slug "Hello,wolrd!This is the title"
-bash: !This: event not found

#15楼 @small_fish__

哪的代码需要格式化?rb代码我记得都重新格式化过了,可能erb代码我忘了重新格式化了。

那个错误不是PlainSite的,那是因为在bash中就这样

echo "Hello,wolrd!This is the title"

你要换成单引号才OK:

echo 'Hello,wolrd!This is the title'

#16楼 @jex 是这样,那你的readme ,就要修改写了,我一开始直接copy的。 https://github.com/JexCheng/plain_site#getting-started

#17楼 @small_fish__

已修,Thanks

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