kickscraper-snow 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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 +228 -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 +119 -0
- data/spec/test_constants.rb +18 -0
- data/spec/user_spec.rb +33 -0
- metadata +217 -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 => {'Content-Type' => 'application/json', '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
|