Module: UserGroupMembershipMixins::ValidityRangeForIndirectMemberships

Extended by:
ActiveSupport::Concern
Included in:
UserGroupMembership
Defined in:
app/models/user_group_membership_mixins/validity_range_for_indirect_memberships.rb

Overview

In this project, user group memberships do not neccessarily last forever. They can begin at some time and end at some time. This is expressed by the ValidityRange of a membership.

Examples:

membership.valid_from  # =>  time
membership.valid_to    # =>  time
membership.invalidate

Scopes:

UserGroupMembership.with_invalid
UserGroupMembership.only_valid
UserGroupMembership.only_invalid
UserGroupMembership.at_time(time)

By default, the ‘only_valid` scope is applied, i.e. only memberships are found that are valid at present time. To override this scope, use either `with_invalid` or `unscoped`.

Instance Method Summary collapse

Instance Method Details

#earliest_direct_membershipObject

The validity range attributes are inherited for indirect memberships.

*-----------------(c)--------------------*
                   |
         |--------------------|
         |                    |
*-------(a)--------*          | 
                   *---------(b)---------*

_________________________________________________________
t1                 t2                    t3      time -->

If membership A is valid from t1 to t2 and membership B is valid from t2 to t3 and membership C is the indirect membership that results from the memberships A and B, then C is valid from t1 to t3.

This means that the valid_from attribute is derived from the valid_from attribute of the earliest direct membership. The valid_to attribute is derived from the latest direct membership.



55
56
57
# File 'app/models/user_group_membership_mixins/validity_range_for_indirect_memberships.rb', line 55

def earliest_direct_membership
  @earliest_direct_membership ||= direct_memberships(with_invalid: true).reorder('valid_from').first
end

#latest_direct_membershipObject



59
60
61
62
# File 'app/models/user_group_membership_mixins/validity_range_for_indirect_memberships.rb', line 59

def latest_direct_membership
  @latest_direct_membership ||= direct_memberships.only_valid.last
  @latest_direct_membership ||= direct_memberships(with_invalid: true).reorder('valid_to').last
end

#make_invalid(time = Time.zone.now) ⇒ Object

For indirect memberships, invalidation is not possible. Only direct memberships can be invalidated. The validity of the indirect memberships inherts from the direct ones.



155
156
157
158
# File 'app/models/user_group_membership_mixins/validity_range_for_indirect_memberships.rb', line 155

def make_invalid(time = Time.zone.now)
  raise 'An indirect membership cannot be invalidated. ' + self.user.id.to_s + ' ' + self.group.id.to_s unless direct?
  super
end

#recalculate_validity_range_from_direct_membershipsObject

This method recalculates the validity range for an indirect membership. This becomes necessary whenever the validity range of a direct membership is changed, so that the validity range of the indirect memberships can be used in database queries, for example, when using scopes.

Attention: At this point, this mechanism does not cover the validity range of indirect memberships where there should be a gap in the membership:

*----------*     *----------* (indirect membership with gap in validity range)
     |--------|--------|
*----------*           |      (direct membership 1)
                 *----------* (direct membership 2)

TODO: This has to be fiexed, probably when switching to neo4j.



114
115
116
117
118
119
120
121
# File 'app/models/user_group_membership_mixins/validity_range_for_indirect_memberships.rb', line 114

def recalculate_validity_range_from_direct_memberships
  unless direct?
    write_attribute :valid_from, earliest_direct_membership.try(:valid_from)
    write_attribute :valid_to, latest_direct_membership.try(:valid_to)
    self.valid_from_will_change!
    self.valid_to_will_change!
  end
end

#recalculate_validity_range_from_direct_memberships!Object



123
124
125
126
127
128
129
130
131
132
133
134
# File 'app/models/user_group_membership_mixins/validity_range_for_indirect_memberships.rb', line 123

def recalculate_validity_range_from_direct_memberships!
  unless direct?
    self.valid_from_will_change!
    self.valid_to_will_change!
    recalculate_validity_range_from_direct_memberships
    self.valid_from_will_change!
    self.valid_to_will_change!
    save!
  else
    raise "Recalculating the validity range makes only sense for indirect memberships. This is a direct one. Membership id: #{self.id}."
  end
end

#save(*args) ⇒ Object

Save the current membership and auto-save also the direct memberships associated with the current (maybe indirect) membership.



91
92
93
94
95
96
97
# File 'app/models/user_group_membership_mixins/validity_range_for_indirect_memberships.rb', line 91

def save(*args)
  super(*args)
  unless self.direct?
    earliest_direct_membership.try(:save)
    latest_direct_membership.try(:save)
  end
end

#valid_fromObject



64
65
66
# File 'app/models/user_group_membership_mixins/validity_range_for_indirect_memberships.rb', line 64

def valid_from
  self.direct? ? super : cached { earliest_direct_membership.try(:valid_from) }
end

#valid_from=(valid_from) ⇒ Object



67
68
69
70
71
72
73
74
# File 'app/models/user_group_membership_mixins/validity_range_for_indirect_memberships.rb', line 67

def valid_from=( valid_from )
  if self.direct? 
    super(valid_from) 
    @need_to_recalculate_indirect_memberships = true
  else
    earliest_direct_membership.try(:valid_from=, valid_from)
  end
end

#valid_toObject



76
77
78
# File 'app/models/user_group_membership_mixins/validity_range_for_indirect_memberships.rb', line 76

def valid_to
  self.direct? ? super : latest_direct_membership.try(:valid_to)
end

#valid_to=(valid_to) ⇒ Object



79
80
81
82
83
84
85
86
# File 'app/models/user_group_membership_mixins/validity_range_for_indirect_memberships.rb', line 79

def valid_to=( valid_to )
  if self.direct? 
    super(valid_to) 
    @need_to_recalculate_indirect_memberships = true
  else
    latest_direct_membership.try(:valid_to=, valid_to)
  end
end