groupify 0.5.1 → 0.6.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -55,17 +55,25 @@ module Groupify
55
55
  @member_klasses ||= Set.new
56
56
  end
57
57
 
58
- def members
59
- self.class.default_member_class.any_in(:group_ids => [self.id])
60
- end
58
+ # def members
59
+ # self.class.default_member_class.any_in(:group_ids => [self.id])
60
+ # end
61
61
 
62
62
  def member_classes
63
63
  self.class.member_classes
64
64
  end
65
65
 
66
- def add(*members)
67
- members.flatten.each do |member|
66
+ def add(*args)
67
+ opts = args.extract_options!
68
+ membership_type = opts[:as]
69
+ members = args.flatten
70
+ return unless members.present?
71
+
72
+ members.each do |member|
68
73
  member.groups << self
74
+ membership = member.group_memberships.find_or_initialize_by(as: membership_type)
75
+ membership.groups << self
76
+ membership.save!
69
77
  end
70
78
  end
71
79
 
@@ -76,11 +84,11 @@ module Groupify
76
84
 
77
85
  module ClassMethods
78
86
  def with_member(member)
79
- criteria.for_ids(member.group_ids)
87
+ member.groups
80
88
  end
81
89
 
82
90
  def default_member_class
83
- @default_member_class ||= register(User)
91
+ @default_member_class ||= User rescue false
84
92
  end
85
93
 
86
94
  def default_member_class=(klass)
@@ -96,11 +104,6 @@ module Groupify
96
104
  def has_members(name)
97
105
  klass = name.to_s.classify.constantize
98
106
  register(klass)
99
-
100
- # Define specific members accessor, i.e. group.users
101
- define_method name.to_s.pluralize.underscore do
102
- klass.any_in(:group_ids => [self.id])
103
- end
104
107
  end
105
108
 
106
109
  # Merge two groups. The members of the source become members of the destination, and the source is destroyed.
@@ -124,8 +127,82 @@ module Groupify
124
127
 
125
128
  def register(member_klass)
126
129
  (@member_klasses ||= Set.new) << member_klass
130
+ associate_member_class(member_klass)
127
131
  member_klass
128
132
  end
133
+
134
+ module MemberAssociationExtensions
135
+ def as(membership_type)
136
+ return self unless membership_type
137
+ where(:group_memberships.elem_match => { as: membership_type.to_s, group_ids: [base.id] })
138
+ end
139
+
140
+ def destroy(*args)
141
+ delete(*args)
142
+ end
143
+
144
+ def delete(*args)
145
+ opts = args.extract_options!
146
+ members = args
147
+
148
+ if opts[:as]
149
+ members.each do |member|
150
+ member.group_memberships.as(opts[:as]).first.groups.delete(base)
151
+ end
152
+ else
153
+ members.each do |member|
154
+ member.group_memberships.in(groups: base).each do |membership|
155
+ membership.groups.delete(base)
156
+ end
157
+ end
158
+
159
+ super(*members)
160
+ end
161
+ end
162
+ end
163
+
164
+ def associate_member_class(member_klass)
165
+ association_name = member_klass.name.to_s.pluralize.underscore.to_sym
166
+
167
+ has_many association_name, class_name: member_klass.to_s, dependent: :nullify, foreign_key: 'group_ids', extend: MemberAssociationExtensions
168
+
169
+ if member_klass == default_member_class
170
+ has_many :members, class_name: member_klass.to_s, dependent: :nullify, foreign_key: 'group_ids', extend: MemberAssociationExtensions
171
+ end
172
+ end
173
+ end
174
+ end
175
+
176
+ module MemberScopedAs
177
+ extend ActiveSupport::Concern
178
+
179
+ module ClassMethods
180
+ def as(membership_type)
181
+ group_ids = criteria.selector["group_ids"]
182
+ named_groups = criteria.selector["named_groups"]
183
+ criteria = self.criteria
184
+
185
+ # If filtering by groups or named groups, merge into the group membership criteria
186
+ if group_ids || named_groups
187
+ elem_match = {as: membership_type}
188
+
189
+ if group_ids
190
+ elem_match.merge!(group_ids: group_ids)
191
+ end
192
+
193
+ if named_groups
194
+ elem_match.merge!(named_groups: named_groups)
195
+ end
196
+
197
+ criteria = where(:group_memberships.elem_match => elem_match)
198
+ criteria.selector.delete("group_ids")
199
+ criteria.selector.delete("named_groups")
200
+ else
201
+ criteria = where(:"group_memberships.as" => membership_type)
202
+ end
203
+
204
+ criteria
205
+ end
129
206
  end
130
207
  end
131
208
 
@@ -141,28 +218,93 @@ module Groupify
141
218
  #
142
219
  module GroupMember
143
220
  extend ActiveSupport::Concern
221
+ include MemberScopedAs
144
222
 
145
223
  included do
146
- has_and_belongs_to_many :groups, :autosave => true, :inverse_of => nil, :class_name => @group_class_name
224
+ has_and_belongs_to_many :groups, autosave: true, dependent: :nullify, inverse_of: nil, class_name: @group_class_name do
225
+ def as(membership_type)
226
+ return self unless membership_type
227
+ group_ids = base.group_memberships.as(membership_type).first.group_ids
228
+
229
+ if group_ids.present?
230
+ self.and(:id.in => group_ids)
231
+ else
232
+ self.and(:id => nil)
233
+ end
234
+ end
235
+
236
+ def destroy(*args)
237
+ delete(*args)
238
+ end
239
+
240
+ def delete(*args)
241
+ opts = args.extract_options!
242
+ groups = args.flatten
243
+
244
+
245
+ if opts[:as]
246
+ base.group_memberships.as(opts[:as]).each do |membership|
247
+ membership.groups.delete(*groups)
248
+ end
249
+ else
250
+ super(*groups)
251
+ end
252
+ end
253
+ end
254
+
255
+ class GroupMembership
256
+ include ::Mongoid::Document
257
+
258
+ embedded_in :member, polymorphic: true
259
+
260
+ field :named_groups, type: Array, default: -> { [] }
261
+
262
+ after_initialize do
263
+ named_groups.extend NamedGroupCollection
264
+ end
265
+
266
+ field :as, as: :membership_type, type: String
267
+ end
268
+
269
+ GroupMembership.send :has_and_belongs_to_many, :groups, class_name: @group_class_name, inverse_of: nil
270
+
271
+ embeds_many :group_memberships, class_name: GroupMembership.to_s, as: :member do
272
+ def as(membership_type)
273
+ where(membership_type: membership_type.to_s)
274
+ end
275
+ end
147
276
  end
148
277
 
149
- def in_group?(group)
150
- self.groups.include?(group)
278
+ def in_group?(group, opts={})
279
+ groups.as(opts[:as]).include?(group)
151
280
  end
152
281
 
153
- def in_any_group?(*groups)
282
+ def in_any_group?(*args)
283
+ opts = args.extract_options!
284
+ groups = args
285
+
154
286
  groups.flatten.each do |group|
155
- return true if in_group?(group)
287
+ return true if in_group?(group, opts)
156
288
  end
157
289
  return false
158
290
  end
159
-
160
- def in_all_groups?(*groups)
161
- Set.new(groups.flatten) == Set.new(self.named_groups)
291
+
292
+ def in_all_groups?(*args)
293
+ opts = args.extract_options!
294
+ groups = args
295
+
296
+ groups.flatten.to_set.subset? self.groups.as(opts[:as]).to_set
162
297
  end
163
-
164
- def shares_any_group?(other)
165
- in_any_group?(other.groups)
298
+
299
+ def in_only_groups?(*args)
300
+ opts = args.extract_options!
301
+ groups = args.flatten
302
+
303
+ groups.to_set == self.groups.as(opts[:as]).to_set
304
+ end
305
+
306
+ def shares_any_group?(other, opts={})
307
+ in_any_group?(other.groups.to_a, opts)
166
308
  end
167
309
 
168
310
  module ClassMethods
@@ -170,23 +312,110 @@ module Groupify
170
312
  def group_class_name=(klass); @group_class_name = klass; end
171
313
 
172
314
  def in_group(group)
173
- group.present? ? where(:group_ids.in => [group.id]) : none
315
+ group.present? ? self.in(group_ids: group.id) : none
174
316
  end
175
317
 
176
318
  def in_any_group(*groups)
177
- groups.present? ? where(:group_ids.in => groups.flatten.map(&:id)) : none
319
+ groups.present? ? self.in(group_ids: groups.flatten.map(&:id)) : none
178
320
  end
179
-
321
+
180
322
  def in_all_groups(*groups)
323
+ groups.present? ? where(:group_ids.all => groups.flatten.map(&:id)) : none
324
+ end
325
+
326
+ def in_only_groups(*groups)
181
327
  groups.present? ? where(:group_ids => groups.flatten.map(&:id)) : none
182
328
  end
183
329
 
184
330
  def shares_any_group(other)
185
- in_any_group(other.groups)
331
+ in_any_group(other.groups.to_a)
186
332
  end
187
333
 
188
334
  end
189
335
  end
336
+
337
+ module NamedGroupCollection
338
+ # Criteria to filter by membership type
339
+ def as(membership_type)
340
+ return self unless membership_type
341
+
342
+ membership = @member.group_memberships.as(membership_type).first
343
+ if membership
344
+ membership.named_groups
345
+ else
346
+ self.class.new
347
+ end
348
+ end
349
+
350
+ def <<(named_group, opts={})
351
+ named_group = named_group.to_sym
352
+ super(named_group)
353
+ uniq!
354
+
355
+ if @member && opts[:as]
356
+ membership = @member.group_memberships.find_or_initialize_by(as: opts[:as])
357
+ membership.named_groups << named_group
358
+ membership.save!
359
+ end
360
+
361
+ self
362
+ end
363
+
364
+ def merge(*args)
365
+ opts = args.extract_options!
366
+ named_groups = args.flatten
367
+
368
+ named_groups.each do |named_group|
369
+ add(named_group, opts)
370
+ end
371
+ end
372
+
373
+ def delete(*args)
374
+ opts = args.extract_options!
375
+ named_groups = args.flatten
376
+
377
+ if @member
378
+ if opts[:as]
379
+ membership = @member.group_memberships.as(opts[:as]).first
380
+ if membership
381
+ if ::Mongoid::VERSION > "4"
382
+ membership.pull_all(named_groups: named_groups)
383
+ else
384
+ membership.pull_all(:named_groups, named_groups)
385
+ end
386
+ end
387
+
388
+ return
389
+ else
390
+ memberships = @member.group_memberships.where(:named_groups.in => named_groups)
391
+ memberships.each do |membership|
392
+ if ::Mongoid::VERSION > "4"
393
+ membership.pull_all(named_groups: named_groups)
394
+ else
395
+ membership.pull_all(:named_groups, named_groups)
396
+ end
397
+ end
398
+ end
399
+ end
400
+
401
+ named_groups.each do |named_group|
402
+ super(named_group)
403
+ end
404
+ end
405
+
406
+ def self.extended(base)
407
+ base.class_eval do
408
+ attr_accessor :member
409
+
410
+ alias_method :delete_all, :clear
411
+ alias_method :destroy_all, :clear
412
+ alias_method :push, :<<
413
+ alias_method :add, :<<
414
+ alias_method :concat, :merge
415
+ alias_method :destroy, :delete
416
+ end
417
+ end
418
+ end
190
419
 
191
420
  # Usage:
192
421
  # class User
@@ -200,51 +429,77 @@ module Groupify
200
429
  #
201
430
  module NamedGroupMember
202
431
  extend ActiveSupport::Concern
432
+ include MemberScopedAs
203
433
 
204
434
  included do
205
- field :named_groups, :type => Array, :default => []
206
-
207
- before_save :uniq_named_groups
208
- protected
209
- def uniq_named_groups
210
- named_groups.uniq!
435
+ field :named_groups, type: Array, default: -> { [] }
436
+
437
+ after_initialize do
438
+ named_groups.extend NamedGroupCollection
439
+ named_groups.member = self
211
440
  end
212
441
  end
213
442
 
214
- def in_named_group?(group)
215
- named_groups.include?(group)
443
+ def in_named_group?(named_group, opts={})
444
+ named_groups.as(opts[:as]).include?(named_group)
216
445
  end
217
446
 
218
- def in_any_named_group?(*groups)
219
- groups.flatten.each do |group|
220
- return true if in_named_group?(group)
447
+ def in_any_named_group?(*args)
448
+ opts = args.extract_options!
449
+ group_names = args.flatten
450
+
451
+ group_names.each do |named_group|
452
+ return true if in_named_group?(named_group)
221
453
  end
454
+
222
455
  return false
223
456
  end
224
457
 
225
- def in_all_named_groups?(*groups)
226
- Set.new(groups.flatten) == Set.new(self.named_groups)
458
+ def in_all_named_groups?(*args)
459
+ opts = args.extract_options!
460
+ named_groups = args.flatten.to_set
461
+
462
+ named_groups.subset? self.named_groups.as(opts[:as]).to_set
463
+ end
464
+
465
+ def in_only_named_groups?(*args)
466
+ opts = args.extract_options!
467
+ named_groups = args.flatten.to_set
468
+ named_groups == self.named_groups.as(opts[:as]).to_set
227
469
  end
228
470
 
229
- def shares_any_named_group?(other)
230
- in_any_named_group?(other.named_groups)
471
+ def shares_any_named_group?(other, opts={})
472
+ in_any_named_group?(other.named_groups, opts)
231
473
  end
232
474
 
233
475
  module ClassMethods
234
- def in_named_group(named_group)
235
- named_group.present? ? where(:named_groups.in => [named_group]) : none
476
+ def in_named_group(named_group, opts={})
477
+ in_any_named_group(named_group, opts)
236
478
  end
237
479
 
238
480
  def in_any_named_group(*named_groups)
239
- named_groups.present? ? where(:named_groups.in => named_groups.flatten) : none
481
+ named_groups.flatten!
482
+ return none unless named_groups.present?
483
+
484
+ self.in(named_groups: named_groups.flatten)
240
485
  end
241
-
486
+
242
487
  def in_all_named_groups(*named_groups)
243
- named_groups.present? ? where(:named_groups => named_groups.flatten) : none
488
+ named_groups.flatten!
489
+ return none unless named_groups.present?
490
+
491
+ where(:named_groups.all => named_groups.flatten)
492
+ end
493
+
494
+ def in_only_named_groups(*named_groups)
495
+ named_groups.flatten!
496
+ return none unless named_groups.present?
497
+
498
+ where(named_groups: named_groups.flatten)
244
499
  end
245
500
 
246
- def shares_any_named_group(other)
247
- in_any_named_group(other.named_groups)
501
+ def shares_any_named_group(other, opts={})
502
+ in_any_named_group(other.named_groups, opts)
248
503
  end
249
504
  end
250
505
  end