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
data/lib/automatic.rb ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+ # Name:: Automatic::Ruby
3
+ # Author:: 774 <http://id774.net>
4
+ # Version:: 12.02.1-devel
5
+ # Created:: Feb 18, 2012
6
+ # Updated:: Mar 2, 2012
7
+ # Copyright:: 774 Copyright (c) 2012
8
+ # License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
9
+
10
+ module Automatic
11
+ require 'automatic/environment'
12
+ require 'automatic/recipe'
13
+ require 'automatic/pipeline'
14
+ require 'automatic/log'
15
+ require 'automatic/feed_parser'
16
+
17
+ VERSION = "12.02.1-devel"
18
+
19
+ def self.root_dir
20
+ @root_dir
21
+ end
22
+
23
+ def self.plugins_dir
24
+ @root_dir + "/plugins/"
25
+ end
26
+
27
+ def self.config_dir
28
+ @root_dir + "/config/"
29
+ end
30
+
31
+ def self.run(root_dir)
32
+ @root_dir = root_dir
33
+ recipe_path = ""
34
+ require 'optparse'
35
+ parser = OptionParser.new { |parser|
36
+ parser.banner = "Usage: automatic [options] arg"
37
+ parser.version = VERSION
38
+ parser.separator "options:"
39
+ parser.on('-c', '--config FILE', String,
40
+ "recipe YAML file"){|c| recipe_path = c}
41
+ parser.on('-h', '--help', "show this message") {
42
+ puts parser
43
+ exit
44
+ }
45
+ }
46
+
47
+ begin
48
+ parser.parse!
49
+ print "Loading #{recipe_path}\n" unless recipe_path == ""
50
+ rescue OptionParser::ParseError => err
51
+ $stderr.puts err.message
52
+ $stderr.puts parser.help
53
+ exit 1
54
+ end
55
+
56
+ # recipe treat as an object.
57
+ recipe = Automatic::Recipe.new(recipe_path)
58
+ Automatic::Pipeline.run(recipe)
59
+ end
60
+ end
@@ -0,0 +1,8 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'rubygems'
3
+
4
+ # Set up gems listed in the Gemfile.
5
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
6
+
7
+ require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
8
+ Bundler.require :default if defined?(Bundler)
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Name:: Automatic::FeedParser
4
+ # Author:: 774 <http://id774.net>
5
+ # Created:: Feb 19, 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
11
+ module FeedParser
12
+ require 'rss'
13
+ require 'uri'
14
+
15
+ def self.get_rss(url)
16
+ begin
17
+ unless url.nil?
18
+ feed = URI.parse(url).normalize
19
+ open(feed) { |http|
20
+ response = http.read
21
+ RSS::Parser.parse(response, false)
22
+ }
23
+ end
24
+ rescue => e
25
+ raise e
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ if __FILE__ == $0
32
+ url = ARGV.shift || abort("Usage: feed_parser.rb <url>")
33
+ rss_results = Automatic::FeedParser.get_rss(url)
34
+ require 'pp'
35
+ pp links
36
+ end
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Name:: Automatic::Log
4
+ # Author:: 774 <http://id774.net>
5
+ # Created:: Feb 20, 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
11
+ module Log
12
+ def self.puts(level, message)
13
+ t = Time.now.strftime("%Y/%m/%d %X")
14
+ print "#{t} [#{level}] #{message}\n"
15
+ end
16
+ end
17
+ end
18
+
19
+ if __FILE__ == $0
20
+ level = ARGV.shift || abort("Usage: log.rb <level> <message>")
21
+ message = ARGV.shift
22
+ Automatic::Log.puts(level, message)
23
+ end
24
+
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Name:: Automatic::Pipeline
4
+ # Author:: 774 <http://id774.net>
5
+ # Created:: Feb 22, 2012
6
+ # Updated:: Mar 3, 2012
7
+ # Copyright:: 774 Copyright (c) 2012
8
+ # License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
9
+
10
+ require 'active_support/core_ext'
11
+
12
+ module Automatic
13
+ module Plugin end
14
+
15
+ module Pipeline
16
+ def self.load_plugin(module_name)
17
+ Dir.foreach(Automatic.plugins_dir) do |subdir|
18
+ if /^#{subdir}_(.*)$/ =~ module_name.underscore
19
+ path = Automatic.plugins_dir + subdir + "/#{$1}.rb"
20
+ Automatic::Plugin.autoload module_name.to_sym, path.to_s
21
+ end
22
+ end
23
+ end
24
+
25
+ def self.run(recipe)
26
+ raise "NoRecipeError" if recipe.nil?
27
+ pipeline = []
28
+ recipe.each_plugin { |plugin|
29
+ load_plugin(plugin.module)
30
+ klass = Automatic::Plugin.const_get(plugin.module)
31
+ pipeline = klass.new(plugin.config, pipeline).run
32
+ }
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,31 @@
1
+ # Name:: Automatic::Recipe
2
+ # Author:: 774 <http://id774.net>
3
+ # Version:: 12.02-devel
4
+ # Created:: Feb 18, 2012
5
+ # Updated:: Feb 24, 2012
6
+ # Copyright:: 774 Copyright (c) 2012
7
+ # License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
8
+
9
+ require 'hashie'
10
+ require 'yaml'
11
+
12
+ module Automatic
13
+ class Recipe
14
+ attr_reader :procedure
15
+
16
+ def initialize(path)
17
+ path = Automatic.config_dir + 'default.yml' if path.to_s.empty?
18
+ load_recipe(path)
19
+ end
20
+
21
+ def load_recipe(path)
22
+ @procedure = Hashie::Mash.new(YAML.load(File.read(path)))
23
+ end
24
+
25
+ def each_plugin
26
+ @procedure.plugins.each { |plugin|
27
+ yield plugin
28
+ }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Name:: Automatic::Config::Validator
4
+ # Author:: aerith <http://aerith.sc/>
5
+ # Created:: Feb 23, 2012
6
+ # Updated:: Feb 23, 2012
7
+ # Copyright:: Copyright (c) 2012
8
+ # License:: Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.0.
9
+
10
+ class Automatic
11
+ module Config
12
+ class Validator
13
+ attr_accessor :config, :result
14
+
15
+ class InvalidException < Exception; end;
16
+
17
+ def initialize(config = nil)
18
+ @config = config || {}
19
+ @result = ValidateResult.new
20
+ end
21
+
22
+ def validate(klass, config = nil)
23
+ return true if config.nil?
24
+ return true if not config.is_a?(Hash)
25
+ return true if not klass.respond_to?(:rules)
26
+
27
+ rules = klass.rules
28
+ return true if not rules.is_a?(Hash)
29
+
30
+ process(rules, config).result.valid?
31
+ end
32
+
33
+ private
34
+ def process(rules, config)
35
+ errors = {}
36
+
37
+ rules.each do |key, rule|
38
+ name = key.to_sym
39
+
40
+ rule.each do |method, args|
41
+ unless ValidateRule.send(method, *[config, name, args])
42
+ errors[name] = {} if errors[name].nil? or errors.size < 1
43
+ errors[name][method] = true
44
+ if @config["stop_on_invalid"] and errors[name][method]
45
+ raise InvalidException
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ @result.errors = errors
52
+
53
+ self
54
+ end
55
+
56
+ class ValidateResult
57
+ attr_accessor :errors
58
+
59
+ def error(name)
60
+ errors.exists?(name)
61
+ end
62
+
63
+ def valid?
64
+ errors.empty?
65
+ end
66
+ end
67
+
68
+ module ValidateRule
69
+ def self.required(config, name, *args)
70
+ return true unless args.size < 1 or args.first
71
+ return true if not config[name.to_s].nil? and not config[name.to_s].empty?
72
+ false
73
+ end
74
+
75
+ def self.default(config, name, *args)
76
+ config[name.to_s] ||= args.first
77
+ true
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+
@@ -0,0 +1,56 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Name:: Automatic::Plugin::CustomFeed::SVNLog
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 'rss/maker'
10
+
11
+ module Automatic::Plugin
12
+ class CustomFeedSVNLog
13
+ require 'xmlsimple'
14
+
15
+ def initialize(config, pipeline=[])
16
+ @config = config
17
+ @pipeline = pipeline
18
+ end
19
+
20
+ def run
21
+ revisions = XmlSimple.xml_in(`svn log #{svn_log_argument}`)["logentry"]
22
+ @pipeline << RSS::Maker.make("1.0") { |maker|
23
+ maker.channel.title = @config["title"] || ""
24
+ maker.channel.about = ""
25
+ maker.channel.description = ""
26
+ maker.channel.link = base_url
27
+ revisions.each { |rev|
28
+ item = maker.items.new_item
29
+ item.title = "#{rev["msg"]} by #{rev["author"]}"
30
+ item.link = base_url+"/!svn/bc/#{rev["revision"]}"
31
+ item.date = Time.parse(rev["date"][0])
32
+ }
33
+ }
34
+ return @pipeline
35
+ end
36
+
37
+ private
38
+
39
+ def base_url
40
+ return @config['target'].gsub(/\/$/, "")
41
+ end
42
+
43
+ def svn_log_argument
44
+ return [
45
+ base_url,
46
+ "--xml",
47
+ "--limit=#{limit}"
48
+ ].join(" ")
49
+ end
50
+
51
+ def limit
52
+ return @config['fetch_items'] || 30
53
+ end
54
+ end
55
+ end
56
+
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Name:: Automatic::Plugin::Filter::Ignore
4
+ # Author:: 774 <http://id774.net>
5
+ # Created:: Feb 22, 2012
6
+ # Updated:: Mar 4, 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 FilterIgnore
12
+ def initialize(config, pipeline=[])
13
+ @config = config
14
+ @pipeline = pipeline
15
+ end
16
+
17
+ def exclude(items)
18
+ detection = false
19
+ unless @config['title'].nil?
20
+ @config['title'].each {|e|
21
+ if items.title.include?(e.chomp)
22
+ detection = true
23
+ Automatic::Log.puts("info", "Excluded by title: #{items.link}")
24
+ end
25
+ }
26
+ end
27
+ unless @config['link'].nil?
28
+ @config['link'].each {|e|
29
+ if items.link.include?(e.chomp)
30
+ detection = true
31
+ Automatic::Log.puts("info", "Excluded by link: #{items.link}")
32
+ end
33
+ }
34
+ end
35
+ unless @config['description'].nil?
36
+ @config['description'].each {|e|
37
+ if items.description.include?(e.chomp)
38
+ detection = true
39
+ Automatic::Log.puts("info", "Excluded by description: #{items.link}")
40
+ end
41
+ }
42
+ end
43
+ detection
44
+ end
45
+
46
+ def run
47
+ return_feeds = []
48
+ @pipeline.each {|feeds|
49
+ ignore = false
50
+ unless feeds.nil?
51
+ feeds.items.each {|items|
52
+ ignore = true if exclude(items)
53
+ }
54
+ end
55
+ return_feeds << feeds unless ignore
56
+ }
57
+ return_feeds
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Name:: Automatic::Plugin::Filter::Image
4
+ # Author:: 774 <http://id774.net>
5
+ # Created:: Feb 28, 2012
6
+ # Updated:: Feb 28, 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 FilterImage
12
+ require 'net/http'
13
+ require 'kconv'
14
+
15
+ def initialize(config, pipeline=[])
16
+ @config = config
17
+ @pipeline = pipeline
18
+ end
19
+
20
+ def parse_array(string)
21
+ array = Array.new
22
+ string.scan(/<img src="(.*?)"/) { |matched|
23
+ array = array | matched
24
+ }
25
+ return array
26
+ end
27
+
28
+ def run
29
+ return_feeds = []
30
+ @pipeline.each {|feeds|
31
+ img_url = ""
32
+ unless feeds.nil?
33
+ feeds.items.each {|feed|
34
+ arr = parse_array(feed.description)
35
+ if arr.length > 0
36
+ feed.link = arr[0]
37
+ else
38
+ feed.link = nil
39
+ end
40
+ }
41
+ end
42
+ return_feeds << feeds
43
+ }
44
+ return_feeds
45
+ end
46
+ end
47
+ end