PlainSite 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +43 -0
  4. data/LICENSE +20 -0
  5. data/PlainSite.gemspec +40 -0
  6. data/README.md +276 -0
  7. data/Rakefile +34 -0
  8. data/_config.yml +6 -0
  9. data/bin/plainsite +76 -0
  10. data/lib/PlainSite.rb +5 -0
  11. data/lib/PlainSite/Commands.rb +76 -0
  12. data/lib/PlainSite/Data/Category.rb +235 -0
  13. data/lib/PlainSite/Data/FrontMatterFile.rb +64 -0
  14. data/lib/PlainSite/Data/Post.rb +237 -0
  15. data/lib/PlainSite/Data/PostList.rb +164 -0
  16. data/lib/PlainSite/Data/PostListPage.rb +80 -0
  17. data/lib/PlainSite/RenderTask.rb +235 -0
  18. data/lib/PlainSite/Site.rb +330 -0
  19. data/lib/PlainSite/SocketPatch.rb +15 -0
  20. data/lib/PlainSite/Tpl/ExtMethods.rb +55 -0
  21. data/lib/PlainSite/Tpl/LayErb.rb +73 -0
  22. data/lib/PlainSite/Utils.rb +79 -0
  23. data/lib/PlainSite/_scaffold/_src/assets/README.md +5 -0
  24. data/lib/PlainSite/_scaffold/_src/assets/css/style.css +506 -0
  25. data/lib/PlainSite/_scaffold/_src/assets/favicon.ico +0 -0
  26. data/lib/PlainSite/_scaffold/_src/config.yml +10 -0
  27. data/lib/PlainSite/_scaffold/_src/data/essays/game-of-life.md +15 -0
  28. data/lib/PlainSite/_scaffold/_src/data/essays/phoenix-rebirth.html +15 -0
  29. data/lib/PlainSite/_scaffold/_src/data/programming/hello-world.md +48 -0
  30. data/lib/PlainSite/_scaffold/_src/extensions/TplExt.rb +23 -0
  31. data/lib/PlainSite/_scaffold/_src/routes.rb +49 -0
  32. data/lib/PlainSite/_scaffold/_src/templates/404.html +16 -0
  33. data/lib/PlainSite/_scaffold/_src/templates/about.html +11 -0
  34. data/lib/PlainSite/_scaffold/_src/templates/base.html +32 -0
  35. data/lib/PlainSite/_scaffold/_src/templates/header.html +8 -0
  36. data/lib/PlainSite/_scaffold/_src/templates/index.html +25 -0
  37. data/lib/PlainSite/_scaffold/_src/templates/list.html +41 -0
  38. data/lib/PlainSite/_scaffold/_src/templates/post.html +81 -0
  39. data/lib/PlainSite/_scaffold/_src/templates/rss.erb +29 -0
  40. data/test/CategoryTest.rb +63 -0
  41. data/test/FrontMatterFileTest.rb +40 -0
  42. data/test/LayErbTest.rb +20 -0
  43. data/test/ObjectProxyTest.rb +30 -0
  44. data/test/PostListTest.rb +55 -0
  45. data/test/PostTest.rb +48 -0
  46. data/test/SiteTest.rb +105 -0
  47. data/test/fixtures/2012-06-12-test.md +7 -0
  48. data/test/fixtures/category-demo/2012-06-12-post1.md +7 -0
  49. data/test/fixtures/category-demo/2013-05-01-post2.md +7 -0
  50. data/test/fixtures/category-demo/_meta.yml +1 -0
  51. data/test/fixtures/category-demo/index.md +6 -0
  52. data/test/fixtures/category-demo/sub-category1/sub-post1.md +1 -0
  53. data/test/fixtures/category-demo/sub-category2/sub-post2.md +1 -0
  54. data/test/fixtures/include.erb +1 -0
  55. data/test/fixtures/invalid-front-matter.html +7 -0
  56. data/test/fixtures/layout.erb +1 -0
  57. data/test/fixtures/no-front-matter.html +2 -0
  58. data/test/fixtures/tpl.erb +7 -0
  59. data/test/fixtures/valid-front-matter.html +14 -0
  60. data/test/runtest +6 -0
  61. metadata +202 -0
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ #coding:utf-8
3
+ module PlainSite
4
+ VERSION='1.2.0'
5
+ end
@@ -0,0 +1,76 @@
1
+ # coding:utf-8
2
+ module PlainSite
3
+ module Commands
4
+ require 'pp'
5
+ require 'fileutils'
6
+ require 'commander/import'
7
+ require 'PlainSite/Site'
8
+
9
+ SELF_DIR=File.dirname(__FILE__)
10
+
11
+ def self.die(msg="\nExit now.\n")
12
+ $stderr.puts msg
13
+ exit 1
14
+ end
15
+
16
+ def self.run(action,args,opts)
17
+ root=opts.root || Dir.pwd
18
+
19
+ trap('INT') {self.die}
20
+ trap('TERM') {self.die}
21
+
22
+ unless File.exist? root
23
+ say_error "Site root directory does not exist:#{root}"
24
+ say_error "Create now? [Y/n]"
25
+ answer=$stdin.gets.strip.downcase # `agree` cannot set default answer
26
+ answer='y' if answer.empty?
27
+ if answer =='y'
28
+ FileUtils.mkdir_p root
29
+ else
30
+ self.die
31
+ end
32
+ end
33
+ root=File.realpath root
34
+ site=Site.new root
35
+ self.send action,site,args,opts
36
+ end
37
+
38
+ def self.init(site,args,opts)
39
+ site.init_scaffold opts.override
40
+ puts 'Site scaffold init success!'
41
+ end
42
+
43
+ def self.build(site,includes,opts)
44
+ site.build(dest:opts.dest,all:opts.all,local:opts.local,includes:includes)
45
+ puts 'Posts build finished.'
46
+ self.clean(site)
47
+ end
48
+
49
+ def self.clean(site,args=nil,opts=nil)
50
+ if site.isolated_files.empty?
51
+ puts "No isolated files found."
52
+ else
53
+ puts 'Do you really want to remove these isolated files?'
54
+ puts ((site.isolated_files.map {|f| f[(site.dest.length+1)..-1]}).join "\n")
55
+ puts "[y/N]"
56
+ answer=$stdin.gets.strip.downcase
57
+ answer='n' if answer.empty?
58
+ if answer =='y'
59
+ site.clean
60
+ puts 'Clean finished.'
61
+ end
62
+ end
63
+ end
64
+
65
+ def self.serve(site,args,opts)
66
+ site.serve(host:opts.host,port:opts.port)
67
+ end
68
+
69
+ def self.newpost(site,args,opts)
70
+ path=site.newpost args[0],args[1]
71
+ puts "New post created at:#{path}"
72
+ end
73
+
74
+ end
75
+ end
76
+
@@ -0,0 +1,235 @@
1
+ #coding:utf-8
2
+ module PlainSite;end
3
+ module PlainSite::Data
4
+ require 'safe_yaml'
5
+ require 'PlainSite/Data/Post'
6
+ require 'PlainSite/Data/PostList'
7
+
8
+ class ConflictNameException<Exception; end
9
+
10
+ # The Category directory class.
11
+ # The category directory can have its custom index post named 'index.md' or 'index.html'.
12
+ # File '_meta.yml' under category dir decribes it's meta info attributes.
13
+ # Supported attributes in '_meta.yml':
14
+ # display_name - The String category display name
15
+ class Category
16
+ META_FILE='_meta.yml'
17
+ attr_reader(
18
+ :path, # The String full path of category directory
19
+ :relpath, # The String path of category directory relative to site.data_path
20
+ :site,
21
+ :index, # The index Post
22
+ :has_index # Bool
23
+ )
24
+ # path - The String category directory abspath
25
+ # site - The Site belongs to
26
+ def initialize(path,site)
27
+ @path=path
28
+ @relpath=@path[(site.data_path.length+1)..-1] || ''
29
+ @site=site
30
+ @is_root = @relpath==''
31
+
32
+
33
+
34
+ # Alias of :relpath
35
+ alias :data_id :relpath
36
+
37
+ @index = self / :index
38
+ @has_index = !! @index
39
+ end
40
+
41
+ # whether this is the root category (aka site.data_path)
42
+ def root?
43
+ @is_root
44
+ end
45
+
46
+ # Return parent category or nil if self is root
47
+ def parent
48
+ return @parent if @parent
49
+ return nil if root?
50
+ @parent=if @relpath['/']
51
+ Category.new File.join(@site.data_path,File.dirname(@relpath)),@site
52
+ else
53
+ Category.new @site.data_path,@site
54
+ end
55
+ end
56
+
57
+ # Return parent categories array
58
+ def parents
59
+ return @parents if @parents
60
+ @parents=[]
61
+ return @parents if root?
62
+ cat=self
63
+ while cat=cat.parent
64
+ @parents.unshift cat
65
+ end
66
+ @parents
67
+ end
68
+
69
+
70
+ # Query data tree
71
+ # path - The String|Symbol *relative* path of post or category,
72
+ # It can be an exact file path,or a category/slug path.
73
+ # For examples,both 'essay/2011-11-11-live-happy.md' and 'essay/live-happy' are valid.
74
+ # '*' Retrieve all posts under this category.
75
+ # (But excludes its index post and includes sub category index posts).
76
+ # '**' retrieve recursively all posts under this category(include index posts)
77
+ # Category path(directory),example 'esssay',will return a new child Category .
78
+ # Category path ends with '/*',will return posts array under the category dir.
79
+ # Category path end with '/**',will return all posts under the category dir recursively.
80
+ # Return
81
+ # The sub Category when path is a category path
82
+ # The PostList when path is category path end with '/*' or '/**'
83
+ # The Post when path is a post path
84
+ def [](path)
85
+ path=path.to_s
86
+ if path['/']
87
+ return path.split('/').reduce(self) {|cat,p| cat && cat[p]}
88
+ end
89
+
90
+ return posts if path=='*'
91
+ return sub_posts if path=='**'
92
+ get_sub path or get_post path
93
+
94
+ end
95
+ alias :/ :[]
96
+
97
+ # Get post by slug or filename
98
+ # p - The String post slug or filename
99
+ # Return the Post or nil when not found
100
+ def get_post(p)
101
+ if p.to_sym==:index
102
+ p=(Post.extnames.map {|t|"index.#{t}"}).detect do |f|
103
+ File.exists? (File.join @path,f)
104
+ end
105
+ end
106
+ return nil unless p
107
+ if p['.'] # filename
108
+ post_path=File.join @path,p
109
+ if (File.exists? post_path) && (File.basename(post_path)[0]!='_')
110
+
111
+ return Post.new post_path,@site
112
+ end
113
+ end
114
+ return posts_map[p] if posts_map.has_key? p
115
+ end
116
+
117
+ # The posts under this category,but exclude its index post.
118
+ # Not contains the sub category's posts but include sub category's index post.
119
+ # Default sort by date desc,slug asc
120
+ # Return PostList
121
+ def posts
122
+ return @posts if @posts
123
+
124
+ posts=Dir.glob(@path+'/*')
125
+ posts.select! do |f|
126
+ if File.basename(f)[0]=='_'
127
+ false
128
+ elsif File.directory? f # if directory has an index
129
+ (Category.new f,@site).has_index
130
+ else
131
+ (File.file? f) && (Post.extname_ok? f) &&
132
+ ((File.basename f,(File.extname f))!='index') # exclude index post
133
+ end
134
+ end
135
+
136
+ return @posts=PostList.new([],@site) if posts.empty?
137
+
138
+ posts.map! {|f| Post.new f,@site}
139
+
140
+ @posts=PostList.new posts,@site
141
+ end
142
+
143
+
144
+ # Get all posts under category recursively(include all index posts)
145
+ # Return PostList
146
+ def sub_posts
147
+ return @sub_posts if @sub_posts
148
+ all_posts=posts.to_a
149
+ subs.each do |c|
150
+ all_posts.concat c.sub_posts.to_a
151
+ end
152
+ all_posts.push @index if @has_index && root?
153
+ @sub_posts=PostList.new all_posts,@site
154
+ end
155
+
156
+ # Get the sub categories
157
+ # Return Category[]
158
+ def subs
159
+
160
+ return @subs if @subs
161
+ subs=Dir.glob @path+'/*'
162
+ subs.select! { |d| File.basename(d)[0]!='_' && (File.directory? d) }
163
+ subs.map! { |d| Category.new d,@site }
164
+
165
+ @subs=subs
166
+ end
167
+
168
+ # Get the sub category
169
+ # sub_path The String path relative to current category
170
+ # Return Category or nil
171
+ def get_sub(sub_path)
172
+ return nil if sub_path[0]=='_'
173
+ sub_path=File.join @path,sub_path
174
+ if File.directory? sub_path
175
+ Category.new sub_path,@site
176
+ end
177
+ end
178
+
179
+
180
+ # The Hash map of slug=>post
181
+ # Return Hash
182
+ def posts_map
183
+ return @posts_map if @posts_map
184
+
185
+ group=posts.group_by &:slug
186
+
187
+ conflicts=group.reject {|k,v| v.length==1} #Filter unconflicts
188
+ unless conflicts.empty?
189
+ msg=(conflicts.map {|slug,v|
190
+ "#{slug}:\n\t"+(v.map &:path).join("\n\t")
191
+ }).join "\n"
192
+ raise ConflictNameException,"These posts use same slug:#{msg}"
193
+ end
194
+
195
+ group.merge!(group) {|k,v| v[0] }
196
+ @posts_map=group
197
+ end
198
+
199
+ # Get the meta info in category path ./_meta.yml
200
+ def meta_info
201
+ return @meta_info if @meta_info
202
+ meta_file=File.join(@path,META_FILE)
203
+ if File.exists? meta_file
204
+ @meta_info = YAML.safe_load_file meta_file
205
+ else
206
+ @meta_info= {}
207
+ end
208
+ end
209
+
210
+ # The String category dir name,root category's name is empty string
211
+ def name
212
+ return @name unless @name.nil?
213
+ @name=File.basename(data_id)
214
+ end
215
+
216
+ def display_name
217
+ return @display_name unless @display_name.nil?
218
+ @display_name= if meta_info['display_name'].nil?
219
+ name.gsub(/-|_/,' ').capitalize
220
+ else
221
+ meta_info['display_name']
222
+ end
223
+ end
224
+
225
+ def to_s
226
+ 'Category:'+(@relpath.empty? ? '#root#' : @relpath)
227
+ end
228
+ alias :to_str :to_s
229
+
230
+ def to_a
231
+ parents+[self]
232
+ end
233
+ alias :to_ary :to_a
234
+ end # end class Category
235
+ end # end PlainSite::Data
@@ -0,0 +1,64 @@
1
+ #coding:utf-8
2
+ module PlainSite;end
3
+ module PlainSite::Data
4
+ # require 'active_support/core_ext/hash' # Silly
5
+ # require 'active_support/hash_with_indifferent_access' # Fat
6
+ require 'safe_yaml'
7
+ class InvalidFrontMatterFileException<Exception;end
8
+
9
+ class FrontMatterFile
10
+ # YAML Front Matter File
11
+ # Example file content:
12
+ # ---
13
+ # title: Hello,world!
14
+ # tags: [C,Java,Ruby,Haskell]
15
+ # ---
16
+ # File content Here!
17
+ #
18
+ attr_reader :path
19
+ DELIMITER='---'
20
+
21
+ def initialize(path)
22
+ # The String file path
23
+ @path=path
24
+ @content_pos=0
25
+ end
26
+
27
+ def headers
28
+ File.open(@path) do |f|
29
+ line=f.readline.strip
30
+ break if line!=DELIMITER
31
+ header_lines=[]
32
+ begin
33
+ while (line=f.readline.strip)!=DELIMITER
34
+ header_lines.push line
35
+ end
36
+ @headers = YAML.safe_load(header_lines.join "\n")
37
+ unless Hash===@headers
38
+ raise InvalidFrontMatterFileException,"Front YAML must be Hash,not #{@headers.class},in file: #{path}"
39
+ end
40
+ @content_pos=f.pos
41
+ @headers['path'] = @path
42
+ return @headers
43
+ rescue YAML::SyntaxError => e
44
+ raise InvalidFrontMatterFileException,"YAML SyntaxError:#{e.message},in file: #{path}"
45
+ rescue EOFError => e
46
+ raise InvalidFrontMatterFileException,"Unclosed YAML in file: #{path}"
47
+ end
48
+ end
49
+
50
+ return {"path" => @path }
51
+ end
52
+
53
+ # Intended no cache, listen directory changes not work on platforms other than linux
54
+ def content
55
+ self.headers # init @content_pos
56
+ File.open(path) do |f|
57
+ f.seek @content_pos,IO::SEEK_SET
58
+ @content=f.read.strip.freeze
59
+ end
60
+ end
61
+
62
+
63
+ end
64
+ end # end PlainSite::Data
@@ -0,0 +1,237 @@
1
+ #coding:utf-8
2
+ module PlainSite;end
3
+ module PlainSite::Data
4
+ require 'pygments'
5
+ require 'kramdown'
6
+ require 'date'
7
+ require 'securerandom'
8
+ require 'PlainSite/Data/FrontMatterFile'
9
+ require 'PlainSite/Tpl/LayErb'
10
+ require 'PlainSite/Data/Category'
11
+
12
+ class Post
13
+ attr_reader(
14
+ :slug, # The String slug of post,default is it's filename without date and dot-extname
15
+ :path, # The String post file path
16
+ :relpath, # The String path relative to site.data_path
17
+ :data_id, # The String data id,format:'category/sub-category/slug'
18
+ :category_path, # The String category path
19
+ :filename, # The String filename
20
+ :site,
21
+ :is_index # The Bool to indicate if this is an index post.
22
+ )
23
+
24
+ attr_accessor( #These properties are inited by others
25
+ :prev_post, # The Post previous,set by PostList
26
+ :next_post, # The Post next,set by PostList
27
+ )
28
+
29
+ DATE_NAME_RE=/^(\d{4})-(\d{1,2})-(\d{1,2})-(.+)$/
30
+ HIGHLIGHT_RE=/<highlight(\s+[^>]+)?\s*>(.+?)<\/highlight>/m
31
+ MARKDOWN_CODE_RE=/```(.+?)```/m
32
+
33
+
34
+ # Init a post
35
+ # path - The String file abs path,at present,only support '.html' and '.md' extname.
36
+ # site - The Site this post belongs to
37
+ def initialize(path,site)
38
+ if File.directory? path # create as category index post
39
+ path=(@@extnames.map {|x| File.join path+'/index'+x}).detect {|f| File.exists? f}
40
+ @is_index=true
41
+ end
42
+ @site=site
43
+ @path=path
44
+ @relpath=@path[(site.data_path.length+1)..-1]
45
+
46
+ @filename=File.basename @path
47
+ @extname=File.extname @filename
48
+ if DATE_NAME_RE =~ @filename
49
+ @date=Date.new $1.to_i,$2.to_i,$3.to_i
50
+ @slug=File.basename $4,@extname
51
+ end
52
+ @slug=File.basename @filename,@extname unless @slug
53
+
54
+ @category_path=File.dirname(@relpath)
55
+
56
+ if @category_path=='/' or @category_path=='.'
57
+ @category_path=''
58
+ @data_id=@slug
59
+ else
60
+ @data_id=File.join @category_path,@slug
61
+ end
62
+
63
+ end
64
+ # The Date of post
65
+ def date
66
+ return @date if @date
67
+ @date=get_date 'date'
68
+ end
69
+
70
+ def get_date(k)
71
+ date=post_file.headers[k]
72
+
73
+ date=if String===date
74
+ begin Date.parse(date) rescue Date.today end
75
+ elsif Date===date
76
+ date
77
+ else
78
+ Date.today
79
+ end
80
+ end
81
+ private :get_date
82
+
83
+ def updated_date
84
+ return @updated_date if @updated_date
85
+ @updated_date=get_date 'updated_date'
86
+ end
87
+
88
+ # The Category this post belongs to
89
+ def category
90
+ return @category if @category
91
+ @category=Category.new File.join(@site.data_path,@category_path),@site
92
+ end
93
+
94
+ # The String content type of post,default is it's extname without dot
95
+ def content_type
96
+ return @content_type if @content_type
97
+ @content_type=post_file.headers['content_type']
98
+ @content_type=@extname[1..-1] if @content_type.nil?
99
+ @content_type
100
+ end
101
+
102
+ # The Boolean value indicates if this post is a draft,default is false,alias is `draft?`
103
+ # def draft
104
+ # return @draft unless @draft.nil?
105
+ # @draft=!!post_file.headers['draft']
106
+ # end
107
+ # alias :draft? :draft
108
+
109
+ # def deleted
110
+ # return @deleted unless @deleted.nil?
111
+ # @deleted=!!post_file.headers['deleted']
112
+ # end
113
+ # alias :deleted? :deleted
114
+
115
+ # Private
116
+ def post_file
117
+ return @post_file if @post_file
118
+ @post_file=FrontMatterFile.new @path
119
+ end
120
+ private :post_file
121
+
122
+ # Post file raw content
123
+ def raw_content
124
+ post_file.content
125
+ end
126
+
127
+ # Rendered html content
128
+ # It must render highlight code first.
129
+ # Highlight syntax:
130
+ # Html tag style:
131
+ # <highlight>puts 'Hello'</highlight>
132
+ # With line numbers and language
133
+ # <highlight ruby linenos>puts 'Hello'</highlight>
134
+ # Set line number start from 10
135
+ # <highlight ruby linenos=10>puts 'Hello'</highlight>
136
+ # Highlight lines
137
+ # <highlight ruby linenos hl_lines=1>puts 'Hello'</highlight>
138
+ #
139
+ # Highlight html tag options:
140
+ # linenos - If provide,output will contains line number
141
+ # linenos=Int - Line number start from,default is 1
142
+ # If no new line in code,the output will be inline nowrap style and no linenos.
143
+ #
144
+ # You can also use markdown style code block,e.g. ```puts 'Code'```.
145
+ # But code this style doesn't rendered by Pygments.
146
+ # You need to load a browser side renderer,viz. SyntaxHighlighter.
147
+ #
148
+ # Then render erb template,context is post itself,you can access self and self.site methods
149
+ #
150
+ # Return the String html content
151
+ def content
152
+ # quick fix,when build local, current path will change
153
+ p=@path + "::"+ @site._cur_page_dir
154
+
155
+ post_content=raw_content.dup
156
+ # stash highlight code
157
+ codeMap={}
158
+ post_content.gsub! HIGHLIGHT_RE do
159
+ placeholder='-HIGHLIGHT '+SecureRandom.uuid+' ENDHIGHLIGHT-'
160
+ attrs=$1
161
+ attrs=attrs.split " "
162
+ lexer=attrs.shift || ""
163
+ attrs=Hash[attrs.map {|v| v.split "="}]
164
+ attrs["hl_lines"]=(attrs["hl_lines"] || "").split ","
165
+ code=$2
166
+ codeMap[placeholder]={
167
+ lexer: lexer,
168
+ linenos: (attrs.key? "linenos") ? 'table' : false ,
169
+ linenostart: attrs["linenos"] || 1,
170
+ hl_lines: attrs["hl_lines"],
171
+ code: code.strip,
172
+ nowrap: code["\n"].nil?
173
+ }
174
+ placeholder
175
+ end
176
+
177
+
178
+ # Then render erb template if needed
179
+ if post_content['<%'] && !post_file.headers['disable_erb']
180
+ post_content=PlainSite::Tpl::LayErb.render_s post_content,self
181
+ end
182
+
183
+ post_content=self.class.content_to_html post_content,content_type
184
+
185
+ #put back code
186
+ codeMap.each do |k,v|
187
+ code=Pygments.highlight v[:code],lexer: v[:lexer],formatter: 'html',options:{
188
+ linenos: v[:linenos],
189
+ linenostart: v[:linenostart],
190
+ nowrap: v[:nowrap],
191
+ hl_lines: v[:hl_lines],
192
+ startinline: v[:lexer] == 'php'
193
+ }
194
+ code="<code class=\"highlight\">#{code}</code>" if v[:nowrap]
195
+ post_content[k]=code # String#sub method has a hole of back reference
196
+ end
197
+
198
+ return post_content
199
+ end
200
+
201
+ # The String url of this post in site
202
+ def url
203
+ @site.url_for @data_id
204
+ end
205
+
206
+ # You can use method call to access post file front-matter data
207
+ def respond_to?(name)
208
+ return true if post_file.headers.key? name.to_s
209
+ super
210
+ end
211
+
212
+ def method_missing(name,*args,&block)
213
+ if args.length==0 && block.nil? && post_file.headers.key?(name.to_s)
214
+ return post_file.headers[name.to_s]
215
+ end
216
+ super
217
+ end
218
+
219
+ def self.content_to_html(content,content_type)
220
+ if content_type=='md'
221
+ content=Kramdown::Document.new(content,input:'GFM').to_html
222
+ end
223
+ content
224
+ end
225
+
226
+ @@extnames=['md','html']
227
+
228
+ def self.extnames
229
+ @@extnames
230
+ end
231
+
232
+ def self.extname_ok?(f)
233
+ @@extnames.include? File.extname(f)[1..-1]
234
+ end
235
+
236
+ end # class Post
237
+ end # module PlainSite::Data