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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd2bf9fe5f866b70a0d08f66a937091892d8d4f3ab09002887eeab9cb6695158
4
- data.tar.gz: d27d7eda7746d390feee7f167226ef444e1ed9a0e837cf9ad4510aa577cba340
3
+ metadata.gz: 6fd16ab1a54faf7be5a951aa8472e997f791ab3dc1bdb06d1588f0630920c2a1
4
+ data.tar.gz: 0c2ba91f9b256c5011f053cdf831d35c905486097a6cb832635d25261fe3f77a
5
5
  SHA512:
6
- metadata.gz: 5d8e892ce5c8a12ca0629cab12f15228a28dc2df3d5d0abce51570c3714838aa4a05da1c96575972e4a0af3197057cf2780380d16e31ec364b58513558de5e2c
7
- data.tar.gz: c793202b5743b3818fea43bdc1ca6c2b44483d197098d0191181f5bada1dd27a862f6675fa183d959eee696294ba8e6bb680fef1f8ae9753542409ba43314a3b
6
+ metadata.gz: 01db118d26095eed595b67747f51cd2203fc767378b1b2c39f5ef4985098bf53ba53a2c60d836717924281b603af09f24700bc9ac3ef1d4b63a0b71495baa932
7
+ data.tar.gz: 29ff89e03d4d9919fbd981f4b34b0fa2ae0a6df5ca57a61b29a50497fd56b3888739139ebd48c554ac120f5aa9369eb8e3658cefcddad19207ea1b42490a1112
data/.gitignore CHANGED
@@ -7,4 +7,5 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
 
10
+ coverage
10
11
  Gemfile.lock
@@ -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 Api Client
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 Api client needs access token.
28
- Set `access_token` to the value you got by above:
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
- # using configure
31
+ # set token by TimeTree.configure methods.
32
32
  TimeTree.configure do |config|
33
- config.access_token = '<YOUR_ACCESS_TOKEN>'
33
+ config.token = '<YOUR_ACCESS_TOKEN>'
34
34
  end
35
35
  client = TimeTree::Client.new
36
36
 
37
- # using initializer
37
+ # set token by TimeTree::Client initializer.
38
38
  client = TimeTree::Client.new('<YOUR_ACCESS_TOKEN>')
39
39
 
40
- # TODO
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
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- Dir[
4
- File.join(
5
- File.dirname(__FILE__),
6
- 'timetree',
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