active_merge 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rdoc +16 -0
  3. data/{MIT-LICENSE → LICENSE} +1 -1
  4. data/README.rdoc +1 -1
  5. data/Rakefile +13 -1
  6. data/lib/active_merge/service.rb +57 -72
  7. data/lib/active_merge/simple_service.rb +36 -39
  8. data/lib/active_merge/version.rb +1 -2
  9. data/lib/active_merge.rb +9 -9
  10. data/spec/active_merge/service_spec.rb +205 -0
  11. data/spec/active_merge/simple_service_spec.rb +127 -0
  12. data/spec/active_merge_spec.rb +21 -0
  13. data/spec/dummy/README.rdoc +28 -0
  14. data/spec/dummy/Rakefile +6 -0
  15. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  16. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  17. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  18. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  19. data/spec/dummy/app/models/domain.rb +3 -0
  20. data/spec/dummy/app/models/lord.rb +4 -0
  21. data/spec/dummy/app/models/man.rb +3 -0
  22. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  23. data/spec/dummy/bin/bundle +3 -0
  24. data/spec/dummy/bin/rails +4 -0
  25. data/spec/dummy/bin/rake +4 -0
  26. data/spec/dummy/config/application.rb +30 -0
  27. data/spec/dummy/config/boot.rb +5 -0
  28. data/spec/dummy/config/database.yml +25 -0
  29. data/spec/dummy/config/environment.rb +5 -0
  30. data/spec/dummy/config/environments/development.rb +29 -0
  31. data/spec/dummy/config/environments/production.rb +80 -0
  32. data/spec/dummy/config/environments/test.rb +36 -0
  33. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  34. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  35. data/spec/dummy/config/initializers/inflections.rb +16 -0
  36. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  37. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  38. data/spec/dummy/config/initializers/session_store.rb +3 -0
  39. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  40. data/spec/dummy/config/locales/ru.yml +7 -0
  41. data/spec/dummy/config/routes.rb +56 -0
  42. data/spec/dummy/config.ru +4 -0
  43. data/spec/dummy/db/migrate/20140416183029_create_lords.rb +6 -0
  44. data/spec/dummy/db/migrate/20140416183043_create_men.rb +8 -0
  45. data/spec/dummy/db/migrate/20140416183059_create_domains.rb +8 -0
  46. data/spec/dummy/db/schema.rb +31 -0
  47. data/spec/dummy/db/test.sqlite3 +0 -0
  48. data/spec/dummy/log/development.log +38 -0
  49. data/spec/dummy/log/test.log +25184 -0
  50. data/spec/dummy/public/404.html +58 -0
  51. data/spec/dummy/public/422.html +58 -0
  52. data/spec/dummy/public/500.html +57 -0
  53. data/spec/dummy/public/favicon.ico +0 -0
  54. data/spec/spec_helper.rb +53 -0
  55. data/spec/support/errors.rb +17 -0
  56. metadata +125 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 94958ab074d4d742ad174a71fa0681622073ac3c
4
- data.tar.gz: b8ab11b01746280d598ffc584622d17131384b6a
3
+ metadata.gz: 9908a773d23bf5b0b67850538549bf130646b257
4
+ data.tar.gz: e61c2879ca3ff6a256ac575651c69432b97c3c15
5
5
  SHA512:
6
- metadata.gz: 95b4c45ac956d9dec90090f4d51b44ec34b35321ce510c8d85e1d60f787558e7c5dd6ff22ed00b7582561480eba0e4c5c8c96d9e931712253c36cd33f3a7420b
7
- data.tar.gz: 966ecb1a67d54ff45bb23a46cef7d5369394851fb9b4707758d9341b48e5769a6098ae7ff1955a41984a91c9cb99cbd5115fa00ef86feff94cbd2b7517d33b0b
6
+ metadata.gz: d189fc4e6e22e3806c1bcf7350130718c4e85170c6d9b51924a7b206cbbdbec9ecae588d339a36bec6fc2650b7a75d3883d51d718663159226b94e7cae6b4511
7
+ data.tar.gz: eccd6bbd86184af049cb31f3229b4c575ab9ba478c7b0b4a7e3ba97af443ec62467424761f25f8e5bd89ea9c3446dc000f3f73b789a787a1c16d13eb961fd1ac
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,16 @@
1
+ = Список изменений в версиях
2
+
3
+ == v.1.0.4
4
+
5
+ * The gem is allowed to use with rails 4.1
6
+
7
+ == v.1.0.2
8
+
9
+ * Documentation bug fixed. References to the <tt>#merge</tt> changed to the <tt>#merge_all</tt>
10
+
11
+ == v.1.0.1
12
+
13
+ * The <tt>Service#klass</tt> and <tt>Service#klasses</tt> methods removed
14
+ * The <tt>Service</tt> class includes <tt>ActiveModels::Validations</tt>
15
+ * The <tt>Service#valid?</tt> method added
16
+ * Service object errors collected in the <tt>Service#errors</tt>
@@ -1,4 +1,4 @@
1
- Copyright 2014 YOURNAME
1
+ Copyright 2014 Andrew Kozin
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.rdoc CHANGED
@@ -28,4 +28,4 @@ When merging a list of instances:
28
28
 
29
29
  == License
30
30
 
31
- This project rocks and uses link:MIT-LICENSE.
31
+ This project rocks and uses MIT-LICENSE[link:LICENSE].
data/Rakefile CHANGED
@@ -20,10 +20,22 @@ RDoc::Task.new(:rdoc) do |rdoc|
20
20
  rdoc.rdoc_files.include('lib/**/*.rb')
21
21
  end
22
22
 
23
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
23
26
  Bundler::GemHelper.install_tasks
24
27
 
25
28
  require "bundler/gem_tasks"
26
29
  require 'rspec/core/rake_task'
27
30
 
28
31
  RSpec::Core::RakeTask.new(:spec)
29
- task default: :spec
32
+ task :default do
33
+ sh "bundle exec rake db:migrate RAILS_ENV=test"
34
+ sh "mkdir spec/dummy/tmp" do |ok, res|
35
+ nunitSuccessFlag = false
36
+ end
37
+ sh "mkdir spec/dummy/tmp/cache" do |ok, res|
38
+ nunitSuccessFlag = false
39
+ end
40
+ sh "bundle exec rspec spec"
41
+ end
@@ -1,24 +1,29 @@
1
1
  # encoding: utf-8
2
2
  module ActiveMerge
3
3
 
4
- # Сервисный объект (паттерн Service Object), отвечающий за объединение
5
- # всех записей ActiveRecord, переданных в аргументе.
4
+ # Service Object responds for merging given ActiveRecord instances into the
5
+ # first one
6
6
  #
7
- # При инициализации указывается ActiveRecord::Association или массив объектов.
8
- # Из массива выбираются только сохраненные объекты ActiveRecord, причем все
9
- # они должны быть объектами одного класса.
7
+ # Object initializes either with ActiveRecord::Association or array of objects.
8
+ # If the argument is an array, then only those items taken into account, that:
10
9
  #
11
- # При объединении:
12
- # * Сохраняется первый объект (с наименьшим id)
13
- # * У всех прочих объектов ассоциации has_many перепривязываются к первому объекту
14
- # * После перепривязки все объекты, кроме первого, удаляются.
10
+ # * inherited from the <tt>ActiveRecord::Base</tt>
11
+ # * persisted
12
+ # * are objects of one class
15
13
  #
16
- # В результате остается только один объект, к которому привязаны все подобъекты,
17
- # ранее относившиеся к объединяемым записям.
14
+ # == Merging algorithm
18
15
  #
19
- # == Пример:
16
+ # * The item with minimal <tt>id</tt> selected from the service argument. It will remaine intact.
17
+ # * All objects, that the other items links to via "has_many" assotiation, are selected...
18
+ # * ... and are rebound to the first item
19
+ # * After rebinding any item from the service argument, except the first one, to be deleted.
20
20
  #
21
- # # Рассмотрим модель королевства, имеющего много графств и жителей
21
+ # As a result it is only the first object from the argument remains intact,
22
+ # and all the instances that belonged to the other ones are now belongs to it.
23
+ #
24
+ # == Example:
25
+ #
26
+ # # Let any kingdom has many shires and men
22
27
  # class Kingdom < ActiveRecord::Base
23
28
  # has_many :shires
24
29
  # has_many :men
@@ -32,102 +37,82 @@ module ActiveMerge
32
37
  # belongs_to :kingdom
33
38
  # end
34
39
  #
35
- # # У Британии есть 100 тыс. жителей и 10 графств
40
+ # # Britain has 10 shires and 100 thousands men lives there
36
41
  # britain = Kingdom.create!
37
- # 1000000.times.each{ britain.men.create! }
42
+ # 100000.times.each{ britain.men.create! }
38
43
  # 10.times.each{ britain.shires.create! }
39
44
  #
40
- # # У Шотландии есть 30 тыс. жителей и 5 графств
45
+ # # Scotland has 5 shires and 30 thousands living men
41
46
  # scotland = Kingdom.create!
42
- # 300000.times.each{ britain.men.create! }
43
- # 5.times.each{ britain.shires.create! }
47
+ # 30000.times.each{ scotland.men.create! }
48
+ # 5.times.each{ scotland.shires.create! }
44
49
  #
45
- # # Объединяем королевства
50
+ # # Lets merge all the kingdoms:
46
51
  # Service.new(Kingdom.all).submit!
47
52
  #
48
- # # К Британии (добавлена ранее) присоединены шотландские люди и графства
53
+ # # Now union Britain (because it is Britain that was created first)
54
+ # # has all those 15 shires and 130 thousand men
55
+ # # from both the old good Britain and the old good Scotland.
49
56
  # britain.reload.men.count # => 130000
50
57
  # britain.reload.shires.count # => 15
51
58
  #
52
- # # Королевство Шотландия удалено
59
+ # # And, alas, the Scotland Kingdom doesn't exists any more
53
60
  # Kingdom.find_by(scotland.id) # => nil
54
61
  #
55
- # == Обратите внимание!
62
+ # == Warning!
56
63
  #
57
- # Объединение происходит единой транзакцией. При любой ошибке все изменения
58
- # отменяются.
64
+ # The merge provided as a whole transaction. In case any error occures, all
65
+ # the changes rolled back.
59
66
  #
60
- # Если в примере выше графство не может быть перепривязано к новому
61
- # королевству (незыблемость суверенитета):
67
+ # Let (see the example above) the shire cannot be rebount to another kingdom:
62
68
  #
63
69
  # class Shire
64
70
  # attr_readonly :kingdom_id
65
71
  # end
66
72
  #
67
- # то объединения не произойдет!
73
+ # then merging won't be finished.
74
+ #
75
+ # Not only shires, but also the scots remain living in their Scotland,
76
+ # not in the United Kingdom!
68
77
  #
69
- class Service
78
+ class Service < ActivePatterns::BaseService
70
79
  include ActiveModel::Validations
71
80
 
72
- def initialize(list = [])
73
- list = extract_from list
81
+ def initialize(list = nil)
82
+ list = Items.new list
74
83
  @item, @items = list.first, Array(list[1..-1])
75
84
  end
76
85
 
77
86
  attr_reader :item, :items, :klass, :klasses
78
87
  validates :items, presence: true
79
88
 
80
- # Объединяет все записи из переменной #items с записью в переменной #item
81
- #
82
- # При этом:
83
- # * все ссылки на записи из массива #items перепривязываются к #item
84
- # * все записи #items удаляются
85
- #
86
- # При любой ошибке все сделанные изменения отменяются.
87
- #
89
+ # Merges all the #items to the #item as a whole transaction
88
90
  def provide
89
- ActiveRecord::Base.transaction requires_new: true do
90
- raise unless valid?
91
- items.each{ |item| merge self.item, item }
91
+ transaction do
92
+ items.each do |item|
93
+ service = SimpleService.new(self.item, item)
94
+ change(service) { service.provide }
95
+ end
92
96
  end
93
97
  end
94
98
 
95
99
  private
96
100
 
97
- # Извлекает из списка только сохраненные объекты ActiveRecord
98
- # Если список содержит элементы ActiveRecord разных классов, возвращает
99
- # пустой массив.
100
- def extract_from(list)
101
- begin
102
- list = select_active_records_from list
103
- list = select_by_class_from list
104
- rescue
105
- []
101
+ # Returns only persisted items of the same ActiveRecord model
102
+ class Items < Array
103
+ def initialize(items)
104
+ items = Array(items).map{ |item| Item.new item }.compact
105
+ items = [] if items.map{ |item| item.class }.uniq.count > 1
106
+ super items.sort_by{ |item| item.id }
106
107
  end
107
108
  end
108
109
 
109
- # Выбирает сохраненные объекты ActiveRecord
110
- def select_active_records_from(list)
111
- list.select do |item|
112
- item.class.ancestors.include?(ActiveRecord::Base) && item.persisted?
113
- end
114
- end
115
-
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 }
121
- end
122
-
123
- # Объединяет две записи.
124
- # Переносит ошибки в текущий объект.
125
- def merge(first, second)
126
- service = ActiveMerge::SimpleService.new(first, second)
127
- begin
128
- service.provide
129
- rescue
130
- service.errors.each{ |key, val| errors.add key, val } and raise
110
+ # Initializes a persisted item of an ActiveRecord model
111
+ class Item
112
+ def self.new(item)
113
+ return unless item.class.ancestors.include? ActiveRecord::Base
114
+ return unless item.persisted?
115
+ item
131
116
  end
132
117
  end
133
118
  end
@@ -1,16 +1,16 @@
1
1
  # encoding: utf-8
2
2
  module ActiveMerge
3
3
 
4
- # Сервисный объект (паттерн Service Object), отвечающий за объединение
5
- # двух записей ActiveRecord
4
+ # Service Object responds for merging two ActiveRecord instances as a whole
5
+ # transaction.
6
6
  #
7
- # В ходе объединения все ссылки на вторую запись перепривязываются к первой,
8
- # затем вторая запись удаляется
7
+ # All objects linked to the second instance via "has_many" association
8
+ # are re-associated to the first one.
9
+ #
10
+ # Then the second instance removed.
11
+ # All errors collected in the #errors method.
9
12
  #
10
- # В случае любой ошибки вызывается исключение, а ошибка добавляется в массив
11
- # <tt>errors</tt>
12
- #
13
- class SimpleService
13
+ class SimpleService < ActivePatterns::BaseService
14
14
  include ActiveModel::Validations
15
15
 
16
16
  def initialize(first, second)
@@ -28,49 +28,46 @@ module ActiveMerge
28
28
  validates :second, presence: true
29
29
 
30
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
31
+ transaction do
32
+ Links.new(second).each { |item, key| rebind(item, key) }
33
+ change(second) { second.destroy! }
35
34
  end
36
35
  end
37
36
 
38
37
  private
39
38
 
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
39
+ # Initializes a hash, whose keys are instances linked to given one,
40
+ # and their values are names of methods for linking to another object.
41
+ class Links
42
+ class << self
43
+
44
+ def new(item)
45
+ @item = item
46
+ return hash
47
+ end
48
+
49
+ # Returns a hash whose keys are "has_many" associations' names
50
+ # and their values are corresponding foreign keys
51
+ def refs
52
+ @item.class.reflect_on_all_associations(:has_many).
53
+ inject({}){ |hash, item| hash.merge(item.name => item.foreign_key) }
54
+ end
46
55
 
47
- # Возвращает хэш, в котором ключами выступают объекты, ссылающихся на
48
- # #second, а значениями - методы присвоения ссылки на #first.
49
- def refs
50
- @refs = {}
51
- klasses.each do |list, key|
52
- second.send(list).each{ |item| @refs[item] = "#{ key }=" }
56
+ def hash
57
+ @hash = {}
58
+ refs.each do |name, foreign_key|
59
+ @item.send(name).each{ |item| @hash[item] = "#{ foreign_key }=" }
60
+ end
61
+ @hash
62
+ end
53
63
  end
54
- return @refs
55
64
  end
56
65
 
57
- # Выполняет перепривязку указанного объекта к первому аргументу
58
- # Ошибки накапливаются в массиве <tt>errors</tt>
66
+ # Re-assigns given object to the #first
59
67
  def rebind(item, key)
60
- begin
68
+ change item do
61
69
  item.send key, first.id
62
70
  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
71
  end
75
72
  end
76
73
  end
@@ -1,4 +1,3 @@
1
1
  module ActiveMerge
2
- # Текущая версия плагина
3
- VERSION = "1.0.4"
2
+ VERSION = "1.0.5"
4
3
  end
data/lib/active_merge.rb CHANGED
@@ -1,9 +1,9 @@
1
- # encoding: utf-8
1
+ require "active_patterns"
2
2
 
3
- # Модуль содержит методы объединения записей ActiveRecord
3
+ # Declares service object for merging ActiveRecord instances.
4
4
  #
5
- # После расширения класса, унаследованного от <tt>ActiveRecord::Base</tt>
6
- # становится доступен метод класса <tt>::merge_all</tt>, объединяющий записи.
5
+ # After extending your active record model with the module,
6
+ # new <tt>::merge_all</tt> method is available.
7
7
  #
8
8
  module ActiveMerge
9
9
  extend ActiveSupport::Autoload
@@ -11,18 +11,18 @@ module ActiveMerge
11
11
  autoload :SimpleService
12
12
  autoload :Service
13
13
 
14
- # Объединение указанных записей.
14
+ # Merges instances from the association
15
15
  #
16
16
  # class Lord < ActiveRecord::Base
17
17
  # extend ActiveMerge
18
18
  # end
19
19
  #
20
- # Lord.all.merge_all # => объединяет все записи
21
- # Lord.where(id > 100) # => объединяет все записи с id > 100
20
+ # Lord.all.merge_all # => merges all the class instances
21
+ # Lord.where("id > :id", id: 100) # => merges instances with id > 100
22
22
  #
23
- # Детали см. в описании метода <tt>ActiveMerge::Service#provide</tt>
23
+ # See details in <tt>ActiveMerge::Service#provide</tt> documentation.
24
24
  #
25
25
  def merge_all
26
- ActiveMerge::Service.new(self).provide
26
+ ActiveMerge::Service.new(all).provide
27
27
  end
28
28
  end
@@ -0,0 +1,205 @@
1
+ # encoding: utf-8
2
+ require "spec_helper"
3
+
4
+ module ActiveMerge
5
+ describe Service do
6
+
7
+ describe "#item" do
8
+
9
+ it "объявлен" do
10
+ Service.new.should respond_to :item
11
+ end
12
+ end
13
+
14
+ describe "#items" do
15
+
16
+ it "объявлен" do
17
+ Service.new.should respond_to :items
18
+ end
19
+ end
20
+
21
+ describe "::new" do
22
+
23
+ let!(:items) { 3.times.map{ Man.create! }}
24
+
25
+ context "если передан запрос ActiveRelations" do
26
+
27
+ let!(:service) { Service.new Man.all }
28
+
29
+ it "присваивает переменной item первый объект (с минимальным id)" do
30
+ service.item.should eq Man.first
31
+ end
32
+
33
+ it "присваивает переменной items все объекты, кроме первого (с минимальным id)" do
34
+ service.items.should eq Man.all.order("id ASC").to_a[1..-1]
35
+ end
36
+ end
37
+
38
+ context "если запрос возвращает только один элемент" do
39
+
40
+ let!(:service) { Service.new Man.where(id: Man.first.id) }
41
+
42
+ it "присваивает переменной item первый объект (с минимальным id)" do
43
+ service.item.should eq Man.first
44
+ end
45
+
46
+ it "присваивает переменной items пустой массив" do
47
+ service.items.should eq []
48
+ end
49
+ end
50
+
51
+ context "если запрос возвращает пустой массив" do
52
+
53
+ let!(:service) { Service.new Man.where(id: 0) }
54
+
55
+ it "присваивает nil переменной item" do
56
+ service.item.should be_nil
57
+ end
58
+
59
+ it "присваивает пустой массив переменной items" do
60
+ service.items.should eq []
61
+ end
62
+ end
63
+
64
+ context "если передан массив объектов ActiveRecord вместе с другими, non-Active record элементами" do
65
+
66
+ let!(:service) { Service.new (items + [Man.new, nil, 1]).shuffle }
67
+
68
+ it "присваивает переменной item первый объект (с минимальным id)" do
69
+ service.item.should eq Man.first
70
+ end
71
+
72
+ it "присваивает переменной items все сохраненные объекты, кроме первого (с минимальным id)" do
73
+ service.items.should eq Man.all.order("id ASC").to_a[1..-1]
74
+ end
75
+ end
76
+
77
+ context "если переданный массив содержит только один сохраненный объект ActiveRecord" do
78
+
79
+ let!(:service) { Service.new [Man.first, 1, nil, Man.new] }
80
+
81
+ it "присваивает переменной item первый объект (с минимальным id)" do
82
+ service.item.should eq Man.first
83
+ end
84
+
85
+ it "присваивает переменной items пустой массив" do
86
+ service.items.should eq []
87
+ end
88
+ end
89
+
90
+ context "если массив не содержит сохраненных объектов ActiveRecord" do
91
+
92
+ let!(:service) { Service.new [1, nil, Man.new] }
93
+
94
+ it "присваивает nil переменной item" do
95
+ service.item.should be_nil
96
+ end
97
+
98
+ it "присваивает пустой массив переменной items" do
99
+ service.items.should eq []
100
+ end
101
+ end
102
+
103
+ context "если массив содержит сохраненные объекты ActiveRecord разных классов" do
104
+
105
+ let!(:service) { Service.new [Man.first, Domain.create!] }
106
+
107
+ it "присваивает nil переменной item" do
108
+ service.item.should be_nil
109
+ end
110
+
111
+ it "присваивает пустой массив переменной items" do
112
+ service.items.should eq []
113
+ end
114
+ end
115
+
116
+ context "если атрибут не передан" do
117
+
118
+ let!(:service) { Service.new }
119
+
120
+ it "присваивает nil переменной item" do
121
+ service.item.should be_nil
122
+ end
123
+
124
+ it "присваивает пустой массив переменной items" do
125
+ service.items.should eq []
126
+ end
127
+ end
128
+ end
129
+
130
+ describe "#valid?" do
131
+
132
+ it "возвращает true если список записей для присоединения установлен" do
133
+ Service.new(2.times.map{ Lord.create! }).should be_valid
134
+ end
135
+
136
+ it "возвращает false если список записей для присоединения пуст" do
137
+ service = Service.new [Lord.create!]
138
+ service.should_not be_valid
139
+ service.errors.should_not be_blank
140
+ end
141
+ end
142
+
143
+ describe "#provide" do
144
+
145
+ let!(:lords) { 2.times.map{ Lord.create! }}
146
+ let!(:domains) { lords.map{ |lord| lord.domains.create! }}
147
+ let!(:men) { lords.map{ |lord| lord.men.create! }}
148
+ let!(:service) { Service.new lords }
149
+
150
+ context "если перепривязка разрешена" do
151
+
152
+ it "не вызывает исключений" do
153
+ expect{ service.provide }.not_to raise_error
154
+ end
155
+
156
+ it "объединяет записи" do
157
+ begin; service.provide; rescue; end
158
+ Lord.count.should eq 1
159
+ Lord.first.domains.count.should eq 2
160
+ Lord.first.men.count.should eq 2
161
+ end
162
+ end
163
+
164
+ context "если перепривязка не может быть выполнена" do
165
+
166
+ before do
167
+ service.items.last.stub(:destroy!).and_raise
168
+ service.items.last.stub(:errors).and_return({ base: "Удаление запрещено." })
169
+ end
170
+
171
+ it "вызывает исключение" do
172
+ expect{ service.provide }.to raise_error
173
+ end
174
+
175
+ it "не объединяет записи" do
176
+ begin; service.provide; rescue; end
177
+ Lord.count.should eq 2
178
+ Lord.all.each do |lord|
179
+ lord.domains.count.should eq 1
180
+ lord.men.count.should eq 1
181
+ end
182
+ end
183
+
184
+ it "добавляет ошибки в массив" do
185
+ begin; service.provide; rescue; end
186
+ service.errors.should_not be_blank
187
+ end
188
+ end
189
+
190
+ context "если атрибут #items пуст" do
191
+
192
+ before { service.stub(:items).and_return [] }
193
+
194
+ it "вызывает исключение" do
195
+ expect{ service.provide }.to raise_error
196
+ end
197
+
198
+ it "добавляет ошибки в массив" do
199
+ begin; service.provide; rescue; end
200
+ service.errors.should_not be_blank
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end