groupify 0.5.1 → 0.6.0.rc1

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.
@@ -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