infopark_reactor_migrations 1.5.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.
- data/.gitignore +5 -0
 - data/Gemfile +4 -0
 - data/LICENSE +165 -0
 - data/README +64 -0
 - data/Rakefile +19 -0
 - data/infopark_reactor_migrations.gemspec +27 -0
 - data/lib/generators/cm/migration/USAGE +8 -0
 - data/lib/generators/cm/migration/migration_generator.rb +15 -0
 - data/lib/generators/cm/migration/templates/template.rb +7 -0
 - data/lib/infopark_reactor_migrations.rb +29 -0
 - data/lib/reactor/cm/attribute.rb +84 -0
 - data/lib/reactor/cm/bridge.rb +49 -0
 - data/lib/reactor/cm/editorial_group.rb +22 -0
 - data/lib/reactor/cm/group.rb +270 -0
 - data/lib/reactor/cm/language.rb +56 -0
 - data/lib/reactor/cm/link.rb +132 -0
 - data/lib/reactor/cm/live_group.rb +22 -0
 - data/lib/reactor/cm/missing_credentials.rb +7 -0
 - data/lib/reactor/cm/obj.rb +402 -0
 - data/lib/reactor/cm/obj_class.rb +186 -0
 - data/lib/reactor/cm/object_base.rb +164 -0
 - data/lib/reactor/cm/user.rb +100 -0
 - data/lib/reactor/cm/workflow.rb +40 -0
 - data/lib/reactor/cm/xml_attribute.rb +35 -0
 - data/lib/reactor/cm/xml_markup.rb +85 -0
 - data/lib/reactor/cm/xml_request.rb +82 -0
 - data/lib/reactor/cm/xml_request_error.rb +16 -0
 - data/lib/reactor/cm/xml_response.rb +41 -0
 - data/lib/reactor/configuration.rb +7 -0
 - data/lib/reactor/migration.rb +82 -0
 - data/lib/reactor/migrations/railtie.rb +10 -0
 - data/lib/reactor/migrations/version.rb +5 -0
 - data/lib/reactor/plans/common_attribute.rb +32 -0
 - data/lib/reactor/plans/common_group.rb +44 -0
 - data/lib/reactor/plans/common_obj_class.rb +69 -0
 - data/lib/reactor/plans/create_attribute.rb +32 -0
 - data/lib/reactor/plans/create_group.rb +34 -0
 - data/lib/reactor/plans/create_obj.rb +48 -0
 - data/lib/reactor/plans/create_obj_class.rb +28 -0
 - data/lib/reactor/plans/delete_attribute.rb +23 -0
 - data/lib/reactor/plans/delete_group.rb +28 -0
 - data/lib/reactor/plans/delete_obj.rb +22 -0
 - data/lib/reactor/plans/delete_obj_class.rb +22 -0
 - data/lib/reactor/plans/prepared.rb +15 -0
 - data/lib/reactor/plans/rename_group.rb +32 -0
 - data/lib/reactor/plans/rename_obj_class.rb +24 -0
 - data/lib/reactor/plans/update_attribute.rb +23 -0
 - data/lib/reactor/plans/update_group.rb +30 -0
 - data/lib/reactor/plans/update_obj.rb +30 -0
 - data/lib/reactor/plans/update_obj_class.rb +26 -0
 - data/lib/reactor/tools/migrator.rb +135 -0
 - data/lib/reactor/tools/response_handler/base.rb +22 -0
 - data/lib/reactor/tools/response_handler/string.rb +19 -0
 - data/lib/reactor/tools/response_handler/xml_attribute.rb +52 -0
 - data/lib/reactor/tools/smart_xml_logger.rb +69 -0
 - data/lib/reactor/tools/sower.rb +89 -0
 - data/lib/reactor/tools/uploader.rb +131 -0
 - data/lib/reactor/tools/versioner.rb +120 -0
 - data/lib/reactor/tools/xml_attributes.rb +70 -0
 - data/lib/tasks/cm_migrate.rake +8 -0
 - data/lib/tasks/cm_seeds.rake +41 -0
 - metadata +193 -0
 
| 
         @@ -0,0 +1,135 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'reactor/tools/versioner'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Reactor
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Class responsible for running a single migration, a helper for Migrator
         
     | 
| 
      
 5 
     | 
    
         
            +
              class MigrationProxy
         
     | 
| 
      
 6 
     | 
    
         
            +
                def initialize(versioner, name, version, direction, filename)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @versioner = versioner
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @name = name
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @version = version
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @filename = filename
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @direction = direction
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def load_migration
         
     | 
| 
      
 15 
     | 
    
         
            +
                  load @filename
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def run
         
     | 
| 
      
 19 
     | 
    
         
            +
                  return down if @direction.to_sym == :down
         
     | 
| 
      
 20 
     | 
    
         
            +
                  return up
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def up
         
     | 
| 
      
 24 
     | 
    
         
            +
                  if @versioner.applied?(@version) then
         
     | 
| 
      
 25 
     | 
    
         
            +
                    puts "Migrating up: #{@name} (#{@filename}) already applied, skipping"
         
     | 
| 
      
 26 
     | 
    
         
            +
                    return true
         
     | 
| 
      
 27 
     | 
    
         
            +
                  else
         
     | 
| 
      
 28 
     | 
    
         
            +
                    result = class_name.send(:up) and @versioner.add(@version)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    class_name.contained.each do |version|
         
     | 
| 
      
 30 
     | 
    
         
            +
                      puts "#{class_name.to_s} contains migration #{version}"
         
     | 
| 
      
 31 
     | 
    
         
            +
                      #@versioner.add(version) # not neccesary!
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end if result
         
     | 
| 
      
 33 
     | 
    
         
            +
                    result
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def down
         
     | 
| 
      
 38 
     | 
    
         
            +
                  result = class_name.send(:down) and @versioner.remove(@version)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  class_name.contained.each do |version|
         
     | 
| 
      
 40 
     | 
    
         
            +
                    puts "#{class_name.to_s} contains migration #{version}"
         
     | 
| 
      
 41 
     | 
    
         
            +
                    @versioner.remove(version)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end if result
         
     | 
| 
      
 43 
     | 
    
         
            +
                  result
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                def class_name
         
     | 
| 
      
 47 
     | 
    
         
            +
                  return Kernel.const_get(@name)
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def name
         
     | 
| 
      
 51 
     | 
    
         
            +
                  @name
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def version
         
     | 
| 
      
 55 
     | 
    
         
            +
                  @version
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                def filename
         
     | 
| 
      
 59 
     | 
    
         
            +
                  @filename
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
              # Migrator is responsible for running migrations.
         
     | 
| 
      
 64 
     | 
    
         
            +
              #
         
     | 
| 
      
 65 
     | 
    
         
            +
              # <b>You should not use this class directly! Use rake cm:migrate instead.</b>
         
     | 
| 
      
 66 
     | 
    
         
            +
              #
         
     | 
| 
      
 67 
     | 
    
         
            +
              # Migrating to a specific version is possible by specifing VERSION environment
         
     | 
| 
      
 68 
     | 
    
         
            +
              # variable: rake cm:migrate VERSION=0
         
     | 
| 
      
 69 
     | 
    
         
            +
              # Depending on your current version migrations will be run up
         
     | 
| 
      
 70 
     | 
    
         
            +
              # (target version > current version) or down (target version < current version)
         
     | 
| 
      
 71 
     | 
    
         
            +
              #
         
     | 
| 
      
 72 
     | 
    
         
            +
              # MIND THE FACT, that you land at the version <i>nearest</i> to target_version
         
     | 
| 
      
 73 
     | 
    
         
            +
              # (possibly target version itself)
         
     | 
| 
      
 74 
     | 
    
         
            +
              class Migrator
         
     | 
| 
      
 75 
     | 
    
         
            +
                # Constructor takes two parameters migrations_path (relative path of migration files)
         
     | 
| 
      
 76 
     | 
    
         
            +
                # and target_version (an integer or nil).
         
     | 
| 
      
 77 
     | 
    
         
            +
                #
         
     | 
| 
      
 78 
     | 
    
         
            +
                # Used by a rake task.
         
     | 
| 
      
 79 
     | 
    
         
            +
                def initialize(migrations_path, target_version=nil)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  @migrations_path = migrations_path
         
     | 
| 
      
 81 
     | 
    
         
            +
                  @target_version = target_version.to_i unless target_version.nil?
         
     | 
| 
      
 82 
     | 
    
         
            +
                  @target_version = 99999999999999 if target_version.nil?
         
     | 
| 
      
 83 
     | 
    
         
            +
                  @versioner = Versioner.instance
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                # Runs the migrations in proper direction (up or down)
         
     | 
| 
      
 87 
     | 
    
         
            +
                # Ouputs current version when done
         
     | 
| 
      
 88 
     | 
    
         
            +
                def migrate
         
     | 
| 
      
 89 
     | 
    
         
            +
                  return up if @target_version.to_i > current_version.to_i
         
     | 
| 
      
 90 
     | 
    
         
            +
                  return down
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                def up
         
     | 
| 
      
 94 
     | 
    
         
            +
                  rem_migrations = migrations.reject do |version, name, file|
         
     | 
| 
      
 95 
     | 
    
         
            +
                    version.to_i > @target_version.to_i or applied?(version)
         
     | 
| 
      
 96 
     | 
    
         
            +
                  end
         
     | 
| 
      
 97 
     | 
    
         
            +
                  run(rem_migrations, :up)
         
     | 
| 
      
 98 
     | 
    
         
            +
                end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                def down
         
     | 
| 
      
 101 
     | 
    
         
            +
                  rem_migrations = migrations.reject do |version, name, file|
         
     | 
| 
      
 102 
     | 
    
         
            +
                    version.to_i <= @target_version.to_i or not applied?(version)
         
     | 
| 
      
 103 
     | 
    
         
            +
                  end
         
     | 
| 
      
 104 
     | 
    
         
            +
                  run(rem_migrations.reverse, :down)
         
     | 
| 
      
 105 
     | 
    
         
            +
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                def migrations
         
     | 
| 
      
 108 
     | 
    
         
            +
                  files = Dir["#{@migrations_path}/[0-9]*_*.rb"].sort.collect do |file|
         
     | 
| 
      
 109 
     | 
    
         
            +
                    version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
         
     | 
| 
      
 110 
     | 
    
         
            +
                    [version, name, file]
         
     | 
| 
      
 111 
     | 
    
         
            +
                  end
         
     | 
| 
      
 112 
     | 
    
         
            +
                end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                def applied?(version)
         
     | 
| 
      
 115 
     | 
    
         
            +
                  @versioner.applied?(version)
         
     | 
| 
      
 116 
     | 
    
         
            +
                end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                def current_version
         
     | 
| 
      
 119 
     | 
    
         
            +
                  @versioner.current_version
         
     | 
| 
      
 120 
     | 
    
         
            +
                end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                def run(rem_migrations, direction)
         
     | 
| 
      
 123 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 124 
     | 
    
         
            +
                    rem_migrations.each do |version, name, file|
         
     | 
| 
      
 125 
     | 
    
         
            +
                      migration = MigrationProxy.new(@versioner, name.camelize, version, direction, file)
         
     | 
| 
      
 126 
     | 
    
         
            +
                      puts "Migrating #{direction.to_s}: #{migration.name} (#{migration.filename})"
         
     | 
| 
      
 127 
     | 
    
         
            +
                      migration.load_migration and migration.run or raise "Migrating #{direction.to_s}: #{migration.name} (#{migration.filename}) failed"
         
     | 
| 
      
 128 
     | 
    
         
            +
                    end
         
     | 
| 
      
 129 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 130 
     | 
    
         
            +
                    puts "At version: " + @versioner.current_version.to_s
         
     | 
| 
      
 131 
     | 
    
         
            +
                    puts "WARNING: Could not store applied migrations!" if not @versioner.store
         
     | 
| 
      
 132 
     | 
    
         
            +
                  end
         
     | 
| 
      
 133 
     | 
    
         
            +
                end
         
     | 
| 
      
 134 
     | 
    
         
            +
              end
         
     | 
| 
      
 135 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Reactor
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              module ResponseHandler
         
     | 
| 
      
 4 
     | 
    
         
            +
                
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Common base class to handle a xml response. Provides helper methods to extract the content 
         
     | 
| 
      
 6 
     | 
    
         
            +
                # from a xml response.
         
     | 
| 
      
 7 
     | 
    
         
            +
                class Base
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  attr_accessor :response
         
     | 
| 
      
 10 
     | 
    
         
            +
                  attr_accessor :context
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  # Common strategy method for each sub class.
         
     | 
| 
      
 13 
     | 
    
         
            +
                  def get(response, context)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @response = response
         
     | 
| 
      
 15 
     | 
    
         
            +
                    @context = context
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                  
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'reactor/tools/response_handler/base'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Reactor
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              module ResponseHandler
         
     | 
| 
      
 6 
     | 
    
         
            +
                
         
     | 
| 
      
 7 
     | 
    
         
            +
                class String < Base
         
     | 
| 
      
 8 
     | 
    
         
            +
                  
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def get(response, string)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    super(response, string)
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                    self.response.xpath(string)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
                
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,52 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'reactor/tools/response_handler/base'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Reactor
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              module ResponseHandler
         
     | 
| 
      
 6 
     | 
    
         
            +
                
         
     | 
| 
      
 7 
     | 
    
         
            +
                class XmlAttribute < Base
         
     | 
| 
      
 8 
     | 
    
         
            +
                  
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def get(response, attribute)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    super(response, attribute)
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                    name = attribute.name
         
     | 
| 
      
 13 
     | 
    
         
            +
                    type = attribute.type
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                    method_name = "extract_#{type}"
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                    self.send(method_name, name)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  private
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  # Extracts a string value with the given +name+ and returns a string.
         
     | 
| 
      
 23 
     | 
    
         
            +
                  def extract_string(name)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    self.response.xpath("//#{name}/text()").to_s
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  # Extracts a list value with the given +name+ and returns an array of strings.
         
     | 
| 
      
 28 
     | 
    
         
            +
                  def extract_list(name)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    result = self.response.xpath("//#{name}/listitem/text()")
         
     | 
| 
      
 30 
     | 
    
         
            +
                    result = result.kind_of?(Array) ? result : [result]
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                    result.map(&:to_s)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  # This shit will break with the slightest change of the CM.
         
     | 
| 
      
 36 
     | 
    
         
            +
                  def extract_signaturelist(name)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    signatures = []
         
     | 
| 
      
 38 
     | 
    
         
            +
                    self.response.xpath("//#{name}/").each do |potential_signature|
         
     | 
| 
      
 39 
     | 
    
         
            +
                      if (potential_signature.name.to_s == "listitem")
         
     | 
| 
      
 40 
     | 
    
         
            +
                        attribute = potential_signature.children.first.text.to_s
         
     | 
| 
      
 41 
     | 
    
         
            +
                        group = potential_signature.children.last.text.to_s
         
     | 
| 
      
 42 
     | 
    
         
            +
                        signatures << {:attribute => attribute, :group => group}
         
     | 
| 
      
 43 
     | 
    
         
            +
                      end
         
     | 
| 
      
 44 
     | 
    
         
            +
                    end
         
     | 
| 
      
 45 
     | 
    
         
            +
                    signatures
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
                
         
     | 
| 
      
 50 
     | 
    
         
            +
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,69 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'nokogiri'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'term/ansicolor'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class SmartXmlLogger
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              include Term::ANSIColor
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              def initialize(forward_to, method = nil)
         
     | 
| 
      
 9 
     | 
    
         
            +
                @logger = forward_to
         
     | 
| 
      
 10 
     | 
    
         
            +
                @method = method
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              def configure(key, options)
         
     | 
| 
      
 14 
     | 
    
         
            +
                @configuration ||= {}
         
     | 
| 
      
 15 
     | 
    
         
            +
                @configuration[key] = options
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              def log(text)
         
     | 
| 
      
 19 
     | 
    
         
            +
                return unless @logger
         
     | 
| 
      
 20 
     | 
    
         
            +
                @logger.send(@method, text)
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              def log_xml(key, xml)
         
     | 
| 
      
 24 
     | 
    
         
            +
                return unless @logger
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                options = @configuration[key]
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                dom = Nokogiri::XML::Document.parse(xml)
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                node_set = options[:xpath] ? dom.xpath(options[:xpath]) : dom
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                self.log(if node_set.respond_to?(:each)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  node_set.map{|node| self.print_node(node, options[:start_indent] || 0)}.join
         
     | 
| 
      
 34 
     | 
    
         
            +
                else
         
     | 
| 
      
 35 
     | 
    
         
            +
                  self.print_node(node_set, options[:start_indent] || 0)
         
     | 
| 
      
 36 
     | 
    
         
            +
                end)
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              #private
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
              def print_node(node, indent = 0)
         
     | 
| 
      
 42 
     | 
    
         
            +
                return '' if node.text?
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                empty = node.children.empty?
         
     | 
| 
      
 45 
     | 
    
         
            +
                has_text = node.children.detect{|child| child.text?}
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                out = ' ' * indent
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                attrs = node.attributes.values.map{|attr| %|#{attr.name}="#{red(attr.value)}"|}.join(' ')
         
     | 
| 
      
 50 
     | 
    
         
            +
                attrs = " #{attrs}" if attrs.present?
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                out << "<#{green(node.name)}#{attrs}#{'/' if empty}>"
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                if has_text
         
     | 
| 
      
 55 
     | 
    
         
            +
                  out << "#{red(node.text)}"
         
     | 
| 
      
 56 
     | 
    
         
            +
                else
         
     | 
| 
      
 57 
     | 
    
         
            +
                  out << "\n"
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                node.children.each do |child|
         
     | 
| 
      
 61 
     | 
    
         
            +
                  out << self.print_node(child, indent + 2)
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                out << ' ' * indent unless has_text || empty
         
     | 
| 
      
 65 
     | 
    
         
            +
                out << "</#{green(node.name)}>\n" unless empty
         
     | 
| 
      
 66 
     | 
    
         
            +
                out
         
     | 
| 
      
 67 
     | 
    
         
            +
              end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,89 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Reactor
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              class Sower
         
     | 
| 
      
 4 
     | 
    
         
            +
                def initialize(filename)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  @filename = filename
         
     | 
| 
      
 6 
     | 
    
         
            +
                end
         
     | 
| 
      
 7 
     | 
    
         
            +
                def sow
         
     | 
| 
      
 8 
     | 
    
         
            +
                  require @filename
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            class SeedObject < RailsConnector::Obj
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            module RailsConnector
         
     | 
| 
      
 18 
     | 
    
         
            +
              class Obj
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                attr_accessor :keep_edited
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def self.plant(path, &block)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  obj = Obj.find_by_path(path)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  raise ActiveRecord::RecordNotFound.new('plant: Ground not found:' +path) if obj.nil?
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #obj.objClass = 'Container' # TODO: enable it!
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #obj.save!
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #obj.release!
         
     | 
| 
      
 28 
     | 
    
         
            +
                  obj.send(:reload_attributes)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  obj.instance_eval(&block) if block_given?
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # ActiveRecord is incompatible with changing the obj class, therefore you get RecordNotFound
         
     | 
| 
      
 31 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 32 
     | 
    
         
            +
                    obj.save!
         
     | 
| 
      
 33 
     | 
    
         
            +
                  rescue ActiveRecord::RecordNotFound
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
                  obj.release unless obj.keep_edited || !Obj.last.edited?
         
     | 
| 
      
 36 
     | 
    
         
            +
                  obj
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                # creates of fetches an obj with given name (within context),
         
     | 
| 
      
 40 
     | 
    
         
            +
                # executes a block on it (instance_eval)
         
     | 
| 
      
 41 
     | 
    
         
            +
                # saves and releases (unless keep_edited = true was called)
         
     | 
| 
      
 42 
     | 
    
         
            +
                # the object afterwards
         
     | 
| 
      
 43 
     | 
    
         
            +
                def obj(name, objClass = 'Container', &block)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  obj = Obj.find_by_path(File.join(self.path.to_s, name.to_s))
         
     | 
| 
      
 45 
     | 
    
         
            +
                  if obj.nil?
         
     | 
| 
      
 46 
     | 
    
         
            +
                    obj = Obj.create(:name => name, :parent => self.path, :obj_class => objClass)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  else
         
     | 
| 
      
 48 
     | 
    
         
            +
                    obj = Obj.find_by_path(File.join(self.path.to_s, name.to_s))
         
     | 
| 
      
 49 
     | 
    
         
            +
                    if obj.obj_class != objClass
         
     | 
| 
      
 50 
     | 
    
         
            +
                      obj.obj_class = objClass
         
     | 
| 
      
 51 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 52 
     | 
    
         
            +
                        obj.save!
         
     | 
| 
      
 53 
     | 
    
         
            +
                      rescue ActiveRecord::RecordNotFound
         
     | 
| 
      
 54 
     | 
    
         
            +
                      end
         
     | 
| 
      
 55 
     | 
    
         
            +
                      obj = Obj.find_by_path(File.join(self.path.to_s, name.to_s))
         
     | 
| 
      
 56 
     | 
    
         
            +
                    end
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
                  obj.send(:reload_attributes, objClass)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  obj.instance_eval(&block) if block_given?
         
     | 
| 
      
 60 
     | 
    
         
            +
                  obj.save!
         
     | 
| 
      
 61 
     | 
    
         
            +
                  obj.release unless obj.keep_edited || !Obj.last.edited?
         
     | 
| 
      
 62 
     | 
    
         
            +
                  obj
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                def self.with(path, objClass = 'Container', &block)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  splitted_path = path.split('/')
         
     | 
| 
      
 67 
     | 
    
         
            +
                  name = splitted_path.pop
         
     | 
| 
      
 68 
     | 
    
         
            +
                  # ensure path exists
         
     | 
| 
      
 69 
     | 
    
         
            +
                  (splitted_path.length).times do |i|
         
     | 
| 
      
 70 
     | 
    
         
            +
                    subpath = splitted_path[0,(i+1)].join('/').presence || '/'
         
     | 
| 
      
 71 
     | 
    
         
            +
                    subpath_parent = splitted_path[0,i].join('/').presence || '/'
         
     | 
| 
      
 72 
     | 
    
         
            +
                    subpath_name = splitted_path[i]
         
     | 
| 
      
 73 
     | 
    
         
            +
                    create(:name => subpath_name, :parent => subpath_parent, :obj_class => 'Container') unless Obj.find_by_path(subpath) unless subpath_name.blank?
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
                  parent_path = splitted_path.join('/').presence || '/'
         
     | 
| 
      
 76 
     | 
    
         
            +
                  parent = Obj.find_by_path(parent_path)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  parent.obj(name, objClass, &block)
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                def do_not_release!
         
     | 
| 
      
 81 
     | 
    
         
            +
                  @keep_edited = true
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                def t(key, opts={})
         
     | 
| 
      
 85 
     | 
    
         
            +
                  I18n.t(key, opts)
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
              end
         
     | 
| 
      
 89 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,131 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Reactor
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Tools
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Uploader
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                  attr_reader :cm_obj
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                  def initialize(cm_obj)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    self.cm_obj = cm_obj
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  # Uses streaming interface to upload data from
         
     | 
| 
      
 12 
     | 
    
         
            +
                  # given IO stream or memory location.
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # Extension is used as basis for content detection.
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # Larger file transfers should be executed through IO
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # streams, which conserve memory.
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # After the data has been successfuly transfered to
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # streaming interface it stores contentType and resulting
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # ticket into Reactor::Cm::Obj provided on initialization.
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # NOTE: there is a known bug for Mac OS X: if you are
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # uploading more files (IO objects) in sequence,
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # the upload may fail randomly. For this platform
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # and this case fallback to memory streaming is used.
         
     | 
| 
      
 25 
     | 
    
         
            +
                  def upload(data_or_io, extension)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    if (data_or_io.kind_of?IO)
         
     | 
| 
      
 27 
     | 
    
         
            +
                      io = data_or_io
         
     | 
| 
      
 28 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 29 
     | 
    
         
            +
                        ticket_id = stream_io(io, extension)
         
     | 
| 
      
 30 
     | 
    
         
            +
                      rescue Errno::EINVAL => e
         
     | 
| 
      
 31 
     | 
    
         
            +
                        if RUBY_PLATFORM.downcase.include?("darwin")
         
     | 
| 
      
 32 
     | 
    
         
            +
                          # mac os x is such a piece of shit
         
     | 
| 
      
 33 
     | 
    
         
            +
                          # writing to a socket can fail with EINVAL, randomly without
         
     | 
| 
      
 34 
     | 
    
         
            +
                          # visible reason when using body_stream
         
     | 
| 
      
 35 
     | 
    
         
            +
                          # in this case fallback to memory upload which always works (?!?!)
         
     | 
| 
      
 36 
     | 
    
         
            +
                          Reactor::Cm::LOGGER.log "MacOS X bug detected for #{io.inspect}"
         
     | 
| 
      
 37 
     | 
    
         
            +
                          io.rewind
         
     | 
| 
      
 38 
     | 
    
         
            +
                          return upload(io.read, extension)
         
     | 
| 
      
 39 
     | 
    
         
            +
                        else
         
     | 
| 
      
 40 
     | 
    
         
            +
                          raise e
         
     | 
| 
      
 41 
     | 
    
         
            +
                        end
         
     | 
| 
      
 42 
     | 
    
         
            +
                      end
         
     | 
| 
      
 43 
     | 
    
         
            +
                    else
         
     | 
| 
      
 44 
     | 
    
         
            +
                      ticket_id = stream_data(data_or_io, extension)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    cm_obj.set(:contentType, extension)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    cm_obj.set(:blob, {ticket_id=>{:encoding=>'stream'}})
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                    ticket_id
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  protected
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  attr_writer :cm_obj
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  # Stream into CM from memory. Used in cases when the file
         
     | 
| 
      
 58 
     | 
    
         
            +
                  # has already been read into memory
         
     | 
| 
      
 59 
     | 
    
         
            +
                  def stream_data(data, extension)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    response, ticket_id = (Net::HTTP.new(self.class.streaming_host, self.class.streaming_port).post('/stream', data,
         
     | 
| 
      
 61 
     | 
    
         
            +
                      {'Content-Type' => self.class.content_type_for_ext(extension)}))
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    handle_response(response, ticket_id)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                  # Stream directly an IO object into CM. Uses minimal memory,
         
     | 
| 
      
 67 
     | 
    
         
            +
                  # as the IO is read in 1024B-Blocks
         
     | 
| 
      
 68 
     | 
    
         
            +
                  def stream_io(io, extension)
         
     | 
| 
      
 69 
     | 
    
         
            +
                    request = Net::HTTP::Post.new('/stream')
         
     | 
| 
      
 70 
     | 
    
         
            +
                    request.body_stream = io
         
     | 
| 
      
 71 
     | 
    
         
            +
                    request.content_length = read_io_content_length(io)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    request.content_type = self.class.content_type_for_ext(extension)
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                    response, ticket_id = nil, nil
         
     | 
| 
      
 75 
     | 
    
         
            +
                    Net::HTTP.start(self.class.streaming_host, self.class.streaming_port) do |http|
         
     | 
| 
      
 76 
     | 
    
         
            +
                      http.read_timeout = 60
         
     | 
| 
      
 77 
     | 
    
         
            +
                      #http.set_debug_output $stderr
         
     | 
| 
      
 78 
     | 
    
         
            +
                      response, ticket_id = http.request(request)
         
     | 
| 
      
 79 
     | 
    
         
            +
                    end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                    handle_response(response, ticket_id)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  # Returns ticket_id if response if one of success (success or redirect)
         
     | 
| 
      
 85 
     | 
    
         
            +
                  def handle_response(response, ticket_id)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPRedirection)
         
     | 
| 
      
 87 
     | 
    
         
            +
                      ticket_id
         
     | 
| 
      
 88 
     | 
    
         
            +
                    else
         
     | 
| 
      
 89 
     | 
    
         
            +
                      nil
         
     | 
| 
      
 90 
     | 
    
         
            +
                    end
         
     | 
| 
      
 91 
     | 
    
         
            +
                  end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                  # Returns the size of the IO stream.
         
     | 
| 
      
 94 
     | 
    
         
            +
                  # The underlying stream must support either
         
     | 
| 
      
 95 
     | 
    
         
            +
                  # the :stat method or be able to seek to
         
     | 
| 
      
 96 
     | 
    
         
            +
                  # random position
         
     | 
| 
      
 97 
     | 
    
         
            +
                  def read_io_content_length(io)
         
     | 
| 
      
 98 
     | 
    
         
            +
                    if (io.respond_to?(:stat))
         
     | 
| 
      
 99 
     | 
    
         
            +
                      # For files it is easy to read the filesize
         
     | 
| 
      
 100 
     | 
    
         
            +
                      return io.stat.size
         
     | 
| 
      
 101 
     | 
    
         
            +
                    else
         
     | 
| 
      
 102 
     | 
    
         
            +
                      # For streams it is not. We seek to end of
         
     | 
| 
      
 103 
     | 
    
         
            +
                      # the stream, read the position, and rewind
         
     | 
| 
      
 104 
     | 
    
         
            +
                      # to the previous location
         
     | 
| 
      
 105 
     | 
    
         
            +
                      old_pos = io.pos
         
     | 
| 
      
 106 
     | 
    
         
            +
                      io.seek(0, IO::SEEK_END)
         
     | 
| 
      
 107 
     | 
    
         
            +
                      content_length = io.pos
         
     | 
| 
      
 108 
     | 
    
         
            +
                      io.seek(old_pos, IO::SEEK_SET)
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                      content_length
         
     | 
| 
      
 111 
     | 
    
         
            +
                    end
         
     | 
| 
      
 112 
     | 
    
         
            +
                  end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                  def self.streaming_host
         
     | 
| 
      
 115 
     | 
    
         
            +
                    Reactor::Configuration.xml_access[:host]
         
     | 
| 
      
 116 
     | 
    
         
            +
                  end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                  def self.streaming_port
         
     | 
| 
      
 119 
     | 
    
         
            +
                    Reactor::Configuration.xml_access[:port]
         
     | 
| 
      
 120 
     | 
    
         
            +
                  end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                  # It should theoretically return correct/matching
         
     | 
| 
      
 123 
     | 
    
         
            +
                  # mime type for given extension. But since the CM
         
     | 
| 
      
 124 
     | 
    
         
            +
                  # accepts 'application/octet-stream', no extra logic
         
     | 
| 
      
 125 
     | 
    
         
            +
                  # or external dependency is required.
         
     | 
| 
      
 126 
     | 
    
         
            +
                  def self.content_type_for_ext(extension)
         
     | 
| 
      
 127 
     | 
    
         
            +
                    'application/octet-stream'
         
     | 
| 
      
 128 
     | 
    
         
            +
                  end
         
     | 
| 
      
 129 
     | 
    
         
            +
                end
         
     | 
| 
      
 130 
     | 
    
         
            +
              end
         
     | 
| 
      
 131 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,120 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'base64'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'singleton'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Reactor
         
     | 
| 
      
 6 
     | 
    
         
            +
              # Class responsible for interfacing with version-storing mechanism
         
     | 
| 
      
 7 
     | 
    
         
            +
              class Versioner
         
     | 
| 
      
 8 
     | 
    
         
            +
                include Singleton
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                # Slave class used by Versioner class to load and store migrated files
         
     | 
| 
      
 11 
     | 
    
         
            +
                # inside the CM. It uses separate object type named "version_store"
         
     | 
| 
      
 12 
     | 
    
         
            +
                # and stores data as base64'ed YAML inside recordSetCallback
         
     | 
| 
      
 13 
     | 
    
         
            +
                # (Versionszuweisungsfunktion).
         
     | 
| 
      
 14 
     | 
    
         
            +
                # Theoretically you could use any class for this purpose, but you would
         
     | 
| 
      
 15 
     | 
    
         
            +
                # lose the ability to set recordSetCallback for this class. Other than
         
     | 
| 
      
 16 
     | 
    
         
            +
                # that, it does not affect the object class in any way!
         
     | 
| 
      
 17 
     | 
    
         
            +
                #
         
     | 
| 
      
 18 
     | 
    
         
            +
                # Maybe the future version won't disrupt even this fuction.
         
     | 
| 
      
 19 
     | 
    
         
            +
                class Slave
         
     | 
| 
      
 20 
     | 
    
         
            +
                  def name
         
     | 
| 
      
 21 
     | 
    
         
            +
                    "version_store"
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def base_name
         
     | 
| 
      
 25 
     | 
    
         
            +
                    "objClass"
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  def exists?
         
     | 
| 
      
 29 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 30 
     | 
    
         
            +
                      request = Reactor::Cm::XmlRequest.prepare do |xml|
         
     | 
| 
      
 31 
     | 
    
         
            +
                        xml.where_key_tag!(base_name, 'name', name)
         
     | 
| 
      
 32 
     | 
    
         
            +
                        xml.get_key_tag!(base_name, 'name')
         
     | 
| 
      
 33 
     | 
    
         
            +
                      end
         
     | 
| 
      
 34 
     | 
    
         
            +
                      response = request.execute!
         
     | 
| 
      
 35 
     | 
    
         
            +
                      return response.ok?
         
     | 
| 
      
 36 
     | 
    
         
            +
                    rescue
         
     | 
| 
      
 37 
     | 
    
         
            +
                      return false
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  def load
         
     | 
| 
      
 42 
     | 
    
         
            +
                    create if not exists?
         
     | 
| 
      
 43 
     | 
    
         
            +
                    request = Reactor::Cm::XmlRequest.prepare do |xml|
         
     | 
| 
      
 44 
     | 
    
         
            +
                      xml.where_key_tag!(base_name, 'name', name)
         
     | 
| 
      
 45 
     | 
    
         
            +
                      xml.get_key_tag!(base_name, 'recordSetCallback')
         
     | 
| 
      
 46 
     | 
    
         
            +
                    end
         
     | 
| 
      
 47 
     | 
    
         
            +
                    response = request.execute!
         
     | 
| 
      
 48 
     | 
    
         
            +
                    base64 = response.xpath("//recordSetCallback").text.to_s
         
     | 
| 
      
 49 
     | 
    
         
            +
                    yaml = Base64::decode64(base64)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    data = YAML::load(yaml)
         
     | 
| 
      
 51 
     | 
    
         
            +
                    return [] if data.nil? or data == false
         
     | 
| 
      
 52 
     | 
    
         
            +
                    return data.to_a
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  def store(data)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    create if not exists?
         
     | 
| 
      
 57 
     | 
    
         
            +
                    yaml = data.to_yaml
         
     | 
| 
      
 58 
     | 
    
         
            +
                    base64 = Base64::encode64(yaml).gsub("\n", '').gsub("\r", '')
         
     | 
| 
      
 59 
     | 
    
         
            +
                    content = '#' + base64
         
     | 
| 
      
 60 
     | 
    
         
            +
                    request = Reactor::Cm::XmlRequest.prepare do |xml|
         
     | 
| 
      
 61 
     | 
    
         
            +
                      xml.where_key_tag!(base_name, 'name', name)
         
     | 
| 
      
 62 
     | 
    
         
            +
                      xml.set_key_tag!(base_name, 'recordSetCallback', content)
         
     | 
| 
      
 63 
     | 
    
         
            +
                    end
         
     | 
| 
      
 64 
     | 
    
         
            +
                    response = request.execute!
         
     | 
| 
      
 65 
     | 
    
         
            +
                    response.ok?
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  def create
         
     | 
| 
      
 69 
     | 
    
         
            +
                    request = Reactor::Cm::XmlRequest.prepare do |xml|
         
     | 
| 
      
 70 
     | 
    
         
            +
                      xml.create_tag!(base_name) do
         
     | 
| 
      
 71 
     | 
    
         
            +
                        xml.tag!('name') do
         
     | 
| 
      
 72 
     | 
    
         
            +
                          xml.text!(name)
         
     | 
| 
      
 73 
     | 
    
         
            +
                        end
         
     | 
| 
      
 74 
     | 
    
         
            +
                        xml.tag!('objType') do
         
     | 
| 
      
 75 
     | 
    
         
            +
                          xml.text!('document')
         
     | 
| 
      
 76 
     | 
    
         
            +
                        end
         
     | 
| 
      
 77 
     | 
    
         
            +
                      end
         
     | 
| 
      
 78 
     | 
    
         
            +
                    end
         
     | 
| 
      
 79 
     | 
    
         
            +
                    response = request.execute!
         
     | 
| 
      
 80 
     | 
    
         
            +
                    response.ok?
         
     | 
| 
      
 81 
     | 
    
         
            +
                  end
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 85 
     | 
    
         
            +
                  @versions = []
         
     | 
| 
      
 86 
     | 
    
         
            +
                  @backend = Slave.new
         
     | 
| 
      
 87 
     | 
    
         
            +
                  load
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                def load
         
     | 
| 
      
 91 
     | 
    
         
            +
                  @versions = @backend.load
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                def store
         
     | 
| 
      
 95 
     | 
    
         
            +
                  @backend.store(@versions)
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                def applied?(version)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  @versions.include? version.to_s
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                def add(version)
         
     | 
| 
      
 103 
     | 
    
         
            +
                  @versions << version.to_s
         
     | 
| 
      
 104 
     | 
    
         
            +
                end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                def remove(version)
         
     | 
| 
      
 107 
     | 
    
         
            +
                  not @versions.delete(version.to_s).nil?
         
     | 
| 
      
 108 
     | 
    
         
            +
                end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                def versions
         
     | 
| 
      
 111 
     | 
    
         
            +
                  @versions
         
     | 
| 
      
 112 
     | 
    
         
            +
                end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                def current_version
         
     | 
| 
      
 115 
     | 
    
         
            +
                  current = @versions.sort.reverse.first
         
     | 
| 
      
 116 
     | 
    
         
            +
                  return 0 if current.nil?
         
     | 
| 
      
 117 
     | 
    
         
            +
                  return current
         
     | 
| 
      
 118 
     | 
    
         
            +
                end
         
     | 
| 
      
 119 
     | 
    
         
            +
              end
         
     | 
| 
      
 120 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,70 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'reactor/cm/xml_attribute'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'reactor/tools/response_handler/xml_attribute'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Reactor
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              module XmlAttributes
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                extend ActiveSupport::Concern
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                included do
         
     | 
| 
      
 11 
     | 
    
         
            +
                  class_attribute :_attributes
         
     | 
| 
      
 12 
     | 
    
         
            +
                  self._attributes = {}
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  class_attribute :response_handler
         
     | 
| 
      
 15 
     | 
    
         
            +
                  self.response_handler = ResponseHandler::XmlAttribute.new
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                module ClassMethods
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  # This method can act as both getter and setter.
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # I admit, that it is not the best design ever.
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # But it makes a pretty good DSL
         
     | 
| 
      
 23 
     | 
    
         
            +
                  def primary_key(new_value = nil)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    if new_value.nil?
         
     | 
| 
      
 25 
     | 
    
         
            +
                      @primary_key
         
     | 
| 
      
 26 
     | 
    
         
            +
                    else
         
     | 
| 
      
 27 
     | 
    
         
            +
                      @primary_key = new_value.to_s
         
     | 
| 
      
 28 
     | 
    
         
            +
                      @primary_key
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def attribute(name, options = {})
         
     | 
| 
      
 33 
     | 
    
         
            +
                    xml_name = options.delete(:name).presence || name
         
     | 
| 
      
 34 
     | 
    
         
            +
                    type = options.delete(:type).presence
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                    attribute = Reactor::Cm::XmlAttribute.new(xml_name, type, options)
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                    self._attributes[name.to_sym] = attribute
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                    attr_accessor name
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  def attributes(scopes = [])
         
     | 
| 
      
 44 
     | 
    
         
            +
                    scopes = Array(scopes)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    attributes = self._attributes
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    if scopes.present?
         
     | 
| 
      
 48 
     | 
    
         
            +
                      attributes.reject { |_, xml_attribute| (xml_attribute.scopes & scopes).blank? }
         
     | 
| 
      
 49 
     | 
    
         
            +
                    else
         
     | 
| 
      
 50 
     | 
    
         
            +
                      attributes
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  def xml_attribute(name)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    self._attributes[name.to_sym]
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  def xml_attribute_names
         
     | 
| 
      
 59 
     | 
    
         
            +
                    self._attributes.values.map(&:name)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                  def attribute_names
         
     | 
| 
      
 63 
     | 
    
         
            +
                    self._attributes.keys
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
            end
         
     |