views:

78

answers:

2

Background, Part 1 I have a form that collects both frequency and duration from the user. We set both the frequency and the duration in seconds, but, to the user, they are always presented as days or weeks. For example, the user sees "every other day", but the value set on the option is '172800' as that is the number of seconds in 2 days.

Background, Part 2 I'm working on an app that we want the user to have an invite code in order to be able to use. So, as part of signing up, the user has to enter an invite code that is connected to a specific email address.

Background, Part 3 I'm a newbie to RoR and am working on someone else's code. I'm about 10 chapters into the 3rd edition of "Agile Web Development with Rails". The person that wrote this isn't available for me to ask them questions until sometime next week and I'd like to solve this before then. If I don't get a resolution before then, I'll post the answer back here once I get it.

Problem When the user submits the form with everything matching except for the invite code and email address, the form returns and the frequency and duration are not re-populated. Now, I know why this is happening. I just don't know how to fix it. Let's dig in to some code, shall we?

Here's the RoR code to create the duration and frequency select form fields:

<%= frequency_select c, :frequency %> 
<%= duration_select c, :duration %>

Here's the details on "frequency_select"

def frequency_select(f, method)
    options = [["day", 1.day], ["other day", 2.days], ["week", 1.week]]
    f.select method, options, :include_blank => true
end

Here're the details on "duration_select"

def duration_select(f, method, unit="day" )
    values, units = *case unit
        when "day" : [[[5, 5], [15, 15], [30, 29]], "days"]
        when "other day" : [[[15, 15], [30, 29], [45,45]], "days"]
        when "week" : [[[4, 29], [6, 43], [8, 57]], "weeks"]
    end
    f.select method, values.map {|(label, i)| ["#{label} #{units}", i.days]}, :include_blank => true
end

Now, the fun part. Here's the code for ":frequency"

def frequency(unit=:days)
    seconds = super
    convert_seconds_to_units(seconds, unit)
end

(Wait for it.)

And here's the code for ":duration"

def duration(unit=:days)
    seconds = super
    convert_seconds_to_units(seconds, unit)
end

The reason the duration and frequency fields aren't re-populating is because the check to match the invite code and email address is happening after the frequency and duration are converted to days.

See, if the form has other errors in addition to the email and invite code not matching, the conversion from seconds to days doesn't happen. But, if everything else checks out, the conversion happens and then the invite code and email address are compared. If there's an error, the data goes back to the browser, but the duration and frequency values are now in days and do not match the values (in seconds) of the duration and frequency in the HTML form.

So, my question is -- and I imagine it's complete newbie question -- how do you hold off on converting the frequency and duration values until after the invite code and email address have been matched.

Lemme know if you need to see more code. I tried to provide everything you would need without overwhelming you. Though, if I missed anything just let me know.

Thanks! Rick

A: 

I think you're tying yourself in knots trying to convert between seconds and days. It's be easier if you had specific accessors for frequency/duration in days that did the conversion for you:

def frequency_in_days
  self.frequency && self.frequency / 86400 
end

def frequency_in_days=(days)
  self.frequency = days.to_i * 86400
end

Then in your form, you can do:

<%= frequency_select c, :frequency_in_days %>

And in your helper:

def frequency_select(f, method)
  options = [["day", 1], ["other day", 2], ["week", 1]]
  f.select method, options, :include_blank => true
end

Should make everything much simpler.

tomafro
So, in the View, I would set one frequency field with options whose values are set in days that would be displayed when there are errors and another frequency field (options with values in seconds) that would be displayed normally? Also, you have ["week", 1] ... should that be ["week", 7]? Thanks for the response.
Rick
That's the principle, yes. You create accessors that allow you to manipulate the frequency (and duration) in days, so you don't have to worry about the time in seconds in your views. In the underlying model though, you still store the data in seconds (to do with whatever you wish).And yeah, it should be ["week", 7].
tomafro
Cool. Thanks for the advice. I'll give this a shot and let you know if it works.
Rick
Can you explain why you have 2 def_frequency_in_days defined? Is one supposed to be duration_in_days? Or can they be combined into a single definition? Thansk!
Rick
Nevermind about that last question. I do believe I understand now. The first declaration converts seconds into days. The second declaration happens after the form is resubmitted and takes the value of the form and converts it back to seconds. Am I right?
Rick
Yeah, that's essentially right. The first (frequency_in_days) is a getter, the second (frequency_in_days=) is a setter.
tomafro
A: 

I could not find a suitable solution to this. The main issue is just that I shouldn't be converting seconds to days -- I should just be using days.

The problem with tomafro's solution is that I would need 2 scripts to generate options for the duration and frequency select options, which is doable, but all this extra code begs the question: why do I need the conversion in the first place? And I'm not sure that I do. I'll be looking into this further to see if changing it creates unforeseen bugs.

Rick