noun-project-api 0.2.2 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Rakefile +9 -7
- data/lib/noun_project_api.rb +38 -0
- data/lib/noun_project_api/base_item.rb +30 -0
- data/lib/noun_project_api/collection.rb +36 -0
- data/lib/noun_project_api/collection_retriever.rb +14 -0
- data/lib/{noun-project-api → noun_project_api}/connection.rb +3 -1
- data/lib/noun_project_api/errors.rb +14 -0
- data/lib/noun_project_api/icon.rb +41 -0
- data/lib/noun_project_api/icon_retriever.rb +14 -0
- data/lib/noun_project_api/icons_retriever.rb +66 -0
- data/lib/noun_project_api/reporter.rb +22 -0
- data/lib/noun_project_api/retriever.rb +18 -0
- data/spec/lib/noun_project_api/base_item_spec.rb +13 -0
- data/spec/lib/noun_project_api/collection_retriever_spec.rb +87 -0
- data/spec/lib/noun_project_api/collection_spec.rb +66 -0
- data/spec/lib/{noun-project-api → noun_project_api}/icon_retriever_spec.rb +20 -18
- data/spec/lib/{noun-project-api → noun_project_api}/icon_spec.rb +21 -19
- data/spec/lib/{noun-project-api → noun_project_api}/icons_retriever_spec.rb +27 -25
- data/spec/lib/{noun-project-api → noun_project_api}/reporter_spec.rb +24 -24
- data/spec/lib/noun_project_api/retriever_spec.rb +22 -0
- data/spec/lib/{nount_project_api_spec.rb → noun_project_api_spec.rb} +5 -3
- data/spec/spec_helper.rb +3 -4
- data/spec/support/fakes.rb +93 -10
- metadata +90 -45
- data/lib/noun-project-api.rb +0 -30
- data/lib/noun-project-api/icon.rb +0 -52
- data/lib/noun-project-api/icon_retriever.rb +0 -20
- data/lib/noun-project-api/icons_retriever.rb +0 -51
- data/lib/noun-project-api/reporter.rb +0 -20
- data/lib/noun-project-api/retriever.rb +0 -6
- data/spec/lib/noun-project-api/retriever_spec.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7599a88ab282cc57200c3907a08b057d466d91af307c992c42fa59ca15bdbe55
|
4
|
+
data.tar.gz: cee01ba5d89c09fb74b62c7ef2b5d630437632ce6c43406b5d65b2fd417c544b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf81da78826aee4244949577e0b103b1dc16435e813115576b81701d72a31a4569e78cc85ec5cd7e921666e12392f18aff9fd02dd9f3ec1d2a006e21cb7c341b
|
7
|
+
data.tar.gz: de4ab0a45285f4979c697beff40d1f02ff497a9d4037487172000cede3ca46f20a47c2a51b8d635ddc9a478d3b0a6cdba07dba8b3417311b6cbc75df73491140
|
data/Rakefile
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler"
|
2
4
|
Bundler::GemHelper.install_tasks
|
3
5
|
|
4
|
-
|
6
|
+
$LOAD_PATH.unshift "lib"
|
5
7
|
|
6
|
-
desc
|
7
|
-
task :
|
8
|
+
desc "Default: run unit tests."
|
9
|
+
task default: [:spec]
|
8
10
|
|
9
|
-
require
|
11
|
+
require "rspec/core/rake_task"
|
10
12
|
|
11
|
-
desc
|
13
|
+
desc "Run specs"
|
12
14
|
RSpec::Core::RakeTask.new do |t|
|
13
|
-
t.pattern =
|
15
|
+
t.pattern = "./spec/**/*_spec.rb"
|
14
16
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "oauth"
|
4
|
+
require "active_support"
|
5
|
+
require "json"
|
6
|
+
require "noun_project_api/errors"
|
7
|
+
require "noun_project_api/connection"
|
8
|
+
require "noun_project_api/icon_retriever"
|
9
|
+
require "noun_project_api/reporter"
|
10
|
+
require "noun_project_api/icons_retriever"
|
11
|
+
require "noun_project_api/icon"
|
12
|
+
require "noun_project_api/collection_retriever"
|
13
|
+
require "noun_project_api/collection"
|
14
|
+
|
15
|
+
# Top level name space for the entire Gem.
|
16
|
+
module NounProjectApi
|
17
|
+
API_BASE = "http://api.thenounproject.com"
|
18
|
+
|
19
|
+
def self.configuration
|
20
|
+
@configuration ||= Configuration.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.configure
|
24
|
+
self.configuration ||= Configuration.new
|
25
|
+
yield(configuration) if block_given?
|
26
|
+
end
|
27
|
+
|
28
|
+
# Main configuration class.
|
29
|
+
class Configuration
|
30
|
+
attr_accessor :public_domain, :cache, :cache_ttl
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
@public_domain = false
|
34
|
+
@cache = ActiveSupport::Cache::NullStore.new
|
35
|
+
@cache_ttl = 604800 # Week in seconds
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NounProjectApi
|
4
|
+
# A basis to Items returned by the noun project.
|
5
|
+
class BaseItem
|
6
|
+
attr_accessor :original_hash
|
7
|
+
ITEM_NAME = nil
|
8
|
+
|
9
|
+
def initialize(origin)
|
10
|
+
raise NotImplementedError, "Must use a subclass" if self.class::ITEM_NAME.nil?
|
11
|
+
|
12
|
+
origin = JSON.parse(origin, symbolize_names: true) if origin.is_a? String
|
13
|
+
if origin.key? self.class::ITEM_NAME
|
14
|
+
origin = origin.delete(
|
15
|
+
self.class::ITEM_NAME
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
@original_hash = origin
|
20
|
+
end
|
21
|
+
|
22
|
+
def id
|
23
|
+
original_hash[:id].to_i
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_json(*_args)
|
27
|
+
JSON.dump(to_hash)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "noun_project_api/base_item"
|
4
|
+
|
5
|
+
module NounProjectApi
|
6
|
+
# A single Collection as an abstracted ruby object.
|
7
|
+
class Collection < BaseItem
|
8
|
+
ITEM_NAME = :collection
|
9
|
+
|
10
|
+
def author_id
|
11
|
+
original_hash[:author_id].to_i
|
12
|
+
end
|
13
|
+
|
14
|
+
def author_name
|
15
|
+
original_hash[:author][:name]
|
16
|
+
end
|
17
|
+
|
18
|
+
def icon_count
|
19
|
+
original_hash[:icon_count].to_i
|
20
|
+
end
|
21
|
+
|
22
|
+
def published?
|
23
|
+
original_hash[:is_published].to_i == 1
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_hash
|
27
|
+
{
|
28
|
+
id: id,
|
29
|
+
author_id: author_id,
|
30
|
+
author_name: author_name,
|
31
|
+
icon_count: icon_count,
|
32
|
+
published: published?
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "noun_project_api/collection"
|
4
|
+
require "noun_project_api/retriever"
|
5
|
+
|
6
|
+
module NounProjectApi
|
7
|
+
# Retrieve a collection.
|
8
|
+
class CollectionRetriever < Retriever
|
9
|
+
API_PATH = "/collection/"
|
10
|
+
ITEM_CLASS = Collection
|
11
|
+
|
12
|
+
alias find_by_slug find
|
13
|
+
end
|
14
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module NounProjectApi
|
2
4
|
# Basic connection methods and setup.
|
3
5
|
module Connection
|
@@ -6,7 +8,7 @@ module NounProjectApi
|
|
6
8
|
def initialize(token, secret)
|
7
9
|
@token = token
|
8
10
|
@secret = secret
|
9
|
-
|
11
|
+
raise ArgumentError, "Missing token or secret" unless @token && @secret
|
10
12
|
|
11
13
|
@access_token = OAuth::AccessToken.new(OAuth::Consumer.new(token, secret))
|
12
14
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "noun_project_api/base_item"
|
4
|
+
|
5
|
+
module NounProjectApi
|
6
|
+
# A single Icon as an abstracted ruby object.
|
7
|
+
class Icon < BaseItem
|
8
|
+
PREVIEW_SIZE_200 = 200
|
9
|
+
PREVIEW_SIZE_42 = 42
|
10
|
+
PREVIEW_SIZE_84 = 84
|
11
|
+
|
12
|
+
PUBLIC_DOMAIN_LICENSE = "public-domain"
|
13
|
+
|
14
|
+
ITEM_NAME = :icon
|
15
|
+
|
16
|
+
def public_domain?
|
17
|
+
original_hash[:license_description] == PUBLIC_DOMAIN_LICENSE
|
18
|
+
end
|
19
|
+
|
20
|
+
def svg_url
|
21
|
+
original_hash[:icon_url]
|
22
|
+
end
|
23
|
+
|
24
|
+
def preview_url(size = PREVIEW_SIZE_200)
|
25
|
+
if size == PREVIEW_SIZE_200
|
26
|
+
original_hash[:preview_url]
|
27
|
+
else
|
28
|
+
original_hash[:"preview_url_#{size}"]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_hash
|
33
|
+
{
|
34
|
+
id: id,
|
35
|
+
preview_url_200: preview_url(PREVIEW_SIZE_200),
|
36
|
+
preview_url_84: preview_url(PREVIEW_SIZE_84),
|
37
|
+
preview_url_42: preview_url(PREVIEW_SIZE_42)
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "noun_project_api/icon"
|
4
|
+
require "noun_project_api/retriever"
|
5
|
+
|
6
|
+
module NounProjectApi
|
7
|
+
# Retrieve an icon.
|
8
|
+
class IconRetriever < Retriever
|
9
|
+
API_PATH = "/icon/"
|
10
|
+
ITEM_CLASS = Icon
|
11
|
+
|
12
|
+
alias find_by_slug find
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "noun_project_api/retriever"
|
4
|
+
|
5
|
+
module NounProjectApi
|
6
|
+
# Retrieve icons.
|
7
|
+
class IconsRetriever < Retriever
|
8
|
+
API_PATH = "/icons/"
|
9
|
+
|
10
|
+
# Finds multiple icons based on the term
|
11
|
+
# * term - search term
|
12
|
+
# * limit - limit the amount of results
|
13
|
+
# * offset - offset the results
|
14
|
+
# * page - page number
|
15
|
+
def find(term, limit = nil, offset = nil, page = nil)
|
16
|
+
cache_key = Digest::MD5.hexdigest("#{term}+#{limit}+#{offset}+#{page}")
|
17
|
+
cache_ttl = NounProjectApi.configuration.cache_ttl
|
18
|
+
|
19
|
+
NounProjectApi.configuration.cache.fetch(cache_key, expires_in: cache_ttl) do
|
20
|
+
raise ArgumentError, "Missing search term" unless term
|
21
|
+
|
22
|
+
search = OAuth::Helper.escape(term)
|
23
|
+
search += "?limit_to_public_domain=#{NounProjectApi.configuration.public_domain ? 1 : 0}"
|
24
|
+
|
25
|
+
args = {
|
26
|
+
"limit" => limit,
|
27
|
+
"offset" => offset,
|
28
|
+
"page" => page
|
29
|
+
}.reject { |_, v| v.nil? }
|
30
|
+
args.each { |k, v| search += "&#{k}=#{v}" } unless args.empty?
|
31
|
+
|
32
|
+
result = access_token.get("#{API_BASE}#{API_PATH}#{search}")
|
33
|
+
raise ServiceError.new(result.code, result.body) unless ["200", "404"].include? result.code
|
34
|
+
|
35
|
+
if result.code == "200"
|
36
|
+
JSON.parse(result.body, symbolize_names: true)[:icons].map { |icon| Icon.new(icon) }
|
37
|
+
else
|
38
|
+
[]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# List recent uploads
|
44
|
+
# * limit - limit the amount of results
|
45
|
+
# * offset - offset the results
|
46
|
+
# * page - page number
|
47
|
+
def recent_uploads(limit = nil, offset = nil, page = nil)
|
48
|
+
args = {
|
49
|
+
"limit" => limit,
|
50
|
+
"offset" => offset,
|
51
|
+
"page" => page
|
52
|
+
}.reject { |_, v| v.nil? }
|
53
|
+
if !args.empty?
|
54
|
+
search = "?"
|
55
|
+
args.each { |k, v| search += "#{k}=#{v}&" }
|
56
|
+
else
|
57
|
+
search = ""
|
58
|
+
end
|
59
|
+
|
60
|
+
result = access_token.get("#{API_BASE}#{API_PATH}recent_uploads#{search}")
|
61
|
+
raise ServiceError.new(result.code, result.body) unless result.code == "200"
|
62
|
+
|
63
|
+
JSON.parse(result.body, symbolize_names: true)[:recent_uploads].map { |icon| Icon.new(icon) }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NounProjectApi
|
4
|
+
# Main class to hold reporting actions back to the Noun Project.
|
5
|
+
class Reporter
|
6
|
+
include Connection
|
7
|
+
|
8
|
+
API_PATH = "/notify/publish"
|
9
|
+
|
10
|
+
def report_used(ids)
|
11
|
+
ids = [ids] if ids.is_a?(String) || ids.is_a?(Integer)
|
12
|
+
raise ArgumentError, "Missing ids" if ids.nil? || ids.empty?
|
13
|
+
|
14
|
+
result = access_token.post(
|
15
|
+
"#{API_BASE}#{API_PATH}",
|
16
|
+
{ icons: ids.join(",") }.to_json,
|
17
|
+
"Accept" => "application/json", "Content-Type" => "application/json"
|
18
|
+
)
|
19
|
+
result.code == "200"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NounProjectApi
|
4
|
+
# A base class for different retriever classes.
|
5
|
+
class Retriever
|
6
|
+
include Connection
|
7
|
+
|
8
|
+
# Find an item based on it's id.
|
9
|
+
def find(id)
|
10
|
+
raise ArgumentError, "Missing id/slug" unless id
|
11
|
+
|
12
|
+
result = access_token.get("#{API_BASE}#{self.class::API_PATH}#{id}")
|
13
|
+
raise ServiceError.new(result.code, result.body) unless result.code == "200"
|
14
|
+
|
15
|
+
self.class::ITEM_CLASS.new(result.body)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
require "ostruct"
|
5
|
+
|
6
|
+
RSpec.describe NounProjectApi::BaseItem do
|
7
|
+
it "raises an error on direct initialization" do
|
8
|
+
data = JSON.parse(Fakes::Results::ICON_VALID)
|
9
|
+
expect {
|
10
|
+
NounProjectApi::BaseItem.new(JSON.dump(data["icon"]))
|
11
|
+
}.to raise_error(NotImplementedError)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
require "ostruct"
|
5
|
+
|
6
|
+
RSpec.describe NounProjectApi::CollectionRetriever do
|
7
|
+
before :each do
|
8
|
+
@collection = NounProjectApi::CollectionRetriever.new(Faker::Internet.password(min_length: 16), Faker::Internet.password(min_length: 16))
|
9
|
+
@valid_hash = JSON.parse(Fakes::Results::COLLECTION_VALID, symbolize_names: true)
|
10
|
+
@valid_response = OpenStruct.new(
|
11
|
+
body: Fakes::Results::COLLECTION_VALID,
|
12
|
+
code: "200"
|
13
|
+
)
|
14
|
+
|
15
|
+
@missing_response = OpenStruct.new(
|
16
|
+
code: "404"
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
context "id" do
|
21
|
+
it "raises an error when no id is provided" do
|
22
|
+
expect { @collection.find(nil) }.to raise_error(ArgumentError)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "returns a proper result with a correct id" do
|
26
|
+
id = 1
|
27
|
+
expect(@collection.access_token).to receive(
|
28
|
+
:get
|
29
|
+
).with(
|
30
|
+
"#{NounProjectApi::API_BASE}#{NounProjectApi::CollectionRetriever::API_PATH}#{id}"
|
31
|
+
).and_return(
|
32
|
+
@valid_response
|
33
|
+
)
|
34
|
+
|
35
|
+
result = @collection.find(id)
|
36
|
+
expect(result).to be_a(NounProjectApi::Collection)
|
37
|
+
expect(result.original_hash).to eq(@valid_hash[:collection])
|
38
|
+
end
|
39
|
+
|
40
|
+
it "raises an error with a missing id" do
|
41
|
+
id = 1
|
42
|
+
expect(@collection.access_token).to receive(
|
43
|
+
:get
|
44
|
+
).with(
|
45
|
+
"#{NounProjectApi::API_BASE}#{NounProjectApi::CollectionRetriever::API_PATH}#{id}"
|
46
|
+
).and_return(
|
47
|
+
@missing_response
|
48
|
+
)
|
49
|
+
|
50
|
+
expect { @collection.find(id) }.to raise_error(NounProjectApi::ServiceError)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "slug" do
|
55
|
+
it "raises an error when no slug is provided" do
|
56
|
+
expect { @collection.find_by_slug(nil) }.to raise_error(ArgumentError)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "returns a proper result with a correct slug" do
|
60
|
+
slug = "existing_slug"
|
61
|
+
expect(@collection.access_token).to receive(
|
62
|
+
:get
|
63
|
+
).with(
|
64
|
+
"#{NounProjectApi::API_BASE}#{NounProjectApi::CollectionRetriever::API_PATH}#{slug}"
|
65
|
+
).and_return(
|
66
|
+
@valid_response
|
67
|
+
)
|
68
|
+
|
69
|
+
result = @collection.find(slug)
|
70
|
+
expect(result).to be_a(NounProjectApi::Collection)
|
71
|
+
expect(result.original_hash).to eq(@valid_hash[:collection])
|
72
|
+
end
|
73
|
+
|
74
|
+
it "raises an error with a missing slug" do
|
75
|
+
slug = "missing_slug"
|
76
|
+
expect(@collection.access_token).to receive(
|
77
|
+
:get
|
78
|
+
).with(
|
79
|
+
"#{NounProjectApi::API_BASE}#{NounProjectApi::CollectionRetriever::API_PATH}#{slug}"
|
80
|
+
).and_return(
|
81
|
+
@missing_response
|
82
|
+
)
|
83
|
+
|
84
|
+
expect { @collection.find_by_slug(slug) }.to raise_error(NounProjectApi::ServiceError)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|