Robert Klemme
11/1/2008 10:30:00 AM
On 01.11.2008 07:11, Jason Leong wrote:
> Dear all,
>
> My application uses a calendar which I'm populating using an array of
> indexed RollingEvent objects. A RollingEvent is one of a series,
> multiplied from its original Event's repeat options (daily, weekly,
> fortnightly etc). The idea is that when a user saves an Event, the app
> uses a loop to generate and store all the RollingEvents for the coming
> year.
>
> So a new Event is added, add_rolling_event is called within the loop
> (I've omitted most of the params for readability):
>
> def add_rolling_event(id, title, calendar_date)
> @events[calendar_date.to_date] ||= []
> @events[calendar_date.to_date] << RollingEvent.new(id, title,
> calendar_date)
> end
You can make that a tad more efficient by doing
def add_rolling_event(id, title, calendar_date)
(@events[calendar_date.to_date] ||= []) <<
RollingEvent.new(id, title, calendar_date)
end
> The indexing works well, as in the calendar view I can quickly pull out
> the RollingEvent objects for that specific day. For the moment, whenever
> an Event is edited the entire @events array is reset and a routine loops
> through all Events to re-generate RollingEvents.
>
> I'm trying to speed up the process by just deleting the RollingEvents
> pertinent to the Event that's being edited, then calling
> add_rolling_event for the Event in question.
>
> I have 3 questions:
>
> 1. Is using an indexed array actually faster than picking out events on
> the day using @events.select { |e| e.calendar_date == date }? It would
> be good to uncomplicate this if possible.
What do you mean by "indexed array"? I am not sure what your #to_date
returns so you might be using a Hash or an Array. Ultimately when it
comes to "faster" you need to implement different variants and benchmark
them. Fortunately this is pretty easily done in Ruby.
> 2. It seems that if I were to use delete_if on an indexed array, I'd
> have to loop through the array of RollingEvent objects for each indexed
> day. Am I right in assuming that this is uber-inefficient? Is there a
> better way to do this?
Well, it seems you have to access paths to an event: lookup by date and
lookup by event id. I do not know whether there are other access paths
(e.g. search for event name or such) but assuming for the moment that
there are just the two a solution with two Hashes seems most appropriate
class Calendar
def initialize
@ev_by_date = Hash.new {|h,k| h[k] = []}
@ev_by_id = Hash.new {|h,k| h[k] = []}
end
def add_rolling_event(id, title, calendar_date)
ev = RollingEvent.new(id, title, calendar_date)
@ev_by_date[calendar_date] = ev
@ev_by_id[id] = ev
end
def delete_by_id(id)
@ev_by_id.delete(id).each do |ev|
@ev_by_date[ev.date].delete(ev)
end
end
def delete_by_date(date)
@ev_by_date.delete(date).each do |ev|
@ev_by_id[ev.id].delete(ev)
end
end
end
> 3. Currently @events (in a model called Event_Roster) and RollingEvents
> are all non-database-backed for performance, and stored in a memcached
> session. My concern has always been that - hypothetically - one could
> have 10 daily Events, resulting in 3650 RollingEvents. This doesn't seem
> to be a scalable solution, and the read/writes to and from the database
> would be costly. Would you advise writing all RollingEvents to a
> database, and is the performance hit negligable?
There are a lot other factors that you need to consider when thinking
about the database. 3600 events is certainly a small number, even when
read from a flat file. You rather want a DB if your application needs
to run on multiple machines concurrently and you want to have a single
repository etc. As long as you can foresee that there will not be
millions of entries I would probably not complicate the application by
adding another component.
Kind regards
robert