message_train 0.6.17 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +16 -13
  3. data/.rubocop_todo.yml +12 -0
  4. data/.ruby-version +1 -1
  5. data/.travis.yml +1 -1
  6. data/Gemfile +28 -30
  7. data/README.md +284 -0
  8. data/Rakefile +6 -7
  9. data/VERSION +1 -1
  10. data/app/assets/javascripts/ckeditor/{config.js.coffee → config.js} +7 -6
  11. data/app/assets/stylesheets/message_train.scss +0 -8
  12. data/app/controllers/concerns/message_train_authorization.rb +37 -0
  13. data/app/controllers/concerns/message_train_support.rb +41 -69
  14. data/app/controllers/message_train/application_controller.rb +1 -0
  15. data/app/controllers/message_train/boxes_controller.rb +5 -4
  16. data/app/controllers/message_train/conversations_controller.rb +7 -5
  17. data/app/controllers/message_train/messages_controller.rb +43 -27
  18. data/app/controllers/message_train/participants_controller.rb +14 -25
  19. data/app/controllers/message_train/unsubscribes_controller.rb +96 -61
  20. data/app/helpers/message_train/application_helper.rb +13 -7
  21. data/app/helpers/message_train/attachments_helper.rb +4 -12
  22. data/app/helpers/message_train/boxes_helper.rb +4 -14
  23. data/app/helpers/message_train/collectives_helper.rb +23 -29
  24. data/app/helpers/message_train/conversations_helper.rb +52 -30
  25. data/app/helpers/message_train/messages_helper.rb +33 -20
  26. data/app/mailers/message_train/receipt_mailer.rb +22 -12
  27. data/app/models/message_train/attachment.rb +2 -2
  28. data/app/models/message_train/box.rb +182 -228
  29. data/app/models/message_train/conversation.rb +100 -103
  30. data/app/models/message_train/ignore.rb +83 -4
  31. data/app/models/message_train/message.rb +66 -117
  32. data/app/models/message_train/receipt.rb +73 -49
  33. data/app/views/message_train/boxes/show.html.haml +11 -7
  34. data/config/locales/en.yml +1 -0
  35. data/config/routes.rb +6 -12
  36. data/lib/message_train.rb +3 -1
  37. data/lib/message_train/class_methods.rb +51 -0
  38. data/lib/message_train/configuration.rb +28 -3
  39. data/lib/message_train/instance_methods.rb +209 -0
  40. data/lib/message_train/mixin.rb +25 -320
  41. data/message_train.gemspec +83 -83
  42. data/spec/controllers/message_train/boxes_controller_spec.rb +37 -19
  43. data/spec/controllers/message_train/concerns_spec.rb +21 -3
  44. data/spec/controllers/message_train/conversations_controller_spec.rb +41 -18
  45. data/spec/controllers/message_train/messages_controller_spec.rb +112 -31
  46. data/spec/controllers/message_train/participants_controller_spec.rb +33 -7
  47. data/spec/controllers/message_train/unsubscribes_controller_spec.rb +10 -8
  48. data/spec/dummy/app/assets/stylesheets/{application.css.scss → application.scss} +2 -1
  49. data/spec/dummy/app/assets/stylesheets/bootstrap-everything.scss +54 -0
  50. data/spec/dummy/app/models/group.rb +1 -1
  51. data/spec/dummy/app/views/layouts/application.html.haml +9 -8
  52. data/spec/dummy/bin/setup +8 -8
  53. data/spec/dummy/config/application.rb +0 -3
  54. data/spec/dummy/config/environments/test.rb +4 -2
  55. data/spec/dummy/db/schema.rb +94 -103
  56. data/spec/dummy/db/test.sqlite3 +0 -0
  57. data/spec/factories/attachment.rb +3 -1
  58. data/spec/factories/message.rb +2 -3
  59. data/spec/features/boxes_spec.rb +0 -3
  60. data/spec/helpers/message_train/application_helper_spec.rb +3 -2
  61. data/spec/helpers/message_train/attachment_helper_spec.rb +4 -0
  62. data/spec/helpers/message_train/boxes_helper_spec.rb +1 -0
  63. data/spec/helpers/message_train/collectives_helper_spec.rb +1 -0
  64. data/spec/helpers/message_train/conversations_helper_spec.rb +3 -2
  65. data/spec/helpers/message_train/messages_helper_spec.rb +2 -1
  66. data/spec/models/group_spec.rb +6 -4
  67. data/spec/models/message_train/box_spec.rb +0 -88
  68. data/spec/models/message_train/ignore_spec.rb +65 -0
  69. data/spec/models/message_train/message_spec.rb +6 -5
  70. data/spec/models/message_train/receipt_spec.rb +6 -8
  71. data/spec/models/role_spec.rb +2 -2
  72. data/spec/models/user_spec.rb +29 -101
  73. data/spec/rails_helper.rb +16 -30
  74. data/spec/support/feature_behaviors.rb +2 -1
  75. data/spec/support/shared_connection.rb +5 -0
  76. data/spec/support/utilities.rb +7 -8
  77. metadata +145 -120
  78. data/README.rdoc +0 -175
  79. data/spec/dummy/app/models/.keep +0 -0
  80. data/spec/dummy/log/.keep +0 -0
@@ -3,20 +3,9 @@ module MessageTrain
3
3
  module MessagesHelper
4
4
  def message_class(box, message)
5
5
  css_classes = []
6
-
7
- css_classes << if message.is_unread_for?(@box_user)
8
- 'unread panel-info'
9
- else
10
- 'read'
11
- end
12
-
13
- message.draft && css_classes << 'draft'
14
-
15
- if box.division == :trash
16
- !message.is_trashed_for?(@box_user) && css_classes << 'hide'
17
- else
18
- !message.is_untrashed_for?(@box_user) && css_classes << 'hide'
19
- end
6
+ css_classes << message_css_for_read_state(message)
7
+ css_classes << message_css_for_draft_state(message)
8
+ css_classes << message_css_for_hide_state(box, message)
20
9
  css_classes.join(' ')
21
10
  end
22
11
 
@@ -48,20 +37,44 @@ module MessageTrain
48
37
  private
49
38
 
50
39
  def message_toggle(message, icon, mark_to_set, title, options = {})
51
- options[:remote] = true
52
- options[:id] = "mark_#{mark_to_set}_#{message.id}"
53
- options[:class] = 'mark-link'
54
- options[:method] = :put
55
- options[:title] = title
56
40
  render(
57
41
  partial: 'message_train/messages/toggle',
58
42
  locals: {
59
43
  message: message,
60
44
  icon: icon,
61
45
  mark_to_set: mark_to_set,
62
- options: options
46
+ options: message_toggle_options(message, mark_to_set, title, options)
63
47
  }
64
48
  )
65
49
  end
50
+
51
+ def message_toggle_options(message, mark_to_set, title, options = {})
52
+ options[:remote] = true
53
+ options[:id] = "mark_#{mark_to_set}_#{message.id}"
54
+ options[:class] = 'mark-link'
55
+ options[:method] = :put
56
+ options[:title] = title
57
+ options
58
+ end
59
+
60
+ def message_css_for_hide_state(box, message)
61
+ if box.division == :trash
62
+ 'hide' unless message.is_trashed_for?(@box_user)
63
+ else
64
+ 'hide' unless message.is_untrashed_for?(@box_user)
65
+ end
66
+ end
67
+
68
+ def message_css_for_draft_state(message)
69
+ 'draft' if message.draft
70
+ end
71
+
72
+ def message_css_for_read_state(message)
73
+ if message.is_unread_for?(@box_user)
74
+ 'unread panel-info'
75
+ else
76
+ 'read'
77
+ end
78
+ end
66
79
  end
67
80
  end
@@ -6,22 +6,32 @@ module MessageTrain
6
6
  @recipient = receipt.recipient
7
7
  @through = receipt.received_through
8
8
  if @recipient == @through
9
- @heading = :new_message_on_site_name.l(
10
- site_name: MessageTrain.configuration.site_name
11
- )
9
+ set_self_heading
12
10
  else
13
- @through_name = @through.send(
14
- MessageTrain.configuration.name_columns[
15
- @through.class.table_name.to_sym
16
- ]
17
- )
18
- @heading = :new_message_through_on_site_name.l(
19
- site_name: MessageTrain.configuration.site_name,
20
- through: @through_name
21
- )
11
+ set_through_heading
22
12
  end
23
13
  @subject = "#{@heading}: #{@receipt.message.subject}"
24
14
  mail(to: @recipient.email, subject: @subject)
25
15
  end
16
+
17
+ private
18
+
19
+ def set_self_heading
20
+ @heading = :new_message_on_site_name.l(
21
+ site_name: MessageTrain.configuration.site_name
22
+ )
23
+ end
24
+
25
+ def set_through_heading
26
+ @through_name = @through.send(
27
+ MessageTrain.configuration.name_columns[
28
+ @through.class.table_name.to_sym
29
+ ]
30
+ )
31
+ @heading = :new_message_through_on_site_name.l(
32
+ site_name: MessageTrain.configuration.site_name,
33
+ through: @through_name
34
+ )
35
+ end
26
36
  end
27
37
  end
@@ -39,9 +39,9 @@ module MessageTrain
39
39
  ]
40
40
  )
41
41
  def image?
42
- # rubocop:disable Style/LineLength
42
+ # rubocop:disable Metrics/LineLength
43
43
  !(attachment_content_type =~ %r{^(image|(x-)?application)/(bmp|gif|jpeg|jpg|pjpeg|png|x-png)$}).nil?
44
- # rubocop:enable Style/LineLength
44
+ # rubocop:enable Metrics/LineLength
45
45
  end
46
46
 
47
47
  Paperclip.interpolates :style_prefix do |attachment, style|
@@ -1,6 +1,19 @@
1
1
  module MessageTrain
2
2
  # Box model
3
3
  class Box
4
+ MARK_METHODS = {
5
+ 'Hash' => :mark_hash,
6
+ 'Array' => :mark_array,
7
+ 'String' => :mark_id,
8
+ 'Fixnum' => :mark_id,
9
+ 'MessageTrain::Conversation' => :mark_communication
10
+ }.freeze
11
+
12
+ AUTHORIZE_METHODS = {
13
+ 'MessageTrain::Conversation' => :authorize_conversation,
14
+ 'MessageTrain::Message' => :authorize_message
15
+ }.freeze
16
+
4
17
  include ActiveModel::Model
5
18
  attr_accessor :parent, :division, :participant, :errors, :results
6
19
  alias id division
@@ -18,8 +31,7 @@ module MessageTrain
18
31
  end
19
32
 
20
33
  def unread_count
21
- found = conversations(unread: true)
22
- found.count
34
+ conversations(read: false).count
23
35
  end
24
36
 
25
37
  def conversations(options = {})
@@ -28,21 +40,6 @@ module MessageTrain
28
40
  if options[:read] == false || options[:unread]
29
41
  found = found.with_unread_for(participant)
30
42
  end
31
- if division == :trash
32
- found = found.with_trashed_for(participant)
33
- else
34
- found = found.with_untrashed_for(participant)
35
- found = if division == :drafts
36
- found.with_drafts_by(participant)
37
- else
38
- found.with_ready_for(participant)
39
- end
40
- found = if division == :ignored
41
- found.ignored(participant)
42
- else
43
- found.unignored(participant)
44
- end
45
- end
46
43
  found
47
44
  end
48
45
 
@@ -54,131 +51,76 @@ module MessageTrain
54
51
  parent.all_messages(participant).find(id)
55
52
  end
56
53
 
57
- def new_message(args = {})
58
- if args[:message_train_conversation_id].nil?
59
- message = MessageTrain::Message.new(args)
54
+ def send_message(attributes)
55
+ message = MessageTrain::Message.new attributes
56
+ return false unless authorize_send_message(message)
57
+ message.sender = participant
58
+ return message_send_error(message) unless message.save
59
+ message_send_success(message)
60
+ end
61
+
62
+ def message_send_error(message)
63
+ errors.add(message, message.errors.full_messages.to_sentence)
64
+ message
65
+ end
66
+
67
+ def message_send_success(message)
68
+ if message.draft
69
+ results.add(message, :message_saved_as_draft.l)
60
70
  else
61
- conversation = find_conversation(args[:message_train_conversation_id])
62
- previous_message = conversation.messages.last
63
- message = conversation.messages.build(args)
64
- message.subject = "Re: #{conversation.subject}"
65
- message.body = "<blockquote>#{previous_message.body}</blockquote>"\
66
- '<p>&nbsp;</p>'
67
- recipient_arrays = {}
68
- conversation.default_recipients_for(parent).each do |recipient|
69
- table_name = recipient.class.table_name
70
- recipient_arrays[table_name] ||= []
71
- recipient_arrays[table_name] << recipient.send(
72
- MessageTrain.configuration.slug_columns[table_name.to_sym]
73
- )
74
- end
75
- recipient_arrays.each do |key, array|
76
- message.recipients_to_save[key] = array.join(', ')
77
- end
71
+ results.add(message, :message_sent.l)
78
72
  end
79
73
  message
80
74
  end
81
75
 
82
- def send_message(attributes)
83
- message_to_send = MessageTrain::Message.new attributes
84
- message_to_send.sender = participant
76
+ def authorize_send_message(message)
85
77
  unless parent.valid_senders.include? participant
86
78
  errors.add(
87
- message_to_send,
79
+ message,
88
80
  :invalid_sender_for_thing.l(
89
81
  thing: "#{parent.class.name} #{parent.id}"
90
82
  )
91
83
  )
92
84
  return false
93
85
  end
94
- if message_to_send.save
95
- if message_to_send.draft
96
- results.add(message_to_send, :message_saved_as_draft.l)
97
- else
98
- results.add(message_to_send, :message_sent.l)
99
- end
100
- else
101
- errors.add(
102
- message_to_send,
103
- message_to_send.errors.full_messages.to_sentence
104
- )
105
- end
106
- message_to_send
86
+ true
107
87
  end
108
88
 
109
89
  def update_message(message, attributes)
90
+ !message.draft && raise(ActiveRecord::RecordNotFound)
110
91
  attributes.delete(:sender)
111
- if message.sender == participant && parent.valid_senders.include?(
112
- participant
113
- )
114
- message.update(attributes)
115
- message.reload
116
- if message.errors.empty?
117
- if message.draft
118
- results.add(message, :message_saved_as_draft.l)
119
- else
120
- results.add(message, :message_sent.l)
121
- end
122
- else
123
- errors.add(
124
- message,
125
- message.errors.full_messages.to_sentence
126
- )
127
- end
128
- message
129
- else
130
- errors.add(
131
- message,
132
- :access_to_message_id_denied.l(id: message.id)
133
- )
134
- false
135
- end
92
+ return false unless authorize_update_message(message)
93
+ message.update(attributes)
94
+ message.reload
95
+ return message_update_error(message) if message.errors.any?
96
+ message_update_success(message)
136
97
  end
137
98
 
138
- def ignore(object)
139
- case object.class.name
140
- when 'Hash'
141
- ignore object.values
142
- when 'Array'
143
- object.collect { |item| ignore(item) }.uniq == [true]
144
- when 'String', 'Fixnum'
145
- ignore(find_conversation(object.to_i))
146
- when 'MessageTrain::Conversation'
147
- if authorize(object)
148
- object.participant_ignore(participant)
149
- # We can assume the previous line has succeeded at this point,
150
- # because participant_ignore raises an ActiveRecord error otherwise.
151
- # Therefore we simply report success, since we got here.
152
- results.add(object, :update_successful.l)
153
- else
154
- false
155
- end
99
+ def message_update_success(message)
100
+ if message.draft
101
+ results.add(message, :message_saved_as_draft.l)
156
102
  else
157
- errors.add(self, :cannot_ignore_type.l(type: object.class.name))
103
+ results.add(message, :message_sent.l)
158
104
  end
105
+ message
159
106
  end
160
107
 
161
- def unignore(object)
162
- case object.class.name
163
- when 'Hash'
164
- unignore object.values
165
- when 'Array'
166
- object.collect { |item| unignore(item) }.uniq == [true]
167
- when 'String', 'Fixnum'
168
- unignore(find_conversation(object.to_i))
169
- when 'MessageTrain::Conversation'
170
- if authorize(object)
171
- object.participant_unignore(participant)
172
- # We can assume the previous line has succeeded at this point,
173
- # because participant_unignore raises an ActiveRecord error
174
- # otherwise. Therefore we simply report success, since we got here.
175
- results.add(object, :update_successful.l)
176
- else
177
- false
178
- end
179
- else
180
- errors.add(self, :cannot_unignore_type.l(type: object.class.name))
108
+ def message_update_error(message)
109
+ errors.add(message, message.errors.full_messages.to_sentence)
110
+ message
111
+ end
112
+
113
+ def authorize_update_message(message)
114
+ unless message.sender == participant &&
115
+ parent.valid_senders.include?(participant)
116
+ return message_access_denied(message)
181
117
  end
118
+ true
119
+ end
120
+
121
+ def message_access_denied(message)
122
+ errors.add(message, :access_to_message_id_denied.l(id: message.id))
123
+ false
182
124
  end
183
125
 
184
126
  def title
@@ -186,74 +128,73 @@ module MessageTrain
186
128
  end
187
129
 
188
130
  def message
189
- if !errors.all.empty?
190
- errors.all.collect { |x| x[:message] }.uniq.to_sentence
191
- elsif results.all.empty?
192
- :nothing_to_do.l
193
- else
194
- results.all.collect { |x| x[:message] }.uniq.to_sentence
195
- end
131
+ what_happened = errors.any? ? errors : results
132
+ return :nothing_to_do.l unless what_happened.any?
133
+ what_happened.all.map { |x| x[:message] }.uniq.to_sentence
196
134
  end
197
135
 
198
136
  def mark(mark_to_set, objects)
199
137
  objects.each do |key, object|
200
- if !object.present?
201
- # Allow skipping empty objects
202
- elsif key.to_s =~ /^(conversations|messages)$/
203
- data_type = object.class.name
204
- case data_type
205
- when 'Hash'
206
- mark(mark_to_set, key => object.values)
207
- when 'Array'
208
- object.collect do |item|
209
- mark(mark_to_set, key => item)
210
- end.uniq == [true]
211
- when 'String', 'Fixnum'
212
- model_name = "MessageTrain::#{key.to_s.classify}"
213
- model = model_name.constantize
214
- mark(mark_to_set, key => model.find_by_id!(object.to_i))
215
- when 'MessageTrain::Conversation', 'MessageTrain::Message'
216
- if authorize(object)
217
- object.mark(mark_to_set, participant)
218
- # We can assume the previous line has succeeded at this point,
219
- # because mark raises an ActiveRecord error otherwise.
220
- # Therefore we simply report success, since we got here.
221
- results.add(object, :update_successful.l)
222
- else
223
- false
224
- end
225
- else
226
- errors.add(
227
- self,
228
- :cannot_mark_with_data_type.l(data_type: data_type)
229
- )
230
- end
231
- else
138
+ next unless object.present? # Allow skipping empty objects
139
+ unless key.to_s =~ /^(conversations|messages)$/
232
140
  errors.add(self, :cannot_mark_type.l(type: key.to_s))
141
+ next
233
142
  end
143
+ mark_object mark_to_set, key, object
234
144
  end
235
145
  end
236
146
 
147
+ def mark_hash(mark_to_set, key, object)
148
+ mark(mark_to_set, key => object.values)
149
+ end
150
+
151
+ def mark_array(mark_to_set, key, object)
152
+ object.collect { |item| mark(mark_to_set, key => item) }
153
+ .uniq == [true]
154
+ end
155
+
156
+ def mark_id(mark_to_set, key, object)
157
+ model = "MessageTrain::#{key.to_s.classify}".constantize
158
+ mark_communication(mark_to_set, key, model.find_by_id!(object.to_i))
159
+ end
160
+
161
+ def mark_communication(mark_to_set, _key, object)
162
+ return unless authorize(object)
163
+ object.mark(mark_to_set, participant)
164
+ results.add(object, :update_successful.l)
165
+ end
166
+
167
+ def marking_error(object)
168
+ errors.add(
169
+ self, :cannot_mark_with_data_type.l(data_type: object.class.name)
170
+ )
171
+ object
172
+ end
173
+
174
+ def mark_object(mark_to_set, key, object)
175
+ method = MARK_METHODS[object.class.name]
176
+ return marking_error(object) if method.nil?
177
+ send(method, mark_to_set, key, object)
178
+ end
179
+
237
180
  def authorize(object)
238
- case object.class.name
239
- when 'MessageTrain::Conversation'
240
- if object.includes_receipts_for? participant
241
- true
242
- else
243
- errors.add(
244
- object,
245
- :access_to_conversation_id_denied.l(id: object.id)
246
- )
247
- end
248
- when 'MessageTrain::Message'
249
- if object.receipts.for(participant).any?
250
- true
251
- else
252
- errors.add(object, :access_to_message_id_denied.l(id: object.id))
253
- end
254
- else
255
- errors.add(object, :cannot_authorize_type.l(type: object.class.name))
256
- end
181
+ method = AUTHORIZE_METHODS[object.class.name]
182
+ return authorize_error(object) if method.nil?
183
+ send(method, object)
184
+ end
185
+
186
+ def authorize_error(object)
187
+ errors.add(object, :cannot_authorize_type.l(type: object.class.name))
188
+ end
189
+
190
+ def authorize_conversation(object)
191
+ object.includes_receipts_for?(participant) ||
192
+ errors.add(object, :access_to_conversation_id_denied.l(id: object.id))
193
+ end
194
+
195
+ def authorize_message(object)
196
+ object.receipts.for(participant).any? ||
197
+ errors.add(object, :access_to_message_id_denied.l(id: object.id))
257
198
  end
258
199
 
259
200
  # Box::Results class
@@ -266,64 +207,77 @@ module MessageTrain
266
207
  end
267
208
 
268
209
  def add(object, message)
269
- item = {}
270
- case object.class.name
271
- when 'MessageTrain::Box'
272
- item[:css_id] = 'box'
273
- route_args = {
274
- controller: 'message_train/boxes',
275
- action: :show,
276
- division: object.division
277
- }
278
- if box.parent != box.participant
279
- collective = box.parent
280
- table_part = collective.class.table_name
281
- slug_part = collective.send(
282
- MessageTrain.configuration.slug_columns[
283
- collective.class.table_name.to_sym
284
- ]
285
- )
286
- route_args[:collective_id] = "#{table_part}:#{slug_part}"
287
- end
288
- item[:path] = MessageTrain::Engine.routes.path_for(route_args)
289
- when 'MessageTrain::Conversation', 'MessageTrain::Message'
290
- if object.new_record?
291
- item[:css_id] = "#{object.class.table_name.singularize}"
292
- item[:path] = nil
293
- else
294
- item[:css_id] = "#{object.class.table_name.singularize}_"\
295
- "#{object.id.to_s}"
296
- route_args = {
297
- controller: object.class.table_name
298
- .gsub('message_train_', 'message_train/'),
299
- action: :show,
300
- box_division: box.division,
301
- id: object.id
302
- }
303
- if box.parent != box.participant
304
- collective = box.parent
305
- table_part = collective.class.table_name
306
- slug_part = collective.send(
307
- MessageTrain.configuration.slug_columns[
308
- collective.class.table_name.to_sym
309
- ]
310
- )
311
- route_args[:collective_id] = "#{table_part}:#{slug_part}"
312
- end
313
- item[:path] = MessageTrain::Engine.routes.path_for(route_args)
314
- end
315
- else
316
- item[:css_id] = object.class.name.singularize.downcase
317
- item[:path] = nil
318
- end
319
- item[:message] = message
320
- items << item
210
+ items << case object.class.name
211
+ when 'MessageTrain::Box'
212
+ result_for_box(object, message)
213
+ when 'MessageTrain::Conversation', 'MessageTrain::Message'
214
+ result_for_communication(object, message)
215
+ else
216
+ result_for_misc_object(object, message)
217
+ end
321
218
  true
322
219
  end
323
220
 
221
+ def result_for_misc_object(object, message)
222
+ css_id = object.class.name.singularize.downcase
223
+ { css_id: css_id, path: nil, message: message }
224
+ end
225
+
226
+ def result_for_communication(object, message)
227
+ return result_for_new(object, message) if object.new_record?
228
+ path = MessageTrain::Engine.routes.path_for(object_route_args(object))
229
+ css_id = "#{object.class.table_name.singularize}_#{object.id}"
230
+ { message: message, css_id: css_id, path: path }
231
+ end
232
+
233
+ def object_route_args(object)
234
+ table_name = object.class.table_name
235
+ {
236
+ controller: table_name.gsub('message_train_', 'message_train/'),
237
+ action: :show,
238
+ box_division: box.division,
239
+ id: object.id,
240
+ collective_id: collective_id_for_box(box)
241
+ }
242
+ end
243
+
244
+ def result_for_new(object, message)
245
+ {
246
+ message: message,
247
+ css_id: object.class.table_name.singularize,
248
+ path: nil
249
+ }
250
+ end
251
+
252
+ def result_for_box(object, message)
253
+ path = MessageTrain::Engine.routes.path_for(box_route_args(object))
254
+ { css_id: 'box', message: message, path: path }
255
+ end
256
+
257
+ def box_route_args(object)
258
+ {
259
+ controller: 'message_train/boxes',
260
+ action: :show,
261
+ division: object.division,
262
+ collective_id: collective_id_for_box(box)
263
+ }
264
+ end
265
+
266
+ def collective_id_for_box(box)
267
+ return if box.parent == box.participant
268
+ collective = box.parent
269
+ table_part = collective.class.table_name
270
+ slug_part = collective.message_train_slug
271
+ "#{table_part}:#{slug_part}"
272
+ end
273
+
324
274
  def all
325
275
  items
326
276
  end
277
+
278
+ def any?
279
+ items.any?
280
+ end
327
281
  end
328
282
  # Box::Errors class
329
283
  class Errors < Results