libertree-model 0.9.11
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/lib/libertree/compat.rb +29 -0
- data/lib/libertree/console.rb +17 -0
- data/lib/libertree/db.rb +37 -0
- data/lib/libertree/embedder.rb +69 -0
- data/lib/libertree/embedding/custom-providers.rb +148 -0
- data/lib/libertree/model/account-settings.rb +14 -0
- data/lib/libertree/model/account.rb +487 -0
- data/lib/libertree/model/chat-message.rb +75 -0
- data/lib/libertree/model/comment-like.rb +68 -0
- data/lib/libertree/model/comment.rb +164 -0
- data/lib/libertree/model/contact-list.rb +81 -0
- data/lib/libertree/model/embed-cache.rb +6 -0
- data/lib/libertree/model/file.rb +6 -0
- data/lib/libertree/model/forest.rb +82 -0
- data/lib/libertree/model/group-member.rb +13 -0
- data/lib/libertree/model/group.rb +47 -0
- data/lib/libertree/model/has-display-text.rb +34 -0
- data/lib/libertree/model/has-searchable-text.rb +19 -0
- data/lib/libertree/model/ignored-member.rb +13 -0
- data/lib/libertree/model/invitation.rb +6 -0
- data/lib/libertree/model/is-remote-or-local.rb +30 -0
- data/lib/libertree/model/job.rb +93 -0
- data/lib/libertree/model/member.rb +222 -0
- data/lib/libertree/model/message.rb +154 -0
- data/lib/libertree/model/node.rb +22 -0
- data/lib/libertree/model/node_affiliation.rb +14 -0
- data/lib/libertree/model/node_subscription.rb +23 -0
- data/lib/libertree/model/notification.rb +71 -0
- data/lib/libertree/model/pool-post.rb +17 -0
- data/lib/libertree/model/pool.rb +172 -0
- data/lib/libertree/model/post-hidden.rb +21 -0
- data/lib/libertree/model/post-like.rb +72 -0
- data/lib/libertree/model/post-revision.rb +9 -0
- data/lib/libertree/model/post.rb +735 -0
- data/lib/libertree/model/profile.rb +25 -0
- data/lib/libertree/model/remote-storage-connection.rb +6 -0
- data/lib/libertree/model/river.rb +249 -0
- data/lib/libertree/model/server.rb +35 -0
- data/lib/libertree/model/session-account.rb +10 -0
- data/lib/libertree/model/url-expansion.rb +6 -0
- data/lib/libertree/model.rb +48 -0
- data/lib/libertree/query.rb +167 -0
- data/lib/libertree/render.rb +28 -0
- metadata +198 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
module Libertree
|
2
|
+
module Model
|
3
|
+
class Profile < Sequel::Model(:profiles)
|
4
|
+
def after_update
|
5
|
+
super
|
6
|
+
if self.member.local?
|
7
|
+
Libertree::Model::Job.create_for_forests(
|
8
|
+
{
|
9
|
+
task: 'request:MEMBER',
|
10
|
+
params: { 'username' => self.member.account.username, }
|
11
|
+
}
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def member
|
17
|
+
@member ||= Member[ self.member_id ]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.search(query)
|
21
|
+
self.where("(to_tsvector('simple', description) || to_tsvector('english', description)) @@ plainto_tsquery(?)", query).or("name_display ILIKE '%' || ? || '%'", query)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,249 @@
|
|
1
|
+
module Libertree
|
2
|
+
module Model
|
3
|
+
class River < Sequel::Model(:rivers)
|
4
|
+
def account
|
5
|
+
@account ||= Account[self.account_id]
|
6
|
+
end
|
7
|
+
|
8
|
+
def should_contain?( post )
|
9
|
+
! self.contains?(post) && ! post.hidden_by?(self.account) && self.matches_post?(post)
|
10
|
+
end
|
11
|
+
|
12
|
+
def contains?( post )
|
13
|
+
Libertree::DB.dbh[ "SELECT river_contains_post(?, ?)", self.id, post.id ].single_value
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_post( post )
|
17
|
+
Libertree::DB.dbh[ "INSERT INTO river_posts ( river_id, post_id ) VALUES ( ?, ? )", self.id, post.id ].get
|
18
|
+
end
|
19
|
+
|
20
|
+
def posts( opts = {} )
|
21
|
+
time = Time.at( opts.fetch(:time, Time.now.to_f) ).strftime("%Y-%m-%d %H:%M:%S.%6N%z")
|
22
|
+
Post.s(%{SELECT * FROM posts_in_river(?,?,?,?,?,?)},
|
23
|
+
self.id,
|
24
|
+
self.account.id,
|
25
|
+
time,
|
26
|
+
opts[:newer],
|
27
|
+
opts[:order_by] == :comment,
|
28
|
+
opts.fetch(:limit, 30))
|
29
|
+
end
|
30
|
+
|
31
|
+
def parsed_query(override_cache=false)
|
32
|
+
return @parsed_query if @parsed_query && ! override_cache
|
33
|
+
|
34
|
+
full_query = self.query
|
35
|
+
if ! self.appended_to_all
|
36
|
+
full_query += ' ' + self.account.rivers_appended.map(&:query).join(' ')
|
37
|
+
full_query.strip!
|
38
|
+
end
|
39
|
+
|
40
|
+
@parsed_query = Libertree::Query.new(full_query, self.account.id, self.id).parsed
|
41
|
+
end
|
42
|
+
|
43
|
+
def term_matches_post?(term, post, data)
|
44
|
+
case term
|
45
|
+
when 'flag'
|
46
|
+
# TODO: most of these are slow
|
47
|
+
case data
|
48
|
+
when 'forest'
|
49
|
+
true # Every post is a post in the forest. :forest is sort of a no-op term
|
50
|
+
when 'tree'
|
51
|
+
post.local?
|
52
|
+
when 'unread'
|
53
|
+
! post.read_by?(self.account)
|
54
|
+
when 'liked'
|
55
|
+
post.liked_by? self.account.member
|
56
|
+
when 'commented'
|
57
|
+
post.commented_on_by? self.account.member
|
58
|
+
when 'subscribed'
|
59
|
+
self.account.subscribed_to? post
|
60
|
+
end
|
61
|
+
when 'contact-list'
|
62
|
+
data.last.include? post.member_id
|
63
|
+
when 'from'
|
64
|
+
post.member_id == data
|
65
|
+
when 'river'
|
66
|
+
data.matches_post?(post)
|
67
|
+
when 'visibility'
|
68
|
+
post.visibility == data
|
69
|
+
when 'word-count'
|
70
|
+
case data
|
71
|
+
when /^< ?([0-9]+)$/
|
72
|
+
n = $1.to_i
|
73
|
+
post.text.scan(/\S+/).count < n
|
74
|
+
when /^> ?([0-9]+)$/
|
75
|
+
n = $1.to_i
|
76
|
+
post.text.scan(/\S+/).count > n
|
77
|
+
end
|
78
|
+
when 'spring'
|
79
|
+
data.includes?(post)
|
80
|
+
when 'via'
|
81
|
+
post.via == data
|
82
|
+
when 'tag'
|
83
|
+
post.hashtags.include? data
|
84
|
+
when 'phrase', 'word'
|
85
|
+
/(?:^|\b|\s)#{Regexp.escape(data)}(?:\b|\s|$)/i === post.text
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def matches_post?(post, ignore_keys=[])
|
90
|
+
# Negations: Must not satisfy any of the conditions
|
91
|
+
# Requirements: Must satisfy every required condition
|
92
|
+
# Regular terms: Must satisfy at least one condition
|
93
|
+
|
94
|
+
conditions = {
|
95
|
+
negations: [],
|
96
|
+
requirements: [],
|
97
|
+
regular: []
|
98
|
+
}
|
99
|
+
|
100
|
+
query = self.parsed_query
|
101
|
+
keys = query.keys - ignore_keys
|
102
|
+
keys.each do |term|
|
103
|
+
test = lambda {|data| term_matches_post?(term, post, data)}
|
104
|
+
query[term].keys.each do |group|
|
105
|
+
conditions[group] += query[term][group].map(&test)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
conditions[:negations].none? &&
|
110
|
+
conditions[:requirements].all? &&
|
111
|
+
(conditions[:regular].count > 0 ? conditions[:regular].any? : true)
|
112
|
+
end
|
113
|
+
|
114
|
+
def refresh_posts( n = 512 )
|
115
|
+
# delete posts early to avoid confusion about posts that don't
|
116
|
+
# match the new query
|
117
|
+
DB.dbh[ "DELETE FROM river_posts WHERE river_id = ?", self.id ].get
|
118
|
+
|
119
|
+
# TODO: this is slow despite indices.
|
120
|
+
#posts = Post.where{|p| ~Sequel.function(:post_hidden_by_account, p.id, account.id)}
|
121
|
+
|
122
|
+
# get posts that are not hidden by account and get cracking
|
123
|
+
posts = Post.filter_by_query(self.parsed_query, self.account, Post.not_hidden_by(account))
|
124
|
+
|
125
|
+
# get up to n posts
|
126
|
+
# this is faster than using find_all on the set
|
127
|
+
count = 0
|
128
|
+
matching = []
|
129
|
+
posts.reverse_order(:id).each do |post|
|
130
|
+
break if count >= n
|
131
|
+
|
132
|
+
if res = self.matches_post?(post, ['flag', 'word', 'phrase', 'tag', 'visibility', 'from', 'via'])
|
133
|
+
count += 1
|
134
|
+
matching << post
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
if matching.any?
|
139
|
+
DB.dbh[ "INSERT INTO river_posts SELECT ?, id FROM posts WHERE id IN ?", self.id, matching.map(&:id)].get
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# @param params Untrusted parameter Hash. Be careful, this input usually comes from the outside world.
|
144
|
+
def revise( params )
|
145
|
+
self.label = params['label'].to_s
|
146
|
+
self.query = params['query'].to_s
|
147
|
+
|
148
|
+
n = River.num_appended_to_all
|
149
|
+
self.appended_to_all = !! params['appended_to_all']
|
150
|
+
if River.num_appended_to_all != n || self.appended_to_all
|
151
|
+
job_data = {
|
152
|
+
task: 'river:refresh-all',
|
153
|
+
params: {
|
154
|
+
'account_id' => self.account_id,
|
155
|
+
}.to_json
|
156
|
+
}
|
157
|
+
existing_jobs = Job.pending_where(
|
158
|
+
%{
|
159
|
+
task = ?
|
160
|
+
AND params = ?
|
161
|
+
},
|
162
|
+
job_data[:task],
|
163
|
+
job_data[:params]
|
164
|
+
)
|
165
|
+
if existing_jobs.empty?
|
166
|
+
Job.create job_data
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
if ! self.appended_to_all
|
171
|
+
Libertree::Model::Job.create(
|
172
|
+
task: 'river:refresh',
|
173
|
+
params: {
|
174
|
+
'river_id' => self.id,
|
175
|
+
}.to_json
|
176
|
+
)
|
177
|
+
end
|
178
|
+
self.save
|
179
|
+
end
|
180
|
+
|
181
|
+
def delete_cascade(force=false)
|
182
|
+
if ! force && self.appended_to_all
|
183
|
+
Libertree::Model::Job.create(
|
184
|
+
task: 'river:refresh-all',
|
185
|
+
params: {
|
186
|
+
'account_id' => self.account_id,
|
187
|
+
}.to_json
|
188
|
+
)
|
189
|
+
end
|
190
|
+
DB.dbh["SELECT delete_cascade_river(?)", self.id].get
|
191
|
+
end
|
192
|
+
|
193
|
+
def self.num_appended_to_all
|
194
|
+
self.where(:appended_to_all).count
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.create(*args)
|
198
|
+
n = River.num_appended_to_all
|
199
|
+
river = super
|
200
|
+
|
201
|
+
if River.num_appended_to_all != n
|
202
|
+
Libertree::Model::Job.create(
|
203
|
+
task: 'river:refresh-all',
|
204
|
+
params: {
|
205
|
+
'account_id' => river.account_id,
|
206
|
+
}.to_json
|
207
|
+
)
|
208
|
+
end
|
209
|
+
|
210
|
+
if ! river.appended_to_all
|
211
|
+
Libertree::Model::Job.create(
|
212
|
+
task: 'river:refresh',
|
213
|
+
params: {
|
214
|
+
'river_id' => river.id,
|
215
|
+
}.to_json
|
216
|
+
)
|
217
|
+
end
|
218
|
+
|
219
|
+
river
|
220
|
+
end
|
221
|
+
|
222
|
+
def home?
|
223
|
+
self.home
|
224
|
+
end
|
225
|
+
|
226
|
+
def to_hash
|
227
|
+
{
|
228
|
+
'id' => self.id,
|
229
|
+
'label' => self.label,
|
230
|
+
'query' => self.query,
|
231
|
+
}
|
232
|
+
end
|
233
|
+
|
234
|
+
def being_processed?
|
235
|
+
!! Job[
|
236
|
+
task: 'river:refresh',
|
237
|
+
params: %|{"river_id":#{self.id}}|,
|
238
|
+
time_finished: nil
|
239
|
+
]
|
240
|
+
end
|
241
|
+
|
242
|
+
def mark_all_posts_as_read
|
243
|
+
DB.dbh[ %{SELECT mark_all_posts_in_river_as_read_by(?,?)},
|
244
|
+
self.id,
|
245
|
+
self.account.id ].get
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Libertree
|
2
|
+
module Model
|
3
|
+
class Server < Sequel::Model(:servers)
|
4
|
+
@@own_domain = nil
|
5
|
+
|
6
|
+
def self.own_domain=(domain)
|
7
|
+
@@own_domain = domain
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.own_domain
|
11
|
+
@@own_domain
|
12
|
+
end
|
13
|
+
|
14
|
+
def name_display
|
15
|
+
self.domain || self.ip || "(unknown)"
|
16
|
+
end
|
17
|
+
|
18
|
+
def forests
|
19
|
+
Forest.s(
|
20
|
+
%{
|
21
|
+
SELECT
|
22
|
+
f.*
|
23
|
+
FROM
|
24
|
+
forests f
|
25
|
+
, forests_servers fs
|
26
|
+
WHERE
|
27
|
+
fs.server_id = ?
|
28
|
+
AND f.id = fs.forest_id
|
29
|
+
},
|
30
|
+
self.id
|
31
|
+
)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'libertree/db'
|
2
|
+
require_relative 'compat'
|
3
|
+
Sequel::Model.plugin :dirty
|
4
|
+
Sequel::Model.plugin :compat
|
5
|
+
Sequel::Model.unrestrict_primary_key
|
6
|
+
Sequel::Model.plugin :json_serializer
|
7
|
+
|
8
|
+
Libertree::DB.dbh.extension :pg_array
|
9
|
+
Sequel.extension :pg_array_ops
|
10
|
+
|
11
|
+
require_relative 'query'
|
12
|
+
|
13
|
+
require_relative 'model/is-remote-or-local'
|
14
|
+
require_relative 'model/has-searchable-text'
|
15
|
+
require_relative 'model/has-display-text'
|
16
|
+
|
17
|
+
require_relative 'model/account'
|
18
|
+
require_relative 'model/account-settings'
|
19
|
+
require_relative 'model/chat-message'
|
20
|
+
require_relative 'model/comment'
|
21
|
+
require_relative 'model/comment-like'
|
22
|
+
require_relative 'model/contact-list'
|
23
|
+
require_relative 'model/file'
|
24
|
+
require_relative 'model/forest'
|
25
|
+
require_relative 'model/embed-cache'
|
26
|
+
require_relative 'model/ignored-member'
|
27
|
+
require_relative 'model/invitation'
|
28
|
+
require_relative 'model/group'
|
29
|
+
require_relative 'model/group-member'
|
30
|
+
require_relative 'model/job'
|
31
|
+
require_relative 'model/member'
|
32
|
+
require_relative 'model/message'
|
33
|
+
require_relative 'model/node'
|
34
|
+
require_relative 'model/node_affiliation'
|
35
|
+
require_relative 'model/node_subscription'
|
36
|
+
require_relative 'model/notification'
|
37
|
+
require_relative 'model/pool'
|
38
|
+
require_relative 'model/pool-post'
|
39
|
+
require_relative 'model/post'
|
40
|
+
require_relative 'model/post-hidden'
|
41
|
+
require_relative 'model/post-like'
|
42
|
+
require_relative 'model/post-revision'
|
43
|
+
require_relative 'model/profile'
|
44
|
+
require_relative 'model/river'
|
45
|
+
require_relative 'model/remote-storage-connection'
|
46
|
+
require_relative 'model/server'
|
47
|
+
require_relative 'model/session-account'
|
48
|
+
require_relative 'model/url-expansion'
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module Libertree
|
2
|
+
class Query
|
3
|
+
private
|
4
|
+
class ParseError < StandardError; end
|
5
|
+
|
6
|
+
def patterns
|
7
|
+
{
|
8
|
+
'phrase' => /(?<sign>[+-])?"(?<arg>[^"]+)"/,
|
9
|
+
'from' => /(?<sign>[+-])?:from ("(?<arg>.+?)"|(?<arg>[^ ]+))/,
|
10
|
+
'river' => /(?<sign>[+-])?:river "(?<arg>.+?)"/,
|
11
|
+
'contact-list' => /(?<sign>[+-])?:contact-list "(?<arg>.+?)"/,
|
12
|
+
'via' => /(?<sign>[+-])?:via "(?<arg>.+?)"/,
|
13
|
+
'visibility' => /(?<sign>[+-])?:visibility (?<arg>[a-z-]+)/,
|
14
|
+
'word-count' => /(?<sign>[+-])?:word-count ?(?<arg>(?<comp>[<>]) ?(?<num>[0-9]+))/,
|
15
|
+
'spring' => /(?<sign>[+-])?:spring (?<arg>"(?<spring_name>.+?)" "(?<handle>.+?)")/,
|
16
|
+
'flag' => /(?<sign>[+-])?:(?<arg>forest|tree|unread|liked|commented|subscribed)/,
|
17
|
+
'tag' => /(?<sign>[+-])?#(?<arg>\S+)/,
|
18
|
+
'word' => /(?<sign>[+-])?(?<arg>\S+)/,
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def check_resource(res, term, &block)
|
23
|
+
err = if res.is_a? Array
|
24
|
+
res.any?(&:nil?)
|
25
|
+
else
|
26
|
+
res.nil?
|
27
|
+
end
|
28
|
+
if err
|
29
|
+
if @fail_on_error
|
30
|
+
fail ParseError, term
|
31
|
+
end
|
32
|
+
else
|
33
|
+
yield(*res)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# We need the river id only to prevent self-referential river
|
38
|
+
# queries. For general purpose queries this is not required.
|
39
|
+
def initialize(query, account_id, river_id=nil, fail_on_error=false)
|
40
|
+
@fail_on_error = fail_on_error
|
41
|
+
@parsed_query = Hash.new
|
42
|
+
@parsed_query.default_proc = proc do |hash,key|
|
43
|
+
hash[key] = {
|
44
|
+
:negations => [],
|
45
|
+
:requirements => [],
|
46
|
+
:regular => []
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
scanner = StringScanner.new(query)
|
51
|
+
until scanner.eos? do
|
52
|
+
scanner.skip(/\s+/)
|
53
|
+
patterns.each_pair do |key, pattern|
|
54
|
+
if term = scanner.scan(pattern)
|
55
|
+
match = term.match(pattern)
|
56
|
+
group = case match[:sign]
|
57
|
+
when '+'
|
58
|
+
:requirements
|
59
|
+
when '-'
|
60
|
+
:negations
|
61
|
+
else
|
62
|
+
:regular
|
63
|
+
end
|
64
|
+
|
65
|
+
case key
|
66
|
+
when
|
67
|
+
'phrase',
|
68
|
+
'via',
|
69
|
+
'visibility',
|
70
|
+
'word-count',
|
71
|
+
'flag',
|
72
|
+
'tag'
|
73
|
+
@parsed_query[key][group] << match[:arg]
|
74
|
+
when 'from'
|
75
|
+
# TODO: eventually remove with_display_name check
|
76
|
+
member = (Model::Member.with_handle(match[:arg]) || Model::Member.with_display_name(match[:arg]))
|
77
|
+
check_resource(member, term) do |member|
|
78
|
+
@parsed_query[key][group] << member.id
|
79
|
+
end
|
80
|
+
when 'river'
|
81
|
+
river = Model::River[label: match[:arg], account_id: account_id]
|
82
|
+
check_resource(river, term) do |river|
|
83
|
+
@parsed_query[key][group] << river if river_id && river.id != river_id
|
84
|
+
end
|
85
|
+
when 'contact-list'
|
86
|
+
list = Model::ContactList[ account_id: account_id, name: match[:arg] ]
|
87
|
+
check_resource(list, term) do |list|
|
88
|
+
ids = list.member_ids
|
89
|
+
@parsed_query[key][group] << [list.id, ids] unless ids.empty?
|
90
|
+
end
|
91
|
+
when 'spring'
|
92
|
+
# TODO: eventually remove with_display_name check
|
93
|
+
member = (Model::Member.with_handle(match[:handle]) || Model::Member.with_display_name(match[:handle]))
|
94
|
+
pool = Model::Pool[ member_id: member.id, name: match[:spring_name], sprung: true ] if member
|
95
|
+
check_resource([member, pool], term) do |list, pool|
|
96
|
+
@parsed_query[key][group] << pool
|
97
|
+
end
|
98
|
+
when 'word'
|
99
|
+
# Only treat a matched word as a simple word if it consists only of word
|
100
|
+
# characters. This excludes URLs or other terms with special characters.
|
101
|
+
if match[:arg] =~ /^[[:word:]]+$/
|
102
|
+
@parsed_query['word'][group] << match[:arg]
|
103
|
+
else
|
104
|
+
@parsed_query['phrase'][group] << match[:arg]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# move on to the next term
|
109
|
+
next @parsed_query
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
@parsed_query
|
114
|
+
end
|
115
|
+
|
116
|
+
public
|
117
|
+
def parsed
|
118
|
+
@parsed_query.dup
|
119
|
+
end
|
120
|
+
|
121
|
+
def simple
|
122
|
+
tags = @parsed_query['tag'][:regular].map {|t| "##{t}"}
|
123
|
+
rest = @parsed_query.
|
124
|
+
select {|k| ['phrase', 'word'].include? k}.
|
125
|
+
flat_map {|h| h.last[:regular]}
|
126
|
+
(tags + rest).join(' ')
|
127
|
+
end
|
128
|
+
|
129
|
+
def to_s
|
130
|
+
res = []
|
131
|
+
apply_template = lambda do |template, groups|
|
132
|
+
res += groups[:negations].map {|v| '-' + template.call(v)}
|
133
|
+
res += groups[:requirements].map {|v| '+' + template.call(v)}
|
134
|
+
res += groups[:regular].map {|v| template.call(v)}
|
135
|
+
end
|
136
|
+
|
137
|
+
@parsed_query.each_pair do |key, groups|
|
138
|
+
template = case key
|
139
|
+
when 'phrase'
|
140
|
+
lambda {|v| "\"%s\"" % v }
|
141
|
+
when 'via'
|
142
|
+
lambda {|v| ":via \"%s\"" % v }
|
143
|
+
when 'visibility'
|
144
|
+
lambda {|v| ":visibility %s" % v }
|
145
|
+
when 'word-count'
|
146
|
+
lambda {|v| ":word-count %s" % v }
|
147
|
+
when 'flag'
|
148
|
+
lambda {|v| ":%s" % v }
|
149
|
+
when 'word'
|
150
|
+
lambda {|v| v }
|
151
|
+
when 'tag'
|
152
|
+
lambda {|v| "#%s" % v }
|
153
|
+
when 'from'
|
154
|
+
lambda {|v| ":from %s" % Model::Member[v.to_i].handle }
|
155
|
+
when 'river'
|
156
|
+
lambda {|v| ":river \"%s\"" % Model::River[v.id.to_i].label }
|
157
|
+
when 'contact-list'
|
158
|
+
template = lambda {|v| ":contact-list \"%s\"" % Model::ContactList[v.first.to_i].name }
|
159
|
+
when 'spring'
|
160
|
+
lambda {|v| pool = Model::Pool[v.id.to_i]; ":spring \"%s\" \"%s\"" % [pool.name, pool.member.handle] }
|
161
|
+
end
|
162
|
+
apply_template.call(template, groups)
|
163
|
+
end
|
164
|
+
res.join(' ')
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'markdown'
|
4
|
+
|
5
|
+
module Libertree
|
6
|
+
module Render
|
7
|
+
Options = [ :filter_html,
|
8
|
+
:smart,
|
9
|
+
:strike,
|
10
|
+
:autolink,
|
11
|
+
:hard_wrap,
|
12
|
+
:notes,
|
13
|
+
:codeblock,
|
14
|
+
:hashtags,
|
15
|
+
:usernames,
|
16
|
+
:spoilerblock
|
17
|
+
]
|
18
|
+
|
19
|
+
def self.to_html_string(s, opts=Options)
|
20
|
+
return '' if s.nil? or s.empty?
|
21
|
+
Markdown.new( s, *opts ).to_html.force_encoding('utf-8')
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.to_html_nodeset(s, opts=Options)
|
25
|
+
Nokogiri::HTML.fragment(self.to_html_string(s, opts))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|