automatic 12.2.1

Sign up to get free protection for your applications and to get access to all the features.
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