automatic 14.1.0 → 14.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -2
- data/README.md +2 -2
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/automatic.gemspec +17 -9
- data/bin/automatic +4 -4
- data/doc/ChangeLog +27 -0
- data/doc/PLUGINS +36 -6
- data/doc/PLUGINS.ja +35 -7
- data/doc/README +4 -4
- data/doc/README.ja +3 -3
- data/lib/automatic.rb +2 -1
- data/lib/automatic/feed_maker.rb +80 -0
- data/lib/automatic/feed_parser.rb +6 -50
- data/lib/automatic/recipe.rb +1 -1
- data/lib/automatic/version.rb +1 -1
- data/plugins/filter/accept.rb +4 -3
- data/plugins/filter/github_feed.rb +12 -11
- data/plugins/filter/ignore.rb +3 -3
- data/plugins/filter/image_source.rb +10 -10
- data/plugins/filter/one.rb +4 -3
- data/plugins/filter/rand.rb +3 -3
- data/plugins/filter/sanitize.rb +2 -3
- data/plugins/provide/fluentd.rb +5 -4
- data/plugins/publish/amazon_s3.rb +58 -0
- data/plugins/publish/fluentd.rb +5 -4
- data/plugins/publish/google_calendar.rb +2 -2
- data/plugins/publish/hipchat.rb +1 -1
- data/plugins/publish/twitter.rb +1 -1
- data/plugins/store/database.rb +4 -4
- data/plugins/store/file.rb +95 -0
- data/plugins/store/full_text.rb +1 -1
- data/plugins/store/permalink.rb +1 -1
- data/plugins/subscription/feed.rb +2 -2
- data/plugins/subscription/g_guide.rb +3 -2
- data/plugins/subscription/link.rb +4 -3
- data/plugins/subscription/pocket.rb +12 -11
- data/plugins/subscription/text.rb +35 -44
- data/plugins/subscription/tumblr.rb +3 -3
- data/plugins/subscription/twitter.rb +1 -1
- data/plugins/subscription/twitter_search.rb +11 -12
- data/plugins/subscription/weather.rb +7 -6
- data/plugins/subscription/xml.rb +4 -3
- data/spec/fixtures/sampleFeeds.tsv +1 -0
- data/spec/fixtures/sampleFeeds2.tsv +2 -0
- data/spec/lib/automatic/pipeline_spec.rb +4 -4
- data/spec/lib/automatic_spec.rb +3 -3
- data/spec/plugins/filter/github_feed_spec.rb +9 -8
- data/spec/plugins/filter/image_source_spec.rb +7 -7
- data/spec/plugins/notify/ikachan_spec.rb +3 -3
- data/spec/plugins/provide/fluentd_spec.rb +6 -5
- data/spec/plugins/publish/amazon_s3_spec.rb +40 -0
- data/spec/plugins/publish/console_spec.rb +3 -3
- data/spec/plugins/publish/fluentd_spec.rb +5 -4
- data/spec/plugins/publish/google_calendar_spec.rb +5 -5
- data/spec/plugins/publish/hatena_bookmark_spec.rb +10 -10
- data/spec/plugins/publish/hipchat_spec.rb +6 -6
- data/spec/plugins/publish/instapaper_spec.rb +6 -6
- data/spec/plugins/publish/memcached_spec.rb +3 -3
- data/spec/plugins/publish/pocket_spec.rb +3 -3
- data/spec/plugins/publish/twitter_spec.rb +4 -4
- data/spec/plugins/store/{target_link_spec.rb → file_spec.rb} +10 -10
- data/spec/plugins/subscription/pocket_spec.rb +3 -3
- data/spec/plugins/subscription/text_spec.rb +32 -2
- data/spec/plugins/subscription/twitter_search_spec.rb +3 -3
- data/test/integration/test_fluentd.yml +1 -0
- data/test/integration/test_image2local.yml +6 -2
- data/test/integration/test_text2feed.yml +8 -0
- data/test/integration/test_tumblr2local.yml +6 -0
- metadata +26 -7
- data/plugins/store/target_link.rb +0 -55
data/plugins/publish/fluentd.rb
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
# Name:: Automatic::Plugin::Publish::Fluentd
|
3
3
|
# Author:: 774 <http://id774.net>
|
4
4
|
# Created:: Jun 21, 2013
|
5
|
-
# Updated::
|
6
|
-
# Copyright:: Copyright (c) 2012-
|
5
|
+
# Updated:: Feb 25, 2014
|
6
|
+
# Copyright:: Copyright (c) 2012-2014 Automatic Ruby Developers.
|
7
7
|
# License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
|
8
8
|
|
9
9
|
module Automatic::Plugin
|
@@ -13,9 +13,10 @@ module Automatic::Plugin
|
|
13
13
|
def initialize(config, pipeline=[])
|
14
14
|
@config = config
|
15
15
|
@pipeline = pipeline
|
16
|
+
@mode = @config['mode']
|
16
17
|
@fluentd = Fluent::Logger::FluentLogger.open(nil,
|
17
18
|
host = @config['host'],
|
18
|
-
port = @config['port'])
|
19
|
+
port = @config['port']) unless @mode == 'test'
|
19
20
|
end
|
20
21
|
|
21
22
|
def run
|
@@ -29,7 +30,7 @@ module Automatic::Plugin
|
|
29
30
|
:description => feed.description,
|
30
31
|
:content => feed.content_encoded,
|
31
32
|
:created_at => Time.now.strftime("%Y/%m/%d %X")
|
32
|
-
})
|
33
|
+
}) unless @mode == 'test'
|
33
34
|
rescue
|
34
35
|
Automatic::Log.puts("warn", "Skip feed due to fault in forward.")
|
35
36
|
end
|
@@ -37,8 +37,8 @@ module Automatic::Plugin
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
Automatic::Log.puts("info", "
|
41
|
-
Automatic::Log.puts("info", "
|
40
|
+
Automatic::Log.puts("info", "Date : #{date}")
|
41
|
+
Automatic::Log.puts("info", "Title : #{text}")
|
42
42
|
|
43
43
|
# Register to calendar
|
44
44
|
require 'rubygems'
|
data/plugins/publish/hipchat.rb
CHANGED
@@ -29,7 +29,7 @@ module Automatic::Plugin
|
|
29
29
|
retry_max = @config['retry'].to_i || 0
|
30
30
|
begin
|
31
31
|
@client.send(@config['username'], feed.description, @options)
|
32
|
-
Automatic::Log.puts("info", "post: #{feed.description.gsub(/[\r\n]/,'')[0..50]}...") rescue nil
|
32
|
+
Automatic::Log.puts("info", "Hipchat post: #{feed.description.gsub(/[\r\n]/,'')[0..50]}...") rescue nil
|
33
33
|
rescue => e
|
34
34
|
retries += 1
|
35
35
|
Automatic::Log.puts("error", "ErrorCount: #{retries}, #{e.message}")
|
data/plugins/publish/twitter.rb
CHANGED
@@ -34,7 +34,7 @@ module Automatic::Plugin
|
|
34
34
|
@pipeline.each {|feeds|
|
35
35
|
unless feeds.nil?
|
36
36
|
feeds.items.each {|feed|
|
37
|
-
Automatic::Log.puts("info", "
|
37
|
+
Automatic::Log.puts("info", "Publish Tweet: #{feed.link}")
|
38
38
|
retries = 0
|
39
39
|
retry_max = @config['retry'].to_i || 0
|
40
40
|
begin
|
data/plugins/store/database.rb
CHANGED
@@ -4,8 +4,8 @@
|
|
4
4
|
# 774 <http://id774.net>
|
5
5
|
# soramugi <http://soramugi.net>
|
6
6
|
# Created:: Feb 27, 2012
|
7
|
-
# Updated::
|
8
|
-
# Copyright:: Copyright (c) 2012-
|
7
|
+
# Updated:: Feb 21, 2014
|
8
|
+
# Copyright:: Copyright (c) 2012-2014 Automatic Ruby Developers.
|
9
9
|
# License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
|
10
10
|
|
11
11
|
require 'active_record'
|
@@ -27,7 +27,7 @@ module Automatic::Plugin
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
}
|
30
|
-
@return_feeds << Automatic::
|
30
|
+
@return_feeds << Automatic::FeedMaker.create_pipeline(new_feeds) if new_feeds.length > 0
|
31
31
|
end
|
32
32
|
}
|
33
33
|
@return_feeds
|
@@ -54,7 +54,7 @@ module Automatic::Plugin
|
|
54
54
|
|
55
55
|
def prepare_database
|
56
56
|
db = File.join(db_dir, @config['db'])
|
57
|
-
Automatic::Log.puts("info", "Database: #{db}")
|
57
|
+
Automatic::Log.puts("info", "Using Database: #{db}")
|
58
58
|
ActiveRecord::Base.establish_connection(
|
59
59
|
:adapter => "sqlite3",
|
60
60
|
:database => db)
|
@@ -0,0 +1,95 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# Name:: Automatic::Plugin::Store::File
|
4
|
+
# Author:: 774 <http://id774.net>
|
5
|
+
# Created:: Feb 28, 2012
|
6
|
+
# Updated:: Feb 25, 2014
|
7
|
+
# Copyright:: Copyright (c) 2012-2014 Automatic Ruby Developers.
|
8
|
+
# License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
|
9
|
+
|
10
|
+
require 'open-uri'
|
11
|
+
require 'uri'
|
12
|
+
require 'aws-sdk'
|
13
|
+
|
14
|
+
module Automatic::Plugin
|
15
|
+
class StoreFile
|
16
|
+
|
17
|
+
def initialize(config, pipeline=[])
|
18
|
+
@config = config
|
19
|
+
@pipeline = pipeline
|
20
|
+
unless @config['bucket_name'].nil?
|
21
|
+
s3 = AWS::S3.new(
|
22
|
+
:access_key_id => @config['access_key'],
|
23
|
+
:secret_access_key => @config['secret_key']
|
24
|
+
)
|
25
|
+
@bucket = s3.buckets[@config['bucket_name']]
|
26
|
+
end
|
27
|
+
@return_feeds = []
|
28
|
+
end
|
29
|
+
|
30
|
+
def run
|
31
|
+
@pipeline.each {|feeds|
|
32
|
+
unless feeds.nil?
|
33
|
+
feeds.items.each {|feed|
|
34
|
+
unless feed.link.nil?
|
35
|
+
Automatic::Log.puts("info", "Downloading File: #{feed.link}")
|
36
|
+
FileUtils.mkdir_p(@config['path']) unless FileTest.exist?(@config['path'])
|
37
|
+
retries = 0
|
38
|
+
retry_max = @config['retry'].to_i || 0
|
39
|
+
begin
|
40
|
+
retries += 1
|
41
|
+
feed.link = get_file(feed.link)
|
42
|
+
sleep ||= @config['interval'].to_i
|
43
|
+
@return_feeds << feed
|
44
|
+
rescue
|
45
|
+
Automatic::Log.puts("error", "ErrorCount: #{retries}, Fault during file download.")
|
46
|
+
sleep ||= @config['interval'].to_i
|
47
|
+
retry if retries <= retry_max
|
48
|
+
end
|
49
|
+
end
|
50
|
+
}
|
51
|
+
end
|
52
|
+
}
|
53
|
+
@pipeline = []
|
54
|
+
@pipeline << Automatic::FeedMaker.create_pipeline(@return_feeds) if @return_feeds.length > 0
|
55
|
+
@pipeline
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def get_file(url)
|
61
|
+
uri = URI.parse(url)
|
62
|
+
case uri.scheme
|
63
|
+
when "s3n"
|
64
|
+
return_path = get_aws(uri)
|
65
|
+
else
|
66
|
+
return_path = wget(uri, url)
|
67
|
+
end
|
68
|
+
Automatic::Log.puts("info", "Saved File: #{return_path}")
|
69
|
+
"file://" + return_path
|
70
|
+
end
|
71
|
+
|
72
|
+
def wget(uri, url)
|
73
|
+
filename = File.basename(uri.path)
|
74
|
+
filepath = File.join(@config['path'], filename)
|
75
|
+
open(url) {|source|
|
76
|
+
open(filepath, "w+b") { |o|
|
77
|
+
o.print(source.read)
|
78
|
+
}
|
79
|
+
}
|
80
|
+
filepath
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_aws(uri)
|
84
|
+
filename = File.basename(uri.path)
|
85
|
+
filepath = File.join(@config['path'], filename)
|
86
|
+
object = @bucket.objects[uri.path]
|
87
|
+
File.open(filepath, 'wb') do |file|
|
88
|
+
object.read do |chunk|
|
89
|
+
file.write(chunk)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
filepath
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/plugins/store/full_text.rb
CHANGED
data/plugins/store/permalink.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# Name:: Automatic::Plugin::SubscriptionFeed
|
3
3
|
# Author:: 774 <http://id774.net>
|
4
4
|
# Created:: Feb 22, 2012
|
5
|
-
# Updated::
|
5
|
+
# Updated:: Feb 21, 2014
|
6
6
|
# Copyright:: Copyright (c) 2012-2014 Automatic Ruby Developers.
|
7
7
|
# License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
|
8
8
|
|
@@ -18,7 +18,7 @@ module Automatic::Plugin
|
|
18
18
|
retries = 0
|
19
19
|
retry_max = @config['retry'].to_i || 0
|
20
20
|
begin
|
21
|
-
rss = Automatic::FeedParser.
|
21
|
+
rss = Automatic::FeedParser.get_url(feed)
|
22
22
|
@pipeline << rss
|
23
23
|
rescue
|
24
24
|
retries += 1
|
@@ -1,8 +1,9 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
# Name:: Automatic::Plugin::Subscription::GGuide
|
3
3
|
# Author:: soramugi <http://soramugi.net>
|
4
|
+
# 774 <http://id774.net>
|
4
5
|
# Created:: Jun 28, 2013
|
5
|
-
# Updated::
|
6
|
+
# Updated:: Feb 21, 2014
|
6
7
|
# Copyright:: Copyright (c) 2012-2014 Automatic Ruby Developers.
|
7
8
|
# License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
|
8
9
|
|
@@ -25,7 +26,7 @@ module Automatic::Plugin
|
|
25
26
|
retry_max = @config['retry'].to_i || 0
|
26
27
|
begin
|
27
28
|
@keywords.each {|keyword|
|
28
|
-
@pipeline << Automatic::FeedParser.
|
29
|
+
@pipeline << Automatic::FeedParser.get_url(feed_url keyword)
|
29
30
|
}
|
30
31
|
rescue
|
31
32
|
retries += 1
|
@@ -2,7 +2,7 @@
|
|
2
2
|
# Name:: Automatic::Plugin::Subscription::Link
|
3
3
|
# Author:: 774 <http://id774.net>
|
4
4
|
# Created:: Sep 18, 2012
|
5
|
-
# Updated::
|
5
|
+
# Updated:: Feb 21, 2014
|
6
6
|
# Copyright:: Copyright (c) 2012-2014 Automatic Ruby Developers.
|
7
7
|
# License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
|
8
8
|
|
@@ -34,11 +34,12 @@ module Automatic::Plugin
|
|
34
34
|
end
|
35
35
|
|
36
36
|
private
|
37
|
+
|
37
38
|
def create_rss(url)
|
38
|
-
Automatic::Log.puts("info", "Parsing: #{url}")
|
39
|
+
Automatic::Log.puts("info", "Parsing Link: #{url}")
|
39
40
|
html = open(url).read
|
40
41
|
unless html.nil?
|
41
|
-
rss = Automatic::FeedParser.
|
42
|
+
rss = Automatic::FeedParser.parse_html(html)
|
42
43
|
sleep ||= @config['interval'].to_i
|
43
44
|
@return_feeds << rss
|
44
45
|
end
|
@@ -1,8 +1,9 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
# Name:: Automatic::Plugin::Subscription::Pocket
|
3
3
|
# Author:: soramugi <http://soramugi.net>
|
4
|
+
# 774 <http://id774.net>
|
4
5
|
# Created:: May 21, 2013
|
5
|
-
# Updated::
|
6
|
+
# Updated:: Feb 21, 2014
|
6
7
|
# Copyright:: Copyright (c) 2012-2014 Automatic Ruby Developers.
|
7
8
|
# License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
|
8
9
|
|
@@ -24,8 +25,8 @@ module Automatic::Plugin
|
|
24
25
|
retries = 0
|
25
26
|
retry_max = @config['retry'].to_i || 0
|
26
27
|
begin
|
27
|
-
|
28
|
-
@pipeline << Automatic::
|
28
|
+
return_feeds = generate_feed(@client.retrieve(@config['optional']))
|
29
|
+
@pipeline << Automatic::FeedMaker.create_pipeline(return_feeds)
|
29
30
|
rescue
|
30
31
|
retries += 1
|
31
32
|
Automatic::Log.puts("error", "ErrorCount: #{retries}, Fault in parsing: #{retries}")
|
@@ -35,16 +36,16 @@ module Automatic::Plugin
|
|
35
36
|
@pipeline
|
36
37
|
end
|
37
38
|
|
38
|
-
def
|
39
|
-
|
39
|
+
def generate_feed(retrieve)
|
40
|
+
return_feeds = []
|
40
41
|
retrieve['list'].each {|key,list|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
42
|
+
hashie = Hashie::Mash.new
|
43
|
+
hashie.title = list['given_title']
|
44
|
+
hashie.link = list['given_url']
|
45
|
+
hashie.description = list['excerpt']
|
46
|
+
return_feeds << hashie
|
46
47
|
}
|
47
|
-
|
48
|
+
return_feeds
|
48
49
|
end
|
49
50
|
end
|
50
51
|
end
|
@@ -1,76 +1,67 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
# Name:: Automatic::Plugin::Subscription::Text
|
3
3
|
# Author:: soramugi <http://soramugi.net>
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
4
|
+
# 774 <http://id774.net>
|
5
|
+
# Created:: May 6, 2013
|
6
|
+
# Updated:: Feb 21, 2014
|
7
|
+
# Copyright:: Copyright (c) 2012-2014 Automatic Ruby Developers.
|
7
8
|
# License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
|
8
9
|
|
9
10
|
module Automatic::Plugin
|
10
|
-
class TextFeed
|
11
|
-
def initialize
|
12
|
-
@link = 'http://dummy'
|
13
|
-
@title = 'dummy'
|
14
|
-
end
|
15
|
-
|
16
|
-
def link
|
17
|
-
@link
|
18
|
-
end
|
19
|
-
|
20
|
-
def title
|
21
|
-
@title
|
22
|
-
end
|
23
|
-
|
24
|
-
def set_link(link)
|
25
|
-
@link = link
|
26
|
-
end
|
27
|
-
|
28
|
-
def set_title(title)
|
29
|
-
@title = title
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
11
|
class SubscriptionText
|
34
|
-
|
35
12
|
def initialize(config, pipeline=[])
|
36
13
|
@config = config
|
37
14
|
@pipeline = pipeline
|
15
|
+
@return_feeds = []
|
16
|
+
end
|
38
17
|
|
18
|
+
def run
|
19
|
+
create_feed
|
20
|
+
@pipeline << Automatic::FeedMaker.create_pipeline(@return_feeds) if @return_feeds.length > 0
|
21
|
+
@pipeline
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def create_feed
|
39
27
|
unless @config.nil?
|
40
28
|
@dummyfeeds = []
|
41
29
|
unless @config['titles'].nil?
|
42
30
|
@config['titles'].each {|title|
|
43
|
-
|
44
|
-
|
45
|
-
@
|
31
|
+
feed = {}
|
32
|
+
feed['title'] = title
|
33
|
+
@return_feeds << Automatic::FeedMaker.generate_feed(feed)
|
46
34
|
}
|
47
35
|
end
|
48
36
|
|
49
37
|
unless @config['urls'].nil?
|
50
38
|
@config['urls'].each {|url|
|
51
|
-
|
52
|
-
|
53
|
-
@
|
39
|
+
feed = {}
|
40
|
+
feed['url'] = url
|
41
|
+
@return_feeds << Automatic::FeedMaker.generate_feed(feed)
|
54
42
|
}
|
55
43
|
end
|
56
44
|
|
57
45
|
unless @config['feeds'].nil?
|
58
46
|
@config['feeds'].each {|feed|
|
59
|
-
|
60
|
-
textFeed.set_title(feed['title']) unless feed['title'].nil?
|
61
|
-
textFeed.set_link(feed['url']) unless feed['url'].nil?
|
62
|
-
@dummyfeeds << textFeed
|
47
|
+
@return_feeds << Automatic::FeedMaker.generate_feed(feed)
|
63
48
|
}
|
64
49
|
end
|
65
|
-
end
|
66
50
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
51
|
+
unless @config['files'].nil?
|
52
|
+
@config['files'].each {|f|
|
53
|
+
open(File.expand_path(f)) do |file|
|
54
|
+
file.each_line do |line|
|
55
|
+
feed = {}
|
56
|
+
feed['title'], feed['url'], feed['description'], feed['author'],
|
57
|
+
feed['comments'] = line.force_encoding("utf-8").strip.split("\t")
|
58
|
+
@return_feeds << Automatic::FeedMaker.generate_feed(feed)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
}
|
62
|
+
end
|
72
63
|
end
|
73
|
-
@pipeline
|
74
64
|
end
|
65
|
+
|
75
66
|
end
|
76
67
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
# Name:: Automatic::Plugin::Subscription::Tumblr
|
3
3
|
# Author:: 774 <http://id774.net>
|
4
4
|
# Created:: Oct 16, 2012
|
5
|
-
# Updated::
|
5
|
+
# Updated:: Feb 21, 2014
|
6
6
|
# Copyright:: Copyright (c) 2012-2014 Automatic Ruby Developers.
|
7
7
|
# License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
|
8
8
|
|
@@ -42,11 +42,11 @@ module Automatic::Plugin
|
|
42
42
|
|
43
43
|
private
|
44
44
|
def create_rss(url)
|
45
|
-
Automatic::Log.puts("info", "Parsing: #{url}")
|
45
|
+
Automatic::Log.puts("info", "Parsing Tumblr: #{url}")
|
46
46
|
html = open(url).read
|
47
47
|
unless html.nil?
|
48
48
|
uri = URI.parse(url)
|
49
|
-
rss = Automatic::FeedParser.
|
49
|
+
rss = Automatic::FeedParser.parse_html(html)
|
50
50
|
rss.items.each {|item|
|
51
51
|
unless item.link =~ Regexp.new(uri.host)
|
52
52
|
item.link = nil
|