timetree 0.0.1 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +24 -0
- data/README.md +63 -7
- data/lib/timetree.rb +9 -12
- data/lib/timetree/activity.rb +46 -0
- data/lib/timetree/api_error.rb +29 -0
- data/lib/timetree/base_model.rb +144 -0
- data/lib/timetree/calendar.rb +80 -0
- data/lib/timetree/client.rb +187 -108
- data/lib/timetree/configuration.rb +5 -1
- data/lib/timetree/event.rb +134 -0
- data/lib/timetree/http_command.rb +79 -0
- data/lib/timetree/label.rb +11 -0
- data/lib/timetree/user.rb +13 -0
- data/lib/timetree/version.rb +1 -1
- data/timetree.gemspec +3 -1
- metadata +39 -4
- data/lib/timetree/models.rb +0 -282
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6fd16ab1a54faf7be5a951aa8472e997f791ab3dc1bdb06d1588f0630920c2a1
|
4
|
+
data.tar.gz: 0c2ba91f9b256c5011f053cdf831d35c905486097a6cb832635d25261fe3f77a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01db118d26095eed595b67747f51cd2203fc767378b1b2c39f5ef4985098bf53ba53a2c60d836717924281b603af09f24700bc9ac3ef1d4b63a0b71495baa932
|
7
|
+
data.tar.gz: 29ff89e03d4d9919fbd981f4b34b0fa2ae0a6df5ca57a61b29a50497fd56b3888739139ebd48c554ac120f5aa9369eb8e3658cefcddad19207ea1b42490a1112
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
# 0.1.4
|
2
|
+
|
3
|
+
- updates comments.
|
4
|
+
|
5
|
+
# 0.1.3
|
6
|
+
|
7
|
+
- refs #7 #9 make the code coverage 100%.
|
8
|
+
|
9
|
+
# 0.1.2
|
10
|
+
|
11
|
+
- refs #1 fix typo Event#recurrences to recurrence
|
12
|
+
- refs #2 add a note for debugging guide on README
|
13
|
+
|
14
|
+
# 0.1.1
|
15
|
+
|
16
|
+
- set current version on this gem's documentation_uri.
|
17
|
+
- fixed typo on README.
|
18
|
+
|
19
|
+
# 0.1.0
|
20
|
+
|
21
|
+
- did refactor and wrote comments.
|
22
|
+
- started to use zeitwerk.
|
23
|
+
- wrote README.
|
24
|
+
|
1
25
|
# 0.0.1
|
2
26
|
|
3
27
|
- Initial release
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# TimeTree
|
1
|
+
# Simple TimeTree APIs client
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/timetree.svg)](http://badge.fury.io/rb/timetree)
|
4
4
|
|
@@ -24,20 +24,76 @@ Or install it yourself as:
|
|
24
24
|
|
25
25
|
## Usage
|
26
26
|
|
27
|
-
The
|
28
|
-
Set `
|
27
|
+
The APIs client needs access token.
|
28
|
+
Set a `token` variable to the value you got by above:
|
29
29
|
|
30
30
|
```ruby
|
31
|
-
#
|
31
|
+
# set token by TimeTree.configure methods.
|
32
32
|
TimeTree.configure do |config|
|
33
|
-
config.
|
33
|
+
config.token = '<YOUR_ACCESS_TOKEN>'
|
34
34
|
end
|
35
35
|
client = TimeTree::Client.new
|
36
36
|
|
37
|
-
#
|
37
|
+
# set token by TimeTree::Client initializer.
|
38
38
|
client = TimeTree::Client.new('<YOUR_ACCESS_TOKEN>')
|
39
39
|
|
40
|
-
#
|
40
|
+
# get a current user's information.
|
41
|
+
user = client.current_user
|
42
|
+
=> #<TimeTree::User id:xxx_u001>
|
43
|
+
user.name
|
44
|
+
=> "USER Name"
|
45
|
+
|
46
|
+
# get current user's calendars.
|
47
|
+
cals = client.calendars
|
48
|
+
=> [#<TimeTree::Calendar id:xxx_cal001>, #<TimeTree::Calendar id:xxx_cal002>, ...]
|
49
|
+
cal = cals.first
|
50
|
+
cal.name
|
51
|
+
=> "Calendar Name"
|
52
|
+
|
53
|
+
# get upcoming events on the calendar.
|
54
|
+
evs = cal.upcoming_events
|
55
|
+
=> [#<TimeTree::Event id:xxx_ev001>, #<TimeTree::Event id:xxx_ev002>, ...]
|
56
|
+
ev = evs.first
|
57
|
+
ev.title
|
58
|
+
=> "Event Title"
|
59
|
+
|
60
|
+
# updates an event.
|
61
|
+
ev.title += ' Updated'
|
62
|
+
ev.start_at = Time.parse('2020-06-20 09:00 +09:00')
|
63
|
+
ev.end_at = Time.parse('2020-06-20 10:00 +09:00')
|
64
|
+
ev.update
|
65
|
+
=> #<TimeTree::Event id:xxx_ev001>
|
66
|
+
|
67
|
+
# creates an event.
|
68
|
+
copy_ev = ev.dup
|
69
|
+
new_ev = copy_ev.create
|
70
|
+
=> #<TimeTree::Event id:xxx_new_ev001>
|
71
|
+
|
72
|
+
# deletes an event.
|
73
|
+
ev.delete
|
74
|
+
=> true
|
75
|
+
|
76
|
+
# creates a comment to an event.
|
77
|
+
ev.create_comment 'Hi there!'
|
78
|
+
=> #<TimeTree::Activity id:xxx_act001>
|
79
|
+
|
80
|
+
# handles APIs error.
|
81
|
+
begin
|
82
|
+
ev.delete
|
83
|
+
ev.delete # 404 Error occured.
|
84
|
+
rescue TimeTree::ApiError => e
|
85
|
+
e
|
86
|
+
=> #<TimeTree::ApiError title:Not Found, status:404>
|
87
|
+
e.response
|
88
|
+
=> #<Faraday::Response>
|
89
|
+
end
|
90
|
+
|
91
|
+
# if the log level set :debug, you can get the request/response information.
|
92
|
+
TimeTree.configuration.logger.level = :debug
|
93
|
+
=> #<TimeTree::Event id:event_id_001_not_found>
|
94
|
+
>> client.event 'cal_id_001', 'event_id_001_not_found'
|
95
|
+
I, [2020-06-24T10:05:07.294807] INFO -- : GET https://timetreeapis.com/calendars/cal_id_001/events/event_id_001_not_found?include=creator%2Clabel%2Cattendees
|
96
|
+
D, [2020-06-24T10:05:07.562038] DEBUG -- : Response status:404, body:{:type=>"https://developers.timetreeapp.com/en/docs/api#client-failure", :title=>"Not Found", :status=>404, :errors=>"Event not found"}
|
41
97
|
```
|
42
98
|
|
43
99
|
## Contributing
|
data/lib/timetree.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
].sort.each do |f|
|
10
|
-
require f
|
11
|
-
end
|
3
|
+
require 'zeitwerk'
|
4
|
+
loader = Zeitwerk::Loader.for_gem
|
5
|
+
loader.inflector.inflect(
|
6
|
+
'timetree' => 'TimeTree'
|
7
|
+
)
|
8
|
+
loader.setup
|
12
9
|
|
10
|
+
# module for TimeTree apis client
|
13
11
|
module TimeTree
|
12
|
+
class Error < StandardError
|
13
|
+
end
|
14
14
|
class << self
|
15
15
|
def configure
|
16
16
|
yield configuration
|
@@ -20,7 +20,4 @@ module TimeTree
|
|
20
20
|
@configuration ||= Configuration.new
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|
24
|
-
class Error < StandardError
|
25
|
-
end
|
26
23
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TimeTree
|
4
|
+
# Model for TimeTree comment.
|
5
|
+
class Activity < BaseModel
|
6
|
+
# @return [String]
|
7
|
+
attr_accessor :content
|
8
|
+
# @return [Time]
|
9
|
+
attr_accessor :updated_at
|
10
|
+
# @return [Time]
|
11
|
+
attr_accessor :created_at
|
12
|
+
# calendar's id.
|
13
|
+
# @return [String]
|
14
|
+
attr_accessor :calendar_id
|
15
|
+
# event's id.
|
16
|
+
# @return [String]
|
17
|
+
attr_accessor :event_id
|
18
|
+
|
19
|
+
TIME_FIELDS = %i[updated_at created_at].freeze
|
20
|
+
|
21
|
+
#
|
22
|
+
# Creates a comment to the associated event.
|
23
|
+
#
|
24
|
+
# @return [TimeTree::Activity]
|
25
|
+
# @raise [TimeTree::Error] if @client is not set.
|
26
|
+
# @raise [TimeTree::Error] if the calendar_id property is not set.
|
27
|
+
# @raise [TimeTree::Error] if the event_id property is not set.
|
28
|
+
# @raise [TimeTree::ApiError] if the http response status will not success.
|
29
|
+
# @since 0.0.1
|
30
|
+
def create
|
31
|
+
check_client
|
32
|
+
@client.create_activity calendar_id, event_id, data_params
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# convert to a TimeTree request body format.
|
37
|
+
#
|
38
|
+
# @return [Hash]
|
39
|
+
# @since 0.0.1
|
40
|
+
def data_params
|
41
|
+
{
|
42
|
+
data: { attributes: { content: content } }
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TimeTree
|
4
|
+
# TimeTree apis client error object.
|
5
|
+
class ApiError < StandardError
|
6
|
+
# @return [Faraday::Response]
|
7
|
+
attr_reader :response
|
8
|
+
# @return [String]
|
9
|
+
attr_reader :type
|
10
|
+
# @return [String]
|
11
|
+
attr_reader :title
|
12
|
+
# @return [String]
|
13
|
+
attr_reader :errors
|
14
|
+
# @return [Integer]
|
15
|
+
attr_reader :status
|
16
|
+
|
17
|
+
def initialize(response)
|
18
|
+
@response = response
|
19
|
+
@type = response.body[:type]
|
20
|
+
@title = response.body[:title]
|
21
|
+
@errors = response.body[:errors]
|
22
|
+
@status = response.status
|
23
|
+
end
|
24
|
+
|
25
|
+
def inspect
|
26
|
+
"\#<#{self.class}:#{object_id} title:#{@title}, status:#{@status}>"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module TimeTree
|
6
|
+
# TimeTree base model object.
|
7
|
+
class BaseModel
|
8
|
+
# @return [Array<Hash<String,String>>]
|
9
|
+
attr_accessor :relationships
|
10
|
+
# @return [String]
|
11
|
+
attr_reader :id
|
12
|
+
# @return [String]
|
13
|
+
attr_reader :type
|
14
|
+
|
15
|
+
# @param data [Hash]
|
16
|
+
# TimeTree apis's response data.
|
17
|
+
# @param included [Hash]
|
18
|
+
# @param client [TimeTree::Client]
|
19
|
+
# @return [TimeTree::User, TimeTree::Label, TimeTree::Calendar, TimeTree::Event, TimeTree::Activity]
|
20
|
+
# A TimeTree model object that be based on the type.
|
21
|
+
# @raise [TimeTree::Error] if the type property is not set or unknown.
|
22
|
+
# @since 0.0.1
|
23
|
+
def self.to_model(data, included: nil, client: nil)
|
24
|
+
id = data[:id]
|
25
|
+
type = data[:type]
|
26
|
+
raise Error, 'type is required.' if type.nil?
|
27
|
+
|
28
|
+
attributes = data[:attributes] || {}
|
29
|
+
relationships = data[:relationships] || {}
|
30
|
+
params = {
|
31
|
+
id: id,
|
32
|
+
type: type,
|
33
|
+
client: client,
|
34
|
+
attributes: attributes,
|
35
|
+
relationships: relationships,
|
36
|
+
included: included
|
37
|
+
}
|
38
|
+
|
39
|
+
case type
|
40
|
+
when 'user'
|
41
|
+
User.new(**params)
|
42
|
+
when 'label'
|
43
|
+
Label.new(**params)
|
44
|
+
when 'calendar'
|
45
|
+
Calendar.new(**params)
|
46
|
+
when 'event'
|
47
|
+
Event.new(**params)
|
48
|
+
when 'activity'
|
49
|
+
Activity.new(**params)
|
50
|
+
else
|
51
|
+
raise Error, "type '#{type}' is unknown."
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize(type:, id: nil, client: nil, attributes: nil, relationships: nil, included: nil)
|
56
|
+
@type = type
|
57
|
+
@id = id
|
58
|
+
@client = client
|
59
|
+
set_attributes attributes
|
60
|
+
set_relationships relationships, included
|
61
|
+
end
|
62
|
+
|
63
|
+
def inspect
|
64
|
+
"\#<#{self.class}:#{object_id} id:#{id}>"
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def check_client
|
70
|
+
raise Error, '@client is nil.' if @client.nil?
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_model(data)
|
74
|
+
self.class.to_model data, client: @client
|
75
|
+
end
|
76
|
+
|
77
|
+
def set_attributes(attributes)
|
78
|
+
return unless attributes.is_a? Hash
|
79
|
+
return if attributes.empty?
|
80
|
+
|
81
|
+
setter_methods = self.class.instance_methods.select { |method| method.to_s.end_with? '=' }
|
82
|
+
attributes.each do |key, value|
|
83
|
+
setter = "#{key.to_sym}=".to_sym
|
84
|
+
next unless setter_methods.include? setter
|
85
|
+
|
86
|
+
if defined?(self.class::TIME_FIELDS) && self.class::TIME_FIELDS.include?(key)
|
87
|
+
value = Time.parse value
|
88
|
+
end
|
89
|
+
instance_variable_set "@#{key}", value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def set_relationships(relationships, included)
|
94
|
+
return unless relationships.is_a? Hash
|
95
|
+
return if relationships.empty?
|
96
|
+
return unless defined? self.class::RELATIONSHIPS
|
97
|
+
return if self.class::RELATIONSHIPS.empty?
|
98
|
+
|
99
|
+
self.class::RELATIONSHIPS.each do |key|
|
100
|
+
relation = relationships[key]
|
101
|
+
next unless relation
|
102
|
+
next unless relation[:data]
|
103
|
+
|
104
|
+
@relationships ||= {}
|
105
|
+
@relationships[key] = relation[:data]
|
106
|
+
end
|
107
|
+
|
108
|
+
return if included.nil?
|
109
|
+
return unless included.is_a? Array
|
110
|
+
return if included.empty?
|
111
|
+
|
112
|
+
set_relationship_data_if_included(included)
|
113
|
+
end
|
114
|
+
|
115
|
+
def set_relationship_data_if_included(included)
|
116
|
+
@_relation_data_dic = {}
|
117
|
+
included.each do |data|
|
118
|
+
item = to_model(data)
|
119
|
+
next unless item
|
120
|
+
|
121
|
+
@_relation_data_dic[item.type] ||= {}
|
122
|
+
@_relation_data_dic[item.type][item.id] = item
|
123
|
+
end
|
124
|
+
detect_relation_data = lambda { |type, id|
|
125
|
+
return unless @_relation_data_dic[type]
|
126
|
+
|
127
|
+
@_relation_data_dic[type][id]
|
128
|
+
}
|
129
|
+
@relationships.each do |key, id_data|
|
130
|
+
relation_data = nil
|
131
|
+
if id_data.is_a? Array
|
132
|
+
relation_data = []
|
133
|
+
id_data.each do |d|
|
134
|
+
item = detect_relation_data.call(d[:type], d[:id])
|
135
|
+
relation_data << item if item
|
136
|
+
end
|
137
|
+
elsif id_data.is_a? Hash
|
138
|
+
relation_data = detect_relation_data.call(id_data[:type], id_data[:id])
|
139
|
+
end
|
140
|
+
instance_variable_set "@#{key}", relation_data if relation_data
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TimeTree
|
4
|
+
# Model for TimeTree calendar.
|
5
|
+
class Calendar < BaseModel
|
6
|
+
# @return [String]
|
7
|
+
attr_accessor :name
|
8
|
+
# @return [String]
|
9
|
+
attr_accessor :description
|
10
|
+
# @return [String]
|
11
|
+
attr_accessor :color
|
12
|
+
# @return [Integer]
|
13
|
+
attr_accessor :order
|
14
|
+
# @return [String]
|
15
|
+
attr_accessor :image_url
|
16
|
+
# @return [Time]
|
17
|
+
attr_accessor :created_at
|
18
|
+
|
19
|
+
TIME_FIELDS = %i[created_at].freeze
|
20
|
+
RELATIONSHIPS = %i[labels members].freeze
|
21
|
+
|
22
|
+
#
|
23
|
+
# Get the event's information.
|
24
|
+
#
|
25
|
+
# @param event_id [String]
|
26
|
+
# event's id.
|
27
|
+
# @return [TimeTree::Event]
|
28
|
+
# @raise [TimeTree::Error] if @client, @id or the event_id arg is empty.
|
29
|
+
# @raise [TimeTree::ApiError] if the http response status will not success.
|
30
|
+
# @since 0.0.1
|
31
|
+
def event(event_id)
|
32
|
+
check_client
|
33
|
+
@client.event id, event_id
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Get the events' information after a request date.
|
38
|
+
#
|
39
|
+
# @param days [Integer]
|
40
|
+
# The number of days to get.
|
41
|
+
# @param timezone [String]
|
42
|
+
# Timezone.
|
43
|
+
# @return [Array<TimeTree::Event>]
|
44
|
+
# @raise [TimeTree::Error] if @client or @id is empty.
|
45
|
+
# @raise [TimeTree::ApiError] if the http response status will not success.
|
46
|
+
# @since 0.0.1
|
47
|
+
def upcoming_events(days: 7, timezone: 'UTC')
|
48
|
+
check_client
|
49
|
+
@client.upcoming_events id, days: days, timezone: timezone
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# Get a calendar's member information.
|
54
|
+
#
|
55
|
+
# @return [Array<TimeTree::User>]
|
56
|
+
# @raise [TimeTree::Error] if @client or @id is empty.
|
57
|
+
# @raise [TimeTree::ApiError] if the http response status will not success.
|
58
|
+
# @since 0.0.1
|
59
|
+
def members
|
60
|
+
return @members if defined? @members
|
61
|
+
|
62
|
+
check_client
|
63
|
+
@members = @client.calendar_members id
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Get a calendar's label information used in event.
|
68
|
+
#
|
69
|
+
# @return [Array<TimeTree::Label>]
|
70
|
+
# @raise [TimeTree::Error] if @client or @id is empty.
|
71
|
+
# @raise [TimeTree::ApiError] if the http response status will not success.
|
72
|
+
# @since 0.0.1
|
73
|
+
def labels
|
74
|
+
return @labels if defined? @labels
|
75
|
+
|
76
|
+
check_client
|
77
|
+
@labels = @client.calendar_labels id
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|