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 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