Jump to content

The ultimate community for Ruby on Rails developers.


Photo

Multiple models from one (1) source ... is there a better (more correct) way?

controllers models multiple models from one (1)

  • Please log in to reply
1 reply to this topic

#1 jmesserer

jmesserer

    Passenger

  • Members
  • 4 posts
  • LocationMinneapolis, MN

Posted 25 September 2013 - 08:35 PM

I'd like a few thoughts on if there's a better (or more correct - DRY'er) way to do this ...  lets say you have page that displays something (a retail product, a car, whatever ...) and there's a form on the page that allows you to email this *thing* to a bunch of people by entering their emails (with some separator). I'd like to create a record for each person that it gets emailed to.

 

Currently I have a 'Share' model with the following attributes: to and note. I'm passing in the list of email addresses to the controller via the 'to' attribute and then iterating through them creating individual records for each valid email address.

 

### Console

C:\app\ror\r4>rails c
Loading development environment (Rails 4.0.0)
irb(main):001:0> Share.new
=> #<Share id: nil, to: nil, note: nil, created_at: nil, updated_at: nil>

### View

= form_for Share.new do |f|
  .field
    = f.label :to
    = f.text_field :to, value: 'john@test.com,jane@test.com,phil@test.com,bill@test.com'
  .field
    = f.label :note
    = f.text_field :note, value: 'note 123 ...'
  .actions
    = f.submit 'Save'

### Controller - /app/controller/shares_controller.rb

  def create
    @share = Share.new(share_params)

    # parse out a list of valid email addresses
    valid_emails = Share::parse_multiple @share.to
    
    # create a record for each share and send an email
    valid_emails.each do |email|

      # create a copy of the original record to retain the other attributes: note
      new_share = @share.clone

      # update the email address in the copied record
      new_share.to = email
      
      # send an email if the record saved successfully
      Notifier.share_created(share).deliver if new_share.save
    end
    
    redirect_to shares_path, notice: 'Shares [#{valid_emails.join ','}] were successfully created.'
  end

### Model - /app/models/share.rb

def self.parse_multiple(emails)
  valid = []

  emails.split(',').each do |email|
    next if email.empty?

    email.strip!
    email.downcase!
    
    valid << email if email =~ /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
  end
  
  return valid.uniq
end

Thanks for reading ... J



#2 Vito Botta

Vito Botta

    Signalman

  • Members
  • 12 posts
  • LocationEspoo, Finland

Posted 03 October 2013 - 08:46 PM

I'd move that logic out of the controller into a SRP class, Sharer; dunno, something like this perhaps

def create
  Sharer.new(params).share
  # redirect....
end

class Sharer < Struct.new(params)
  def share
    valid_emails = (params[:to] || "").scan(/[A-Z0-9+_.-]+@[A-Z0-9.-]+/i).uniq # or whatever regex

    new_shares = valid_emails.map do |email|
      { email: email, note: params[:note] }
    end

    Share.create(new_shares)

    valid_emails
  end
end

class Share
  after_create :notify # perhaps in an observer?

  private

  def notify
    Notifier.share_created(self).deliver 
  end
end


  • james likes this





Also tagged with one or more of these keywords: controllers, models, multiple models from one (1)

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users