rtmapi 0.5.2 → 0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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