kickscraper 0.2.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 +7 -0
- data/.gitignore +18 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +74 -0
- data/CONTRIBUTORS.md +14 -0
- data/Gemfile +9 -0
- data/Guardfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +144 -0
- data/Rakefile +18 -0
- data/examples/my_projects.rb +18 -0
- data/kickscraper.gemspec +31 -0
- data/lib/kickscraper.rb +19 -0
- data/lib/kickscraper/api.rb +32 -0
- data/lib/kickscraper/client.rb +225 -0
- data/lib/kickscraper/client/category.rb +18 -0
- data/lib/kickscraper/client/comment.rb +5 -0
- data/lib/kickscraper/client/location.rb +0 -0
- data/lib/kickscraper/client/notifications.rb +0 -0
- data/lib/kickscraper/client/project.rb +71 -0
- data/lib/kickscraper/client/update.rb +5 -0
- data/lib/kickscraper/client/user.rb +34 -0
- data/lib/kickscraper/configure.rb +11 -0
- data/lib/kickscraper/connection.rb +63 -0
- data/lib/kickscraper/response.rb +15 -0
- data/lib/kickscraper/version.rb +3 -0
- data/spec/category_spec.rb +26 -0
- data/spec/client_spec.rb +84 -0
- data/spec/kickscraper_spec.rb +16 -0
- data/spec/no_email_password_client_spec.rb +73 -0
- data/spec/no_email_password_project_spec.rb +76 -0
- data/spec/project_spec.rb +57 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support/shared_examples.rb +103 -0
- data/spec/test_constants.rb +18 -0
- data/spec/user_spec.rb +33 -0
- metadata +216 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module Kickscraper
|
2
|
+
class Category < Api
|
3
|
+
attr_accessor :projects
|
4
|
+
|
5
|
+
def to_s
|
6
|
+
name
|
7
|
+
end
|
8
|
+
|
9
|
+
def inspect
|
10
|
+
"<Category: '#{to_s}'>"
|
11
|
+
end
|
12
|
+
|
13
|
+
def projects
|
14
|
+
return [] unless @projects || self.urls.web.discover
|
15
|
+
@projects ||= Kickscraper.client.process_api_url("Projects", self.urls.web.discover)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
File without changes
|
File without changes
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require_relative 'user.rb'
|
2
|
+
require_relative 'category.rb'
|
3
|
+
|
4
|
+
module Kickscraper
|
5
|
+
class Project < Api
|
6
|
+
coerce_key :creator, Kickscraper::User
|
7
|
+
coerce_key :category, Kickscraper::Category
|
8
|
+
|
9
|
+
attr_accessor :comments, :updates, :rewards
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
name
|
13
|
+
end
|
14
|
+
|
15
|
+
def inspect
|
16
|
+
"<Project: '#{to_s}'>"
|
17
|
+
end
|
18
|
+
|
19
|
+
def reload!
|
20
|
+
if self.urls.api.nil?
|
21
|
+
the_full_project = Kickscraper.client.find_project(self.id)
|
22
|
+
project_api_url = the_full_project.nil? ? nil : the_full_project.urls.api.project
|
23
|
+
else
|
24
|
+
project_api_url = self.urls.api.project
|
25
|
+
end
|
26
|
+
@raw = Kickscraper.client.process_api_url("Project", project_api_url, false) unless project_api_url.nil?
|
27
|
+
Kickscraper::Project::do_coercion(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def successful?
|
31
|
+
pledged >= goal
|
32
|
+
end
|
33
|
+
|
34
|
+
def active?
|
35
|
+
state == "live"
|
36
|
+
end
|
37
|
+
|
38
|
+
def comments
|
39
|
+
return @comments if @comments
|
40
|
+
|
41
|
+
# must reload to get urls.api, not returned in public discover search
|
42
|
+
reload! unless (self.urls.api && self.urls.api.comments)
|
43
|
+
|
44
|
+
# if logged in and can use private API, self.urls.api.updates should now be defined
|
45
|
+
if (self.urls.api && self.urls.api.updates)
|
46
|
+
@comments = Kickscraper.client.process_api_url("Comments", self.urls.api.comments)
|
47
|
+
else
|
48
|
+
@comments= []
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def updates
|
53
|
+
return @updates if @updates
|
54
|
+
|
55
|
+
# must reload to get urls.api, not returned in public discover search
|
56
|
+
reload! unless (self.urls.api && self.urls.api.updates)
|
57
|
+
|
58
|
+
# if logged in and can use private API, self.urls.api.updates should now be defined
|
59
|
+
if (self.urls.api && self.urls.api.updates)
|
60
|
+
@updates = Kickscraper.client.process_api_url("Updates", self.urls.api.updates)
|
61
|
+
else
|
62
|
+
@updates = []
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def rewards
|
67
|
+
reload! unless @raw['rewards']
|
68
|
+
@raw['rewards']
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Kickscraper
|
2
|
+
class User < Api
|
3
|
+
attr_accessor :backed_projects, :starred_projects
|
4
|
+
|
5
|
+
def to_s
|
6
|
+
name
|
7
|
+
end
|
8
|
+
|
9
|
+
def reload!
|
10
|
+
@raw = Kickscraper.client.process_api_url("User", self.urls.api.user, false)
|
11
|
+
Kickscraper::User::do_coercion(self)
|
12
|
+
end
|
13
|
+
|
14
|
+
def biography
|
15
|
+
reload! unless @raw['biography']
|
16
|
+
@raw['biography']
|
17
|
+
end
|
18
|
+
|
19
|
+
def backed_projects
|
20
|
+
return [] unless self.urls.api.backed_projects
|
21
|
+
@backed_projects ||= Kickscraper.client.process_api_url("Projects", self.urls.api.backed_projects)
|
22
|
+
end
|
23
|
+
|
24
|
+
def starred_projects
|
25
|
+
return [] unless self.urls.api.starred_projects
|
26
|
+
@starred_projects ||= Kickscraper.client.process_api_url("Projects", self.urls.api.starred_projects)
|
27
|
+
end
|
28
|
+
|
29
|
+
def created_projects
|
30
|
+
return [] unless self.urls.api.created_projects
|
31
|
+
@created_projects ||= Kickscraper.client.process_api_url("Projects", self.urls.api.created_projects)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
require 'uri/query_params'
|
4
|
+
|
5
|
+
class KSToken < Faraday::Middleware
|
6
|
+
def initialize(app)
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
# replace '+' symbols in the query params with their original spaces, because there
|
12
|
+
# seems to be a bug in the way some versions of Fararay escape parameters with spaces
|
13
|
+
env[:url].query_params.each { |key, value|
|
14
|
+
env[:url].query_params[key] = value.tr('+', ' ')
|
15
|
+
}
|
16
|
+
|
17
|
+
# add format=json to all public search requests, or add the oauth_token to all api requests once we have it
|
18
|
+
if env[:url].to_s.index('https://api.kickstarter.com').nil?
|
19
|
+
env[:url].query_params['format'] = 'json'
|
20
|
+
else
|
21
|
+
env[:url].query_params['oauth_token'] = Kickscraper.token unless Kickscraper.token.nil?
|
22
|
+
end
|
23
|
+
|
24
|
+
# make the call
|
25
|
+
@app.call(env)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module Kickscraper
|
30
|
+
module Connection
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def connection(api_or_search = "api")
|
35
|
+
options = {
|
36
|
+
:headers => {'Accept' => "application/json; charset=utf-8", 'User-Agent' => "Kickscraper/XXX"},
|
37
|
+
:ssl => {:verify => false},
|
38
|
+
:url => api_or_search == "api" ? "https://api.kickstarter.com" : "https://www.kickstarter.com",
|
39
|
+
:proxy => Kickscraper.proxy.nil? ? "" : Kickscraper.proxy
|
40
|
+
}
|
41
|
+
|
42
|
+
if api_or_search == "api"
|
43
|
+
@api_connection ||= Faraday::Connection.new(options) do |connection|
|
44
|
+
connection.use Faraday::Request::UrlEncoded
|
45
|
+
connection.use FaradayMiddleware::Mashify
|
46
|
+
connection.use FaradayMiddleware::FollowRedirects
|
47
|
+
connection.use Faraday::Response::ParseJson
|
48
|
+
connection.use ::KSToken
|
49
|
+
connection.adapter(Faraday.default_adapter)
|
50
|
+
end
|
51
|
+
else
|
52
|
+
@search_connection ||= Faraday::Connection.new(options) do |connection|
|
53
|
+
connection.use Faraday::Request::UrlEncoded
|
54
|
+
connection.use FaradayMiddleware::Mashify
|
55
|
+
connection.use FaradayMiddleware::FollowRedirects
|
56
|
+
connection.use Faraday::Response::ParseJson
|
57
|
+
connection.use ::KSToken
|
58
|
+
connection.adapter(Faraday.default_adapter)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
describe Kickscraper::Category do
|
2
|
+
let(:client) { Kickscraper.client }
|
3
|
+
let(:category) { client.categories.first }
|
4
|
+
|
5
|
+
context "loading category info" do
|
6
|
+
subject { category }
|
7
|
+
|
8
|
+
it { should be_a Kickscraper::Category }
|
9
|
+
its(:name) { should_not be_empty }
|
10
|
+
its(:projects_count) { should be > 0 }
|
11
|
+
end
|
12
|
+
|
13
|
+
context "urls" do
|
14
|
+
subject { category.urls }
|
15
|
+
|
16
|
+
it { should_not be_empty }
|
17
|
+
its(:web) { should_not be_empty }
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
context "projects" do
|
22
|
+
subject { category.projects }
|
23
|
+
it_returns "a collection", Kickscraper::Project
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
describe Kickscraper::Client do
|
2
|
+
let(:client) { Kickscraper.client }
|
3
|
+
|
4
|
+
context "finds a user by id" do
|
5
|
+
subject(:user) { client.find_user TEST_USER_ID }
|
6
|
+
|
7
|
+
its(:id) { should == TEST_USER_ID }
|
8
|
+
its(:name) { should == 'Zach Braff' }
|
9
|
+
end
|
10
|
+
|
11
|
+
context "returns nil when finding a non-existent user" do
|
12
|
+
subject { client.find_user 9999 }
|
13
|
+
|
14
|
+
it { should be_nil }
|
15
|
+
end
|
16
|
+
|
17
|
+
context "finds a project by id" do
|
18
|
+
subject(:project) { client.find_project TEST_PROJECT_ID }
|
19
|
+
|
20
|
+
its(:id) { should == TEST_PROJECT_ID }
|
21
|
+
its(:slug) { should == TEST_PROJECT_SLUG }
|
22
|
+
its(:name) { should == TEST_PROJECT_NAME }
|
23
|
+
end
|
24
|
+
|
25
|
+
context "finds a project by slug" do
|
26
|
+
subject(:project) { client.find_project TEST_PROJECT_SLUG }
|
27
|
+
|
28
|
+
its(:id) { should == TEST_PROJECT_ID }
|
29
|
+
its(:slug) { should == TEST_PROJECT_SLUG }
|
30
|
+
its(:name) { should == TEST_PROJECT_NAME }
|
31
|
+
end
|
32
|
+
|
33
|
+
context "returns nil when finding a non-existent project" do
|
34
|
+
subject { client.find_project 9999 }
|
35
|
+
it { should be_nil }
|
36
|
+
end
|
37
|
+
|
38
|
+
describe ".ending_soon_projects" do
|
39
|
+
it_behaves_like "ending_soon projects"
|
40
|
+
end
|
41
|
+
|
42
|
+
describe ".recently_launched_project" do
|
43
|
+
it_behaves_like "recently_launched projects"
|
44
|
+
end
|
45
|
+
|
46
|
+
# findes recently launched projects with the 'newest_projects' method for backwards compatibility
|
47
|
+
describe ".newest_projects" do
|
48
|
+
it_behaves_like "newest_projects projects"
|
49
|
+
end
|
50
|
+
|
51
|
+
describe ".popular_projects" do
|
52
|
+
it_behaves_like "popular projects"
|
53
|
+
end
|
54
|
+
|
55
|
+
describe ".search_project" do
|
56
|
+
it_behaves_like "search projects"
|
57
|
+
end
|
58
|
+
|
59
|
+
# context "loads recently launched projects starting at a specific timestamp" do
|
60
|
+
# subject { client.recently_launched_projects((Time.now - (2 * 24 * 60 * 60)).to_i) }
|
61
|
+
# it_returns "a collection", Kickscraper::Project
|
62
|
+
# end
|
63
|
+
|
64
|
+
# context "loads projects ending soon starting at a specific deadline" do
|
65
|
+
# subject { client.ending_soon_projects((Time.now + (2 * 24 * 60 * 60)).to_i) }
|
66
|
+
# it_returns "a collection", Kickscraper::Project
|
67
|
+
# end
|
68
|
+
|
69
|
+
context "lists all categories" do
|
70
|
+
subject { client.categories }
|
71
|
+
it_returns "a collection", Kickscraper::Category
|
72
|
+
end
|
73
|
+
|
74
|
+
context "loads a category from string" do
|
75
|
+
subject { client.category(TEST_CATEGORY_NAME) }
|
76
|
+
its(:name) { should eq TEST_CATEGORY_NAME }
|
77
|
+
end
|
78
|
+
|
79
|
+
context "loads a category from an id" do
|
80
|
+
subject { client.category(TEST_CATEGORY_ID) }
|
81
|
+
its(:name) { should eq TEST_CATEGORY_NAME }
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
describe Kickscraper do
|
2
|
+
subject { Kickscraper.client }
|
3
|
+
|
4
|
+
it "accepts configuration" do
|
5
|
+
Kickscraper.email.should == KICKSCRAPER_TEST_API_EMAIL
|
6
|
+
end
|
7
|
+
|
8
|
+
context "connects to the kickstarter api" do
|
9
|
+
it { should_not be_nil }
|
10
|
+
it { should be_a Kickscraper::Client }
|
11
|
+
|
12
|
+
its(:user) { should_not be_nil }
|
13
|
+
its(:user) { should be_a Kickscraper::User }
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
describe Kickscraper::Client do
|
2
|
+
context "no email no password client" do
|
3
|
+
before(:all) do
|
4
|
+
@save_token = Kickscraper.token
|
5
|
+
Kickscraper.token=nil
|
6
|
+
end
|
7
|
+
|
8
|
+
after (:all) do
|
9
|
+
Kickscraper.token=@save_token
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:client) do
|
13
|
+
Kickscraper.configure do |config|
|
14
|
+
config.email = nil
|
15
|
+
config.password = nil
|
16
|
+
end
|
17
|
+
Kickscraper::Client.new
|
18
|
+
end
|
19
|
+
|
20
|
+
describe ".find_user" do
|
21
|
+
subject(:user) { client.find_user TEST_USER_ID }
|
22
|
+
|
23
|
+
it { should be_nil } # don't know how to do on purely Public API
|
24
|
+
end
|
25
|
+
|
26
|
+
describe ".find_product" do
|
27
|
+
subject(:project) { client.find_project TEST_PROJECT_ID }
|
28
|
+
|
29
|
+
it { should be_nil } # don't know how to do on purely Public API
|
30
|
+
end
|
31
|
+
|
32
|
+
describe ".categories" do
|
33
|
+
subject { client.categories }
|
34
|
+
|
35
|
+
it { should eq [] } # don't know how to do on purely Public API
|
36
|
+
end
|
37
|
+
|
38
|
+
describe ".category" do
|
39
|
+
context "loads a category from string" do
|
40
|
+
subject { client.category(TEST_CATEGORY_NAME) }
|
41
|
+
|
42
|
+
it { should be_nil } # don't know how to do on purely Public API
|
43
|
+
end
|
44
|
+
|
45
|
+
context "loads a category from an id" do
|
46
|
+
subject { client.category(TEST_CATEGORY_ID) }
|
47
|
+
|
48
|
+
it { should be_nil } # don't know how to do on purely Public API
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe ".ending_soon_projects" do
|
53
|
+
it_behaves_like "ending_soon projects"
|
54
|
+
end
|
55
|
+
|
56
|
+
describe ".recently_launched_project" do
|
57
|
+
it_behaves_like "recently_launched projects"
|
58
|
+
end
|
59
|
+
|
60
|
+
# findes recently launched projects with the 'newest_projects' method for backwards compatibility
|
61
|
+
describe ".newest_projects" do
|
62
|
+
it_behaves_like "newest_projects projects"
|
63
|
+
end
|
64
|
+
|
65
|
+
describe ".popular_projects" do
|
66
|
+
it_behaves_like "popular projects"
|
67
|
+
end
|
68
|
+
|
69
|
+
describe ".search_project" do
|
70
|
+
it_behaves_like "search projects"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|