pexels 0.0.3 → 0.3.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 -0
- data/CHANGES.md +25 -0
- data/README.md +56 -1
- data/lib/pexels.rb +28 -3
- data/lib/pexels/client.rb +27 -37
- data/lib/pexels/client/collections.rb +29 -0
- data/lib/pexels/client/photos.rb +49 -30
- data/lib/pexels/client/request.rb +53 -0
- data/lib/pexels/client/response.rb +29 -0
- data/lib/pexels/client/search_filters.rb +31 -0
- data/lib/pexels/client/videos.rb +46 -29
- data/lib/pexels/collection.rb +25 -0
- data/lib/pexels/collection_media_set.rb +31 -0
- data/lib/pexels/collection_set.rb +11 -0
- data/lib/pexels/paginated_response.rb +63 -0
- data/lib/pexels/photo.rb +15 -2
- data/lib/pexels/photo_set.rb +14 -0
- data/lib/pexels/version.rb +1 -1
- data/lib/pexels/video.rb +12 -0
- data/lib/pexels/video_set.rb +14 -0
- data/pexels.gemspec +2 -0
- data/test/client_test.rb +2 -2
- data/test/collection_test.rb +82 -0
- data/test/photo_test.rb +53 -13
- data/test/video_test.rb +38 -15
- metadata +15 -4
- data/lib/pexels/response.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fbc208fe2d27e3dca7c1b94e0e2a3901334d06b9cf3460d952a44aea437bd82e
|
4
|
+
data.tar.gz: f4796400297cf53c41b0740eb57ae9b70efd697018d20c17c0e91f92c9030dc6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e050b1e25f4f66b370f1acd2695af58305cd221e7c7d9a67cf4f989bdc3959af172aaebc65b545507992af0a6d24594e19601f316f5b1e2145ebbbc59eae46ce
|
7
|
+
data.tar.gz: 5a334832f860fcbb21dde178bdd871491d70efdeccfa5515309186596fcd1884b4ca3884bd6a6574343daa40623524ce20137366096460a015372a09365020aa
|
data/.gitignore
CHANGED
data/CHANGES.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Change log
|
2
|
+
|
3
|
+
## 0.3.0
|
4
|
+
* Add support for photo and video search with filters.
|
5
|
+
* Added `avg_color` attribute to `Photo` object.
|
6
|
+
|
7
|
+
## 0.2.1
|
8
|
+
* Added `type`, `photo?` and `video?` helper methods to `Photo` and `Video` classes.
|
9
|
+
|
10
|
+
## 0.2.0
|
11
|
+
* Fixed incorrect URL for collections endpoints.
|
12
|
+
* Added pagination methods `next_page` and `prev_page` to `PaginatedResponse`.
|
13
|
+
* Added `total_pages` to `PaginatedResponse`.
|
14
|
+
* Extracted `Request` and `Response` objects for reusability.
|
15
|
+
* Added `Pexels/Ruby` `User-Agent` header to requests.
|
16
|
+
|
17
|
+
## 0.1.0
|
18
|
+
* Add support for returning collections belonging to the API user.
|
19
|
+
* Add support for returning media from a collection.
|
20
|
+
|
21
|
+
## 0.0.4
|
22
|
+
* Add `find` as an alias for `photos[]` and `videos[]`.
|
23
|
+
|
24
|
+
## 0.0.3
|
25
|
+
* Initial release.
|
data/README.md
CHANGED
@@ -12,7 +12,7 @@ Or add it to your `Gemfile` and run `bundle install`.
|
|
12
12
|
|
13
13
|
## Documentation
|
14
14
|
|
15
|
-
See the API docs [here](https://www.pexels.com/api/documentation/?language=
|
15
|
+
See the API docs [here](https://www.pexels.com/api/documentation/?language=rb)
|
16
16
|
|
17
17
|
|
18
18
|
## Basic usage
|
@@ -34,6 +34,8 @@ client.photos.search('Cloud')
|
|
34
34
|
|
35
35
|
```ruby
|
36
36
|
client.photos[2014422]
|
37
|
+
# or
|
38
|
+
client.photos.find(2014422)
|
37
39
|
```
|
38
40
|
|
39
41
|
### Browse curated photos
|
@@ -52,6 +54,8 @@ client.videos.search('waves')
|
|
52
54
|
|
53
55
|
```ruby
|
54
56
|
client.videos[2014422]
|
57
|
+
# or
|
58
|
+
client.videos.find(2014422)
|
55
59
|
```
|
56
60
|
|
57
61
|
### Browse popular videos
|
@@ -60,6 +64,57 @@ client.videos[2014422]
|
|
60
64
|
client.videos.popular
|
61
65
|
```
|
62
66
|
|
67
|
+
### List all collections
|
68
|
+
|
69
|
+
Note: this is limited to collections belonging to the API user.
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
client.collections.all
|
73
|
+
```
|
74
|
+
|
75
|
+
### Get all media for a collection
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
client.collections['collection-id'].media
|
79
|
+
# or
|
80
|
+
client.collections.find('collection-id').media
|
81
|
+
```
|
82
|
+
|
83
|
+
You can also filter for only `photos` or `videos`.
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
client.collections['collection-id', type: 'photos'].media
|
87
|
+
client.collections['collection-id', type: 'videos'].media
|
88
|
+
```
|
89
|
+
|
90
|
+
## Rate Limiting
|
91
|
+
|
92
|
+
After performing a request, you can access your remaining rate limit via the client.
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
client.ratelimit_remaining
|
96
|
+
```
|
97
|
+
|
98
|
+
## Pagination
|
99
|
+
|
100
|
+
Requests that return multiple objects are paginated. You can pass in `page` and `per_page` options to these requests to get a specific page. You can also access the total number of results by accessing `total_results` on the response.
|
101
|
+
|
102
|
+
Note: The Pexels API returns a maximum of 80 records for one request.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
response = client.photos.search('dog', page: 2, per_page: 50)
|
106
|
+
response.total_results #=> 1000
|
107
|
+
response.total_pages #= 20
|
108
|
+
```
|
109
|
+
|
110
|
+
If there are further pages, you can also paginate through the API client:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
response = client.photos.search('dog', page: 2, per_page: 50)
|
114
|
+
response.prev_page # queries page 1
|
115
|
+
response.next_page # queries page 3
|
116
|
+
```
|
117
|
+
|
63
118
|
## Running the test suite
|
64
119
|
|
65
120
|
You'll need your own API key to run the test suite, you can get one on the [Pexels API Key management page](https://www.pexels.com/api/new/)
|
data/lib/pexels.rb
CHANGED
@@ -1,19 +1,44 @@
|
|
1
1
|
module Pexels
|
2
|
-
@api_base_url = 'https://api.pexels.com'
|
2
|
+
@api_base_url = ENV['PEXELS_API_BASE_URL'] || 'https://api.pexels.com'
|
3
|
+
@api_version = ENV['PEXELS_API_VERSION'] || 'v1'
|
3
4
|
|
4
5
|
class << self
|
5
|
-
attr_reader :api_base_url
|
6
|
+
attr_reader :api_base_url, :api_version
|
7
|
+
|
8
|
+
# Local headers can be defined inside a `.headers` file at the project root,
|
9
|
+
# with the following format:
|
10
|
+
#
|
11
|
+
# header1=value
|
12
|
+
# header2=value
|
13
|
+
# etc.
|
14
|
+
#
|
15
|
+
def local_headers
|
16
|
+
@local_headers ||= if File.exist?('.headers')
|
17
|
+
File.read('.headers').split.to_h { |header| header.split('=') }
|
18
|
+
else
|
19
|
+
{}
|
20
|
+
end
|
21
|
+
end
|
6
22
|
end
|
7
23
|
end
|
8
24
|
|
9
25
|
require_relative 'pexels/client'
|
26
|
+
require_relative 'pexels/client/request'
|
27
|
+
require_relative 'pexels/client/response'
|
28
|
+
require_relative 'pexels/client/search_filters'
|
29
|
+
require_relative 'pexels/client/collections'
|
10
30
|
require_relative 'pexels/client/photos'
|
11
31
|
require_relative 'pexels/client/videos'
|
12
32
|
require_relative 'pexels/version'
|
13
33
|
require_relative 'pexels/errors'
|
34
|
+
require_relative 'pexels/collection'
|
14
35
|
require_relative 'pexels/photo'
|
15
36
|
require_relative 'pexels/video'
|
16
37
|
require_relative 'pexels/video/file'
|
17
38
|
require_relative 'pexels/video/picture'
|
18
39
|
require_relative 'pexels/user'
|
19
|
-
require_relative 'pexels/
|
40
|
+
require_relative 'pexels/paginated_response'
|
41
|
+
require_relative 'pexels/collection_set'
|
42
|
+
require_relative 'pexels/collection_media_set'
|
43
|
+
require_relative 'pexels/photo_set'
|
44
|
+
require_relative 'pexels/video_set'
|
data/lib/pexels/client.rb
CHANGED
@@ -1,39 +1,29 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
)
|
29
|
-
|
30
|
-
@ratelimit_remaining = results.headers['x-ratelimit-remaining'].first.to_i
|
31
|
-
|
32
|
-
return JSON.parse(results.body)
|
33
|
-
rescue StandardError => exception
|
34
|
-
raise Pexels::APIError.new(exception)
|
1
|
+
module Pexels
|
2
|
+
class Client
|
3
|
+
attr_reader :api_key,
|
4
|
+
:ratelimit_remaining
|
5
|
+
|
6
|
+
def initialize(api_key = ENV['PEXELS_API_KEY'])
|
7
|
+
@api_key = api_key
|
8
|
+
end
|
9
|
+
|
10
|
+
def photos
|
11
|
+
@photos ||= Pexels::Client::Photos.new(self)
|
12
|
+
end
|
13
|
+
|
14
|
+
def videos
|
15
|
+
@videos ||= Pexels::Client::Videos.new(self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def collections
|
19
|
+
@collections ||= Pexels::Client::Collections.new(self)
|
20
|
+
end
|
21
|
+
|
22
|
+
def request(path, method: 'GET', params: {})
|
23
|
+
request = Request.new(api_key, path, method, params)
|
24
|
+
request.call.tap do |response|
|
25
|
+
@ratelimit_remaining = response.ratelimit_remaining
|
26
|
+
end
|
27
|
+
end
|
35
28
|
end
|
36
29
|
end
|
37
|
-
|
38
|
-
require 'pexels/client/photos'
|
39
|
-
require 'pexels/client/videos'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Pexels::Client::Collections
|
2
|
+
def initialize(client)
|
3
|
+
@client = client
|
4
|
+
end
|
5
|
+
|
6
|
+
def all(per_page: 15, page: 1)
|
7
|
+
response = @client.request(
|
8
|
+
"#{Pexels.api_version}/collections",
|
9
|
+
params: {
|
10
|
+
per_page: per_page,
|
11
|
+
page: page
|
12
|
+
})
|
13
|
+
|
14
|
+
Pexels::CollectionSet.new(response)
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](id, type: nil, per_page: 15, page: 1)
|
18
|
+
response = @client.request(
|
19
|
+
"#{Pexels.api_version}/collections/#{id}",
|
20
|
+
params: {
|
21
|
+
per_page: per_page,
|
22
|
+
page: page,
|
23
|
+
type: type
|
24
|
+
})
|
25
|
+
|
26
|
+
Pexels::CollectionMediaSet.new(response)
|
27
|
+
end
|
28
|
+
alias_method :find, :[]
|
29
|
+
end
|
data/lib/pexels/client/photos.rb
CHANGED
@@ -1,37 +1,56 @@
|
|
1
|
-
|
1
|
+
module Pexels
|
2
|
+
class Client
|
3
|
+
class Photos
|
4
|
+
include SearchFilters
|
2
5
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
|
+
end
|
6
9
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
10
|
+
def [](id)
|
11
|
+
response = @client.request("#{Pexels.api_version}/photos/#{id}")
|
12
|
+
Pexels::Photo.new(response.body)
|
13
|
+
end
|
14
|
+
alias_method :find, :[]
|
11
15
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
def search(query, per_page: 15, page: 1, locale: 'en-US', orientation: nil, size: nil, color: nil)
|
17
|
+
validate_search_params(orientation, size, color)
|
18
|
+
|
19
|
+
response = @client.request(
|
20
|
+
"#{Pexels.api_version}/search",
|
21
|
+
params: {
|
22
|
+
query: query,
|
23
|
+
per_page: per_page,
|
24
|
+
page: page,
|
25
|
+
locale: locale,
|
26
|
+
orientation: orientation,
|
27
|
+
size: size,
|
28
|
+
color: color
|
29
|
+
}.compact
|
30
|
+
)
|
31
|
+
|
32
|
+
Pexels::PhotoSet.new(response)
|
33
|
+
end
|
34
|
+
|
35
|
+
def curated(per_page: 15, page: 1)
|
36
|
+
response = @client.request(
|
37
|
+
"#{Pexels.api_version}/curated",
|
38
|
+
params: {
|
39
|
+
per_page: per_page,
|
40
|
+
page: page
|
41
|
+
}
|
42
|
+
)
|
43
|
+
|
44
|
+
Pexels::PhotoSet.new(response)
|
45
|
+
end
|
25
46
|
|
26
|
-
|
27
|
-
response = @client.request(
|
28
|
-
'/v1/curated',
|
29
|
-
params: {
|
30
|
-
per_page: per_page,
|
31
|
-
page: page,
|
32
|
-
}
|
33
|
-
)
|
47
|
+
private
|
34
48
|
|
35
|
-
|
49
|
+
def validate_search_params(orientation, size, color)
|
50
|
+
validate_orientation(orientation) &&
|
51
|
+
validate_size(size) &&
|
52
|
+
validate_color(color)
|
53
|
+
end
|
54
|
+
end
|
36
55
|
end
|
37
56
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'requests'
|
2
|
+
|
3
|
+
module Pexels
|
4
|
+
class Client
|
5
|
+
class Request
|
6
|
+
attr_reader :api_key, :path, :method, :params
|
7
|
+
|
8
|
+
def initialize(api_key, path, method, params)
|
9
|
+
@api_key = api_key
|
10
|
+
@path = path
|
11
|
+
@method = method
|
12
|
+
@params = params
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
log_request if ENV['DEBUG']
|
17
|
+
|
18
|
+
Response.new(self, execute)
|
19
|
+
|
20
|
+
rescue StandardError => exception
|
21
|
+
raise Pexels::APIError.new(exception)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def execute
|
27
|
+
Requests.request(
|
28
|
+
method,
|
29
|
+
url,
|
30
|
+
params: params,
|
31
|
+
headers: headers
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def url
|
36
|
+
@url ||= File.join(Pexels.api_base_url, path)
|
37
|
+
end
|
38
|
+
|
39
|
+
def headers
|
40
|
+
@headers = {
|
41
|
+
'Authorization' => api_key,
|
42
|
+
'User-Agent' => "Pexels/Ruby (#{Pexels::VERSION})"
|
43
|
+
}.merge(Pexels.local_headers)
|
44
|
+
end
|
45
|
+
|
46
|
+
def log_request
|
47
|
+
puts "Requesting #{url}"
|
48
|
+
puts " → params: #{params}"
|
49
|
+
puts " → headers: #{headers}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'requests'
|
2
|
+
|
3
|
+
module Pexels
|
4
|
+
class Client
|
5
|
+
class Response
|
6
|
+
attr_reader :request, :response
|
7
|
+
|
8
|
+
def initialize(request, response)
|
9
|
+
@request = request
|
10
|
+
@response = response
|
11
|
+
end
|
12
|
+
|
13
|
+
def body
|
14
|
+
JSON.parse(response.body)
|
15
|
+
|
16
|
+
rescue JSON::JSONError => exception
|
17
|
+
raise Pexels::APIError.new(exception)
|
18
|
+
end
|
19
|
+
|
20
|
+
def headers
|
21
|
+
response.headers
|
22
|
+
end
|
23
|
+
|
24
|
+
def ratelimit_remaining
|
25
|
+
headers['x-ratelimit-remaining']&.first&.to_i
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Pexels
|
2
|
+
class Client
|
3
|
+
module SearchFilters
|
4
|
+
ORIENTATIONS = %w(portrait landscape square).freeze
|
5
|
+
SIZES = %w(small medium large).freeze
|
6
|
+
COLORS = %w(red orange yellow green turquoise blue violet pink brown black gray white).freeze
|
7
|
+
|
8
|
+
def validate_orientation(orientation)
|
9
|
+
return true unless orientation
|
10
|
+
return true if ORIENTATIONS.include?(orientation.to_s)
|
11
|
+
|
12
|
+
raise ArgumentError, "`orientation` must be one of #{ORIENTATIONS.join(', ')}."
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate_size(size)
|
16
|
+
return true unless size
|
17
|
+
return true if SIZES.include?(size.to_s)
|
18
|
+
|
19
|
+
raise ArgumentError, "`size` must be one of #{SIZES.join(', ')}."
|
20
|
+
end
|
21
|
+
|
22
|
+
def validate_color(color)
|
23
|
+
return true unless color
|
24
|
+
return true if COLORS.include?(color.to_s)
|
25
|
+
return true if color.to_s =~ /\A#?(?:[0-9a-f]{3}){1,2}\z/i
|
26
|
+
|
27
|
+
raise ArgumentError, "`color` must be one of #{COLORS.join(', ')} or a valid hex code."
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/pexels/client/videos.rb
CHANGED
@@ -1,36 +1,53 @@
|
|
1
|
-
|
1
|
+
module Pexels
|
2
|
+
class Client
|
3
|
+
class Videos
|
4
|
+
include SearchFilters
|
2
5
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
|
+
end
|
6
9
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
10
|
+
def [](id)
|
11
|
+
response = @client.request("/videos/videos/#{id}")
|
12
|
+
Pexels::Video.new(response.body)
|
13
|
+
end
|
14
|
+
alias_method :find, :[]
|
11
15
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
def search(query, per_page: 15, page: 1, orientation: nil, size: nil)
|
17
|
+
validate_search_params(orientation, size)
|
18
|
+
|
19
|
+
response = @client.request(
|
20
|
+
'/videos/search',
|
21
|
+
params: {
|
22
|
+
query: query,
|
23
|
+
per_page: per_page,
|
24
|
+
page: page,
|
25
|
+
orientation: orientation,
|
26
|
+
size: size
|
27
|
+
}.compact
|
28
|
+
)
|
29
|
+
|
30
|
+
Pexels::VideoSet.new(response)
|
31
|
+
end
|
32
|
+
|
33
|
+
def popular(per_page: 15, page: 1)
|
34
|
+
response = @client.request(
|
35
|
+
'/videos/popular',
|
36
|
+
params: {
|
37
|
+
per_page: per_page,
|
38
|
+
page: page,
|
39
|
+
}
|
40
|
+
)
|
41
|
+
|
42
|
+
Pexels::VideoSet.new(response)
|
43
|
+
end
|
24
44
|
|
25
|
-
|
26
|
-
response = @client.request(
|
27
|
-
'/videos/popular',
|
28
|
-
params: {
|
29
|
-
per_page: per_page,
|
30
|
-
page: page,
|
31
|
-
}
|
32
|
-
)
|
45
|
+
private
|
33
46
|
|
34
|
-
|
47
|
+
def validate_search_params(orientation, size)
|
48
|
+
validate_orientation(orientation) &&
|
49
|
+
validate_size(size)
|
50
|
+
end
|
51
|
+
end
|
35
52
|
end
|
36
53
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Pexels
|
2
|
+
class Collection
|
3
|
+
attr_reader :id,
|
4
|
+
:title,
|
5
|
+
:description,
|
6
|
+
:private,
|
7
|
+
:media_count,
|
8
|
+
:photos_count,
|
9
|
+
:videos_count
|
10
|
+
|
11
|
+
|
12
|
+
def initialize(attrs)
|
13
|
+
@id = attrs.fetch('id')
|
14
|
+
@title = attrs.fetch('title')
|
15
|
+
@description = attrs.fetch('description')
|
16
|
+
@private = attrs.fetch('private')
|
17
|
+
@media_count = attrs.fetch('media_count')
|
18
|
+
@photos_count = attrs.fetch('photos_count')
|
19
|
+
@videos_count = attrs.fetch('videos_count')
|
20
|
+
|
21
|
+
rescue KeyError => exception
|
22
|
+
raise Pexels::MalformedAPIResponseError.new(exception)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Pexels
|
2
|
+
class CollectionMediaSet < PaginatedResponse
|
3
|
+
alias_method :media, :data
|
4
|
+
public :media
|
5
|
+
|
6
|
+
attr_reader :id
|
7
|
+
|
8
|
+
def initialize(response)
|
9
|
+
super
|
10
|
+
@id = attrs.fetch('id')
|
11
|
+
@data = attrs.fetch('media', []).map do |attrs|
|
12
|
+
if attrs['type'] == 'Photo'
|
13
|
+
Pexels::Photo.new(attrs)
|
14
|
+
elsif attrs['type'] == 'Video'
|
15
|
+
Pexels::Video.new(attrs)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
rescue KeyError => exception
|
20
|
+
raise Pexels::MalformedAPIResponseError.new(exception)
|
21
|
+
end
|
22
|
+
|
23
|
+
def photos
|
24
|
+
@photos ||= media.select { |m| m.is_a?(Pexels::Photo) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def videos
|
28
|
+
@videos ||= media.select { |m| m.is_a?(Pexels::Video) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module Pexels
|
4
|
+
class PaginatedResponse
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
attr_reader :total_results,
|
8
|
+
:page,
|
9
|
+
:per_page,
|
10
|
+
:data
|
11
|
+
|
12
|
+
private :data
|
13
|
+
|
14
|
+
def initialize(response)
|
15
|
+
@response = response
|
16
|
+
@attrs = @response.body
|
17
|
+
|
18
|
+
@total_results = attrs.fetch('total_results', nil)
|
19
|
+
@page = attrs.fetch('page')
|
20
|
+
@per_page = attrs.fetch('per_page')
|
21
|
+
@prev_page = attrs.fetch('prev_page', nil)
|
22
|
+
@next_page = attrs.fetch('next_page', nil)
|
23
|
+
end
|
24
|
+
|
25
|
+
def total_pages
|
26
|
+
total_results.fdiv(per_page).ceil
|
27
|
+
end
|
28
|
+
|
29
|
+
def each(&block)
|
30
|
+
if block_given?
|
31
|
+
data.each(&block)
|
32
|
+
else
|
33
|
+
to_enum(:each)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def next_page
|
38
|
+
return unless @next_page
|
39
|
+
|
40
|
+
request.params[:page] = extract_page(@next_page)
|
41
|
+
self.class.new(request.call)
|
42
|
+
end
|
43
|
+
|
44
|
+
def prev_page
|
45
|
+
return unless @prev_page
|
46
|
+
|
47
|
+
request.params[:page] = extract_page(@next_page)
|
48
|
+
self.class.new(request.call)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_reader :response, :attrs
|
54
|
+
|
55
|
+
def request
|
56
|
+
response.request
|
57
|
+
end
|
58
|
+
|
59
|
+
def extract_page(url)
|
60
|
+
CGI.parse(URI.parse(url).query)['page'].first
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/pexels/photo.rb
CHANGED
@@ -4,8 +4,8 @@ class Pexels::Photo
|
|
4
4
|
:height,
|
5
5
|
:url,
|
6
6
|
:user,
|
7
|
-
:src
|
8
|
-
|
7
|
+
:src,
|
8
|
+
:avg_color
|
9
9
|
|
10
10
|
def initialize(attrs)
|
11
11
|
@id = attrs.fetch('id')
|
@@ -18,8 +18,21 @@ class Pexels::Photo
|
|
18
18
|
url: attrs.fetch('photographer_url')
|
19
19
|
)
|
20
20
|
@src = attrs.fetch('src')
|
21
|
+
@avg_color = attrs.fetch('avg_color')
|
21
22
|
|
22
23
|
rescue KeyError => exception
|
23
24
|
raise Pexels::MalformedAPIResponseError.new(exception)
|
24
25
|
end
|
26
|
+
|
27
|
+
def type
|
28
|
+
'Photo'
|
29
|
+
end
|
30
|
+
|
31
|
+
def photo?
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
def video?
|
36
|
+
false
|
37
|
+
end
|
25
38
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Pexels
|
2
|
+
class PhotoSet < PaginatedResponse
|
3
|
+
alias_method :photos, :data
|
4
|
+
public :photos
|
5
|
+
|
6
|
+
def initialize(response)
|
7
|
+
super
|
8
|
+
@data = attrs.fetch('photos', []).map { |attrs| Pexels::Photo.new(attrs) }
|
9
|
+
|
10
|
+
rescue KeyError => exception
|
11
|
+
raise Pexels::MalformedAPIResponseError.new(exception)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/pexels/version.rb
CHANGED
data/lib/pexels/video.rb
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Pexels
|
2
|
+
class VideoSet < PaginatedResponse
|
3
|
+
alias_method :videos, :data
|
4
|
+
public :videos
|
5
|
+
|
6
|
+
def initialize(response)
|
7
|
+
super
|
8
|
+
@data = attrs.fetch('videos', []).map { |attrs| Pexels::Video.new(attrs) }
|
9
|
+
|
10
|
+
rescue KeyError => exception
|
11
|
+
raise Pexels::MalformedAPIResponseError.new(exception)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/pexels.gemspec
CHANGED
data/test/client_test.rb
CHANGED
@@ -15,7 +15,7 @@ class TestClient < Minitest::Test
|
|
15
15
|
assert remaining >= 0
|
16
16
|
|
17
17
|
@client.photos.search('test')
|
18
|
-
assert_equal
|
18
|
+
assert_equal @client.ratelimit_remaining, remaining - 1
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_exceptions
|
@@ -26,7 +26,7 @@ class TestClient < Minitest::Test
|
|
26
26
|
@client.photos.search('test')
|
27
27
|
raise 'this shouldnt happen'
|
28
28
|
rescue StandardError => exception
|
29
|
-
|
29
|
+
assert_kind_of Pexels::APIError, exception
|
30
30
|
assert exception.message != 'this shouldnt happen'
|
31
31
|
end
|
32
32
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'pexels'
|
3
|
+
|
4
|
+
class TestCollections < Minitest::Test
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@client = Pexels::Client.new(ENV.fetch('PEXELS_API_KEY'))
|
8
|
+
@collection = @client.collections.all(per_page: 1).first
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_all
|
12
|
+
collection = @client.collections.all
|
13
|
+
|
14
|
+
assert_kind_of Pexels::CollectionSet, collection
|
15
|
+
assert_equal collection.per_page, 15
|
16
|
+
assert_equal collection.page, 1
|
17
|
+
|
18
|
+
assert collection.collections.is_a? Array
|
19
|
+
assert collection.collections.any?
|
20
|
+
assert collection.first.is_a? Pexels::Collection
|
21
|
+
|
22
|
+
collection_with_params = @client.collections.all(per_page: 1, page: 2)
|
23
|
+
assert_equal collection_with_params.per_page, 1
|
24
|
+
assert_equal collection_with_params.page, 2
|
25
|
+
assert_equal collection_with_params.collections.length, 1
|
26
|
+
assert_kind_of Pexels::CollectionSet, collection_with_params.next_page
|
27
|
+
assert_kind_of Pexels::CollectionSet, collection_with_params.prev_page
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_get_collection_media
|
31
|
+
collection = @client.collections[@collection.id]
|
32
|
+
assert_kind_of Pexels::CollectionMediaSet, collection
|
33
|
+
assert_equal collection.id, @collection.id
|
34
|
+
|
35
|
+
assert_kind_of Array, collection.media
|
36
|
+
assert collection.media.any?
|
37
|
+
|
38
|
+
assert_includes([Pexels::Photo, Pexels::Video], collection.media.first.class)
|
39
|
+
|
40
|
+
refute_includes([Pexels::Video], collection.photos.map(&:class))
|
41
|
+
refute_includes([Pexels::Photo], collection.videos.map(&:class))
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_get_collection_photos
|
45
|
+
collection = @client.collections[@collection.id, type: 'photos']
|
46
|
+
assert_kind_of Pexels::CollectionMediaSet, collection
|
47
|
+
assert_kind_of Array, collection.media
|
48
|
+
assert collection.media.all? { |m| m.is_a?(Pexels::Photo) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_get_collection_videos
|
52
|
+
collection = @client.collections[@collection.id, type: 'videos']
|
53
|
+
assert_kind_of Pexels::CollectionMediaSet, collection
|
54
|
+
assert_kind_of Array, collection.media
|
55
|
+
assert collection.media.all? { |m| m.is_a?(Pexels::Video) }
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_get_collection_invalid_type
|
59
|
+
collection = @client.collections[@collection.id, type: 'foo']
|
60
|
+
assert_kind_of Pexels::CollectionMediaSet, collection
|
61
|
+
assert_kind_of Array, collection.media
|
62
|
+
assert collection.any?
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_get_collection_pagination
|
66
|
+
collection = @client.collections[@collection.id, per_page: 1, page: 1]
|
67
|
+
assert_kind_of Pexels::CollectionMediaSet, collection
|
68
|
+
assert_kind_of Array, collection.media
|
69
|
+
assert collection.media.any?
|
70
|
+
|
71
|
+
assert_equal collection.per_page, 1
|
72
|
+
assert_equal collection.page, 1
|
73
|
+
assert_equal collection.media.length, 1
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_invalid_get_collection
|
77
|
+
error = assert_raises(Pexels::APIError) do
|
78
|
+
@client.collections['this-is-not-a-valid-id']
|
79
|
+
end
|
80
|
+
assert error.message, 'Not Found'
|
81
|
+
end
|
82
|
+
end
|
data/test/photo_test.rb
CHANGED
@@ -5,44 +5,46 @@ class TestPhoto < Minitest::Test
|
|
5
5
|
|
6
6
|
def setup
|
7
7
|
@client = Pexels::Client.new(ENV.fetch('PEXELS_API_KEY'))
|
8
|
-
@photo = @client.photos.search('test', per_page: 1).
|
8
|
+
@photo = @client.photos.search('test', per_page: 1).first
|
9
9
|
end
|
10
10
|
|
11
11
|
def test_successful_searches
|
12
12
|
search_result = @client.photos.search('test')
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
assert_kind_of Pexels::PhotoSet, search_result
|
15
|
+
assert_kind_of Pexels::PhotoSet, search_result.next_page
|
16
|
+
assert_kind_of Integer, search_result.total_results
|
17
17
|
assert_equal search_result.per_page, 15
|
18
18
|
assert_equal search_result.page, 1
|
19
19
|
|
20
20
|
assert search_result.photos.is_a? Array
|
21
21
|
assert search_result.photos.any?
|
22
|
-
assert search_result.
|
22
|
+
assert search_result.first.is_a? Pexels::Photo
|
23
23
|
|
24
24
|
search_result_with_params = @client.photos.search('test', per_page: 1, page: 2)
|
25
25
|
assert_equal search_result_with_params.per_page, 1
|
26
26
|
assert_equal search_result_with_params.page, 2
|
27
27
|
assert_equal search_result_with_params.photos.length, 1
|
28
|
+
assert_kind_of Pexels::PhotoSet, search_result_with_params.prev_page
|
28
29
|
end
|
29
30
|
|
30
31
|
def test_curated_photos
|
31
32
|
search_result = @client.photos.curated
|
32
33
|
|
33
|
-
|
34
|
-
|
34
|
+
assert_kind_of Pexels::PhotoSet, search_result
|
35
|
+
assert_kind_of Pexels::PhotoSet, search_result.next_page
|
35
36
|
assert_equal search_result.per_page, 15
|
36
37
|
assert_equal search_result.page, 1
|
37
38
|
|
38
39
|
assert search_result.photos.is_a? Array
|
39
40
|
assert search_result.photos.any?
|
40
|
-
assert search_result.
|
41
|
+
assert search_result.first.is_a? Pexels::Photo
|
41
42
|
|
42
43
|
search_result_with_params = @client.photos.curated(per_page: 1, page: 2)
|
43
44
|
assert_equal search_result_with_params.per_page, 1
|
44
45
|
assert_equal search_result_with_params.page, 2
|
45
46
|
assert_equal search_result_with_params.photos.length, 1
|
47
|
+
assert_kind_of Pexels::PhotoSet, search_result_with_params.prev_page
|
46
48
|
end
|
47
49
|
|
48
50
|
def test_get_photo
|
@@ -58,13 +60,51 @@ class TestPhoto < Minitest::Test
|
|
58
60
|
assert_equal photo.user.url, @photo.user.url
|
59
61
|
assert_equal photo.user.id, @photo.user.id
|
60
62
|
assert_equal photo.src, @photo.src
|
63
|
+
assert_equal photo.avg_color, @photo.avg_color
|
64
|
+
|
65
|
+
assert photo.photo?
|
66
|
+
assert_equal photo.type, 'Photo'
|
67
|
+
refute photo.video?
|
61
68
|
end
|
62
69
|
|
63
70
|
def test_invalid_get_photo
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
71
|
+
error = assert_raises(Pexels::APIError) do
|
72
|
+
@client.photos['this-is-not-a-valid-id']
|
73
|
+
end
|
74
|
+
assert_equal 'Not Found', error.message
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_invalid_orientation
|
78
|
+
error = assert_raises(ArgumentError) do
|
79
|
+
@client.photos.search('dog', orientation: 'foo')
|
80
|
+
end
|
81
|
+
assert_match '`orientation` must be one of', error.message
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_invalid_size
|
85
|
+
error = assert_raises(ArgumentError) do
|
86
|
+
@client.photos.search('dog', size: 'foo')
|
87
|
+
end
|
88
|
+
assert_match '`size` must be one of', error.message
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_invalid_color
|
92
|
+
error = assert_raises(ArgumentError) do
|
93
|
+
@client.photos.search('dog', color: 'foo')
|
94
|
+
end
|
95
|
+
assert_match '`color` must be one of', error.message
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_invalid_color_hex
|
99
|
+
error = assert_raises(ArgumentError) do
|
100
|
+
@client.photos.search('dog', color: '#gggggg')
|
101
|
+
end
|
102
|
+
assert_match '`color` must be one of', error.message
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_search_filters
|
106
|
+
search_result = @client.photos.search('dog', color: '#FF0000', size: :large, orientation: :square)
|
107
|
+
assert_kind_of Pexels::PhotoSet, search_result
|
108
|
+
assert search_result.photos.any?
|
69
109
|
end
|
70
110
|
end
|
data/test/video_test.rb
CHANGED
@@ -5,42 +5,46 @@ class TestVideo < Minitest::Test
|
|
5
5
|
|
6
6
|
def setup
|
7
7
|
@client = Pexels::Client.new(ENV.fetch('PEXELS_API_KEY'))
|
8
|
-
@video = @client.videos.search('test', per_page: 1).
|
8
|
+
@video = @client.videos.search('test', per_page: 1).first
|
9
9
|
end
|
10
10
|
|
11
11
|
def test_successful_searches
|
12
12
|
search_result = @client.videos.search('test')
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
assert_kind_of Pexels::VideoSet, search_result
|
15
|
+
assert_kind_of Pexels::VideoSet, search_result.next_page
|
16
|
+
assert_kind_of Integer, search_result.total_results
|
16
17
|
assert_equal search_result.per_page, 15
|
17
18
|
assert_equal search_result.page, 1
|
18
19
|
|
19
20
|
assert search_result.videos.is_a? Array
|
20
21
|
assert search_result.videos.any?
|
21
|
-
assert search_result.
|
22
|
+
assert search_result.first.is_a? Pexels::Video
|
22
23
|
|
23
24
|
search_result_with_params = @client.videos.search('test', per_page: 1, page: 2)
|
24
25
|
assert_equal search_result_with_params.per_page, 1
|
25
26
|
assert_equal search_result_with_params.page, 2
|
26
27
|
assert_equal search_result_with_params.videos.length, 1
|
28
|
+
assert_kind_of Pexels::VideoSet, search_result_with_params.prev_page
|
27
29
|
end
|
28
30
|
|
29
31
|
def test_popular_videos
|
30
32
|
search_result = @client.videos.popular
|
31
33
|
|
32
|
-
|
34
|
+
assert_kind_of Pexels::VideoSet, search_result
|
35
|
+
assert_kind_of Pexels::VideoSet, search_result.next_page
|
33
36
|
assert_equal search_result.per_page, 15
|
34
37
|
assert_equal search_result.page, 1
|
35
38
|
|
36
39
|
assert search_result.videos.is_a? Array
|
37
40
|
assert search_result.videos.any?
|
38
|
-
assert search_result.
|
41
|
+
assert search_result.first.is_a? Pexels::Video
|
39
42
|
|
40
43
|
search_result_with_params = @client.videos.popular(per_page: 1, page: 2)
|
41
44
|
assert_equal search_result_with_params.per_page, 1
|
42
45
|
assert_equal search_result_with_params.page, 2
|
43
46
|
assert_equal search_result_with_params.videos.length, 1
|
47
|
+
assert_kind_of Pexels::VideoSet, search_result_with_params.prev_page
|
44
48
|
end
|
45
49
|
|
46
50
|
def test_get_video
|
@@ -53,6 +57,10 @@ class TestVideo < Minitest::Test
|
|
53
57
|
assert_equal video.height, @video.height
|
54
58
|
assert_equal video.url, @video.url
|
55
59
|
|
60
|
+
assert video.video?
|
61
|
+
assert_equal video.type, 'Video'
|
62
|
+
refute video.photo?
|
63
|
+
|
56
64
|
assert video.user.is_a?(Pexels::User)
|
57
65
|
assert_equal video.user.name, @video.user.name
|
58
66
|
assert_equal video.user.url, @video.user.url
|
@@ -66,14 +74,29 @@ class TestVideo < Minitest::Test
|
|
66
74
|
end
|
67
75
|
|
68
76
|
def test_invalid_get_video
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
77
|
+
error = assert_raises(Pexels::APIError) do
|
78
|
+
@client.videos['this-is-not-a-valid-id']
|
79
|
+
end
|
80
|
+
assert_equal 'Not Found', error.message
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_invalid_orientation
|
84
|
+
error = assert_raises(ArgumentError) do
|
85
|
+
@client.photos.search('dog', orientation: 'foo')
|
86
|
+
end
|
87
|
+
assert_match '`orientation` must be one of', error.message
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_invalid_size
|
91
|
+
error = assert_raises(ArgumentError) do
|
92
|
+
@client.photos.search('dog', size: 'foo')
|
93
|
+
end
|
94
|
+
assert_match '`size` must be one of', error.message
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_search_filters
|
98
|
+
search_result = @client.videos.search('cat', size: :medium, orientation: :square)
|
99
|
+
assert_kind_of Pexels::VideoSet, search_result
|
100
|
+
assert search_result.videos.any?
|
78
101
|
end
|
79
102
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pexels
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pexels dev team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-04-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: requests
|
@@ -34,23 +34,34 @@ files:
|
|
34
34
|
- ".env.sample"
|
35
35
|
- ".gems"
|
36
36
|
- ".gitignore"
|
37
|
+
- CHANGES.md
|
37
38
|
- LICENSE
|
38
39
|
- Makefile
|
39
40
|
- README.md
|
40
41
|
- lib/pexels.rb
|
41
42
|
- lib/pexels/client.rb
|
43
|
+
- lib/pexels/client/collections.rb
|
42
44
|
- lib/pexels/client/photos.rb
|
45
|
+
- lib/pexels/client/request.rb
|
46
|
+
- lib/pexels/client/response.rb
|
47
|
+
- lib/pexels/client/search_filters.rb
|
43
48
|
- lib/pexels/client/videos.rb
|
49
|
+
- lib/pexels/collection.rb
|
50
|
+
- lib/pexels/collection_media_set.rb
|
51
|
+
- lib/pexels/collection_set.rb
|
44
52
|
- lib/pexels/errors.rb
|
53
|
+
- lib/pexels/paginated_response.rb
|
45
54
|
- lib/pexels/photo.rb
|
46
|
-
- lib/pexels/
|
55
|
+
- lib/pexels/photo_set.rb
|
47
56
|
- lib/pexels/user.rb
|
48
57
|
- lib/pexels/version.rb
|
49
58
|
- lib/pexels/video.rb
|
50
59
|
- lib/pexels/video/file.rb
|
51
60
|
- lib/pexels/video/picture.rb
|
61
|
+
- lib/pexels/video_set.rb
|
52
62
|
- pexels.gemspec
|
53
63
|
- test/client_test.rb
|
64
|
+
- test/collection_test.rb
|
54
65
|
- test/photo_test.rb
|
55
66
|
- test/video_test.rb
|
56
67
|
homepage: https://github.com/pexels/pexels-ruby
|
@@ -65,7 +76,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
65
76
|
requirements:
|
66
77
|
- - ">="
|
67
78
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
79
|
+
version: 2.4.0
|
69
80
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
81
|
requirements:
|
71
82
|
- - ">="
|
data/lib/pexels/response.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
class Pexels::Response
|
2
|
-
attr_reader :photos,
|
3
|
-
:videos,
|
4
|
-
:total_results,
|
5
|
-
:page,
|
6
|
-
:per_page,
|
7
|
-
:next_page
|
8
|
-
|
9
|
-
def initialize(attrs, type: :Photo)
|
10
|
-
@total_results = attrs.fetch('total_results', nil)
|
11
|
-
@page = attrs.fetch('page')
|
12
|
-
@per_page = attrs.fetch('per_page')
|
13
|
-
@next_page = attrs.fetch('next_page', nil)
|
14
|
-
|
15
|
-
@photos = attrs.fetch('photos', []).map { |attrs| Pexels::Photo.new(attrs) }
|
16
|
-
@videos = attrs.fetch('videos', []).map { |attrs| Pexels::Video.new(attrs) }
|
17
|
-
|
18
|
-
return self
|
19
|
-
end
|
20
|
-
end
|