active_merge 1.0.0 → 1.0.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.
- checksums.yaml +7 -0
- data/lib/active_merge/service.rb +23 -55
- data/lib/active_merge/simple_service.rb +77 -0
- data/lib/active_merge/version.rb +1 -1
- data/lib/active_merge.rb +2 -0
- metadata +22 -33
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: 9f04e96b337986ab01516a571764953292c4cf46
         | 
| 4 | 
            +
              data.tar.gz: e51483094adf87e9f17596876c74feda706dddad
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: b1c2930d0ffb8c6c0e996fb2aeef7701d211031dfcad1ff6e90f0ef117620de45f676028347c1763785f2a51acab8311b26592873ccd483ea982972f449aecc2
         | 
| 7 | 
            +
              data.tar.gz: a4365f27dd04f85929042e8f07a25837386af8bfc0c96d12dd5f7c0ddaca69d867a74b50c5853c319f8dd503cd86399fc2b59269a5f13b265a5b56a69a52d9f2
         | 
    
        data/lib/active_merge/service.rb
    CHANGED
    
    | @@ -67,99 +67,67 @@ module ActiveMerge | |
| 67 67 | 
             
              # то объединения не произойдет!
         | 
| 68 68 | 
             
              #
         | 
| 69 69 | 
             
              class Service
         | 
| 70 | 
            +
                include ActiveModel::Validations
         | 
| 70 71 |  | 
| 71 72 | 
             
                def initialize(list = [])
         | 
| 72 | 
            -
                  list =  | 
| 73 | 
            +
                  list = extract_from list
         | 
| 73 74 | 
             
                  @item, @items = list.first, Array(list[1..-1])
         | 
| 74 75 | 
             
                end
         | 
| 75 76 |  | 
| 76 77 | 
             
                attr_reader :item, :items, :klass, :klasses
         | 
| 77 | 
            -
             | 
| 78 | 
            -
                # Возвращает класс объединяемых объектов
         | 
| 79 | 
            -
                def klass
         | 
| 80 | 
            -
                  @klass ||= item.class
         | 
| 81 | 
            -
                end
         | 
| 82 | 
            -
             | 
| 83 | 
            -
                # Возвращает хэш, в котором ключами выступают связи has_many,
         | 
| 84 | 
            -
                # а значениями - foreign keys
         | 
| 85 | 
            -
                def klasses
         | 
| 86 | 
            -
                  @klasses ||= klass.reflect_on_all_associations(:has_many).
         | 
| 87 | 
            -
                    inject({}){ |hash, item| hash.merge(item.name => item.foreign_key) }
         | 
| 88 | 
            -
                end
         | 
| 78 | 
            +
                validates   :items, presence: true
         | 
| 89 79 |  | 
| 90 80 | 
             
                # Объединяет все записи из переменной #items с записью в переменной #item
         | 
| 91 81 | 
             
                #
         | 
| 92 82 | 
             
                # При этом:
         | 
| 93 | 
            -
                # * все ссылки на записи из массива #items  | 
| 83 | 
            +
                # * все ссылки на записи из массива #items перепривязываются к #item
         | 
| 94 84 | 
             
                # * все записи #items удаляются
         | 
| 95 85 | 
             
                #
         | 
| 96 86 | 
             
                # При любой ошибке все сделанные изменения отменяются.
         | 
| 97 87 | 
             
                #
         | 
| 98 88 | 
             
                def provide
         | 
| 99 89 | 
             
                  ActiveRecord::Base.transaction requires_new: true do
         | 
| 100 | 
            -
                     | 
| 101 | 
            -
                    items.each{ |item|  | 
| 90 | 
            +
                    raise unless valid?
         | 
| 91 | 
            +
                    items.each{ |item| merge self.item, item }
         | 
| 102 92 | 
             
                  end
         | 
| 103 93 | 
             
                end
         | 
| 104 94 |  | 
| 105 95 | 
             
                private
         | 
| 106 96 |  | 
| 107 | 
            -
                  # Вызывает исключение указанного типа с переданными парамертами
         | 
| 108 | 
            -
                  def _raise(type, item = nil)
         | 
| 109 | 
            -
                    options = item ? { type: item.class.name.underscore, id: item.id } : {}
         | 
| 110 | 
            -
                    options.merge! scope: %w(active_merge errors service)
         | 
| 111 | 
            -
                    raise I18n.t(type, options)
         | 
| 112 | 
            -
                  end
         | 
| 113 | 
            -
             | 
| 114 97 | 
             
                  # Извлекает из списка только сохраненные объекты ActiveRecord
         | 
| 115 98 | 
             
                  # Если список содержит элементы ActiveRecord разных классов, возвращает
         | 
| 116 99 | 
             
                  # пустой массив.
         | 
| 117 | 
            -
                  def  | 
| 100 | 
            +
                  def extract_from(list)
         | 
| 118 101 | 
             
                    begin
         | 
| 119 | 
            -
                      list | 
| 120 | 
            -
                       | 
| 121 | 
            -
                      list.each { |item| raise unless item.class == klass }
         | 
| 122 | 
            -
                      list.sort_by { |item| item.id }
         | 
| 102 | 
            +
                      list = select_active_records_from list
         | 
| 103 | 
            +
                      list = select_by_class_from list
         | 
| 123 104 | 
             
                    rescue
         | 
| 124 105 | 
             
                      []
         | 
| 125 106 | 
             
                    end
         | 
| 126 107 | 
             
                  end
         | 
| 127 108 |  | 
| 128 | 
            -
                  #  | 
| 129 | 
            -
                  def  | 
| 130 | 
            -
                     | 
| 109 | 
            +
                  # Выбирает сохраненные объекты ActiveRecord
         | 
| 110 | 
            +
                  def select_active_records_from(list)
         | 
| 111 | 
            +
                    list.select do |item|
         | 
| 131 112 | 
             
                      item.class.ancestors.include?(ActiveRecord::Base) && item.persisted?
         | 
| 132 | 
            -
                    rescue
         | 
| 133 | 
            -
                      false
         | 
| 134 | 
            -
                    end
         | 
| 135 | 
            -
                  end
         | 
| 136 | 
            -
             | 
| 137 | 
            -
                  # Объединяет указанный объект с объектом из переменной #item
         | 
| 138 | 
            -
                  # * Перепривязывает ссылки на объект #item
         | 
| 139 | 
            -
                  # * Удаляет объект, переданный в аргументе
         | 
| 140 | 
            -
                  def _merge_one(item)
         | 
| 141 | 
            -
                    klasses.each do |list, foreign_key|
         | 
| 142 | 
            -
                      item.send(list).each { |ref| _rebind! ref, foreign_key }
         | 
| 143 113 | 
             
                    end
         | 
| 144 | 
            -
                    _destroy! item
         | 
| 145 114 | 
             
                  end
         | 
| 146 115 |  | 
| 147 | 
            -
                  #  | 
| 148 | 
            -
                  def  | 
| 149 | 
            -
                     | 
| 150 | 
            -
             | 
| 151 | 
            -
             | 
| 152 | 
            -
                    rescue
         | 
| 153 | 
            -
                      _raise :rebind, item
         | 
| 154 | 
            -
                    end
         | 
| 116 | 
            +
                  # Выбирает объекты того же класса, что и у первой записи списка
         | 
| 117 | 
            +
                  def select_by_class_from(list)
         | 
| 118 | 
            +
                    klass = list.first.class
         | 
| 119 | 
            +
                    list.each { |item| raise unless item.class == klass }
         | 
| 120 | 
            +
                    list.sort_by { |item| item.id }
         | 
| 155 121 | 
             
                  end
         | 
| 156 122 |  | 
| 157 | 
            -
                  #  | 
| 158 | 
            -
                   | 
| 123 | 
            +
                  # Объединяет две записи.
         | 
| 124 | 
            +
                  # Переносит ошибки в текущий объект.
         | 
| 125 | 
            +
                  def merge(first, second)
         | 
| 126 | 
            +
                    service = ActiveMerge::SimpleService.new(first, second)
         | 
| 159 127 | 
             
                    begin
         | 
| 160 | 
            -
                       | 
| 128 | 
            +
                      service.provide
         | 
| 161 129 | 
             
                    rescue
         | 
| 162 | 
            -
                       | 
| 130 | 
            +
                      service.errors.each{ |key, val| errors.add key, val } and raise
         | 
| 163 131 | 
             
                    end
         | 
| 164 132 | 
             
                  end
         | 
| 165 133 | 
             
              end
         | 
| @@ -0,0 +1,77 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
            module ActiveMerge
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              # Сервисный объект (паттерн Service Object), отвечающий за объединение
         | 
| 5 | 
            +
              # двух записей ActiveRecord
         | 
| 6 | 
            +
              #
         | 
| 7 | 
            +
              # В ходе объединения все ссылки на вторую запись перепривязываются к первой,
         | 
| 8 | 
            +
              # затем вторая запись удаляется
         | 
| 9 | 
            +
              #
         | 
| 10 | 
            +
              # В случае любой ошибки вызывается исключение, а ошибка добавляется в массив
         | 
| 11 | 
            +
              # <tt>errors</tt>
         | 
| 12 | 
            +
              #
         | 
| 13 | 
            +
              class SimpleService
         | 
| 14 | 
            +
                include ActiveModel::Validations
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def initialize(first, second)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  if first.class.ancestors.include?(ActiveRecord::Base) && first.persisted?
         | 
| 19 | 
            +
                    @first = first
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  if @first && (second.class == @first.class) && second.persisted?
         | 
| 23 | 
            +
                    @second = second
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
                attr_reader :first, :second
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                validates :second, presence: true
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def provide
         | 
| 31 | 
            +
                  ActiveRecord::Base.transaction requires_new: true do
         | 
| 32 | 
            +
                    raise unless valid?
         | 
| 33 | 
            +
                    refs.each{ |item, key| rebind(item, key) }
         | 
| 34 | 
            +
                    destroy
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                private
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  # Возвращает хэш, в котором ключами выступают связи has_many,
         | 
| 41 | 
            +
                  # а значениями - foreign keys
         | 
| 42 | 
            +
                  def klasses
         | 
| 43 | 
            +
                    second.class.reflect_on_all_associations(:has_many).
         | 
| 44 | 
            +
                      inject({}){ |hash, item| hash.merge(item.name => item.foreign_key) }
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  # Возвращает хэш, в котором ключами выступают объекты, ссылающихся на 
         | 
| 48 | 
            +
                  # #second, а значениями - методы присвоения ссылки на #first.
         | 
| 49 | 
            +
                  def refs
         | 
| 50 | 
            +
                    @refs = {}
         | 
| 51 | 
            +
                    klasses.each do |list, key|
         | 
| 52 | 
            +
                      second.send(list).each{ |item| @refs[item] = "#{ key }=" }
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
                    return @refs
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  # Выполняет перепривязку указанного объекта к первому аргументу
         | 
| 58 | 
            +
                  # Ошибки накапливаются в массиве <tt>errors</tt>
         | 
| 59 | 
            +
                  def rebind(item, key)
         | 
| 60 | 
            +
                    begin
         | 
| 61 | 
            +
                      item.send key, first.id
         | 
| 62 | 
            +
                      item.save!
         | 
| 63 | 
            +
                    rescue
         | 
| 64 | 
            +
                      item.errors.each{ |key, val| errors.add key, val } and raise
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  # Удаляет объединяемый объект
         | 
| 69 | 
            +
                  def destroy
         | 
| 70 | 
            +
                    begin
         | 
| 71 | 
            +
                      second.destroy!
         | 
| 72 | 
            +
                    rescue
         | 
| 73 | 
            +
                      second.errors.each{ |key, val| errors.add key, val } and raise
         | 
| 74 | 
            +
                    end
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
              end
         | 
| 77 | 
            +
            end
         | 
    
        data/lib/active_merge/version.rb
    CHANGED
    
    
    
        data/lib/active_merge.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,94 +1,83 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: active_merge
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1.0. | 
| 5 | 
            -
              prerelease: 
         | 
| 4 | 
            +
              version: 1.0.1
         | 
| 6 5 | 
             
            platform: ruby
         | 
| 7 6 | 
             
            authors:
         | 
| 8 7 | 
             
            - Andrew Kozin
         | 
| 9 8 | 
             
            autorequire: 
         | 
| 10 9 | 
             
            bindir: bin
         | 
| 11 10 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2014-03- | 
| 11 | 
            +
            date: 2014-03-17 00:00:00.000000000 Z
         | 
| 13 12 | 
             
            dependencies:
         | 
| 14 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 14 | 
             
              name: rails
         | 
| 16 15 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 17 | 
            -
                none: false
         | 
| 18 16 | 
             
                requirements:
         | 
| 19 | 
            -
                - - ~>
         | 
| 17 | 
            +
                - - "~>"
         | 
| 20 18 | 
             
                  - !ruby/object:Gem::Version
         | 
| 21 19 | 
             
                    version: 4.0.3
         | 
| 22 20 | 
             
              type: :runtime
         | 
| 23 21 | 
             
              prerelease: false
         | 
| 24 22 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 25 | 
            -
                none: false
         | 
| 26 23 | 
             
                requirements:
         | 
| 27 | 
            -
                - - ~>
         | 
| 24 | 
            +
                - - "~>"
         | 
| 28 25 | 
             
                  - !ruby/object:Gem::Version
         | 
| 29 26 | 
             
                    version: 4.0.3
         | 
| 30 27 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 31 28 | 
             
              name: sqlite3
         | 
| 32 29 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 33 | 
            -
                none: false
         | 
| 34 30 | 
             
                requirements:
         | 
| 35 | 
            -
                - -  | 
| 31 | 
            +
                - - ">="
         | 
| 36 32 | 
             
                  - !ruby/object:Gem::Version
         | 
| 37 33 | 
             
                    version: '0'
         | 
| 38 34 | 
             
              type: :development
         | 
| 39 35 | 
             
              prerelease: false
         | 
| 40 36 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 41 | 
            -
                none: false
         | 
| 42 37 | 
             
                requirements:
         | 
| 43 | 
            -
                - -  | 
| 38 | 
            +
                - - ">="
         | 
| 44 39 | 
             
                  - !ruby/object:Gem::Version
         | 
| 45 40 | 
             
                    version: '0'
         | 
| 46 41 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 47 42 | 
             
              name: rspec
         | 
| 48 43 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 49 | 
            -
                none: false
         | 
| 50 44 | 
             
                requirements:
         | 
| 51 | 
            -
                - -  | 
| 45 | 
            +
                - - ">="
         | 
| 52 46 | 
             
                  - !ruby/object:Gem::Version
         | 
| 53 47 | 
             
                    version: '0'
         | 
| 54 48 | 
             
              type: :development
         | 
| 55 49 | 
             
              prerelease: false
         | 
| 56 50 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 57 | 
            -
                none: false
         | 
| 58 51 | 
             
                requirements:
         | 
| 59 | 
            -
                - -  | 
| 52 | 
            +
                - - ">="
         | 
| 60 53 | 
             
                  - !ruby/object:Gem::Version
         | 
| 61 54 | 
             
                    version: '0'
         | 
| 62 55 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 63 56 | 
             
              name: yard
         | 
| 64 57 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 65 | 
            -
                none: false
         | 
| 66 58 | 
             
                requirements:
         | 
| 67 | 
            -
                - -  | 
| 59 | 
            +
                - - ">="
         | 
| 68 60 | 
             
                  - !ruby/object:Gem::Version
         | 
| 69 61 | 
             
                    version: '0'
         | 
| 70 62 | 
             
              type: :development
         | 
| 71 63 | 
             
              prerelease: false
         | 
| 72 64 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 73 | 
            -
                none: false
         | 
| 74 65 | 
             
                requirements:
         | 
| 75 | 
            -
                - -  | 
| 66 | 
            +
                - - ">="
         | 
| 76 67 | 
             
                  - !ruby/object:Gem::Version
         | 
| 77 68 | 
             
                    version: '0'
         | 
| 78 69 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 79 70 | 
             
              name: database_cleaner
         | 
| 80 71 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 81 | 
            -
                none: false
         | 
| 82 72 | 
             
                requirements:
         | 
| 83 | 
            -
                - -  | 
| 73 | 
            +
                - - ">="
         | 
| 84 74 | 
             
                  - !ruby/object:Gem::Version
         | 
| 85 75 | 
             
                    version: '0'
         | 
| 86 76 | 
             
              type: :development
         | 
| 87 77 | 
             
              prerelease: false
         | 
| 88 78 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 89 | 
            -
                none: false
         | 
| 90 79 | 
             
                requirements:
         | 
| 91 | 
            -
                - -  | 
| 80 | 
            +
                - - ">="
         | 
| 92 81 | 
             
                  - !ruby/object:Gem::Version
         | 
| 93 82 | 
             
                    version: '0'
         | 
| 94 83 | 
             
            description: Declares the ActiveMerge module with the 'merge!' class method.
         | 
| @@ -98,36 +87,36 @@ executables: [] | |
| 98 87 | 
             
            extensions: []
         | 
| 99 88 | 
             
            extra_rdoc_files: []
         | 
| 100 89 | 
             
            files:
         | 
| 101 | 
            -
            - lib/active_merge/service.rb
         | 
| 102 | 
            -
            - lib/active_merge/version.rb
         | 
| 103 | 
            -
            - lib/active_merge.rb
         | 
| 104 90 | 
             
            - MIT-LICENSE
         | 
| 105 | 
            -
            - Rakefile
         | 
| 106 91 | 
             
            - README.rdoc
         | 
| 92 | 
            +
            - Rakefile
         | 
| 93 | 
            +
            - lib/active_merge.rb
         | 
| 94 | 
            +
            - lib/active_merge/service.rb
         | 
| 95 | 
            +
            - lib/active_merge/simple_service.rb
         | 
| 96 | 
            +
            - lib/active_merge/version.rb
         | 
| 107 97 | 
             
            homepage: https://github.com/nepalez/active_merge
         | 
| 108 98 | 
             
            licenses:
         | 
| 109 99 | 
             
            - MIT
         | 
| 100 | 
            +
            metadata: {}
         | 
| 110 101 | 
             
            post_install_message: 
         | 
| 111 102 | 
             
            rdoc_options: []
         | 
| 112 103 | 
             
            require_paths:
         | 
| 113 104 | 
             
            - lib
         | 
| 114 105 | 
             
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 115 | 
            -
              none: false
         | 
| 116 106 | 
             
              requirements:
         | 
| 117 | 
            -
              - -  | 
| 107 | 
            +
              - - ">="
         | 
| 118 108 | 
             
                - !ruby/object:Gem::Version
         | 
| 119 109 | 
             
                  version: '0'
         | 
| 120 110 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 121 | 
            -
              none: false
         | 
| 122 111 | 
             
              requirements:
         | 
| 123 | 
            -
              - -  | 
| 112 | 
            +
              - - ">="
         | 
| 124 113 | 
             
                - !ruby/object:Gem::Version
         | 
| 125 114 | 
             
                  version: '0'
         | 
| 126 115 | 
             
            requirements: []
         | 
| 127 116 | 
             
            rubyforge_project: 
         | 
| 128 | 
            -
            rubygems_version:  | 
| 117 | 
            +
            rubygems_version: 2.2.2
         | 
| 129 118 | 
             
            signing_key: 
         | 
| 130 | 
            -
            specification_version:  | 
| 119 | 
            +
            specification_version: 4
         | 
| 131 120 | 
             
            summary: Merges instances of an ActiveRecord class.
         | 
| 132 121 | 
             
            test_files: []
         | 
| 133 122 | 
             
            has_rdoc: 
         |