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
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