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.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.rubocop.yml +10 -0
- data/CHANGELOG.md +62 -1
- data/Gemfile.lock +22 -23
- data/README.md +16 -0
- data/camper.gemspec +1 -0
- data/examples/comments.rb +12 -1
- data/examples/message_types.rb +29 -0
- data/examples/messages.rb +13 -2
- data/examples/people.rb +11 -0
- data/examples/projects.rb +12 -0
- data/examples/recordings.rb +13 -0
- data/examples/todolists.rb +50 -0
- data/examples/todos.rb +21 -6
- data/lib/camper.rb +2 -0
- data/lib/camper/api/comments.rb +87 -0
- data/lib/camper/api/message_board.rb +23 -0
- data/lib/camper/api/message_types.rb +90 -0
- data/lib/camper/api/messages.rb +108 -0
- data/lib/camper/api/people.rb +99 -0
- data/lib/camper/api/projects.rb +120 -0
- data/lib/camper/api/recordings.rb +84 -0
- data/lib/camper/api/resource.rb +6 -4
- data/lib/camper/api/todolists.rb +95 -0
- data/lib/camper/api/todos.rb +176 -0
- data/lib/camper/client.rb +10 -5
- data/lib/camper/core_extensions/object.rb +156 -0
- data/lib/camper/error.rb +8 -0
- data/lib/camper/paginated_response.rb +2 -0
- data/lib/camper/recording_types.rb +22 -0
- data/lib/camper/request.rb +3 -13
- data/lib/camper/url_utils.rb +26 -0
- data/lib/camper/version.rb +1 -1
- metadata +34 -7
- data/lib/camper/api/comment.rb +0 -13
- data/lib/camper/api/message.rb +0 -9
- data/lib/camper/api/project.rb +0 -24
- data/lib/camper/api/todo.rb +0 -80
@@ -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
|
data/lib/camper/api/resource.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
data/lib/camper/client.rb
CHANGED
@@ -12,12 +12,17 @@ module Camper
|
|
12
12
|
|
13
13
|
# Keep in alphabetical order
|
14
14
|
include Authorization
|
15
|
-
include
|
15
|
+
include CommentsAPI
|
16
16
|
include Logging
|
17
|
-
include
|
18
|
-
include
|
17
|
+
include MessageBoardsAPI
|
18
|
+
include MessageTypesAPI
|
19
|
+
include MessagesAPI
|
20
|
+
include PeopleAPI
|
21
|
+
include ProjectsAPI
|
22
|
+
include RecordingsAPI
|
19
23
|
include ResourceAPI
|
20
|
-
include
|
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
|