dca 0.1.0
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.
- data/.document +5 -0
- data/.rspec +2 -0
- data/Gemfile +48 -0
- data/Gemfile.lock +126 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +48 -0
- data/VERSION +1 -0
- data/bin/dca +5 -0
- data/dca.gemspec +160 -0
- data/lib/dca.rb +64 -0
- data/lib/dca/cli.rb +32 -0
- data/lib/dca/commands/area.rb +133 -0
- data/lib/dca/commands/templates/area/analyzer.rb.erb +34 -0
- data/lib/dca/commands/templates/area/area.rb.erb +2 -0
- data/lib/dca/commands/templates/area/models.rb.erb +2 -0
- data/lib/dca/commands/templates/area/page.rb.erb +17 -0
- data/lib/dca/commands/templates/area/position.rb.erb +8 -0
- data/lib/dca/commands/templates/config.yml.erb +38 -0
- data/lib/dca/commands/templates/spec/analyzer_spec.rb.erb +15 -0
- data/lib/dca/commands/templates/spec/spec_helper.rb.erb +2 -0
- data/lib/dca/config.rb +20 -0
- data/lib/dca/helpers.rb +2 -0
- data/lib/dca/helpers/logger.rb +50 -0
- data/lib/dca/jobs.rb +3 -0
- data/lib/dca/jobs/analyzer_job.rb +119 -0
- data/lib/dca/jobs/job.rb +62 -0
- data/lib/dca/models.rb +5 -0
- data/lib/dca/models/base_model.rb +73 -0
- data/lib/dca/models/binder.rb +68 -0
- data/lib/dca/models/binder_helper.rb +48 -0
- data/lib/dca/models/nokogiri_binder.rb +43 -0
- data/lib/dca/models/position.rb +15 -0
- data/lib/dca/net.rb +1 -0
- data/lib/dca/net/browser_helper.rb +20 -0
- data/lib/dca/notifier.rb +2 -0
- data/lib/dca/notifier/notifier.rb +11 -0
- data/lib/dca/notifier/redis/models/analyze_notify.rb +12 -0
- data/lib/dca/notifier/redis/models/failure_notify.rb +8 -0
- data/lib/dca/notifier/redis/models/fetch_notify.rb +15 -0
- data/lib/dca/notifier/redis/models/session.rb +52 -0
- data/lib/dca/notifier/redis/notifier.rb +25 -0
- data/lib/dca/notifier/redis_notifier.rb +9 -0
- data/lib/dca/storage.rb +3 -0
- data/lib/dca/storage/elasticsearch_storage.rb +80 -0
- data/lib/dca/storage/mongo_storage.rb +51 -0
- data/lib/dca/storage/storage.rb +55 -0
- data/spec/analyzer_spec.rb +64 -0
- data/spec/area_task_spec.rb +45 -0
- data/spec/base_model_spec.rb +34 -0
- data/spec/binder_spec.rb +69 -0
- data/spec/config.yml +18 -0
- data/spec/elasticsearch_storage_spec.rb +28 -0
- data/spec/fixtures/page.html +12 -0
- data/spec/fixtures/positions.yml +13 -0
- data/spec/fixtures/positions_with_error.yml +14 -0
- data/spec/fixtures/states.yml +3 -0
- data/spec/job_spec.rb +31 -0
- data/spec/mock/analyzer_job.rb +30 -0
- data/spec/mock/file_storage.rb +28 -0
- data/spec/mock/notify_object.rb +13 -0
- data/spec/mock/page.rb +13 -0
- data/spec/mock/position.rb +40 -0
- data/spec/mock/web_notifier.rb +30 -0
- data/spec/mongo_storage_spec.rb +20 -0
- data/spec/redis_notifier_spec.rb +98 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/support/storage_examples.rb +103 -0
- metadata +408 -0
    
        data/lib/dca.rb
    ADDED
    
    | @@ -0,0 +1,64 @@ | |
| 1 | 
            +
            require 'yaml'
         | 
| 2 | 
            +
            require 'active_model'
         | 
| 3 | 
            +
            require 'rake'
         | 
| 4 | 
            +
            require 'resque'
         | 
| 5 | 
            +
            require 'resque/tasks'
         | 
| 6 | 
            +
            require 'ohm'
         | 
| 7 | 
            +
            require 'uuid'
         | 
| 8 | 
            +
            require 'yajl/json_gem'
         | 
| 9 | 
            +
            require 'tire'
         | 
| 10 | 
            +
            require 'logger'
         | 
| 11 | 
            +
            require 'thor'
         | 
| 12 | 
            +
            require 'thor/group'
         | 
| 13 | 
            +
            require 'nokogiri'
         | 
| 14 | 
            +
            require 'mongo'
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            module DCA
         | 
| 17 | 
            +
              class ApplicationError < Exception; end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              def self.root
         | 
| 20 | 
            +
                Dir.pwd
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              def self.used?
         | 
| 24 | 
            +
                File.basename(self.root).downcase != 'dca'
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              def self.project_name
         | 
| 28 | 
            +
                return @project if @project.present?
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                gemspec = Dir[File.join self.root, '*.gemspec'].first
         | 
| 31 | 
            +
                raise 'Generate gemspec file' if gemspec.blank?
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                gem = Gem::Specification.load gemspec
         | 
| 34 | 
            +
                raise 'Set gem name in gemspec'  if gem.name.blank?
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                @project = gem.name.camelize
         | 
| 37 | 
            +
                if @project.safe_constantize.nil?
         | 
| 38 | 
            +
                  @project = (Object.constants.detect { |const|
         | 
| 39 | 
            +
                    const.to_s.downcase == @project.downcase}).to_s
         | 
| 40 | 
            +
                  raise "Unknown project name" if @project.nil?
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                @project
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              def self.project_path
         | 
| 47 | 
            +
                @project_path ||= File.join(DCA.root, 'lib', File.basename(DCA.root))
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              def self.project_file
         | 
| 51 | 
            +
                @project_path ||= project_path + '.rb'
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
            end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            require File.expand_path('../dca/config', __FILE__)
         | 
| 56 | 
            +
            require File.expand_path('../dca/helpers', __FILE__)
         | 
| 57 | 
            +
            require File.expand_path('../dca/storage', __FILE__)
         | 
| 58 | 
            +
            require File.expand_path('../dca/jobs', __FILE__)
         | 
| 59 | 
            +
            require File.expand_path('../dca/net', __FILE__)
         | 
| 60 | 
            +
            require File.expand_path('../dca/notifier', __FILE__)
         | 
| 61 | 
            +
            require File.expand_path('../dca/models', __FILE__)
         | 
| 62 | 
            +
            require File.expand_path('../dca/cli', __FILE__)
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            require DCA.project_path if DCA.used? && File.exist?(DCA.project_file)
         | 
    
        data/lib/dca/cli.rb
    ADDED
    
    | @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            require File.expand_path('../commands/area', __FILE__)
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module DCA
         | 
| 4 | 
            +
              class CLI < Thor
         | 
| 5 | 
            +
                include Thor::Actions
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def self.source_root
         | 
| 8 | 
            +
                  File.expand_path('../commands/templates', __FILE__)
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                desc 'config', 'Create default config file'
         | 
| 12 | 
            +
                def config
         | 
| 13 | 
            +
                  empty_directory 'config'
         | 
| 14 | 
            +
                  template 'config.yml.erb', 'config/config.yml'
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                desc 'area SUBCOMMAND ...ARGS', 'Manage project areas'
         | 
| 18 | 
            +
                subcommand 'area', Commands::Area
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                desc 'install', 'Install dca project'
         | 
| 21 | 
            +
                def install
         | 
| 22 | 
            +
                  project = "#{DCA.project_name}::Project".constantize
         | 
| 23 | 
            +
                  project.install
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                desc 'uninstall', 'Uninstall dca  project'
         | 
| 27 | 
            +
                def uninstall
         | 
| 28 | 
            +
                  project = "#{DCA.project_name}::Project".constantize
         | 
| 29 | 
            +
                  project.remove
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
            end
         | 
| @@ -0,0 +1,133 @@ | |
| 1 | 
            +
            module DCA
         | 
| 2 | 
            +
              module Commands
         | 
| 3 | 
            +
                class Area < Thor
         | 
| 4 | 
            +
                  include Thor::Actions
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def self.source_root
         | 
| 7 | 
            +
                    File.expand_path('../templates', __FILE__)
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  desc 'create NAME TITLE DESCRIPTION URL', 'Generate and add new area to project'
         | 
| 11 | 
            +
                  long_desc <<-LONGDESC
         | 
| 12 | 
            +
                    Generate and add a new area named <name> with to project. Parameters <name>, <title>, <description> and <url>
         | 
| 13 | 
            +
                    are required
         | 
| 14 | 
            +
                  LONGDESC
         | 
| 15 | 
            +
                  def create name, title, description, url
         | 
| 16 | 
            +
                    @name = name
         | 
| 17 | 
            +
                    @class_name = name.camelize
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    template 'area/analyzer.rb.erb', "lib/areas/#{name.downcase}/analyzer.rb"
         | 
| 20 | 
            +
                    template 'area/position.rb.erb', "lib/areas/#{name.downcase}/models/position.rb"
         | 
| 21 | 
            +
                    template 'area/page.rb.erb', "lib/areas/#{name.downcase}/models/page.rb"
         | 
| 22 | 
            +
                    template 'area/models.rb.erb', "lib/areas/#{name.downcase}/models.rb"
         | 
| 23 | 
            +
                    template 'area/area.rb.erb', "lib/areas/#{name.downcase}.rb"
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    template 'spec/analyzer_spec.rb.erb', "spec/areas/#{name.downcase}/analyzer_spec.rb"
         | 
| 26 | 
            +
                    template 'spec/spec_helper.rb.erb', "spec/areas/#{name.downcase}/spec_helper.rb"
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    empty_directory 'config'
         | 
| 29 | 
            +
                    area_file = 'config/areas.yml'
         | 
| 30 | 
            +
                    areas = {}
         | 
| 31 | 
            +
                    areas = YAML.load_file(area_file) if File.exist? area_file
         | 
| 32 | 
            +
                    area_hash = {'title' => title, 'description' => description, 'url' => url}
         | 
| 33 | 
            +
                    if areas.has_key? name
         | 
| 34 | 
            +
                      if areas[name] == area_hash
         | 
| 35 | 
            +
                        shell.say_status :identical, area_file, :blue
         | 
| 36 | 
            +
                      else
         | 
| 37 | 
            +
                        areas[name] = area_hash
         | 
| 38 | 
            +
                        shell.say_status :conflict, area_file, :red
         | 
| 39 | 
            +
                        if shell.file_collision(area_file) { areas.to_yaml }
         | 
| 40 | 
            +
                          open(area_file, 'w:utf-8') { |file| file.write areas.to_yaml }
         | 
| 41 | 
            +
                          shell.say_status :force, area_file, :yellow
         | 
| 42 | 
            +
                        end
         | 
| 43 | 
            +
                      end
         | 
| 44 | 
            +
                    else
         | 
| 45 | 
            +
                      status = areas.empty? ? :create : :update
         | 
| 46 | 
            +
                      areas[name] = area_hash
         | 
| 47 | 
            +
                      open(area_file, 'w:utf-8') { |file| file.write areas.to_yaml }
         | 
| 48 | 
            +
                      shell.say_status status, area_file, :green
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  desc 'start NAME', 'Start area to analyze'
         | 
| 53 | 
            +
                  def start name
         | 
| 54 | 
            +
                    shell.say "Starting analyze area #{name}"
         | 
| 55 | 
            +
                    config = area_config name.to_sym
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    job_ops = {}
         | 
| 58 | 
            +
                    job_ops[:distributed] = true if config[:distributed]
         | 
| 59 | 
            +
                    job = "#{DCA.project_name}::Areas::#{name}::AnalyzerJob".constantize
         | 
| 60 | 
            +
                    job.create job_ops
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    background = config[:background].nil? ? true : config[:background]
         | 
| 63 | 
            +
                    run_worker name, config[:workers] || 1, background
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  desc 'stop NAME', 'Stop area to analyze'
         | 
| 67 | 
            +
                  method_option :force, :type => :boolean, :aliases => '-f', :desc => 'force stop area analyzing process'
         | 
| 68 | 
            +
                  def stop name
         | 
| 69 | 
            +
                    shell.say "Stopping analyze area #{name}"
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                    pids = workers_pids name
         | 
| 72 | 
            +
                    unless pids.empty?
         | 
| 73 | 
            +
                      system("kill -s #{options[:force] ? 'TERM' : 'QUIT'} #{pids.join(' ')}")
         | 
| 74 | 
            +
                    end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                    wait_worker name
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                    Resque.remove_queue name
         | 
| 79 | 
            +
                  end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  private
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                  def area_config area_name
         | 
| 84 | 
            +
                    config = {}
         | 
| 85 | 
            +
                    config = APP_CONFIG[:areas][area_name] if APP_CONFIG[:areas]
         | 
| 86 | 
            +
                    config
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  def run_worker(queue, count = 1, background = true)
         | 
| 90 | 
            +
                    puts "Starting #{count} worker(s) with QUEUE: #{queue}"
         | 
| 91 | 
            +
                    unless background
         | 
| 92 | 
            +
                      ENV['QUEUE'] = queue
         | 
| 93 | 
            +
                      ENV['VERBOSE'] = '1'
         | 
| 94 | 
            +
                      Rake::Task['resque:work'].invoke
         | 
| 95 | 
            +
                    else
         | 
| 96 | 
            +
                      log_dir = File.join DCA.root, 'log'
         | 
| 97 | 
            +
                      Dir.mkdir log_dir unless Dir.exist? log_dir
         | 
| 98 | 
            +
                      ops = { :pgroup => true }
         | 
| 99 | 
            +
                      if APP_CONFIG[:logger]
         | 
| 100 | 
            +
                        debug_file = [File.join(DCA.root, "log/#{queue.underscore}.debug"), 'a']
         | 
| 101 | 
            +
                        ops[:err] = debug_file
         | 
| 102 | 
            +
                        ops[:out] = debug_file
         | 
| 103 | 
            +
                      end
         | 
| 104 | 
            +
                      env_vars = {'QUEUE' => queue}
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                      count.times {
         | 
| 107 | 
            +
                        ## Using Kernel.spawn and Process.detach because regular system() call would
         | 
| 108 | 
            +
                        ## cause the processes to quit when capistrano finishes
         | 
| 109 | 
            +
                        pid = spawn(env_vars, "rake resque:work", ops)
         | 
| 110 | 
            +
                        Process.detach(pid)
         | 
| 111 | 
            +
                      }
         | 
| 112 | 
            +
                    end
         | 
| 113 | 
            +
                  end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                  def workers_pids name
         | 
| 116 | 
            +
                    pids = Array.new
         | 
| 117 | 
            +
                    Resque.workers.each do |worker|
         | 
| 118 | 
            +
                      host, pid, queues = worker.id.split(':')
         | 
| 119 | 
            +
                      next unless host == worker.hostname
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                      queues = queues.split(',')
         | 
| 122 | 
            +
                      next unless queues.include? name
         | 
| 123 | 
            +
                      pids.concat(worker.worker_pids)
         | 
| 124 | 
            +
                    end
         | 
| 125 | 
            +
                    pids.uniq
         | 
| 126 | 
            +
                  end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                  def wait_worker name
         | 
| 129 | 
            +
                    sleep 1 while workers_pids(name).count > 0
         | 
| 130 | 
            +
                  end
         | 
| 131 | 
            +
                end
         | 
| 132 | 
            +
              end
         | 
| 133 | 
            +
            end
         | 
| @@ -0,0 +1,34 @@ | |
| 1 | 
            +
            module <%= DCA.project_name %>
         | 
| 2 | 
            +
              module Areas
         | 
| 3 | 
            +
                module <%= @class_name %>
         | 
| 4 | 
            +
                  # Base analyze jobs class for <%=@class_name%> area.
         | 
| 5 | 
            +
                  class AnalyzerJob < DCA::Jobs::AnalyzerJob
         | 
| 6 | 
            +
                    def change
         | 
| 7 | 
            +
                      false
         | 
| 8 | 
            +
                    end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    def positions &block
         | 
| 11 | 
            +
                      page = Page.find options[:category]
         | 
| 12 | 
            +
                      until page.nil? do
         | 
| 13 | 
            +
                        logger.debug "Page number #{page.number}"
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                        page.positions.each do |position|
         | 
| 16 | 
            +
                          begin
         | 
| 17 | 
            +
                            block.call position
         | 
| 18 | 
            +
                          rescue Exception => e
         | 
| 19 | 
            +
                            logger.exception e
         | 
| 20 | 
            +
                          end
         | 
| 21 | 
            +
                        end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                        break if shutdown?
         | 
| 24 | 
            +
                        break unless page.next
         | 
| 25 | 
            +
                      end
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    def fetch position
         | 
| 29 | 
            +
                      position
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
            end
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            module <%= DCA.project_name %>
         | 
| 2 | 
            +
              module Areas
         | 
| 3 | 
            +
                module <%= @class_name %>
         | 
| 4 | 
            +
                  class Page < DCA::Models::BaseModel
         | 
| 5 | 
            +
                    attr_reader :url, :number
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                    # need to ser right selector
         | 
| 8 | 
            +
                    # has_many :positions, :selector => 'div'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    def next
         | 
| 11 | 
            +
                      @number += 1
         | 
| 12 | 
            +
                      # get next page if it posible or return nil
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         | 
| @@ -0,0 +1,38 @@ | |
| 1 | 
            +
            production:
         | 
| 2 | 
            +
              dca_db:
         | 
| 3 | 
            +
                driver: ElasticSearch
         | 
| 4 | 
            +
                host: localhost
         | 
| 5 | 
            +
                port: 27017
         | 
| 6 | 
            +
              redis:
         | 
| 7 | 
            +
                host: localhost
         | 
| 8 | 
            +
                port: 6379
         | 
| 9 | 
            +
              notifier:
         | 
| 10 | 
            +
                driver: Redis
         | 
| 11 | 
            +
                host: localhost
         | 
| 12 | 
            +
                port: 6379
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            development:
         | 
| 15 | 
            +
              dca_db:
         | 
| 16 | 
            +
                driver: ElasticSearch
         | 
| 17 | 
            +
                host: localhost
         | 
| 18 | 
            +
                port: 27017
         | 
| 19 | 
            +
              redis:
         | 
| 20 | 
            +
                host: localhost
         | 
| 21 | 
            +
                port: 6379
         | 
| 22 | 
            +
              notifier:
         | 
| 23 | 
            +
                driver: Redis
         | 
| 24 | 
            +
                host: localhost
         | 
| 25 | 
            +
                port: 6379
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            test:
         | 
| 28 | 
            +
              dca_db:
         | 
| 29 | 
            +
                driver: ElasticSearch
         | 
| 30 | 
            +
                host: localhost
         | 
| 31 | 
            +
                port: 27017
         | 
| 32 | 
            +
              redis:
         | 
| 33 | 
            +
                host: localhost
         | 
| 34 | 
            +
                port: 6379
         | 
| 35 | 
            +
              notifier:
         | 
| 36 | 
            +
                driver: Redis
         | 
| 37 | 
            +
                host: localhost
         | 
| 38 | 
            +
                port: 6379
         | 
| @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            require File.expand_path('../spec_helper', __FILE__)
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            include <%= DCA.project_name%>::Areas::<%= @class_name %>
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            describe '<%= @class_name%> analyzer' do
         | 
| 6 | 
            +
              before :all do
         | 
| 7 | 
            +
                <%= DCA.project_name %>::Project.remove
         | 
| 8 | 
            +
                <%= DCA.project_name %>::Project.install
         | 
| 9 | 
            +
                DCA::Notifier.create APP_CONFIG[:notifier]
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              after :all do
         | 
| 13 | 
            +
                <%= DCA.project_name %>::Project.remove
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
            end
         | 
    
        data/lib/dca/config.rb
    ADDED
    
    | @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            SYS_ENV = 'development' unless defined? SYS_ENV
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            unless defined? APP_CONFIG
         | 
| 4 | 
            +
              if File.exist? './config/config.yml'
         | 
| 5 | 
            +
                APP_CONFIG = YAML.load_file('./config/config.yml')[SYS_ENV].deep_symbolize_keys
         | 
| 6 | 
            +
              else
         | 
| 7 | 
            +
                APP_CONFIG = {}
         | 
| 8 | 
            +
                puts 'WARNING! Missing config file. Use rake system:config to create default config file.' if DCA.used?
         | 
| 9 | 
            +
              end
         | 
| 10 | 
            +
            end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            unless defined? AREAS_CONFIG
         | 
| 13 | 
            +
              if APP_CONFIG[:areas]
         | 
| 14 | 
            +
                AREAS_CONFIG = APP_CONFIG[:areas]
         | 
| 15 | 
            +
              else
         | 
| 16 | 
            +
                AREAS_CONFIG = {}
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
             | 
    
        data/lib/dca/helpers.rb
    ADDED
    
    
| @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            module DCA
         | 
| 2 | 
            +
              module Helpers
         | 
| 3 | 
            +
                class VerboseLogger < ::Logger
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                  def initialize(logdev, shift_age = 0, shift_size = 1048576, verbose = false)
         | 
| 6 | 
            +
                    super logdev, shift_age, shift_size
         | 
| 7 | 
            +
                    @verbose_logdev = LogDevice.new(STDOUT, :shift_age => shift_age, :shift_size => shift_size) if verbose
         | 
| 8 | 
            +
                    @default_logdev = @logdev
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def add(severity, message = nil, progname = nil, &block)
         | 
| 12 | 
            +
                    super severity, message, progname, &block
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    if @verbose_logdev
         | 
| 15 | 
            +
                      @logdev = @verbose_logdev
         | 
| 16 | 
            +
                      super severity, message, progname, &block
         | 
| 17 | 
            +
                      @logdev = @default_logdev
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  def exception(error, progname = nil, &block)
         | 
| 22 | 
            +
                    add(FATAL, "#{error.message}\n#{error.backtrace.join("\n")}", progname, &block)
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                module Logger
         | 
| 27 | 
            +
                  extend ActiveSupport::Concern
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  module ClassMethods
         | 
| 30 | 
            +
                    def logger_name name
         | 
| 31 | 
            +
                      define_method :logger do
         | 
| 32 | 
            +
                        @logger unless @logger.nil?
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                        out = APP_CONFIG[:logger] ? File.join(DCA.root, 'log', "#{(self.class.send name).underscore}.log") : NIL
         | 
| 35 | 
            +
                        @logger ||= VerboseLogger.new out, 0, 1048576, APP_CONFIG[:verbose]
         | 
| 36 | 
            +
                      end
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    def logger= value
         | 
| 40 | 
            +
                      @logger = value
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  def logger
         | 
| 45 | 
            +
                    self.class.logger
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
            end
         | 
    
        data/lib/dca/jobs.rb
    ADDED
    
    
| @@ -0,0 +1,119 @@ | |
| 1 | 
            +
            module DCA
         | 
| 2 | 
            +
              module Jobs
         | 
| 3 | 
            +
                class AnalyzerJob < Job
         | 
| 4 | 
            +
                  def session
         | 
| 5 | 
            +
                    @session ||= options[:session] || UUID.generate(:compact)
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def perform
         | 
| 9 | 
            +
                    return on_change if change
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    if options[:distributed] && options[:position]
         | 
| 12 | 
            +
                      analyze position options[:position]
         | 
| 13 | 
            +
                      return
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    index = 0
         | 
| 17 | 
            +
                    # get list of positions and add to cache
         | 
| 18 | 
            +
                    positions do |position|
         | 
| 19 | 
            +
                      if options[:distributed]
         | 
| 20 | 
            +
                        distribute position
         | 
| 21 | 
            +
                      else
         | 
| 22 | 
            +
                        analyze position
         | 
| 23 | 
            +
                      end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                      index += 1
         | 
| 26 | 
            +
                      break if options[:limit] == index || shutdown?
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  def change
         | 
| 31 | 
            +
                    false
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  def distribute position
         | 
| 35 | 
            +
                    self.class.create :distributed => true, :position => position.to_hash, session => self.session
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  # Return all positions or newly created or modified if possible. Some cases not possible to get newly created or
         | 
| 39 | 
            +
                  # modified positions. In this case cache will be used to identify only newly created or modified positions.
         | 
| 40 | 
            +
                  # Position must be a hash and should contain unique key :id and checksum for compare with cached positions and
         | 
| 41 | 
            +
                  # identify newly created or modified
         | 
| 42 | 
            +
                  def positions(&block)
         | 
| 43 | 
            +
                    raise NotImplementedError
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  # Return position model from hash
         | 
| 47 | 
            +
                  def position hash
         | 
| 48 | 
            +
                    Models::Position.new hash
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  # Fetch newly created or modified positions
         | 
| 52 | 
            +
                  def fetch position
         | 
| 53 | 
            +
                    raise NotImplementedError
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  def on_change
         | 
| 57 | 
            +
                    notify(:change)
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  def on_analyze(position, state)
         | 
| 61 | 
            +
                    logger.debug "[#{position.class}] Analyze position base_id:#{position.base_id} state:#{state}"
         | 
| 62 | 
            +
                    notify(:analyze, :position => position, :state => state)
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  def on_fetch(position, state, result)
         | 
| 66 | 
            +
                    if result
         | 
| 67 | 
            +
                      logger.debug "[#{position.class}] Fetch valid position id:#{position.id} base_id:#{position.base_id} state:#{state}"
         | 
| 68 | 
            +
                    else
         | 
| 69 | 
            +
                      logger.debug "[#{position.class}] Fetch invalid position base_id:#{position.base_id} state:#{state}"
         | 
| 70 | 
            +
                      logger.debug "  Validation errors:\n    #{position.errors.full_messages.join("\n    ")}"
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
                    notify(:fetch, :position => position, :state => state, :result => result )
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  def on_failure(error)
         | 
| 76 | 
            +
                    logger.exception error
         | 
| 77 | 
            +
                    notify(:failure, :exception => error)
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  def on_success
         | 
| 81 | 
            +
                    notify(:success)
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  protected
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  def notify(event, options={})
         | 
| 87 | 
            +
                    Notifier.push self, event, options
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  def analyze position
         | 
| 91 | 
            +
                    state = position.state
         | 
| 92 | 
            +
                    on_analyze position, state
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    unless state == :unmodified
         | 
| 95 | 
            +
                      new_position = fetch_safe! position
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                      unless new_position
         | 
| 98 | 
            +
                        on_fetch position, state, false
         | 
| 99 | 
            +
                      else
         | 
| 100 | 
            +
                        position = new_position
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                        valid = new_position.valid?
         | 
| 103 | 
            +
                        state = position.save if valid
         | 
| 104 | 
            +
                        on_fetch position, state, valid
         | 
| 105 | 
            +
                      end
         | 
| 106 | 
            +
                    end
         | 
| 107 | 
            +
                  rescue Exception => exception
         | 
| 108 | 
            +
                    on_failure exception
         | 
| 109 | 
            +
                  end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                  def fetch_safe!(position)
         | 
| 112 | 
            +
                    fetch position
         | 
| 113 | 
            +
                  rescue Exception => exception
         | 
| 114 | 
            +
                    on_failure exception
         | 
| 115 | 
            +
                    false
         | 
| 116 | 
            +
                  end
         | 
| 117 | 
            +
                end
         | 
| 118 | 
            +
              end
         | 
| 119 | 
            +
            end
         |