timetree 0.0.1 → 0.1.4
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.
- 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
|
[](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
|