
[o] merge schedule_queue and unschedule_queue (and merge [un]schedule steps)
[x] OR stop using queue, since we've got the thread-safe JobArray
[x] if possible, drop the mutex in JobArray
    NO, that mutex is necessary for Scheduler#jobs (on JRuby an co)...
[o] named mutexes
[o] drop the schedule queue, rely on the mutex in JobArray
[o] def jobs; (@jobs.to_a + running_jobs).uniq; end
[o] replace @unscheduled by @unscheduled_at
[o] make sure #jobs doesn't return unscheduled jobs
[o] job tags and find_by_tag(t) (as in rs 2.x)
[o] require tzinfo anyway (runtime dep)
[o] document frequency
[o] accept :frequency => '5s'
[o] timeout (as in rufus-scheduler 2.x)
[o] Rufus::Scheduler#running_jobs (as in rufus-scheduler 2.x)
[o] Rufus::Scheduler#terminate_all_jobs
[o] Rufus::Scheduler::Job#kill
[x] Rufus::Scheduler#kill_all_jobs
[o] Rufus::Scheduler#shutdown(:terminate or :kill (or nothing))
[o] RepeatJob #pause / #resume (think about discard past)
[o] Rufus::Scheduler.start_new (backward comp) (with deprec note?)
[o] pass job to scheduled block? What does rs 2.x do?
[o] :first[_in|_at] for RepeatJob
[o] :last[_in|_at] for RepeatJob
[o] :times for RepeatJob (how many recurrences)
[o] fix issue #39 (first_at parses as UTC)
[o] about issue #43, raise if cron/every job frequency < scheduler frequency
[o] unlock spec/parse_spec.rb:30 "parse datimes with timezones"
[o] some kind of Schedulable (Xyz#call(job, time))
[o] add Jruby and Rubinius to Travis
[o] make Job #first_at= / #last_at= automatically parse strings?
[o] bring in Kratob's spec about mutex vs timeout and adapt 3.0 to it,
    https://github.com/jmettraux/rufus-scheduler/pull/67
[x] :unschedule_if => lambda { |job| ... }
[o] OR look at how it was done in rs 2.0.x, some return value?
    no, pass the job as arg to the block, then let the block do job.unschedule
    so, document schedule.every('10d') { |j| j.unschedule if x?() }
[x] remove the time in job.trigger(time)
[o] add spec for job queued then unscheduled
[o] add spec for Scheduler#shutdown and work threads
[o] at some point, bring back rbx19 to Travis
[o] move the parse/util part of scheduler.rb to util.rb
[o] rescue KillSignal in job thread loop to kill just the job
[o] add spec for raise if scheduling a job while scheduler is shutting down
[o] schedule_in(2.days.from_now) {}
    at and in could understand each others time parameter, ftw...
    use the new #parse_to_time? no
[o] do repeat jobs reschedule after timing out? yes
[o] schedule_interval('20s')?
[x] Scheduler#reschedule(job) (new copy of the job)
[x] #free_all_work_threads is missing an implementation
[x] rescue StandardError
    :on_error => :crash[_scheduler]
    :on_error => :ignore
    :on_error => ...
[o] on_error: what about TimeoutError in that scheme?
    TimeoutError goes to $stderr, like a normal error
[o] link to SO for support
    - sublink to "how to report bugs effectively"
[o] link to #ruote for support
[x] lockblock? pass a block to teach the scheduler how to lock?
    is not necessary, @scheduler = Scheduler.new if should_start?
    the surrounding Ruby code checks
[o] introduce job "vars", as in
    http://stackoverflow.com/questions/18202848/how-to-have-a-variable-that-will-available-to-particular-scheduled-task-whenever
    or job['key'] Job #[] and #[]=, as with Thread #[] #[]=
    job-local variables #keys #key?
[o] thread-safety for job-local variables?
[x] discard past? discard_past => true or => "1d"
    default would be discard_past => "1m" or scheduler freq * 2 ?
    jobs would adjust their next_time until it fits the window...
    ~~ discard past by default
[o] expanded block/schedulable (it's "callable")
    ```
      scheduler.every '10m' do
        def pre
          return false if Backend.down?
          # ...
        end
        def post
          # ...
        end
        def trigger
          puts "oh hai!"
        end
      end
    ```
    or something like that...
    ...
    OR accept a class (and instantiate it the first time)
    ```
      scheduler.every '10m', Class.new do
        def call(job, time)
          # ...
        end
      end
    ```
    the job contains the instance in its @callable
[x] add spec case for corner case in Job#trigger (overlap vs reschedule) !!!
[o] rethink job array vs job set for #scheduled?
[x] introduce common parent class for EveryJob and IntervalJob
[o] create spec/ at_job_spec.rb, repeat_job_spec.rb, cron_job_spec.rb, ...
[x] ensure EveryJob do not schedule in the past (it's already like that)
[o] CronLine#next_time should return a time with subseconds chopped off
[o] drop min work threads setting?
[o] thread pool something? Thread upper limit?
[o] Rufus::Scheduler.singleton, Rufus::Scheduler.s
[o] EveryJob#first_at= and IntervalJob#first_at= should alter @next_time
[o] scheduler.schedule duration/time/cron ... for at/in/cron
    (not every, nor interval)
    scheduler.repeat time/cron ... for every/cron

[o] :lockfile => x, timestamp, process_id, thread_id...
    warning: have to clean up that file on exit... or does the scheduler
    timestamps it?
[ ] develop lockfile timestamp thinggy
    ~ if the timestamp is too old (twice the default frequency?) then
    lock [file] take over...
    Is that really what we want all the time?

[ ] idea: :mutex => x and :skip_on_mutex => true ?
    would prevent blocking/waiting for the mutex to get available
    :mutex => [ "mutex_name", true ]
    :mutex => [ [ "mutex_name", true ], [ "other_mutex_name", false ] ]

[ ] bring back EM (but only EM.defer ?) :defer => true (Job or Scheduler
    or both option?)

[ ] prepare a daemon, trust daemon-kit for that

[ ] :if => lambda { |job, time| ... } why not?
    :unless => lambda { ...
    :block => lambda { ...
    can help get the block themselves leaner
    #
    investigate guards for schedulables... def if_guard; ...; end

[ ] scheduler.every '10', Class.new do
      def call(job, time)
        # might fail...
      end
      def on_error(err, job)
        # catches...
      end
    end

~~~

[ ] scheduler.at('chronic string', chronic_options...)

