camper 0.0.7 → 0.1.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.
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Camper
4
+ class Client
5
+ # Defines methods related to recordings.
6
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/recordings.md
7
+ module RecordingsAPI
8
+ # Get a paginated response of recordings of a given type
9
+ #
10
+ # @example
11
+ # client.recordings('Todo')
12
+ # @example
13
+ # client.recordings(
14
+ # 'Document',
15
+ # bucket: [1,2],
16
+ # status: 'archived',
17
+ # sort: 'updated_at',
18
+ # direction: 'asc'
19
+ # )
20
+ #
21
+ # @param type [String] type of the recording
22
+ # @param options [Hash] extra options to filter the recordings to be resulted
23
+ # @return [PaginatedResponse<Resource>]
24
+ # @raise [Error::InvalidParameter] if type is not one of the allowed types
25
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/recordings.md#get-recordings
26
+ def recordings(type, options = {})
27
+ raise Error::InvalidParameter, type unless RecordingTypes.all.include?(type)
28
+
29
+ get('/projects/recordings', query: options.merge(type: type))
30
+ end
31
+
32
+ # Trash a given recording
33
+ #
34
+ # @example
35
+ # client.trash_recording(my_todo)
36
+ # @example
37
+ # client.trash_recording(my_message)
38
+ #
39
+ # @param recording [Resource] a resource of a valid recording type
40
+ # @raise [Error::InvalidParameter] if type field in recording param
41
+ # is not one of the allowed types
42
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/recordings.md#trash-a-recording
43
+ def trash_recording(recording)
44
+ raise Error::InvalidParameter, recording unless RecordingTypes.all.include?(recording.type)
45
+
46
+ put("/buckets/#{recording.bucket.id}/recordings/#{recording.id}/status/trashed")
47
+ end
48
+
49
+ # Archive a given recording
50
+ #
51
+ # @example
52
+ # client.archive_recording(my_todo)
53
+ # @example
54
+ # client.archive_recording(my_message)
55
+ #
56
+ # @param recording [Resource] a resource of a valid recording type
57
+ # @raise [Error::InvalidParameter] if type field in recording param
58
+ # is not one of the allowed types
59
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/recordings.md#archive-a-recording
60
+ def archive_recording(recording)
61
+ raise Error::InvalidParameter, recording unless RecordingTypes.all.include?(recording.type)
62
+
63
+ put("/buckets/#{recording.bucket.id}/recordings/#{recording.id}/status/archived")
64
+ end
65
+
66
+ # Unarchive a given recording
67
+ #
68
+ # @example
69
+ # client.unarchive_recording(my_todo)
70
+ # @example
71
+ # client.unarchive_recording(my_message)
72
+ #
73
+ # @param recording [Resource] a resource of a valid recording type
74
+ # @raise [Error::InvalidParameter] if type field in recording param
75
+ # is not one of the allowed types
76
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/recordings.md#unarchive-a-recording
77
+ def unarchive_recording(recording)
78
+ raise Error::InvalidParameter, recording unless RecordingTypes.all.include?(recording.type)
79
+
80
+ put("/buckets/#{recording.bucket.id}/recordings/#{recording.id}/status/active")
81
+ end
82
+ end
83
+ end
84
+ end
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Camper::Client
4
- module ResourceAPI
5
- def resource(url)
6
- get(url, override_path: true)
3
+ module Camper
4
+ class Client
5
+ module ResourceAPI
6
+ def resource(url)
7
+ get(url, override_path: true)
8
+ end
7
9
  end
8
10
  end
9
11
  end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Camper
4
+ class Client
5
+ # Defines methods related to todolists.
6
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/todolists.md
7
+ module TodolistsAPI
8
+ # Get the todolists associated with the todoset
9
+ #
10
+ # @example
11
+ # client.todolists(todoset)
12
+ # @example
13
+ # client.todolists(todoset, status: 'archived')
14
+ #
15
+ # @param todoset [Resource] the parent todoset resource
16
+ # @param options [Hash] extra options to filter the list of todolist
17
+ # @return [PaginatedResponse<Resource>]
18
+ # @raise [Error::InvalidParameter] if todolists_url field in todoset param
19
+ # is not a valid basecamp url
20
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/todolists.md#get-to-do-lists
21
+ def todolists(todoset, options = {})
22
+ get(todoset.todolists_url, query: options, override_path: true)
23
+ end
24
+
25
+ # Get a todolist with a given id
26
+ #
27
+ # @example
28
+ # client.todolist(todoset, '2345')
29
+ #
30
+ # @param todoset [Resource] the parent todoset resource
31
+ # @param id [Integer, String] the id of the todolist to get
32
+ # @return [Resource]
33
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/todolists.md#get-a-to-do-list
34
+ def todolist(todoset, id)
35
+ get("/buckets/#{todoset.bucket.id}/todolists/#{id}")
36
+ end
37
+
38
+ # Create a todolist within the given todoset
39
+ #
40
+ # @example
41
+ # client.create_todolist(todoset, 'Launch', "<div><em>Finish it!</em></div>")
42
+ #
43
+ # @param todoset [Resource] the parent todoset resource
44
+ # @param name [String] the name of the new todolist
45
+ # @param description [String] an optional description for the todolist
46
+ # @return [Resource]
47
+ # @raise [Error::InvalidParameter] if todolists_url field in todoset param
48
+ # is not a valid basecamp url
49
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/todolists.md#create-a-to-do-list
50
+ def create_todolist(todoset, name, description = '')
51
+ body = { name: name, description: description }
52
+
53
+ post(todoset.todolists_url, body: body, override_path: true)
54
+ end
55
+
56
+ # Update a todolist to change name and description
57
+ #
58
+ # @example
59
+ # client.update_todolist(todolist, 'Launch')
60
+ # @example
61
+ # client.update_todolist(todolist, 'Launch', "<div><em>Finish it!</em></div>")
62
+ # @example
63
+ # client.update_todolist(todolist, 'Launch', '')
64
+ #
65
+ # @param todolist [Resource] the todolist resource to update
66
+ # @param name [String] the new name of the todolist
67
+ # @param description [String] a new optional description for the todolist. If not specified,
68
+ # it will be set to the current description value
69
+ # @return [Resource]
70
+ # @raise [Error::InvalidParameter] if url field in todolist param
71
+ # is not a valid basecamp url
72
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/todolists.md#update-a-to-do-list
73
+ def update_todolist(todolist, name, description = nil)
74
+ body = { name: name }
75
+ body[:description] = description.nil? ? todolist.description : description
76
+
77
+ put(todolist.url, body: body, override_path: true)
78
+ end
79
+
80
+ # Trash a todolist
81
+ # it calls the trash_recording endpoint under the hood
82
+ #
83
+ # @example
84
+ # client.trash_todolist(todolist)
85
+ #
86
+ # @param todolist [Resource] the todolist to be trashed
87
+ # @raise [Error::InvalidParameter] if the type field in todolist param
88
+ # is not an allowed type in the recording API
89
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/recordings.md#trash-a-recording
90
+ def trash_todolist(todolist)
91
+ trash_recording(todolist)
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Camper
4
+ class Client
5
+ # Defines methods related to todos.
6
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/todos.md
7
+ module TodosAPI
8
+ PARAMETERS = %w[
9
+ content
10
+ description
11
+ assignee_ids
12
+ completion_subscriber_ids
13
+ notify
14
+ due_on
15
+ starts_on
16
+ ].freeze
17
+
18
+ # Get the todos in a todolist
19
+ #
20
+ # @example
21
+ # client.todos(todolist)
22
+ # @example
23
+ # client.todos(todolist, completed: true)
24
+ #
25
+ # @param todolist [Resource] the parent todolist resource
26
+ # @param options [Hash] options to filter the list of todos
27
+ # @return [Resource]
28
+ # @raise [Error::InvalidParameter] if todos_url field in todolist param
29
+ # is not a valid basecamp url
30
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/todos.md#get-to-dos
31
+ def todos(todolist, options = {})
32
+ get(todolist.todos_url, query: options, override_path: true)
33
+ end
34
+
35
+ # Get a todo with a given id using a particular parent resource.
36
+ #
37
+ # @example
38
+ # client.todo(my_project, '10')
39
+ # @example
40
+ # client.todo(new_todolist, 134)
41
+ # @example
42
+ # client.todo(67543, '2440')
43
+ #
44
+ # @param parent [Integer|String|Project|Resource] can be either a project id, a project or a todolist resource
45
+ # @param id [Integer|String] id of the todo
46
+ # @return [Resource]
47
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/todos.md#get-a-to-do
48
+ def todo(parent, id)
49
+ bucket_id = parent
50
+
51
+ if parent.is_a? Camper::Project
52
+ bucket_id = parent.id
53
+ elsif parent.respond_to?(:type)
54
+ bucket_id = parent.bucket.id
55
+ end
56
+
57
+ get("/buckets/#{bucket_id}/todos/#{id}")
58
+ end
59
+
60
+ # Create a todo within a todolist
61
+ #
62
+ # @example
63
+ # client.create_todo(todolist, 'First Todo')
64
+ # @example
65
+ # client.create_todo(
66
+ # todolist,
67
+ # 'Program it',
68
+ # description: "<div><em>Try that new language!</em></div>, due_on: "2016-05-01"
69
+ # )
70
+ #
71
+ # @param todolist [Resource] the todolist where the todo is going to be created
72
+ # @param content [String] what the to-do is for
73
+ # @param options [Hash] extra parameters for the todo such as due_date and description
74
+ # @return [Resource]
75
+ # @raise [Error::InvalidParameter] if todos_url field in todolist param
76
+ # is not a valid basecamp url
77
+ # @raise [Error::InvalidParameter] if content parameter is blank
78
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/todos.md#create-a-to-do
79
+ def create_todo(todolist, content, options = {})
80
+ raise Error::InvalidParameter, content if content.blank?
81
+
82
+ post(todolist.todos_url, body: { content: content, **options }, override_path: true)
83
+ end
84
+
85
+ # Update a todo.
86
+ #
87
+ # @example
88
+ # client.update_todo(todo, 'Todo')
89
+ # @example
90
+ # client.update_todo(
91
+ # todo,
92
+ # 'Program it',
93
+ # description: "<div><em>Try that new language!</em></div>,
94
+ # due_on: "2016-05-01",
95
+ # starts_on: "2016-04-30"
96
+ # )
97
+ #
98
+ # @param todo [Resource] the todo to be updated
99
+ # @param options [Hash] parameters to be changed. The ones that are not specified
100
+ # will be set to the current values of the todo object
101
+ # @return [Resource]
102
+ # @raise [Error::InvalidParameter] if url field in todo param
103
+ # is not a valid basecamp url
104
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/todos.md#update-a-to-do
105
+ def update_todo(todo, options)
106
+ body = {}.merge(options)
107
+ PARAMETERS.each { |p| body[p.to_sym] = todo[p] unless key_is_present?(body, p) }
108
+
109
+ put(todo.url, body: body, override_path: true)
110
+ end
111
+
112
+ # Complete a todo
113
+ #
114
+ # @example
115
+ # client.complete_todo(todo)
116
+ #
117
+ # @param todo [Resource] the todo to be marked as completed
118
+ # @raise [Error::InvalidParameter] if url field in todo param
119
+ # is not a valid basecamp url
120
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/todos.md#complete-a-to-do
121
+ def complete_todo(todo)
122
+ post("#{todo.url}/completion", override_path: true)
123
+ end
124
+
125
+ # Uncomplete a todo
126
+ #
127
+ # @example
128
+ # client.uncomplete_todo(todo)
129
+ #
130
+ # @param todo [Resource] the todo to be marked as uncompleted
131
+ # @raise [Error::InvalidParameter] if url field in todo param
132
+ # is not a valid basecamp url
133
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/todos.md#uncomplete-a-to-do
134
+ def uncomplete_todo(todo)
135
+ delete("#{todo.url}/completion", override_path: true)
136
+ end
137
+
138
+ # Reposition a todo
139
+ #
140
+ # @example
141
+ # client.uncomplete_todo(todo)
142
+ #
143
+ # @param todo [Resource] the todo to be repositioned
144
+ # @param position [Integer|String] new position for the todo
145
+ # @raise [Error::InvalidParameter] if url field in todo param
146
+ # is not a valid basecamp url
147
+ # @raise [Error::InvalidParameter] if position param is less than 1
148
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/todos.md#reposition-a-to-do
149
+ def reposition_todo(todo, position)
150
+ raise Error::InvalidParameter, position if position.to_i < 1
151
+
152
+ put("#{todo.url}/position", position: position, override_path: true)
153
+ end
154
+
155
+ # Trash a todo
156
+ # it calls the trash_recording endpoint under the hood
157
+ #
158
+ # @example
159
+ # client.trash_todo(todo)
160
+ #
161
+ # @param todo [Resource] the todo to be trashed
162
+ # @raise [Error::InvalidParameter] if url field in todo param
163
+ # is not a valid basecamp url
164
+ # @see https://github.com/basecamp/bc3-api/blob/master/sections/recordings.md#trash-a-recording
165
+ def trash_todo(todo)
166
+ trash_recording(todo)
167
+ end
168
+
169
+ private
170
+
171
+ def key_is_present?(hash, key)
172
+ hash.key?(key.to_sym) || hash.key?(key.to_s)
173
+ end
174
+ end
175
+ end
176
+ end
@@ -12,12 +12,17 @@ module Camper
12
12
 
13
13
  # Keep in alphabetical order
14
14
  include Authorization
15
- include CommentAPI
15
+ include CommentsAPI
16
16
  include Logging
17
- include MessageAPI
18
- include ProjectAPI
17
+ include MessageBoardsAPI
18
+ include MessageTypesAPI
19
+ include MessagesAPI
20
+ include PeopleAPI
21
+ include ProjectsAPI
22
+ include RecordingsAPI
19
23
  include ResourceAPI
20
- include TodoAPI
24
+ include TodolistsAPI
25
+ include TodosAPI
21
26
 
22
27
  # Creates a new Client instance.
23
28
  # @raise [Error:MissingCredentials]
@@ -41,7 +46,7 @@ module Camper
41
46
  # by yielding the config object to the block
42
47
  # @return [Camper::Client] the client instance being configured
43
48
  def configure
44
- yield @config
49
+ yield @config if block_given?
45
50
 
46
51
  self
47
52
  end
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+ # Copied from https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/object/blank.rb
3
+
4
+ require 'concurrent/map'
5
+
6
+ class Object
7
+ # An object is blank if it's false, empty, or a whitespace string.
8
+ # For example, +nil+, '', ' ', [], {}, and +false+ are all blank.
9
+ #
10
+ # This simplifies
11
+ #
12
+ # !address || address.empty?
13
+ #
14
+ # to
15
+ #
16
+ # address.blank?
17
+ #
18
+ # @return [true, false]
19
+ def blank?
20
+ respond_to?(:empty?) ? !!empty? : !self
21
+ end
22
+
23
+ # An object is present if it's not blank.
24
+ #
25
+ # @return [true, false]
26
+ def present?
27
+ !blank?
28
+ end
29
+
30
+ # Returns the receiver if it's present otherwise returns +nil+.
31
+ # <tt>object.presence</tt> is equivalent to
32
+ #
33
+ # object.present? ? object : nil
34
+ #
35
+ # For example, something like
36
+ #
37
+ # state = params[:state] if params[:state].present?
38
+ # country = params[:country] if params[:country].present?
39
+ # region = state || country || 'US'
40
+ #
41
+ # becomes
42
+ #
43
+ # region = params[:state].presence || params[:country].presence || 'US'
44
+ #
45
+ # @return [Object]
46
+ def presence
47
+ self if present?
48
+ end
49
+ end
50
+
51
+ class NilClass
52
+ # +nil+ is blank:
53
+ #
54
+ # nil.blank? # => true
55
+ #
56
+ # @return [true]
57
+ def blank?
58
+ true
59
+ end
60
+ end
61
+
62
+ class FalseClass
63
+ # +false+ is blank:
64
+ #
65
+ # false.blank? # => true
66
+ #
67
+ # @return [true]
68
+ def blank?
69
+ true
70
+ end
71
+ end
72
+
73
+ class TrueClass
74
+ # +true+ is not blank:
75
+ #
76
+ # true.blank? # => false
77
+ #
78
+ # @return [false]
79
+ def blank?
80
+ false
81
+ end
82
+ end
83
+
84
+ class Array
85
+ # An array is blank if it's empty:
86
+ #
87
+ # [].blank? # => true
88
+ # [1,2,3].blank? # => false
89
+ #
90
+ # @return [true, false]
91
+ alias_method :blank?, :empty?
92
+ end
93
+
94
+ class Hash
95
+ # A hash is blank if it's empty:
96
+ #
97
+ # {}.blank? # => true
98
+ # { key: 'value' }.blank? # => false
99
+ #
100
+ # @return [true, false]
101
+ alias_method :blank?, :empty?
102
+ end
103
+
104
+ class String
105
+ BLANK_RE = /\A[[:space:]]*\z/
106
+ ENCODED_BLANKS = Concurrent::Map.new do |h, enc|
107
+ h[enc] = Regexp.new(BLANK_RE.source.encode(enc), BLANK_RE.options | Regexp::FIXEDENCODING)
108
+ end
109
+
110
+ # A string is blank if it's empty or contains whitespaces only:
111
+ #
112
+ # ''.blank? # => true
113
+ # ' '.blank? # => true
114
+ # "\t\n\r".blank? # => true
115
+ # ' blah '.blank? # => false
116
+ #
117
+ # Unicode whitespace is supported:
118
+ #
119
+ # "\u00a0".blank? # => true
120
+ #
121
+ # @return [true, false]
122
+ def blank?
123
+ # The regexp that matches blank strings is expensive. For the case of empty
124
+ # strings we can speed up this method (~3.5x) with an empty? call. The
125
+ # penalty for the rest of strings is marginal.
126
+ empty? ||
127
+ begin
128
+ BLANK_RE.match?(self)
129
+ rescue Encoding::CompatibilityError
130
+ ENCODED_BLANKS[self.encoding].match?(self)
131
+ end
132
+ end
133
+ end
134
+
135
+ class Numeric #:nodoc:
136
+ # No number is blank:
137
+ #
138
+ # 1.blank? # => false
139
+ # 0.blank? # => false
140
+ #
141
+ # @return [false]
142
+ def blank?
143
+ false
144
+ end
145
+ end
146
+
147
+ class Time #:nodoc:
148
+ # No Time is blank:
149
+ #
150
+ # Time.now.blank? # => false
151
+ #
152
+ # @return [false]
153
+ def blank?
154
+ false
155
+ end
156
+ end