views:

134

answers:

2

I have two Models: Campaign and Contact.

A Campaign has_many Contacts.

A Contact has_many Campaigns.

Currently, each Contact has a contact.date_entered attribute. A Campaign uses that date as the ate to count down to the different Events that belong_to the Campaign.

However, there are situations where a Campaign for a specific Contact may need to be delayed by X number of days. In this instance, the campaigncontact.delaydays = 10.

In some cases, the Campaign must be stopped altogether for the specific Contact, so for now I set campaigncontact.delaydays = 1. (Are there major problems with that?)

By default, I am assuming that no campaigncontact exists (but not sure how that works?)

So here's what I've tried to do:

class Contact < ActiveRecord::Base
  has_many :campaigncontacts
  has_many :campaigns, :through => :campaigncontacts
end

class Campaign < ActiveRecord::Base
  has_many :campaigncontacts
  has_many :contacts, :through => :campaigncontacts
end

script/generate model campaigncontact campaign_id:integer contact_id:integer delaydays:integer

class Campaigncontact < ActiveRecord::Base
  belongs_to :campaign
  belongs_to :contact
end

So, here's the question: Is the above correct? If so, how do I allow a user to edit the delay of a campaign for a specific Contact.

For now, I want to do so from the Contact View.

This is what I tried:

In the Contact controller (?)

  in_place_edit_for :campaigncontact, column.delaydays

And in the View

<%= in_place_editor_field :campaigncontact, :delaydays %>

How can I get it right?

A: 

A good way to do this is use a "fake" attribute on your Contact model like so:

class Contact < ActiveRecord::Base
  has_many :campaigncontacts
  has_many :campaigns, :through => :campaigncontacts

  attr_accessor :delay

  def delay #edit
    self.campaigncontacts.last.delaydays
  end

  def delay=(val)
    self.campaigncontacts.each do |c|
      c.delaydays = val
    end
  end
end

Then just set the in_place_editor for this fake field:

in_place_edit_for :contact, :delay

and

<%= in_place_editor_field :contact, :delay %>

I'm not sure I understood exactly what you wanted to accomplish, but I hope this at least points you into the right direction.

Jakub Hampl
Hi, I had to modify '<%= in_place_editor_field @contact, :delay %>' to be '<%= in_place_editor_field :contact, :delay %>' but nothing shows up because it is NULL. Can I default some character in this? Thanks.
Angela
To display the value you should edit the model as above.
Jakub Hampl
Hi, I encountered an error where it says: undefined method `delaydays' for nil:NilClass ... does an instance for campaigncontacts need to exist fo revery campaigncontact?
Angela
+1  A: 

I would add an integer field to your Campaigncontacts resource called days_to_delay_communication_by, since this information relates to the association of a campaign and a contact rather than a contact itself.

in your migration:

def self.up
  add_column(:campaigncontacts, :days_to_delay_communication_by, :integer)
end

def self.down
  remove_column(:campaigncontacts, :days_to_delay_communication_by)
end

Now you can set that value by:

campaigncontact = Campaigncontacts.find(:first, :conditions => { :campaign_id => campaign_id, :contact_id => contact_id })
campaigncontact.days_to_delay_communication_by = 10

Then in the admin side of your application you can have a controller and a view for campaign communications that lets you set the days_to_delay_communication_by field for campaigncontacts. I can expand on this further for you if you're interested, but I think you get the idea.

Then you'll need to run a background process of some sort (probably a cron job, or use the delayed_job plugin), to find communications that haven't happened yet, and make them happen when the date has passed. You could do this in a rake task like so:

namespace :communications do
  namespace :monitor do
    desc 'Monitor and send communications for campaigns'
    task :erma => :environment do
      Rails.logger.info "-----BEGIN COMMUNICATION MONITORING-----"

      unsent_communications = Communication.all(:conditions => { :date_sent => nil})
      unsent_communications.each do |communication|
        Rails.logger.info "**sending communication**"
        communication.send if communication.time_to_send < Time.now
        Rails.logger.info "**communication sent**"
      end

      Rails.logger.info "-----END COMMUNICATION MONITORING-----"
    end #end erma task
  end #end sync namespace
end #end db namespace

Then your cron job would do something like:

cd /path/to/application && rake communications:monitor RAILS_ENV=production

Also, I'd consider changing the name of your join model to something more descriptive of it's purpose, for instance memberships, a campaign has many memberships and a contact has many memberships. Then a membership has a days_to_delay_communication field.

Patrick Klingemann
yes..interesting...I think you're right.....I think this is the way I need to digest it....
Angela
re-reading this, how do I do the in_place_editor....would I create a "fake" attribute on Contact like the othe rposter suggested?
Angela