rtmapi 0.5.2 → 0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README +11 -0
- data/lib/rtmapi.rb +214 -96
- metadata +1 -1
data/README
CHANGED
@@ -143,6 +143,17 @@ But this will allow you to add special functionality to tasks
|
|
143
143
|
like). If there is interest, we can do the same thing for
|
144
144
|
groups, lists, etc etc.
|
145
145
|
|
146
|
+
RememberTheMilkTask also has a number of helper methods, so you can
|
147
|
+
do this:
|
148
|
+
|
149
|
+
task = @rtm.tasks.getList.values[0].values[0] # grabs 1st task off of first list returned by API
|
150
|
+
modified_task = task.setTags "tag1,tag2"
|
151
|
+
modified_task_2 = modified_task.addTags "tag3"
|
152
|
+
modified_task.tags => ['tag1','tag2']
|
153
|
+
modified_task_2.tags => ['tag1','tag2', 'tag3']
|
154
|
+
|
155
|
+
all the methods for rtm.tasks.* have helper methods defined (except for getList)
|
156
|
+
|
146
157
|
Dates
|
147
158
|
-----
|
148
159
|
|
data/lib/rtmapi.rb
CHANGED
@@ -31,7 +31,7 @@ require 'xml/libxml'
|
|
31
31
|
#TODO: allow specifying whether retval should be indexed by rtm_id or list name for lists
|
32
32
|
|
33
33
|
class RememberTheMilk
|
34
|
-
RUBY_API_VERSION = '0.
|
34
|
+
RUBY_API_VERSION = '0.6'
|
35
35
|
# you can just put set these here so you don't have to pass them in with
|
36
36
|
# every constructor call
|
37
37
|
API_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
@@ -59,8 +59,12 @@ class RememberTheMilk
|
|
59
59
|
@user_settings_cache[auth_token]
|
60
60
|
end
|
61
61
|
|
62
|
+
def get_timeline
|
63
|
+
user[:timeline] ||= timelines.create
|
64
|
+
end
|
65
|
+
|
62
66
|
def time_to_user_tz( time )
|
63
|
-
return time unless(@use_user_tz && @auth_token && defined?(TZInfo::Timezone
|
67
|
+
return time unless(@use_user_tz && @auth_token && defined?(TZInfo::Timezone))
|
64
68
|
begin
|
65
69
|
unless defined?(@user_settings_cache[auth_token]) && defined?(@user_settings_cache[auth_token][:tz])
|
66
70
|
@user_settings_cache[auth_token] = settings.getList
|
@@ -296,7 +300,7 @@ class RememberTheMilk
|
|
296
300
|
tasks = RememberTheMilkHash.new
|
297
301
|
|
298
302
|
list.each do |taskseries_as_hash|
|
299
|
-
taskseries = RememberTheMilkTask.new.merge(taskseries_as_hash)
|
303
|
+
taskseries = RememberTheMilkTask.new(self).merge(taskseries_as_hash)
|
300
304
|
|
301
305
|
taskseries[:parent_list] = list_id # parent pointers are nice
|
302
306
|
taskseries[:tasks] = taskseries.arrayify_value(:task)
|
@@ -489,109 +493,223 @@ class RememberTheMilkHash < Hash
|
|
489
493
|
end
|
490
494
|
|
491
495
|
|
496
|
+
## TODO -- better rrule support. start here with this code, commented out for now
|
492
497
|
## DateSet is to manage rrules
|
493
498
|
## this comes from the iCal ruby module as mentioned here:
|
494
499
|
## http://www.macdevcenter.com/pub/a/mac/2003/09/03/rubycocoa.html
|
495
500
|
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
@startDate = startDate
|
500
|
-
@frequency = nil
|
501
|
-
@count = nil
|
502
|
-
@untilDate = nil
|
503
|
-
@byMonth = nil
|
504
|
-
@byDay = nil
|
505
|
-
@starts = nil
|
506
|
-
if not rule.nil? then
|
507
|
-
@starts = rule.every == 1 ? 'every' : 'after'
|
508
|
-
parseRecurrenceRule(rule.rule)
|
509
|
-
end
|
510
|
-
end
|
511
|
-
|
512
|
-
def parseRecurrenceRule(rule)
|
513
|
-
|
514
|
-
if rule =~ /FREQ=(.*?);/ then
|
515
|
-
@frequency = $1
|
516
|
-
end
|
517
|
-
|
518
|
-
if rule =~ /COUNT=(\d*)/ then
|
519
|
-
@count = $1.to_i
|
520
|
-
end
|
521
|
-
|
522
|
-
if rule =~ /UNTIL=(.*?)[;\r]/ then
|
523
|
-
@untilDate = DateParser.parse($1)
|
524
|
-
end
|
525
|
-
|
526
|
-
if rule =~ /INTERVAL=(\d*)/ then
|
527
|
-
@interval = $1.to_i
|
528
|
-
end
|
501
|
+
# The API is aware it's creating tasks. You may want to add semantics to a "task"
|
502
|
+
# elsewhere in your program. This gives you that flexibility
|
503
|
+
# plus, we've added some helper methods
|
529
504
|
|
530
|
-
|
531
|
-
|
532
|
-
|
505
|
+
class RememberTheMilkTask < RememberTheMilkHash
|
506
|
+
attr_accessor :rtm
|
507
|
+
|
508
|
+
def timeline
|
509
|
+
@timeline ||= rtm.get_timeline # this caches timelines per user
|
510
|
+
end
|
533
511
|
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
512
|
+
def initialize( rtm_api_handle=nil )
|
513
|
+
super
|
514
|
+
@rtm = rtm_api_handle # keep track of this so we can do setters (see factory below)
|
515
|
+
end
|
516
|
+
|
517
|
+
def task() tasks[-1] end
|
518
|
+
def taskseries_id() rtm_id end
|
519
|
+
def task_id() task.rtm_id end
|
520
|
+
def list_id() parent_list end
|
521
|
+
def due() task.due end
|
522
|
+
|
523
|
+
def has_due?() due.class == Time end
|
524
|
+
def has_due_time?() task.has_due_time == '1' end
|
525
|
+
def complete?() task[:completed] != '' end
|
526
|
+
def to_s
|
527
|
+
"#{parent_list}/#{taskseries_id}/#{task_id}: #{name}"
|
528
|
+
end
|
529
|
+
|
530
|
+
def due_display
|
531
|
+
if has_due?
|
532
|
+
if has_due_time?
|
533
|
+
due.strftime("%a %d %b %y at %I:%M%p")
|
534
|
+
else
|
535
|
+
due.strftime("%a %d %b %y")
|
536
|
+
end
|
537
|
+
else
|
538
|
+
'[no due date]'
|
538
539
|
end
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
540
|
+
end
|
541
|
+
|
542
|
+
@@BeginningOfEpoch = Time.parse("Jan 1 1904") # kludgey.. sure. life's a kludge. deal with it.
|
543
|
+
include Comparable
|
544
|
+
def <=>(other)
|
545
|
+
due = (has_key?(:tasks) && tasks.class == Array) ? task[:due] : nil
|
546
|
+
due = @@BeginningOfEpoch unless due.class == Time
|
547
|
+
other_due = (other.has_key?(:tasks) && other.tasks.class == Array) ? other.task[:due] : nil
|
548
|
+
other_due = @@BeginningOfEpoch unless other_due.class == Time
|
549
|
+
|
550
|
+
# sort based on due date, then priority, then name
|
551
|
+
# if 0 was false in ruby, we could have done
|
552
|
+
# prio <=> other_due || due <=> other_due || self['name'].to_s <=> other['name'].to_s
|
553
|
+
# but it's not, so oh well....
|
554
|
+
prio = priority.to_i
|
555
|
+
prio += 666 if prio == 0 # prio of 0 is no priority which means it should show up below 1-3
|
556
|
+
other_prio = other.priority.to_i
|
557
|
+
other_prio += 666 if other_prio == 0
|
558
|
+
|
559
|
+
if prio != other_prio
|
560
|
+
return prio <=> other_prio
|
561
|
+
elsif due != other_due
|
562
|
+
return due <=> other_due
|
563
|
+
else
|
564
|
+
# TODO: should this be case insensitive?
|
565
|
+
return self[:name].to_s <=> other[:name].to_s
|
544
566
|
end
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
when 'WEEKLY'
|
578
|
-
return true if @startDate.wday == date.wday
|
579
|
-
|
580
|
-
when 'MONTHLY'
|
581
|
-
|
582
|
-
when 'YEARLY'
|
567
|
+
end
|
568
|
+
|
569
|
+
# Factory Methods...
|
570
|
+
# these are for methods that take arguments and apply to the taskseries
|
571
|
+
# if you have RememberTheMilkTask called task, you might do:
|
572
|
+
# task.addTags( 'tag1, tag2, tag3' )
|
573
|
+
# task.setRecurrence # turns off all rrules
|
574
|
+
# task.complete # marks last task as complete
|
575
|
+
# task.setDueDate # unsets due date for last task
|
576
|
+
# task.setDueDate( nil, :task_id => task.tasks[0].id ) # unsets due date for first task in task array
|
577
|
+
# task.setDueDate( "tomorrow at 1pm", :parse => 1 ) # sets due date for last task to tomorrow at 1pm
|
578
|
+
[['addTags','tags'], ['setTags', 'tags'], ['removeTags', 'tags'], ['setName', 'name'],
|
579
|
+
['setRecurrence', 'repeat'], ['complete', ''], ['uncomplete', ''], ['setDueDate', 'due'],
|
580
|
+
['setPriority', 'priority'], ['movePriority', 'direction'], ['setEstimate', 'estimate'],
|
581
|
+
['setURL', 'url'], ['postpone', ''], ['delete', ''] ].each do |method_name, arg|
|
582
|
+
class_eval <<-RTM_METHOD
|
583
|
+
def #{method_name} ( value=nil, args={} )
|
584
|
+
if @rtm == nil
|
585
|
+
raise RememberTheMilkAPIError.new( :code => '667', :msg => "#{method_name} called without a handle to an rtm object [#{self.to_s}]" )
|
586
|
+
end
|
587
|
+
method_args = {}
|
588
|
+
method_args["#{arg}"] = value if "#{arg}" != '' && value
|
589
|
+
method_args[:timeline] = timeline
|
590
|
+
method_args[:list_id] = list_id
|
591
|
+
method_args[:taskseries_id] = taskseries_id
|
592
|
+
method_args[:task_id] = task_id
|
593
|
+
method_args.merge!( args )
|
594
|
+
@rtm.call_api_method( "tasks.#{method_name}", method_args ) # returns the modified task
|
595
|
+
end
|
596
|
+
RTM_METHOD
|
597
|
+
end
|
583
598
|
|
584
|
-
|
585
|
-
|
586
|
-
|
599
|
+
# We have to do this because moveTo takes a "from_list_id", not "list_id", so the above factory
|
600
|
+
# wouldn't work. sigh.
|
601
|
+
def moveTo( to_list_id, args = {} )
|
602
|
+
if @rtm == nil
|
603
|
+
raise RememberTheMilkAPIError.new( :code => '667', :msg => "moveTO called without a handle to an rtm object [#{self.to_s}]" )
|
587
604
|
end
|
588
|
-
|
589
|
-
|
590
|
-
|
605
|
+
method_args = {}
|
606
|
+
method_args[:timeline] = timeline
|
607
|
+
method_args[:from_list_id] = list_id
|
608
|
+
method_args[:to_list_id] = to_list_id
|
609
|
+
method_args[:taskseries_id] = taskseries_id
|
610
|
+
method_args[:task_id] = task_id
|
611
|
+
method_args.merge( args )
|
612
|
+
@rtm.call_api_method( :moveTo, method_args )
|
613
|
+
end
|
614
|
+
|
591
615
|
end
|
592
616
|
|
593
617
|
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
618
|
+
#
|
619
|
+
# class DateSet
|
620
|
+
#
|
621
|
+
# def initialize(startDate, rule)
|
622
|
+
# @startDate = startDate
|
623
|
+
# @frequency = nil
|
624
|
+
# @count = nil
|
625
|
+
# @untilDate = nil
|
626
|
+
# @byMonth = nil
|
627
|
+
# @byDay = nil
|
628
|
+
# @starts = nil
|
629
|
+
# if not rule.nil? then
|
630
|
+
# @starts = rule.every == 1 ? 'every' : 'after'
|
631
|
+
# parseRecurrenceRule(rule.rule)
|
632
|
+
# end
|
633
|
+
# end
|
634
|
+
#
|
635
|
+
# def parseRecurrenceRule(rule)
|
636
|
+
#
|
637
|
+
# if rule =~ /FREQ=(.*?);/ then
|
638
|
+
# @frequency = $1
|
639
|
+
# end
|
640
|
+
#
|
641
|
+
# if rule =~ /COUNT=(\d*)/ then
|
642
|
+
# @count = $1.to_i
|
643
|
+
# end
|
644
|
+
#
|
645
|
+
# if rule =~ /UNTIL=(.*?)[;\r]/ then
|
646
|
+
# @untilDate = DateParser.parse($1)
|
647
|
+
# end
|
648
|
+
#
|
649
|
+
# if rule =~ /INTERVAL=(\d*)/ then
|
650
|
+
# @interval = $1.to_i
|
651
|
+
# end
|
652
|
+
#
|
653
|
+
# if rule =~ /BYMONTH=(.*?);/ then
|
654
|
+
# @byMonth = $1
|
655
|
+
# end
|
656
|
+
#
|
657
|
+
# if rule =~ /BYDAY=(.*?);/ then
|
658
|
+
# @byDay = $1
|
659
|
+
# #puts "byDay = #{@byDay}"
|
660
|
+
# end
|
661
|
+
# end
|
662
|
+
#
|
663
|
+
# def to_s
|
664
|
+
# # after/every FREQ
|
665
|
+
# puts "UNIMPLETEMENT"
|
666
|
+
# # puts "#<DateSet: starts: #{@startDate.strftime("%m/%d/%Y")}, occurs: #{@frequency}, count: #{@count}, until: #{@untilDate}, byMonth: #{@byMonth}, byDay: #{@byDay}>"
|
667
|
+
# end
|
668
|
+
#
|
669
|
+
# def includes?(date)
|
670
|
+
# return true if date == @startDate
|
671
|
+
# return false if @untilDate and date > @untilDate
|
672
|
+
#
|
673
|
+
# case @frequency
|
674
|
+
# when 'DAILY'
|
675
|
+
# #if @untilDate then
|
676
|
+
# # return (@startDate..@untilDate).include?(date)
|
677
|
+
# #end
|
678
|
+
# increment = @interval ? @interval : 1
|
679
|
+
# d = @startDate
|
680
|
+
# counter = 0
|
681
|
+
# until d > date
|
682
|
+
#
|
683
|
+
# if @count then
|
684
|
+
# counter += 1
|
685
|
+
# if counter >= @count
|
686
|
+
# return false
|
687
|
+
# end
|
688
|
+
# end
|
689
|
+
#
|
690
|
+
# d += (increment * SECONDS_PER_DAY)
|
691
|
+
# if d.day == date.day and
|
692
|
+
# d.year == date.year and
|
693
|
+
# d.month == date.month then
|
694
|
+
# puts "true for start: #{@startDate}, until: #{@untilDate}"
|
695
|
+
# return true
|
696
|
+
# end
|
697
|
+
#
|
698
|
+
# end
|
699
|
+
#
|
700
|
+
# when 'WEEKLY'
|
701
|
+
# return true if @startDate.wday == date.wday
|
702
|
+
#
|
703
|
+
# when 'MONTHLY'
|
704
|
+
#
|
705
|
+
# when 'YEARLY'
|
706
|
+
#
|
707
|
+
# end
|
708
|
+
#
|
709
|
+
# false
|
710
|
+
# end
|
711
|
+
#
|
712
|
+
# attr_reader :frequency
|
713
|
+
# attr_accessor :startDate
|
714
|
+
# end
|
715
|
+
#
|
metadata
CHANGED