slimtimer4r 0.2.0
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/History.txt +9 -0
- data/Manifest.txt +7 -0
- data/README.txt +51 -0
- data/Rakefile +18 -0
- data/bin/slimtimer4r +0 -0
- data/lib/slimtimer4r.rb +207 -0
- data/test/test_slimtimer4r.rb +0 -0
- metadata +70 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
== SlimTimer4R
|
2
|
+
|
3
|
+
== Overview
|
4
|
+
|
5
|
+
A basic wrapper against the SlimTimer (www.slimtimer.com) API.
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
RubyGems FTW!
|
10
|
+
|
11
|
+
sudo gem install slimtimer4r
|
12
|
+
|
13
|
+
== Usage
|
14
|
+
|
15
|
+
require 'slimtimer4r'
|
16
|
+
|
17
|
+
slimtimer = SlimTimer.new("EMAIL", "PASSWORD", "API_KEY")
|
18
|
+
tasks = slimtimer.list_tasks
|
19
|
+
=> [#<Record(Task) "name"=>"Ta...">, #<Record(Task) "name"=>"Br...">...]
|
20
|
+
|
21
|
+
== Thanks
|
22
|
+
- 37 Signals for their sample Backpack/Basecamp Ruby wrappers
|
23
|
+
|
24
|
+
== Author
|
25
|
+
- Dylan Markow (dylan@dylanmarkow.com)
|
26
|
+
|
27
|
+
|
28
|
+
== License
|
29
|
+
|
30
|
+
(The MIT License)
|
31
|
+
|
32
|
+
Copyright (c) 2008
|
33
|
+
|
34
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
35
|
+
a copy of this software and associated documentation files (the
|
36
|
+
'Software'), to deal in the Software without restriction, including
|
37
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
38
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
39
|
+
permit persons to whom the Software is furnished to do so, subject to
|
40
|
+
the following conditions:
|
41
|
+
|
42
|
+
The above copyright notice and this permission notice shall be
|
43
|
+
included in all copies or substantial portions of the Software.
|
44
|
+
|
45
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
46
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
47
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
48
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
49
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
50
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
51
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require './lib/slimtimer4r.rb'
|
6
|
+
|
7
|
+
Hoe.new('slimtimer4r', SlimTimer::VERSION) do |p|
|
8
|
+
p.rubyforge_name = 'slimtimer4r'
|
9
|
+
p.author = 'Dylan Markow'
|
10
|
+
p.email = 'dylan@dylanmarkow.com'
|
11
|
+
p.remote_rdoc_dir = ''
|
12
|
+
# p.summary = 'FIX'
|
13
|
+
# p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
|
14
|
+
# p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
|
15
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
16
|
+
end
|
17
|
+
|
18
|
+
# vim: syntax=Ruby
|
data/bin/slimtimer4r
ADDED
File without changes
|
data/lib/slimtimer4r.rb
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
class SlimTimer
|
5
|
+
VERSION = '0.2.0'
|
6
|
+
|
7
|
+
#
|
8
|
+
# The Record class is used to encapsulate the data returned from the SlimTimer API
|
9
|
+
class Record
|
10
|
+
attr_reader :type, :hash
|
11
|
+
|
12
|
+
def initialize(type, hash)
|
13
|
+
@type = type
|
14
|
+
@hash = hash
|
15
|
+
end
|
16
|
+
|
17
|
+
# Checks to see if one of the values of the hash is another hash, and if so, changes it to a Record object. This allows you to use time_entry.task.name instead of time_entry.task["name"]
|
18
|
+
def [](name)
|
19
|
+
case @hash[name]
|
20
|
+
when Hash then
|
21
|
+
@hash[name] = (@hash[name].keys.length == 1 && Array === @hash[name].values.first) ? @hash[name].values.first.map { |v| Record.new(@hash[name].keys.first, v) } : Record.new(name, @hash[name])
|
22
|
+
else
|
23
|
+
@hash[name]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def id
|
28
|
+
@hash["id"]
|
29
|
+
end
|
30
|
+
|
31
|
+
def attributes
|
32
|
+
@hash.keys
|
33
|
+
end
|
34
|
+
|
35
|
+
def respond_to?(sym)
|
36
|
+
super || @hash.has_key?(sym)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Used to convert an unknown method into a hash key. For example, item.user_id would become item["user_id"]
|
40
|
+
def method_missing(sym, *args)
|
41
|
+
if args.empty? && !block_given? && respond_to?(sym.to_s)
|
42
|
+
self[sym.to_s]
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
"\#<Record(#{@type}) #{@hash.inspect[1..-2]}>"
|
50
|
+
end
|
51
|
+
|
52
|
+
def inspect
|
53
|
+
to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def dashify(name)
|
59
|
+
name.to_s.tr("_","-")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
attr_accessor :email, :password, :api_key, :user_id, :access_token, :request
|
64
|
+
|
65
|
+
# Creates a new SlimTimer object and obtains the +access_token+ and +user_id+ from the SlimTimer API by sending your +email+, +password+, and +api_key+. Raises a _RuntimeError_ if it can't authenticate.
|
66
|
+
#
|
67
|
+
# slim_timer = SlimTimer.new("person@example.com", "password", "12345")
|
68
|
+
# => #<SlimTimer:0x68bca8 @password="password"...>
|
69
|
+
#
|
70
|
+
# slim_timer = SlimTimer.new("bademail@example.com", "badpassword", "12345")
|
71
|
+
# => RuntimeError: Error occurred (500)
|
72
|
+
def initialize(email, password, api_key)
|
73
|
+
@email, @password, @api_key = email, password, api_key
|
74
|
+
connect
|
75
|
+
get_token
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns a list of tasks.
|
79
|
+
#
|
80
|
+
# Options:
|
81
|
+
# <tt>show_completed</tt>:: Include completed tasks. Specify +only+ to only include the completed tasks. Valid options are +yes+, +no+, and +only+. Default is +yes+.
|
82
|
+
# <tt>role</tt>:: Include tasks where the user's role is one of the roles given (comma delimited). Valid options include +owner+, +coworker+, and +reporter+. Default is +owner,coworker+.
|
83
|
+
def list_tasks(show_completed="yes", role="owner,coworker")
|
84
|
+
request("get", "#{@user_id}/tasks?api_key=#{@api_key}&access_token=#{@access_token}&show_completed=#{show_completed}&role=#{role}", "Tasks")
|
85
|
+
end
|
86
|
+
|
87
|
+
# Returns a specific task. Returns _nil_ if the record is not found.
|
88
|
+
#
|
89
|
+
# Options:
|
90
|
+
# <tt>task_id</tt>:: The id of the task you would like to retrieve.
|
91
|
+
def show_task(task_id)
|
92
|
+
request("get", "#{@user_id}/tasks/#{task_id}?api_key=#{@api_key}&access_token=#{@access_token}", "Task")
|
93
|
+
end
|
94
|
+
|
95
|
+
def delete_task(task_id)
|
96
|
+
request("delete", "#{@user_id}/tasks/#{task_id}?api_key=#{@api_key}&access_token=#{@access_token}", "Task")
|
97
|
+
end
|
98
|
+
|
99
|
+
def create_task(name, tags=nil, coworker_emails=nil, reporter_emails=nil, completed_on=nil)
|
100
|
+
request("post", "#{@user_id}/tasks", {"access_token" => @access_token, "api_key" => @api_key, "task" => {"name" => name, "tags" => tags, "coworker_emails" => coworker_emails, "reporter_emails" => reporter_emails, "completed_on" => completed_on}}, "Task")
|
101
|
+
end
|
102
|
+
|
103
|
+
def update_task(task_id, name, tags=nil, coworker_emails=nil, reporter_emails=nil, completed_on=nil)
|
104
|
+
request("put", "#{@user_id}/tasks/#{task_id}", {"access_token" => @access_token, "api_key" => @api_key, "task" => {"name" => name, "tags" => tags, "coworker_emails" => coworker_emails, "reporter_emails" => reporter_emails, "completed_on" => completed_on}}, "Task")
|
105
|
+
end
|
106
|
+
|
107
|
+
def list_timeentries(range_start=nil, range_end=nil)
|
108
|
+
range_start = range_start.strftime("%Y-%m-%dT%H:%M:%SZ") unless range_start.nil?
|
109
|
+
range_end = range_end.strftime("%Y-%m-%dT%H:%M:%SZ") unless range_end.nil?
|
110
|
+
request("get", "#{@user_id}/time_entries?api_key=#{@api_key}&access_token=#{@access_token}&range_start=#{range_start}&range_end=#{range_end}", "TimeEntries")
|
111
|
+
end
|
112
|
+
|
113
|
+
def update_timeentry(timeentry_id, start_time, duration_in_seconds, task_id, end_time, tags=nil, comments=nil, in_progress=nil)
|
114
|
+
start_time = start_time.strftime("%Y-%m-%dT%H:%M:%SZ")
|
115
|
+
end_time = end_time.strftime("%Y-%m-%dT%H:%M:%SZ") unless end_time.nil?
|
116
|
+
|
117
|
+
# add the default params
|
118
|
+
params = {
|
119
|
+
"start_time" => start_time,
|
120
|
+
"duration_in_seconds" => duration_in_seconds,
|
121
|
+
"task_id" => task_id,
|
122
|
+
"end_time" => end_time,
|
123
|
+
}
|
124
|
+
|
125
|
+
# only add the applicable params
|
126
|
+
params.merge!({"tags" => tags}) unless tags.nil?
|
127
|
+
params.merge!({"comments" => comments}) unless comments.nil?
|
128
|
+
params.merge!({"in_progress" => in_progress}) unless in_progress.nil?
|
129
|
+
|
130
|
+
|
131
|
+
request("put", "#{@user_id}/time_entries/#{timeentry_id}", {"access_token" => @access_token, "api_key" => @api_key, "time_entry" => params}, "TimeEntry")
|
132
|
+
end
|
133
|
+
|
134
|
+
def create_timeentry(start_time, duration_in_seconds, task_id, end_time, tags=nil, comments=nil, in_progress=nil)
|
135
|
+
start_time = start_time.strftime("%Y-%m-%dT%H:%M:%SZ")
|
136
|
+
end_time = end_time.strftime("%Y-%m-%dT%H:%M:%SZ") unless end_time.nil?
|
137
|
+
|
138
|
+
# add the default params
|
139
|
+
params = {
|
140
|
+
"start_time" => start_time,
|
141
|
+
"duration_in_seconds" => duration_in_seconds,
|
142
|
+
"task_id" => task_id,
|
143
|
+
"end_time" => end_time,
|
144
|
+
}
|
145
|
+
|
146
|
+
# only add the applicable params
|
147
|
+
params.merge!({"tags" => tags}) unless tags.nil?
|
148
|
+
params.merge!({"comments" => comments}) unless comments.nil?
|
149
|
+
params.merge!({"in_progress" => in_progress}) unless in_progress.nil?
|
150
|
+
|
151
|
+
|
152
|
+
request("post", "#{@user_id}/time_entries", {"access_token" => @access_token, "api_key" => @api_key, "time_entry" => params}, "TimeEntry")
|
153
|
+
end
|
154
|
+
|
155
|
+
def show_timeentry(timeentry_id)
|
156
|
+
request("get", "#{@user_id}/time_entries/#{timeentry_id}?api_key=#{@api_key}&access_token=#{@access_token}", "TimeEntry")
|
157
|
+
end
|
158
|
+
|
159
|
+
def delete_timeentry(timeentry_id)
|
160
|
+
request("delete", "#{@user_id}/time_entries/#{timeentry_id}?api_key=#{@api_key}&access_token=#{@access_token}", "TimeEntry")
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
private
|
165
|
+
def get_token
|
166
|
+
values = request("post", "token", {"user" => {"email" => @email, "password" => @password}, "api_key" => @api_key})
|
167
|
+
@access_token = values.access_token
|
168
|
+
@user_id = values.user_id
|
169
|
+
end
|
170
|
+
|
171
|
+
def connect
|
172
|
+
@connection = Net::HTTP.new("www.slimtimer.com", 80)
|
173
|
+
end
|
174
|
+
|
175
|
+
def request(method, path, parameters = {}, type="Result")
|
176
|
+
method.downcase!
|
177
|
+
if !['post','get','delete','put'].include?(method)
|
178
|
+
raise "Error: bad method parameter"
|
179
|
+
end
|
180
|
+
if %w(get delete).include?(method)
|
181
|
+
response = @connection.send(method, "/users/#{path}", {"Accept" => "application/x-yaml"})
|
182
|
+
else
|
183
|
+
response = @connection.send(method, "/users/#{path}", parameters.to_yaml, {"Content-Type" => "application/x-yaml", "Accept" => "application/x-yaml"})
|
184
|
+
end
|
185
|
+
|
186
|
+
if response.code == "200"
|
187
|
+
|
188
|
+
# If this was a delete request, return true
|
189
|
+
if method == 'delete'
|
190
|
+
true
|
191
|
+
else
|
192
|
+
result = YAML::load(response.body)
|
193
|
+
if result.is_a?(Array)
|
194
|
+
result.map { |row| Record.new(type, row) }
|
195
|
+
else
|
196
|
+
Record.new(type, result)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
elsif response.code == "404"
|
200
|
+
method == 'get' ? nil : false
|
201
|
+
else
|
202
|
+
raise "Error occurred (#{response.code}): #{response.body}"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
|
File without changes
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: slimtimer4r
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dylan Markow
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-01-09 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.4.0
|
23
|
+
version:
|
24
|
+
description: The author was too lazy to write a description
|
25
|
+
email: dylan@dylanmarkow.com
|
26
|
+
executables:
|
27
|
+
- slimtimer4r
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files:
|
31
|
+
- History.txt
|
32
|
+
- Manifest.txt
|
33
|
+
- README.txt
|
34
|
+
files:
|
35
|
+
- History.txt
|
36
|
+
- Manifest.txt
|
37
|
+
- README.txt
|
38
|
+
- Rakefile
|
39
|
+
- bin/slimtimer4r
|
40
|
+
- lib/slimtimer4r.rb
|
41
|
+
- test/test_slimtimer4r.rb
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://www.zenspider.com/ZSS/Products/slimtimer4r/
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options:
|
46
|
+
- --main
|
47
|
+
- README.txt
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
version:
|
62
|
+
requirements: []
|
63
|
+
|
64
|
+
rubyforge_project: slimtimer4r
|
65
|
+
rubygems_version: 1.0.1
|
66
|
+
signing_key:
|
67
|
+
specification_version: 2
|
68
|
+
summary: The author was too lazy to write a summary
|
69
|
+
test_files:
|
70
|
+
- test/test_slimtimer4r.rb
|