groupify 0.6.0.rc1 → 0.6.0.rc2

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.
@@ -0,0 +1,105 @@
1
+ module Groupify
2
+ module ActiveRecord
3
+
4
+ # Usage:
5
+ # class User < ActiveRecord::Base
6
+ # acts_as_named_group_member
7
+ # ...
8
+ # end
9
+ #
10
+ # user.named_groups << :admin
11
+ #
12
+ module NamedGroupMember
13
+ extend ActiveSupport::Concern
14
+
15
+ included do
16
+ unless respond_to?(:group_memberships)
17
+ has_many :group_memberships, :as => :member, :autosave => true, :dependent => :destroy
18
+ end
19
+ end
20
+
21
+ def named_groups
22
+ @named_groups ||= NamedGroupCollection.new(self)
23
+ end
24
+
25
+ def named_groups=(groups)
26
+ groups.each do |group|
27
+ self.named_groups << group
28
+ end
29
+ end
30
+
31
+ def in_named_group?(named_group, opts={})
32
+ named_groups.include?(named_group, opts)
33
+ end
34
+
35
+ def in_any_named_group?(*args)
36
+ opts = args.extract_options!
37
+ named_groups = args.flatten
38
+ named_groups.each do |named_group|
39
+ return true if in_named_group?(named_group, opts)
40
+ end
41
+ return false
42
+ end
43
+
44
+ def in_all_named_groups?(*args)
45
+ opts = args.extract_options!
46
+ named_groups = args.flatten.to_set
47
+ named_groups.subset? self.named_groups.as(opts[:as]).to_set
48
+ end
49
+
50
+ def in_only_named_groups?(*args)
51
+ opts = args.extract_options!
52
+ named_groups = args.flatten.to_set
53
+ named_groups == self.named_groups.as(opts[:as]).to_set
54
+ end
55
+
56
+ def shares_any_named_group?(other, opts={})
57
+ in_any_named_group?(other.named_groups.to_a, opts)
58
+ end
59
+
60
+ module ClassMethods
61
+ def as(membership_type)
62
+ joins(:group_memberships).where(group_memberships: {membership_type: membership_type})
63
+ end
64
+
65
+ def in_named_group(named_group)
66
+ return none unless named_group.present?
67
+
68
+ joins(:group_memberships).where(group_memberships: {group_name: named_group}).uniq
69
+ end
70
+
71
+ def in_any_named_group(*named_groups)
72
+ named_groups.flatten!
73
+ return none unless named_groups.present?
74
+
75
+ joins(:group_memberships).where(group_memberships: {group_name: named_groups.flatten}).uniq
76
+ end
77
+
78
+ def in_all_named_groups(*named_groups)
79
+ named_groups.flatten!
80
+ return none unless named_groups.present?
81
+
82
+ joins(:group_memberships).
83
+ group(:"group_memberships.member_id").
84
+ where(:group_memberships => {:group_name => named_groups}).
85
+ having("COUNT(DISTINCT group_memberships.group_name) = #{named_groups.count}").
86
+ uniq
87
+ end
88
+
89
+ def in_only_named_groups(*named_groups)
90
+ named_groups.flatten!
91
+ return none unless named_groups.present?
92
+
93
+ joins(:group_memberships).
94
+ group("group_memberships.member_id").
95
+ having("COUNT(DISTINCT group_memberships.group_name) = #{named_groups.count}").
96
+ uniq
97
+ end
98
+
99
+ def shares_any_named_group(other)
100
+ in_any_named_group(other.named_groups.to_a)
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -1,509 +1,16 @@
1
1
  require 'mongoid'
2
2
  require 'set'
3
3
 
4
- # Groups and members
5
4
  module Groupify
6
5
  module Mongoid
7
- module Adapter
8
- extend ActiveSupport::Concern
9
-
10
- included do
11
- def none; where(:id => nil); end
12
- end
13
-
14
- module ClassMethods
15
- def acts_as_group(opts = {})
16
- include Groupify::Mongoid::Group
17
-
18
- if (member_klass = opts.delete :default_members)
19
- self.default_member_class = member_klass.to_s.classify.constantize
20
- end
21
-
22
- if (member_klasses = opts.delete :members)
23
- member_klasses.each do |member_klass|
24
- has_members(member_klass)
25
- end
26
- end
27
- end
28
-
29
- def acts_as_group_member(opts = {})
30
- @group_class_name = opts[:class_name] || 'Group'
31
- include Groupify::Mongoid::GroupMember
32
- end
33
-
34
- def acts_as_named_group_member(opts = {})
35
- include Groupify::Mongoid::NamedGroupMember
36
- end
37
- end
38
- end
6
+ require 'groupify/adapter/mongoid/model'
39
7
 
40
- # Usage:
41
- # class Group
42
- # include Mongoid::Document
43
- #
44
- # acts_as_group, :members => [:users]
45
- # ...
46
- # end
47
- #
48
- # group.add(member)
49
- #
50
- module Group
51
- extend ActiveSupport::Concern
52
-
53
- included do
54
- @default_member_class = nil
55
- @member_klasses ||= Set.new
56
- end
57
-
58
- # def members
59
- # self.class.default_member_class.any_in(:group_ids => [self.id])
60
- # end
61
-
62
- def member_classes
63
- self.class.member_classes
64
- end
65
-
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|
73
- member.groups << self
74
- membership = member.group_memberships.find_or_initialize_by(as: membership_type)
75
- membership.groups << self
76
- membership.save!
77
- end
78
- end
79
-
80
- # Merge a source group into this group.
81
- def merge!(source)
82
- self.class.merge!(source, self)
83
- end
84
-
85
- module ClassMethods
86
- def with_member(member)
87
- member.groups
88
- end
89
-
90
- def default_member_class
91
- @default_member_class ||= User rescue false
92
- end
93
-
94
- def default_member_class=(klass)
95
- @default_member_class = klass
96
- end
97
-
98
- # Returns the member classes defined for this class, as well as for the super classes
99
- def member_classes
100
- (@member_klasses ||= Set.new).merge(superclass.method_defined?(:member_classes) ? superclass.member_classes : [])
101
- end
102
-
103
- # Define which classes are members of this group
104
- def has_members(name)
105
- klass = name.to_s.classify.constantize
106
- register(klass)
107
- end
108
-
109
- # Merge two groups. The members of the source become members of the destination, and the source is destroyed.
110
- def merge!(source_group, destination_group)
111
- # Ensure that all the members of the source can be members of the destination
112
- invalid_member_classes = (source_group.member_classes - destination_group.member_classes)
113
- invalid_member_classes.each do |klass|
114
- if klass.any_in(:group_ids => [source_group.id]).count > 0
115
- raise ArgumentError.new("#{source_group.class} has members that cannot belong to #{destination_group.class}")
116
- end
117
- end
118
-
119
- source_group.member_classes.each do |klass|
120
- klass.any_in(:group_ids => [source_group.id]).update_all(:$set => {:"group_ids.$" => destination_group.id})
121
- end
122
-
123
- source_group.delete
124
- end
125
-
126
- protected
127
-
128
- def register(member_klass)
129
- (@member_klasses ||= Set.new) << member_klass
130
- associate_member_class(member_klass)
131
- member_klass
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
206
- end
207
- end
208
-
209
- # Usage:
210
- # class User
211
- # include Mongoid::Document
212
- #
213
- # acts_as_group_member
214
- # ...
215
- # end
216
- #
217
- # user.groups << group
218
- #
219
- module GroupMember
220
- extend ActiveSupport::Concern
221
- include MemberScopedAs
222
-
223
- included do
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
276
- end
277
-
278
- def in_group?(group, opts={})
279
- groups.as(opts[:as]).include?(group)
280
- end
281
-
282
- def in_any_group?(*args)
283
- opts = args.extract_options!
284
- groups = args
285
-
286
- groups.flatten.each do |group|
287
- return true if in_group?(group, opts)
288
- end
289
- return false
290
- end
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
297
- end
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)
308
- end
309
-
310
- module ClassMethods
311
- def group_class_name; @group_class_name ||= 'Group'; end
312
- def group_class_name=(klass); @group_class_name = klass; end
313
-
314
- def in_group(group)
315
- group.present? ? self.in(group_ids: group.id) : none
316
- end
317
-
318
- def in_any_group(*groups)
319
- groups.present? ? self.in(group_ids: groups.flatten.map(&:id)) : none
320
- end
321
-
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)
327
- groups.present? ? where(:group_ids => groups.flatten.map(&:id)) : none
328
- end
329
-
330
- def shares_any_group(other)
331
- in_any_group(other.groups.to_a)
332
- end
333
-
334
- end
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
419
-
420
- # Usage:
421
- # class User
422
- # include Mongoid::Document
423
- #
424
- # acts_as_named_group_member
425
- # ...
426
- # end
427
- #
428
- # user.named_groups << :admin
429
- #
430
- module NamedGroupMember
431
- extend ActiveSupport::Concern
432
- include MemberScopedAs
433
-
434
- included do
435
- field :named_groups, type: Array, default: -> { [] }
436
-
437
- after_initialize do
438
- named_groups.extend NamedGroupCollection
439
- named_groups.member = self
440
- end
441
- end
442
-
443
- def in_named_group?(named_group, opts={})
444
- named_groups.as(opts[:as]).include?(named_group)
445
- end
446
-
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)
453
- end
454
-
455
- return false
456
- end
457
-
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
469
- end
470
-
471
- def shares_any_named_group?(other, opts={})
472
- in_any_named_group?(other.named_groups, opts)
473
- end
474
-
475
- module ClassMethods
476
- def in_named_group(named_group, opts={})
477
- in_any_named_group(named_group, opts)
478
- end
479
-
480
- def in_any_named_group(*named_groups)
481
- named_groups.flatten!
482
- return none unless named_groups.present?
483
-
484
- self.in(named_groups: named_groups.flatten)
485
- end
486
-
487
- def in_all_named_groups(*named_groups)
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)
499
- end
500
-
501
- def shares_any_named_group(other, opts={})
502
- in_any_named_group(other.named_groups, opts)
503
- end
504
- end
505
- end
8
+ autoload :Group, 'groupify/adapter/mongoid/group'
9
+ autoload :GroupMember, 'groupify/adapter/mongoid/group_member'
10
+ autoload :MemberScopedAs, 'groupify/adapter/mongoid/member_scoped_as'
11
+ autoload :NamedGroupCollection, 'groupify/adapter/mongoid/named_group_collection'
12
+ autoload :NamedGroupMember, 'groupify/adapter/mongoid/named_group_member'
506
13
  end
507
14
  end
508
15
 
509
- Mongoid::Document.send :include, Groupify::Mongoid::Adapter
16
+