Class: Member

Inherits:
ActiveRecord::Base
  • Object
show all
Includes:
Ext::DeviseConfiguration, Ext::Integrations::Member, Ext::S3Link, Ext::Uuid
Defined in:
app/models/member.rb

Constant Summary collapse

CURRENT =
:current
LAPSED =
:lapsed
PAST =
:past
NONE =
:none

Constants included from Ext::Uuid

Ext::Uuid::PREFIX

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Ext::S3Link

#download_link_for

Methods included from Ext::Uuid

included, #set_uuid, uuid

Methods included from Ext::DeviseConfiguration

included

Class Method Details

.find_by_membership(membership, person) ⇒ Object



128
129
130
# File 'app/models/member.rb', line 128

def self.find_by_membership(membership, person)
  Member.find_by_email_and_organization_id(person.email, membership.organization)    #TODO: Biggest join ever
end

.for(membership, person) ⇒ Object

  • Creates a member for this membership if one does not already exist

  • Attaches that member to the applicable person

  • Invites that member if membership.send_email is true

  • Generates a new qr code if this is a new member

  • Generates a membership card if this is a new member

This method CANNOT be run in any synchronous flow



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'app/models/member.rb', line 145

def self.for(membership, person)
  member = Member.find_by_membership(membership, person)

  if member.nil?
    member = Member.create!({:email => person.email,
                             :organization => membership.organization,
                             :person => person,
                             :password => SecureRandom.hex(32)}, :without_protection => true)

    member.person.create_address if member.person.address.nil?

    membership.member = member
    membership.save

    member.generate_qr_code
    member.generate_pdf

    member.invite! if membership.send_email
  else
    membership.member = member
    membership.save
  end

  member
end

.generate_passwordObject



113
114
115
# File 'app/models/member.rb', line 113

def self.generate_password
  Devise.friendly_token
end

.statesObject



103
104
105
# File 'app/models/member.rb', line 103

def self.states
  [CURRENT, LAPSED, PAST, NONE]
end

Instance Method Details

#active_for_authentication?Boolean

Returns:

  • (Boolean)


124
125
126
# File 'app/models/member.rb', line 124

def active_for_authentication?
  super && !suspended?
end

#attachments_for(action) ⇒ Object

returns an array of attachments in this format: [ [filename, file], [filename, file] ]



45
46
47
48
49
50
51
52
# File 'app/models/member.rb', line 45

def attachments_for(action)
  case action.to_s
  when "invitation_instructions"
    [ ["membership_card.pdf", member_card_pdf] ]
  else
    []
  end
end

#count_membershipsObject

This is always run DJ’d



76
77
78
79
80
81
# File 'app/models/member.rb', line 76

def count_memberships
  self.current_memberships_count  = self.memberships.current.count
  self.lapsed_memberships_count   = self.memberships.lapsed.count
  self.past_memberships_count     = self.memberships.past.count
  self.save
end

#current_membership_typesObject



61
62
63
# File 'app/models/member.rb', line 61

def current_membership_types
  memberships.current.collect(&:membership_type)
end

#generate_pdfObject



171
172
173
174
175
176
177
# File 'app/models/member.rb', line 171

def generate_pdf
  pdf = PdfGeneration.new(self).generate
  file = Tempfile.new(["#{self.id}", '.pdf'])
  file << pdf.force_encoding("UTF-8")
  self.pdf = file
  self.save
end

#generate_qr_codeObject



179
180
181
182
183
184
185
186
187
188
189
190
# File 'app/models/member.rb', line 179

def generate_qr_code
  file = Tempfile.new(['qr-code', '.png'])
  Ticket::QRCode.new(self, nil).render(file)
  self.qr_code = file

  # If we don't save the ticket here, paperclip will leak a file handle.
  # Even though we close our Tempfile below, paperclip copies that into
  # another Tempfile, and only closes the handle when the ticket is saved.
  save

  file.close
end

#headers_for(action) ⇒ Object

devise_invitable needs this otherwise it can’t set the :from param in an email



33
34
35
36
37
38
39
40
# File 'app/models/member.rb', line 33

def headers_for(action)
  case action.to_s
  when "invitation_instructions"
    {:from => self.organization.email}
  else
    {}
  end
end

#member_card_pdfObject



54
55
56
57
58
59
# File 'app/models/member.rb', line 54

def member_card_pdf
  file = Tempfile.new(["member#{self.id}", '.pdf'])
  self.pdf.copy_to_local_file(:original, file)
  file.rewind
  file.read
end

#member_throughObject



132
133
134
# File 'app/models/member.rb', line 132

def member_through
  self.memberships.map{|membership| membership.ends_at}.max
end

#member_tickets_purchased_for(event) ⇒ Object



65
66
67
68
69
70
71
# File 'app/models/member.rb', line 65

def member_tickets_purchased_for(event)
  Ticket.joins(:ticket_type)
        .joins(:show => :event)
        .where(:buyer_id => self.person.id)
        .where('shows.event_id' => event.id)
        .where('ticket_types.member_ticket = 1')
end

#set_member_numberObject



117
118
119
# File 'app/models/member.rb', line 117

def set_member_number
  self.member_number = MemberNumberGenerator.next_number_for(self.organization)
end

#stateObject

Intentionally did not use a state machine for this for a few reasons 1) I’m not all that happy with transitions 2) Can’t find another state machine that I like and is worth the cost of switching to 3) This works just fine. I prefer calculating state on the fly here because we’re couning the memberships

on this member anyway

4) Determining a lapsed or past member is a touch more complicated than it sounds

A lapsed member is a member with lapsed memberships *and no current memberships*
It's that last bit that makes grabbing all last members quite difficult in SQL

Note that this method uses the cached values for current_memberships, lapsed_memberships, and past_memberships



96
97
98
99
100
101
# File 'app/models/member.rb', line 96

def state
  return CURRENT if self.current_memberships_count > 0
  return LAPSED  if self.lapsed_memberships_count  > 0
  return PAST    if self.past_memberships_count    > 0
  return NONE
end