active_merge 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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