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.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +43 -0
- data/LICENSE +20 -0
- data/PlainSite.gemspec +40 -0
- data/README.md +276 -0
- data/Rakefile +34 -0
- data/_config.yml +6 -0
- data/bin/plainsite +76 -0
- data/lib/PlainSite.rb +5 -0
- data/lib/PlainSite/Commands.rb +76 -0
- data/lib/PlainSite/Data/Category.rb +235 -0
- data/lib/PlainSite/Data/FrontMatterFile.rb +64 -0
- data/lib/PlainSite/Data/Post.rb +237 -0
- data/lib/PlainSite/Data/PostList.rb +164 -0
- data/lib/PlainSite/Data/PostListPage.rb +80 -0
- data/lib/PlainSite/RenderTask.rb +235 -0
- data/lib/PlainSite/Site.rb +330 -0
- data/lib/PlainSite/SocketPatch.rb +15 -0
- data/lib/PlainSite/Tpl/ExtMethods.rb +55 -0
- data/lib/PlainSite/Tpl/LayErb.rb +73 -0
- data/lib/PlainSite/Utils.rb +79 -0
- data/lib/PlainSite/_scaffold/_src/assets/README.md +5 -0
- data/lib/PlainSite/_scaffold/_src/assets/css/style.css +506 -0
- data/lib/PlainSite/_scaffold/_src/assets/favicon.ico +0 -0
- data/lib/PlainSite/_scaffold/_src/config.yml +10 -0
- data/lib/PlainSite/_scaffold/_src/data/essays/game-of-life.md +15 -0
- data/lib/PlainSite/_scaffold/_src/data/essays/phoenix-rebirth.html +15 -0
- data/lib/PlainSite/_scaffold/_src/data/programming/hello-world.md +48 -0
- data/lib/PlainSite/_scaffold/_src/extensions/TplExt.rb +23 -0
- data/lib/PlainSite/_scaffold/_src/routes.rb +49 -0
- data/lib/PlainSite/_scaffold/_src/templates/404.html +16 -0
- data/lib/PlainSite/_scaffold/_src/templates/about.html +11 -0
- data/lib/PlainSite/_scaffold/_src/templates/base.html +32 -0
- data/lib/PlainSite/_scaffold/_src/templates/header.html +8 -0
- data/lib/PlainSite/_scaffold/_src/templates/index.html +25 -0
- data/lib/PlainSite/_scaffold/_src/templates/list.html +41 -0
- data/lib/PlainSite/_scaffold/_src/templates/post.html +81 -0
- data/lib/PlainSite/_scaffold/_src/templates/rss.erb +29 -0
- data/test/CategoryTest.rb +63 -0
- data/test/FrontMatterFileTest.rb +40 -0
- data/test/LayErbTest.rb +20 -0
- data/test/ObjectProxyTest.rb +30 -0
- data/test/PostListTest.rb +55 -0
- data/test/PostTest.rb +48 -0
- data/test/SiteTest.rb +105 -0
- data/test/fixtures/2012-06-12-test.md +7 -0
- data/test/fixtures/category-demo/2012-06-12-post1.md +7 -0
- data/test/fixtures/category-demo/2013-05-01-post2.md +7 -0
- data/test/fixtures/category-demo/_meta.yml +1 -0
- data/test/fixtures/category-demo/index.md +6 -0
- data/test/fixtures/category-demo/sub-category1/sub-post1.md +1 -0
- data/test/fixtures/category-demo/sub-category2/sub-post2.md +1 -0
- data/test/fixtures/include.erb +1 -0
- data/test/fixtures/invalid-front-matter.html +7 -0
- data/test/fixtures/layout.erb +1 -0
- data/test/fixtures/no-front-matter.html +2 -0
- data/test/fixtures/tpl.erb +7 -0
- data/test/fixtures/valid-front-matter.html +14 -0
- data/test/runtest +6 -0
- metadata +202 -0
@@ -0,0 +1,164 @@
|
|
1
|
+
#coding:utf-8
|
2
|
+
module PlainSite;end
|
3
|
+
module PlainSite::Data
|
4
|
+
require 'PlainSite/Data/Post'
|
5
|
+
require 'PlainSite/Data/PostListPage'
|
6
|
+
class PostList
|
7
|
+
include Enumerable
|
8
|
+
# PostList,default sort by date desc and by name asc
|
9
|
+
# posts - The Post[]|String[] of posts or posts file path(abs or relative to site.data_path) array
|
10
|
+
# site - The Site
|
11
|
+
def initialize(posts,site)
|
12
|
+
if String===posts[0]
|
13
|
+
@posts=posts.map {|f| Post.new f,site }
|
14
|
+
else
|
15
|
+
@posts=posts.map &:dup
|
16
|
+
end
|
17
|
+
@posts.sort! do |a,b|
|
18
|
+
# sort by date desc and by name asc
|
19
|
+
[b.date,a.slug] <=> [a.date,b.slug]
|
20
|
+
end
|
21
|
+
@site=site
|
22
|
+
@custom_index= @posts.any? &:is_index
|
23
|
+
end
|
24
|
+
|
25
|
+
# Array like slice method
|
26
|
+
# Return the Post or slice PostList
|
27
|
+
def [](*args)
|
28
|
+
if args.length==1
|
29
|
+
i=args[0]
|
30
|
+
if Range===i
|
31
|
+
posts=@posts[i]
|
32
|
+
return nil if posts.nil? || posts.empty?
|
33
|
+
PostList.new posts,@site
|
34
|
+
else
|
35
|
+
post=@posts[i]
|
36
|
+
if post
|
37
|
+
post.next_post= i-1 < 0 ? nil : @posts[i-1] # Because -1 will index from last
|
38
|
+
post.prev_post= @posts[i+1]
|
39
|
+
end
|
40
|
+
post
|
41
|
+
end
|
42
|
+
else
|
43
|
+
start,len=args
|
44
|
+
posts=@posts[start,len]
|
45
|
+
return nil if posts.nil? || posts.empty?
|
46
|
+
PostList.new posts,@site
|
47
|
+
end
|
48
|
+
end
|
49
|
+
alias :slice :[]
|
50
|
+
|
51
|
+
# Paginate post list
|
52
|
+
# This is a smart paginater. Version Control System friendly!
|
53
|
+
# What's VCS friendly? It's page nums use a revert order.
|
54
|
+
# The old the page's date is,the smaller the page's num is.
|
55
|
+
# Options:
|
56
|
+
# page_size - The Integer page size number,must be more than zero,default is 10
|
57
|
+
# revert_nos - The Boolean value to control if use revert order page num,default is true
|
58
|
+
# Return: The PostListPage[]
|
59
|
+
def paginate(opts={})
|
60
|
+
revert_nos=opts[:revert_nos].nil? ? true : opts[:revert_nos]
|
61
|
+
page_size=opts[:page_size] || 10
|
62
|
+
total=@posts.length
|
63
|
+
return [] if total==0
|
64
|
+
|
65
|
+
# In revert nos case,the first page need padding to fit full page
|
66
|
+
if revert_nos && total>page_size
|
67
|
+
start=total % page_size
|
68
|
+
pages=[self.slice(0,page_size)]
|
69
|
+
else
|
70
|
+
start=0
|
71
|
+
pages=[]
|
72
|
+
end
|
73
|
+
|
74
|
+
while posts=self.slice(start,page_size)
|
75
|
+
pages.push posts
|
76
|
+
start+=page_size
|
77
|
+
end
|
78
|
+
|
79
|
+
nos_list=(1..pages.length).to_a
|
80
|
+
display_nums=nos_list.dup
|
81
|
+
slugs=('a'..'zzz').take pages.length # Use letters id instead of numbers
|
82
|
+
|
83
|
+
if revert_nos
|
84
|
+
nos_list.reverse!
|
85
|
+
slugs.reverse!
|
86
|
+
end
|
87
|
+
slugs[0]='index' unless @custom_index # Category has its custom index post
|
88
|
+
|
89
|
+
total_pages_count=pages.length
|
90
|
+
pages= pages.zip(nos_list,display_nums,slugs).map do |a|
|
91
|
+
posts,num,display_num,slug=a
|
92
|
+
PostListPage.new(
|
93
|
+
# num: num, # It's useles
|
94
|
+
slug: slug, display_num: display_num,
|
95
|
+
posts: posts, site: @site,
|
96
|
+
total_pages_count: total_pages_count,
|
97
|
+
total_posts_count: total,
|
98
|
+
page_size: page_size,
|
99
|
+
revert_nos: revert_nos
|
100
|
+
)
|
101
|
+
end
|
102
|
+
|
103
|
+
next_pages=[nil]+pages[0..-2]
|
104
|
+
prev_pages=(pages[1..-1] or [])+[nil]
|
105
|
+
pages.zip(prev_pages,next_pages) do |a|
|
106
|
+
page,prev_page,next_page=a
|
107
|
+
page.prev_page=prev_page
|
108
|
+
page.next_page=next_page
|
109
|
+
page.all_pages=pages
|
110
|
+
end
|
111
|
+
|
112
|
+
pages
|
113
|
+
end
|
114
|
+
|
115
|
+
# Install B
|
116
|
+
# `posts / 5` is same as `posts.paginate(page_size:5)`
|
117
|
+
def /(page_size)
|
118
|
+
paginate(page_size:page_size)
|
119
|
+
end
|
120
|
+
|
121
|
+
def +(other)
|
122
|
+
raise TypeError,"Except #{PostList} Type" unless PostList===other
|
123
|
+
PostList.new @posts+other.to_a
|
124
|
+
end
|
125
|
+
|
126
|
+
# Check if contains one post
|
127
|
+
# p - The Post object or the String path or relpath or data_id of post
|
128
|
+
def include?(p)
|
129
|
+
if Post===p
|
130
|
+
return @posts.include? p
|
131
|
+
end
|
132
|
+
return @posts.any? {|post| post.path==p || post.relpath==p || post.data_id==p}
|
133
|
+
end
|
134
|
+
|
135
|
+
def length
|
136
|
+
@posts.length
|
137
|
+
end
|
138
|
+
|
139
|
+
def empty?
|
140
|
+
length==0
|
141
|
+
end
|
142
|
+
|
143
|
+
def each(&block)
|
144
|
+
block_given? or return enum_for __method__
|
145
|
+
0.upto @posts.length-1 do |i|
|
146
|
+
yield self[i]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def to_a
|
151
|
+
@posts.dup
|
152
|
+
end
|
153
|
+
alias :to_ary :to_a
|
154
|
+
|
155
|
+
%w(drop drop_while find_all select reject sort sort_by take take_while).each do |method|
|
156
|
+
define_method(method) do |*args,&block|
|
157
|
+
ary=super *args,&block
|
158
|
+
if ary
|
159
|
+
PostList.new ary.to_a,@site
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#coding:utf-8
|
2
|
+
module PlainSite;end
|
3
|
+
module PlainSite::Data
|
4
|
+
require 'ostruct'
|
5
|
+
# PostList Pagination
|
6
|
+
class PostListPage<OpenStruct
|
7
|
+
# These attributes' accessors are auto generated by OpenStruct
|
8
|
+
# attr_reader(
|
9
|
+
# :slug,:display_num,
|
10
|
+
# :posts,:prev_page,:next_page,
|
11
|
+
# :total_pages_count,:total_posts_count,:page_size,:all_pages,
|
12
|
+
# :revert_nos,:site
|
13
|
+
# )
|
14
|
+
attr_accessor :prev_page,:next_page,:all_pages # set after create
|
15
|
+
|
16
|
+
# Options:
|
17
|
+
# slug - The String page name suggested to used in url,first page is 'index',others are 'a'...'zzz' string
|
18
|
+
# display_num - The Integer always be ascending,first page is 1,second page is 2
|
19
|
+
# posts - The PostList
|
20
|
+
# prev_page - The previous PostListPage
|
21
|
+
# next_page - The next PostListPage
|
22
|
+
# total_pages_count - The Integer of total count in this pagination
|
23
|
+
# total_posts_count - The Integer of total count in this pagination
|
24
|
+
# page_size - The Integer of preset page size
|
25
|
+
# revert_nos - The Boolean indicate if this post list page use revert nos.
|
26
|
+
# When false case,it's html page must be regenerated every time.
|
27
|
+
# site - The Site belongs to
|
28
|
+
def initialize(opts)
|
29
|
+
super opts
|
30
|
+
end
|
31
|
+
|
32
|
+
# The String url of self
|
33
|
+
def url
|
34
|
+
site.url_for self
|
35
|
+
end
|
36
|
+
|
37
|
+
# See PostList#include?
|
38
|
+
def include?(a)
|
39
|
+
posts.include? a
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# @deprecated
|
44
|
+
|
45
|
+
# Remove posts,resort all previous pages
|
46
|
+
# list - The String[] of post's path list
|
47
|
+
def remove(list)
|
48
|
+
posts=posts.select do |p|
|
49
|
+
list.include? p.path
|
50
|
+
end
|
51
|
+
try_pad_from_other_page
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
# Pop posts
|
56
|
+
# Return Post[]
|
57
|
+
def pop_multi(n)
|
58
|
+
ret=posts.slice(-n,n)
|
59
|
+
posts=posts.slice(0,-n-1)
|
60
|
+
ret
|
61
|
+
end
|
62
|
+
|
63
|
+
def try_pad_from_other_page
|
64
|
+
n=page_size-posts.length
|
65
|
+
return [] unless n>0
|
66
|
+
related_pages=[self]
|
67
|
+
if prev_page
|
68
|
+
posts=prev_page.posts.pop_multi(n)+posts
|
69
|
+
related_pages.concat prev_page.try_pad_from_other_page
|
70
|
+
elsif next_page
|
71
|
+
posts+=next_page.posts.take n
|
72
|
+
end
|
73
|
+
related_pages
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
#coding:utf-8
|
2
|
+
|
3
|
+
module PlainSite
|
4
|
+
require 'fileutils'
|
5
|
+
require 'pathname'
|
6
|
+
require 'PlainSite/Data/PostList'
|
7
|
+
require 'PlainSite/Data/PostListPage'
|
8
|
+
require 'PlainSite/Data/Post'
|
9
|
+
require 'PlainSite/Tpl/LayErb'
|
10
|
+
require 'PlainSite/Utils'
|
11
|
+
|
12
|
+
class BadUrlPatternException<Exception;end
|
13
|
+
class RenderTask
|
14
|
+
Post=Data::Post
|
15
|
+
PostList=Data::PostList
|
16
|
+
PostListPage=Data::PostListPage
|
17
|
+
attr_reader :site
|
18
|
+
# site - The Site
|
19
|
+
def initialize(site)
|
20
|
+
@site=site
|
21
|
+
@tasks=[]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Options:
|
25
|
+
# url_pattern - The String with var replacement pattern "{property.property}"
|
26
|
+
# "{property.property}" is the data Hash key path.
|
27
|
+
# Example:
|
28
|
+
# "/article/{date.year}/{name}.html" will render url
|
29
|
+
# "/article/2011/hello-world.html"
|
30
|
+
# with post "posts/2011-09-09-hello-world.md"
|
31
|
+
# data - The Array|PostList|Object|String data to render
|
32
|
+
# In String case,it represents the post.data_id or category.data_id.
|
33
|
+
# Example: 'essay/*' same as $site.data['essay/*']
|
34
|
+
# If `data` type is Array or PostList,it will render each item with template,
|
35
|
+
# else only generate one page
|
36
|
+
# template - The String template path relative to '_site/templates'
|
37
|
+
# build_anyway - The Boolean value to indicate
|
38
|
+
# this route rule will build anyway even if no post updates
|
39
|
+
def route(opts)
|
40
|
+
url_pattern=opts[:url_pattern]
|
41
|
+
items=opts[:data]
|
42
|
+
template=opts[:template]
|
43
|
+
build_anyway=opts[:build_anyway]
|
44
|
+
|
45
|
+
if String===items
|
46
|
+
items=@site.data[items]
|
47
|
+
raise Exception,"Data not found:#{opts[:data]}!" if items.nil?
|
48
|
+
end
|
49
|
+
items=[items] unless Data::PostList===items || Array===items
|
50
|
+
|
51
|
+
tasks= items.map do |item|
|
52
|
+
urlpath=RenderTask.sub_url url_pattern,item
|
53
|
+
urlpath[0]='' if urlpath[0]=='/'
|
54
|
+
id =if item.respond_to? :data_id
|
55
|
+
item.data_id
|
56
|
+
else
|
57
|
+
item.object_id
|
58
|
+
end
|
59
|
+
|
60
|
+
{ id: id, urlpath: urlpath, item: item,
|
61
|
+
template:File.join(@site.templates_path,template),
|
62
|
+
build_anyway:build_anyway }
|
63
|
+
end
|
64
|
+
@tasks.concat tasks
|
65
|
+
end
|
66
|
+
|
67
|
+
# Get the url for object
|
68
|
+
# obj - The Post|PageListPage|Category|String
|
69
|
+
# Return the String url prefix with site root url(site.url) or relative path if build --local
|
70
|
+
def url_for(obj)
|
71
|
+
obj[0]='' if String===obj && obj[0]=='/'
|
72
|
+
|
73
|
+
if String===obj && (File.exists? (File.join @site.assets_path,obj))
|
74
|
+
# static file path
|
75
|
+
urlpath=obj
|
76
|
+
else
|
77
|
+
urlpath=object2url obj
|
78
|
+
end
|
79
|
+
|
80
|
+
if @site.local
|
81
|
+
urlpath=urlpath+'/index.html' if urlpath.end_with? '/'
|
82
|
+
urlpath='index.html' if urlpath.empty?
|
83
|
+
p1=Pathname.new (File.dirname urlpath)
|
84
|
+
basename=File.basename urlpath
|
85
|
+
(p1.relative_path_from Pathname.new(@site._cur_page_dir)).to_s+'/'+basename
|
86
|
+
else
|
87
|
+
#URI.join @site.url,urlpath
|
88
|
+
'/' + urlpath
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def object2url(obj)
|
93
|
+
id =if String===obj
|
94
|
+
obj
|
95
|
+
elsif obj.respond_to? :data_id
|
96
|
+
obj.data_id
|
97
|
+
else
|
98
|
+
obj.object_id
|
99
|
+
end
|
100
|
+
return (id2url_map[id] || id).to_s
|
101
|
+
end
|
102
|
+
|
103
|
+
# Return all valid output pages url path
|
104
|
+
def all_urlpath
|
105
|
+
return @all_urlpath if @all_urlpath
|
106
|
+
@all_urlpath=@tasks.map do |t|
|
107
|
+
t[:urlpath]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Render pages
|
112
|
+
# partials - The Hash of new or updated and deleted posts and templates.
|
113
|
+
# If nil,it will render all pages.
|
114
|
+
# Structure:
|
115
|
+
# {
|
116
|
+
# updated_posts:[],
|
117
|
+
# updated_templates:[],
|
118
|
+
# has_deleted_posts:Bool
|
119
|
+
# }
|
120
|
+
def render(partials=nil)
|
121
|
+
if partials.nil?
|
122
|
+
return @tasks.each {|t|render_task t}
|
123
|
+
end
|
124
|
+
build_tasks,other_tasks=@tasks.partition {|t|!!t[:build_anyway]}
|
125
|
+
|
126
|
+
if tpls=partials[:updated_templates]
|
127
|
+
a,other_tasks=other_tasks.partition {|t|tpls.include? t[:template] }
|
128
|
+
build_tasks.concat a
|
129
|
+
end
|
130
|
+
if posts=partials[:updated_posts]
|
131
|
+
a,other_tasks=other_tasks.partition do |t|
|
132
|
+
_detectContainsPosts(t[:item],posts)
|
133
|
+
end
|
134
|
+
build_tasks.concat a
|
135
|
+
end
|
136
|
+
|
137
|
+
if partials[:has_deleted_posts]
|
138
|
+
# rebuild all post list pages
|
139
|
+
a,other_tasks=other_tasks.partition {|t|PostList===t[:item] || PostListPage===t[:item]}
|
140
|
+
build_tasks.concat a
|
141
|
+
end
|
142
|
+
build_tasks.each do |t|
|
143
|
+
render_task t
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Render single url corresponding task
|
148
|
+
# url - The String url pathname one can be used in browser,such as
|
149
|
+
# '/posts','/posts/','/posts/index.html' are both valid
|
150
|
+
# Returns the String page content
|
151
|
+
def render_url(url)
|
152
|
+
url=url.dup
|
153
|
+
url[0]='' if url[0]=='/'
|
154
|
+
url=url+'index.html' if url.end_with? '/'
|
155
|
+
# url=url+'/index.html' if site.url.start_with? url
|
156
|
+
t = @tasks.detect {|t|t[:urlpath]==url}
|
157
|
+
if t
|
158
|
+
return render_task t,false
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
# The Hash of data_id=>url or object_id=>url
|
164
|
+
def id2url_map
|
165
|
+
return @id2url_map if @id2url_map
|
166
|
+
@id2url_map=@tasks.group_by {|a| a[:id]}
|
167
|
+
@id2url_map.merge!(@id2url_map) do |k,v|
|
168
|
+
if v.length>1
|
169
|
+
urls=(v.map {|a|a[:urlpath]}).join "\n\t"
|
170
|
+
$stderr.puts "Object[#{k}] has more than one url:"
|
171
|
+
$stderr.puts "\t#{urls}"
|
172
|
+
end
|
173
|
+
v[0][:urlpath]
|
174
|
+
end
|
175
|
+
@id2url_map
|
176
|
+
end
|
177
|
+
|
178
|
+
def render_task(t,write_file=true)
|
179
|
+
item=t[:item]
|
180
|
+
urlpath=t[:urlpath]
|
181
|
+
template=t[:template]
|
182
|
+
|
183
|
+
@site._cur_page_dir = File.dirname(urlpath) if @site.local
|
184
|
+
|
185
|
+
item=Utils::ObjectProxy.new item,{site:@site} # Keep site always accessable
|
186
|
+
erb=Tpl::LayErb.new template
|
187
|
+
result=erb.render item
|
188
|
+
|
189
|
+
return result unless write_file
|
190
|
+
output_path=File.join(@site.dest,urlpath)
|
191
|
+
dir=File.dirname output_path
|
192
|
+
FileUtils.mkdir_p dir
|
193
|
+
File.open(output_path,'wb') do |f|
|
194
|
+
f.write result
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Render url_pattern with context data
|
199
|
+
# url_pattern - The String url pattern.
|
200
|
+
# context - The Object context
|
201
|
+
# Return url pathname
|
202
|
+
def self.sub_url(url_pattern,context)
|
203
|
+
url_pattern.gsub(/\{([^{}\/]+)\}/) do
|
204
|
+
m=$1
|
205
|
+
key_path=m.strip.split '.'
|
206
|
+
key_path.reduce(context) do |o,key|
|
207
|
+
v = if o.respond_to? key
|
208
|
+
o.send key
|
209
|
+
elsif (o.respond_to? :[])
|
210
|
+
o[key] || o[key.to_sym]
|
211
|
+
end
|
212
|
+
next v if v
|
213
|
+
raise BadUrlPatternException,"Unresolved property `#{m}` in url pattern [#{url_pattern}]!"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def _detectContainsPosts(item,posts)
|
219
|
+
return posts.include? item.path if Post===item
|
220
|
+
return posts.any? {|p|item.include? p} if PostList===item || PostListPage===item
|
221
|
+
if Hash===item
|
222
|
+
item.each do |k,v|
|
223
|
+
return true if _detectContainsPosts(v,posts)
|
224
|
+
end
|
225
|
+
elsif Array===item
|
226
|
+
item.each do |a|
|
227
|
+
return true if _detectContainsPosts(a,posts)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
return false
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|