automatic 12.2.1

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 (60) hide show
  1. data/Gemfile +23 -0
  2. data/README.md +109 -0
  3. data/Rakefile +42 -0
  4. data/VERSION +1 -0
  5. data/app.rb +15 -0
  6. data/automatic.gemspec +122 -0
  7. data/bin/automatic +15 -0
  8. data/config/default.yml +29 -0
  9. data/config/feed2console.yml +15 -0
  10. data/db/.gitkeep +0 -0
  11. data/doc/COPYING +674 -0
  12. data/doc/ChangeLog +17 -0
  13. data/doc/PLUGINS.ja +205 -0
  14. data/doc/README.ja +449 -0
  15. data/lib/automatic.rb +60 -0
  16. data/lib/automatic/environment.rb +8 -0
  17. data/lib/automatic/feed_parser.rb +36 -0
  18. data/lib/automatic/log.rb +24 -0
  19. data/lib/automatic/pipeline.rb +36 -0
  20. data/lib/automatic/recipe.rb +31 -0
  21. data/lib/config/validator.rb +83 -0
  22. data/plugins/custom_feed/svn_log.rb +56 -0
  23. data/plugins/filter/ignore.rb +60 -0
  24. data/plugins/filter/image.rb +47 -0
  25. data/plugins/filter/tumblr_resize.rb +40 -0
  26. data/plugins/notify/ikachan.rb +85 -0
  27. data/plugins/publish/console.rb +31 -0
  28. data/plugins/publish/google_calendar.rb +86 -0
  29. data/plugins/publish/hatena_bookmark.rb +103 -0
  30. data/plugins/store/full_text.rb +57 -0
  31. data/plugins/store/permalink.rb +47 -0
  32. data/plugins/store/store_database.rb +53 -0
  33. data/plugins/store/target_link.rb +47 -0
  34. data/plugins/subscription/feed.rb +30 -0
  35. data/script/bootstrap +117 -0
  36. data/spec/plugins/custom_feed/svn_log_spec.rb +31 -0
  37. data/spec/plugins/filter/ignore_spec.rb +37 -0
  38. data/spec/plugins/filter/image_spec.rb +55 -0
  39. data/spec/plugins/filter/tumblr_resize_spec.rb +102 -0
  40. data/spec/plugins/notify/ikachan_spec.rb +28 -0
  41. data/spec/plugins/publish/console_spec.rb +24 -0
  42. data/spec/plugins/publish/google_calendar_spec.rb +82 -0
  43. data/spec/plugins/publish/hatena_bookmark_spec.rb +36 -0
  44. data/spec/plugins/store/full_text_spec.rb +39 -0
  45. data/spec/plugins/store/permalink_spec.rb +39 -0
  46. data/spec/plugins/store/target_link_spec.rb +30 -0
  47. data/spec/plugins/subscription/feed_spec.rb +36 -0
  48. data/spec/spec_helper.rb +82 -0
  49. data/test/integration/test_activerecord.yml +24 -0
  50. data/test/integration/test_fulltext.yml +24 -0
  51. data/test/integration/test_hatenabookmark.yml +26 -0
  52. data/test/integration/test_ignore.yml +22 -0
  53. data/test/integration/test_ignore2.yml +25 -0
  54. data/test/integration/test_image2local.yml +26 -0
  55. data/test/integration/test_svnlog.yml +14 -0
  56. data/test/integration/test_tumblr2local.yml +26 -0
  57. data/utils/auto_discovery.rb +18 -0
  58. data/utils/opml_parser.rb +247 -0
  59. data/vendor/.gitkeep +0 -0
  60. metadata +205 -0
@@ -0,0 +1,53 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Name:: Automatic::Plugin::Store::Database
3
+ # Author:: kzgs
4
+ # Created:: Feb 27, 2012
5
+ # Updated:: Feb 29, 2012
6
+ # Copyright:: kzgs Copyright (c) 2012
7
+ # License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
8
+
9
+ require 'active_record'
10
+
11
+ module Automatic::Plugin
12
+ module StoreDatabase
13
+ def for_each_new_feed
14
+ prepare_database
15
+ existing_records = model_class.find(:all)
16
+ return_feeds = []
17
+ @pipeline.each { |feeds|
18
+ unless feeds.nil?
19
+ new_feed = false
20
+ feeds.items.each { |feed|
21
+ unless existing_records.detect { |b| b.try(unique_key) == feed.link }
22
+ yield(feed)
23
+ new_feed = true
24
+ end
25
+ }
26
+ return_feeds << feeds if new_feed
27
+ end
28
+ }
29
+ return_feeds
30
+ end
31
+
32
+ private
33
+
34
+ def create_table
35
+ ActiveRecord::Migration.create_table(model_class.table_name) { |t|
36
+ column_definition.each_pair { |column_name, column_type|
37
+ t.column column_name, column_type
38
+ }
39
+ }
40
+ end
41
+
42
+ def db_dir
43
+ return File.join(File.dirname(__FILE__), '..', '..', 'db')
44
+ end
45
+
46
+ def prepare_database
47
+ ActiveRecord::Base.establish_connection(
48
+ :adapter => "sqlite3",
49
+ :database => File.join(db_dir, @config['db']))
50
+ create_table unless model_class.table_exists?
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Name:: Automatic::Plugin::Store::TargetLink
4
+ # Author:: 774 <http://id774.net>
5
+ # Created:: Feb 28, 2012
6
+ # Updated:: Mar 1, 2012
7
+ # Copyright:: 774 Copyright (c) 2012
8
+ # License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
9
+
10
+ module Automatic::Plugin
11
+ class StoreTargetLink
12
+ require 'open-uri'
13
+
14
+ def initialize(config, pipeline=[])
15
+ @config = config
16
+ @pipeline = pipeline
17
+ end
18
+
19
+ def wget(url)
20
+ filename = url.split(/\//).last
21
+ open(url) { |source|
22
+ open(File.join(@config['path'], filename), "w+b") { |o|
23
+ o.print source.read
24
+ }
25
+ }
26
+ end
27
+
28
+ def run
29
+ @pipeline.each {|feeds|
30
+ unless feeds.nil?
31
+ feeds.items.each {|feed|
32
+ begin
33
+ unless feed.link.nil?
34
+ Automatic::Log.puts("info", "Get: #{feed.link}")
35
+ wget(feed.link)
36
+ end
37
+ rescue
38
+ Automatic::Log.puts("error", "Error found during file download.")
39
+ end
40
+ sleep @config['interval'].to_i
41
+ }
42
+ end
43
+ }
44
+ @pipeline
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Name:: Automatic::Plugin::SubscriptionFeed
4
+ # Author:: 774 <http://id774.net>
5
+ # Created:: Feb 22, 2012
6
+ # Updated:: Feb 24, 2012
7
+ # Copyright:: 774 Copyright (c) 2012
8
+ # License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
9
+
10
+ module Automatic::Plugin
11
+ class SubscriptionFeed
12
+ def initialize(config, pipeline=[])
13
+ @config = config
14
+ @pipeline = pipeline
15
+ end
16
+
17
+ def run
18
+ @config['feeds'].each {|feed|
19
+ begin
20
+ Automatic::Log.puts("info", "Parsing: #{feed}")
21
+ rss = Automatic::FeedParser.get_rss(feed)
22
+ @pipeline << rss
23
+ rescue
24
+ Automatic::Log.puts("error", "Fault in parsing: #{feed}")
25
+ end
26
+ }
27
+ @pipeline
28
+ end
29
+ end
30
+ end
data/script/bootstrap ADDED
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env ruby
2
+ #/ Usage: script/bootstrap [<options>]
3
+ #/ Bootstraps the gem environment.
4
+ #/
5
+ #/ Options are passed through to the bundle-install command. In most cases you
6
+ #/ won't need these. They're used primarily in production environments.
7
+ #/ --local use gems in vendor/cache instead of rubygems.org
8
+ #/ --without=<groups> do not install gems in the groups specified
9
+ #
10
+ # =============================================================================
11
+ # Uses bundler to install all gems specified in the Gemfile under vendor/gems,
12
+ # records the load path in config/loadpath, and generates bundler-free binstubs
13
+ # under bin.
14
+ #
15
+ # The basic idea is to use bundler to install necessary gems but not
16
+ # rely on it to manage the load path at runtime because it's slow. Requiring
17
+ # 'bundler/setup' takes ~500ms user CPU time in production and ~1500ms in
18
+ # development/test. This makes it unusable in scenarios that require a fast
19
+ # boot (e.g., script/gerve, proxymachine daemons, ernie/smoke, etc.). It's also
20
+ # a problem in development where it slows tools like rake command line
21
+ # completion to a crawl and adds at least a second to single-file test runs.
22
+ #
23
+ # There's very little reason to use bundler at runtime since everything
24
+ # is known at install time. We simply save off the result of the work done by
25
+ # bundle/setup and use it until bundle-install is run again.
26
+
27
+ # show usage message with --help
28
+ if ARGV.include?('--help')
29
+ system "grep '^#/' <'#{__FILE__}' |cut -c4-"
30
+ exit 2
31
+ end
32
+
33
+ # go into the project root because it makes everything easier
34
+ root = File.expand_path('../..', __FILE__)
35
+ Dir.chdir(root)
36
+
37
+ # point bundler to the right stuff
38
+ ENV['BUNDLE_GEMFILE'] = "#{root}/Gemfile"
39
+ ENV['BUNDLE_PATH'] = "#{root}/vendor/gems"
40
+
41
+ # bring in rubygems and make sure bundler is installed.
42
+ require 'rubygems'
43
+ begin
44
+ require 'bundler'
45
+ rescue LoadError => boom
46
+ warn "Bundler not found. Install it with `gem install bundler' and try again."
47
+ exit 0
48
+ end
49
+
50
+ # record the Gemfile checksum so we can tell if the Gemfile has changed
51
+ # since our loadpath was last generated. this is used in config/basic.rb
52
+ # to verify the environment is bootstrapped and up-to-date.
53
+ checksum = `cksum Gemfile`.to_i
54
+ installed = File.read('.bundle/checksum').to_i rescue nil
55
+
56
+ # run a quick check to see if everything's installed and up-to-date so we can
57
+ # skip the install and loadpath generation step if possible.
58
+ if checksum == installed && system('bundle check 1>/dev/null 2>&1')
59
+ puts "Gem environment up-to-date."
60
+ else
61
+ # run bundle-install to install any missing gems
62
+ argv = ['--no-color', 'install', '--path', 'vendor/gems'] + ARGV
63
+ system("bundle", *argv) || begin
64
+ warn "bundle executable not found. Ensure bundler is installed (`gem " +
65
+ "install bundler`) and that the gem bin path is in your PATH"
66
+ exit($?.exitstatus)
67
+ end
68
+
69
+ # load the Gemfile
70
+ bundle = Bundler.setup
71
+
72
+ # extract load paths for each gem and write to the config/loadpath file.
73
+ load_paths = []
74
+ bundle.gems.each do |gem|
75
+ next if gem.name == 'bundler'
76
+ gem.load_paths.each do |path|
77
+ if path[0, root.size] == root
78
+ path = path[(root.size + 1), path.size]
79
+ load_paths << path
80
+ else
81
+ warn "external load path directory detected: #{path}"
82
+ end
83
+ end
84
+ end
85
+
86
+ # move the loadpath and checksum files into place if everything was installed
87
+ # okay and the load path file was written successfully.
88
+ File.open('.bundle/loadpath+', 'wb') { |fd| fd.write(load_paths.join("\n")) }
89
+ File.rename('.bundle/loadpath+', '.bundle/loadpath')
90
+ File.open('.bundle/checksum', 'wb') { |fd| fd.puts(checksum) }
91
+
92
+ # write binstubs for all executables. we can't use bundler's --binstubs option
93
+ # because the generated executables require 'bundler/setup'. the binstubs
94
+ # generated here require only config/basic.rb, which sets up the loadpath
95
+ # manually using the .bundle/loadpath file.
96
+ Dir.mkdir "bin" unless File.directory?("bin")
97
+ template = DATA.read
98
+ lineno = File.read(__FILE__).count("\n") - template.count("\n")
99
+ bundle.gems.each do |spec|
100
+ spec.executables.each do |executable|
101
+ script = eval('%Q{' + template + '}', binding, __FILE__, lineno)
102
+ File.open("bin/#{executable}+", 'wb') { |fd| fd.write(script) }
103
+ File.chmod 0755, "bin/#{executable}+"
104
+ File.rename("bin/#{executable}+", "bin/#{executable}")
105
+ end
106
+ end
107
+ end
108
+
109
+ __END__
110
+ #!/usr/bin/env #{RbConfig::CONFIG['ruby_install_name']}
111
+ #
112
+ # This file was generated by script/bootstrap.
113
+ root = File.expand_path('../..', __FILE__)
114
+ #require "#\{root\}/config/load"
115
+ gem_path = "#{spec.full_gem_path.sub(root, "#\{root\}")}"
116
+ load File.join(gem_path, '#{spec.bindir}', '#{executable}')
117
+
@@ -0,0 +1,31 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Name:: Automatic::Plugin::CustomFeed::SVNFLog
3
+ # Author:: kzgs
4
+ # Created:: Feb 29, 2012
5
+ # Updated:: Mar 3, 2012
6
+ # Copyright:: kzgs Copyright (c) 2012
7
+ # License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
8
+
9
+ require File.expand_path(File.dirname(__FILE__) + '../../../spec_helper')
10
+
11
+ require 'custom_feed/svn_log'
12
+
13
+ describe Automatic::Plugin::CustomFeedSVNLog do
14
+ context "with feeds whose valid URL" do
15
+ subject {
16
+ Automatic::Plugin::CustomFeedSVNLog.new(
17
+ {
18
+ 'target' => 'http://redmine.rubyforge.org/svn',
19
+ 'fetch_items' => 2
20
+ })
21
+ }
22
+
23
+ its(:run) { should have(1).feed }
24
+
25
+ specify {
26
+ feed = subject.run[0]
27
+ feed.should have(2).items
28
+ }
29
+ end
30
+ end
31
+
@@ -0,0 +1,37 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '../../../spec_helper')
2
+
3
+ require 'filter/ignore'
4
+
5
+ describe Automatic::Plugin::FilterIgnore do
6
+ context "with exclusion by description" do
7
+ subject {
8
+ Automatic::Plugin::FilterIgnore.new({
9
+ 'description' => ["comment"],
10
+ },
11
+ AutomaticSpec.generate_pipeline {
12
+ feed { item "http://github.com" }
13
+ feed { item "http://google.com" }
14
+ })
15
+ }
16
+
17
+ describe "#run" do
18
+ its(:run) { should have(2).feeds }
19
+ end
20
+ end
21
+
22
+ context "with exclusion by link" do
23
+ subject {
24
+ Automatic::Plugin::FilterIgnore.new({
25
+ 'link' => ["github"],
26
+ },
27
+ AutomaticSpec.generate_pipeline {
28
+ feed { item "http://github.com" }
29
+ feed { item "http://google.com" }
30
+ })
31
+ }
32
+
33
+ describe "#run" do
34
+ its(:run) { should have(1).feeds }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,55 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Name:: Automatic::Plugin::CustomFeed::SVNFLog
3
+ # Author:: kzgs
4
+ # Created:: Mar 1, 2012
5
+ # Updated:: Mar 1, 2012
6
+ # Copyright:: kzgs Copyright (c) 2012
7
+ # License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
8
+
9
+ require File.expand_path(File.dirname(__FILE__) + '../../../spec_helper')
10
+
11
+ require 'filter/image'
12
+
13
+ describe Automatic::Plugin::FilterImage do
14
+ context "with description with image tag" do
15
+ subject {
16
+ Automatic::Plugin::FilterImage.new({},
17
+ AutomaticSpec.generate_pipeline {
18
+ feed {
19
+ item "http://tumblr.com", "",
20
+ "<img src=\"http://27.media.tumblr.com/tumblr_lzrubkfPlt1qb8vzto1_500.png\">"
21
+ }})}
22
+
23
+ describe "#run" do
24
+ its(:run) { should have(1).feeds }
25
+
26
+ specify {
27
+ subject.run
28
+ subject.instance_variable_get(:@pipeline)[0].items[0].link.
29
+ should == "http://27.media.tumblr.com/tumblr_lzrubkfPlt1qb8vzto1_500.png"
30
+ }
31
+ end
32
+ end
33
+ end
34
+
35
+ describe Automatic::Plugin::FilterImage do
36
+ context "with description with image tag" do
37
+ subject {
38
+ Automatic::Plugin::FilterImage.new({},
39
+ AutomaticSpec.generate_pipeline {
40
+ feed {
41
+ item "http://tumblr.com", "",
42
+ "<img src=\"http://24.media.tumblr.com/tumblr_m07wttnIdy1qzoj1jo1_400.jpg\">"
43
+ }})}
44
+
45
+ describe "#run" do
46
+ its(:run) { should have(1).feeds }
47
+
48
+ specify {
49
+ subject.run
50
+ subject.instance_variable_get(:@pipeline)[0].items[0].link.
51
+ should == "http://24.media.tumblr.com/tumblr_m07wttnIdy1qzoj1jo1_400.jpg"
52
+ }
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,102 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '../../../spec_helper')
2
+
3
+ require 'filter/tumblr_resize'
4
+
5
+ describe Automatic::Plugin::FilterTumblrResize do
6
+ context "with description with filename of tumblr should be renamed 500 to 1280" do
7
+ subject {
8
+ Automatic::Plugin::FilterTumblrResize.new({},
9
+ AutomaticSpec.generate_pipeline {
10
+ feed {
11
+ item "http://27.media.tumblr.com/tumblr_lzrubkfPlt1qb8vzto1_500.png"
12
+ item "http://24.media.tumblr.com/tumblr_m07wttnIdy1qzoj1jo1_400.jpg"
13
+ item "http://28.media.tumblr.com/tumblr_m07wtaxxSa1qzoj1jo1_450.jpg"
14
+ item "http://29.media.tumblr.com/tumblr_m07wrcDBBF1qzoj1jo1_500.jpg"
15
+ }})}
16
+
17
+ describe "#run" do
18
+ its(:run) { should have(1).feeds }
19
+
20
+ specify {
21
+ subject.run
22
+ subject.instance_variable_get(:@pipeline)[0].items[0].link.
23
+ should == "http://27.media.tumblr.com/tumblr_lzrubkfPlt1qb8vzto1_1280.png"
24
+ subject.instance_variable_get(:@pipeline)[0].items[1].link.
25
+ should == "http://24.media.tumblr.com/tumblr_m07wttnIdy1qzoj1jo1_1280.jpg"
26
+ subject.instance_variable_get(:@pipeline)[0].items[2].link.
27
+ should == "http://28.media.tumblr.com/tumblr_m07wtaxxSa1qzoj1jo1_450.jpg"
28
+ subject.instance_variable_get(:@pipeline)[0].items[3].link.
29
+ should == "http://29.media.tumblr.com/tumblr_m07wrcDBBF1qzoj1jo1_1280.jpg"
30
+ }
31
+ end
32
+ end
33
+ end
34
+
35
+ describe Automatic::Plugin::FilterTumblrResize do
36
+ context "with description with filename of tumblr should be renamed 400 to 1280" do
37
+ subject {
38
+ Automatic::Plugin::FilterTumblrResize.new({},
39
+ AutomaticSpec.generate_pipeline {
40
+ feed {
41
+ item "http://24.media.tumblr.com/tumblr_m07wttnIdy1qzoj1jo1_400.jpg"
42
+ }})}
43
+
44
+ describe "#run" do
45
+ its(:run) { should have(1).feeds }
46
+
47
+ specify {
48
+ subject.run
49
+ subject.instance_variable_get(:@pipeline)[0].items[0].link.
50
+ should == "http://24.media.tumblr.com/tumblr_m07wttnIdy1qzoj1jo1_1280.jpg"
51
+ }
52
+ end
53
+ end
54
+ end
55
+
56
+ describe Automatic::Plugin::FilterTumblrResize do
57
+ context "with description with filename of tumblr should be renamed 250 to 1280" do
58
+ subject {
59
+ Automatic::Plugin::FilterTumblrResize.new({},
60
+ AutomaticSpec.generate_pipeline {
61
+ feed {
62
+ item "http://28.media.tumblr.com/tumblr_m07wtaxxSa1qzoj1jo1_250.jpg"
63
+ }})}
64
+
65
+ describe "#run" do
66
+ its(:run) { should have(1).feeds }
67
+
68
+ specify {
69
+ subject.run
70
+ subject.instance_variable_get(:@pipeline)[0].items[0].link.
71
+ should == "http://28.media.tumblr.com/tumblr_m07wtaxxSa1qzoj1jo1_1280.jpg"
72
+ }
73
+ end
74
+ end
75
+ end
76
+
77
+ describe Automatic::Plugin::FilterTumblrResize do
78
+ context "with description with filename of tumblr should be renamed 100 to 1280" do
79
+ subject {
80
+ Automatic::Plugin::FilterTumblrResize.new({},
81
+ AutomaticSpec.generate_pipeline {
82
+ feed {
83
+ item "http://24.media.tumblr.com/tumblr_m07wt8BRWc1qzoj1jo1_100.jpg"
84
+ item "http://25.media.tumblr.com/tumblr_m07wr8RnJ91qzoj1jo1_750.jpg"
85
+ item "http://29.media.tumblr.com/tumblr_m07wrcDBBF1qzoj1jo1_75sq.jpg"
86
+ }})}
87
+
88
+ describe "#run" do
89
+ its(:run) { should have(1).feeds }
90
+
91
+ specify {
92
+ subject.run
93
+ subject.instance_variable_get(:@pipeline)[0].items[0].link.
94
+ should == "http://24.media.tumblr.com/tumblr_m07wt8BRWc1qzoj1jo1_1280.jpg"
95
+ subject.instance_variable_get(:@pipeline)[0].items[1].link.
96
+ should == "http://25.media.tumblr.com/tumblr_m07wr8RnJ91qzoj1jo1_750.jpg"
97
+ subject.instance_variable_get(:@pipeline)[0].items[2].link.
98
+ should == "http://29.media.tumblr.com/tumblr_m07wrcDBBF1qzoj1jo1_1280.jpg"
99
+ }
100
+ end
101
+ end
102
+ end