camper 0.0.7 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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