kickscraper 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 87aea1accb9c5d850a4e99e590e430eec8cce9c0
4
+ data.tar.gz: 72f2f4a49a7a6a102c0e850912e43e62549255a4
5
+ SHA512:
6
+ metadata.gz: e992d00b4be9af29e8b3ad4c836cafb30265d5a003370df363c3516e692061c4be2f93d5d8b06804019b57c7d2a6c38b19f32492a0cb04648c8a1be5005b9615
7
+ data.tar.gz: 4f50b09cd04a405e4b80e43867590e7c3add968b68bf8fca74f42106a6c02066554b2f7636dd14958f2c0fce253bde677c670b4df5895c78c006f837806545a5
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ -fd
3
+ --color
data/CHANGELOG.md ADDED
@@ -0,0 +1,74 @@
1
+ ### 0.2.0
2
+
3
+ * Changing the minor version because we've rewritten several things to accommodate Kickstarter's change to public search
4
+ * This encapsulates the changes starting at v 0.1.2
5
+
6
+ ### 0.1.4
7
+
8
+ * changes
9
+ * Using ssl (https) for all public www.kickstarter.com searches
10
+
11
+ ### 0.1.3
12
+
13
+ * changes
14
+ * Using the public url for categories instead of the api url
15
+ * Fixing project comments and updates so that they are now pulled in when a project didn't already have them
16
+ * Adding more tests to cover the new scenarios for pulling public info from Kickstarter without API credentials
17
+
18
+ ### 0.1.2
19
+
20
+ * changes
21
+ * Using the public search instead of the authenticated API for project searches
22
+ * Allowing configuration with no email/pass (which now works for some features)
23
+ * NOTE: These changes were made in response to a change in Kickstarter's API, and there are currently some broken features
24
+
25
+ ### 0.1.1
26
+
27
+ * enhancements
28
+ * Adding a created_projects method to user
29
+
30
+ * bug fixes
31
+ * Fixing the backed_projects and starred_projects methods for user
32
+ * Fixes an error when searching for projects with spaces in the name
33
+
34
+ ### 0.1.0
35
+
36
+ * enhancements
37
+ * adding support for Kickstarter categories!
38
+ * basic pagination when searching projects
39
+
40
+ * optimize
41
+ * huge refactor of tests to DRY them up
42
+
43
+ * deprecate
44
+ * client.newest_projects (replaced by client.recently_launched_projects)
45
+ * client.can_load_more_projects (replaced by client.more_projects_available?)
46
+
47
+ * bug fixes
48
+ * a little bit more error handling
49
+
50
+
51
+ ### 0.0.3
52
+
53
+ * enhancements
54
+ * added tests!
55
+ * a little bit of basic error checking
56
+
57
+ * optimize
58
+ * DRYing up the API calls
59
+
60
+ * bug fixes
61
+ * updating the search for "newest" projects, which was renamed by Kickstarter
62
+ * handling special characters in searches
63
+
64
+
65
+ ### 0.0.2
66
+
67
+ * enhancements
68
+ * Project searches (popular, recent, ending soon)
69
+ * User biography
70
+
71
+
72
+ ### 0.0.1
73
+
74
+ * Basic working API (getting projects and users)
data/CONTRIBUTORS.md ADDED
@@ -0,0 +1,14 @@
1
+ CONTRIBUTORS
2
+ ------------
3
+
4
+
5
+ #### Originally created by:
6
+
7
+ * Mark Olson - github @markolson
8
+
9
+
10
+ #### Contributions by:
11
+
12
+ * Ben Rugg - github @benrugg
13
+ * James Allen - github @jamlen
14
+ * Mark Anderson - github @NolaMark
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in kickscraper.gemspec
4
+ gemspec
5
+
6
+ group :test, :development do
7
+ gem 'guard-rspec'
8
+ gem 'terminal-notifier-guard'
9
+ end
data/Guardfile ADDED
@@ -0,0 +1,6 @@
1
+ guard :rspec do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{kickscraper/(?:client/)?(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ watch(%r{spec/support/.*\.rb$}) { "spec" }
6
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Mark Olson
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # Kickscraper
2
+
3
+ Kickscraper is a library for interfacing with Kickstarter's undocumented/unannounced API. With it, you can get a lot of data about projects, whether you've backed them or not, including reward levels, amount pledged, links to the videos, updates, if it's been successful or not, and details about the creator.
4
+
5
+ Much of the of data available is documented in the [wiki for this project](https://github.com/markolson/kickscraper/wiki/_pages).
6
+
7
+
8
+ ## Status Update
9
+
10
+ This version now uses Kickstarter's public search for all project searches. It should be a drop-in replacement for the
11
+ previous version.
12
+
13
+
14
+ ## Installation
15
+
16
+ $ gem install kickscraper
17
+
18
+ Or for use in another app, add it to your Gemfile
19
+
20
+ # use the gem
21
+ gem 'kickscraper'
22
+
23
+ # or stay up to date with the repo (which should be stable)
24
+ gem 'kickscraper', :git => 'git://github.com/markolson/kickscraper.git'
25
+
26
+
27
+ ## Quick way to get started in the console
28
+
29
+ 1. Put your real Kickstarter user credentials in spec/test_constants.rb (or leave blank, which will still allow some kickscraper features)
30
+ 2. Enter the console with "rake console"
31
+ 3. Start using kickscraper with any of the examples below (starting with "c = Kickscraper.client")
32
+
33
+
34
+ ## Sample Usage
35
+
36
+ Provided with your user credentials this will list the first 20 or so projects you've backed, along with if the project is still active and if it has met it's funding goal.
37
+
38
+ require 'kickscraper'
39
+
40
+ Kickscraper.configure do |config|
41
+ config.email = 'your-kickstarter-email-address@domain.com'
42
+ config.password = 'This is not my real password. Seriously.'
43
+ end
44
+
45
+ client = Kickscraper.client
46
+ puts " A | C |"
47
+ puts "------------------------"
48
+ client.user.backed_projects.each {|x|
49
+ print (x.active? ? ' X |' : ' |')
50
+ print (x.successful? ? ' X | ' : ' | ')
51
+ puts x.name
52
+ }
53
+
54
+
55
+ ## API Examples
56
+ c = Kickscraper.client
57
+
58
+ c.user.class
59
+ => Kickscraper::User
60
+
61
+ # List what data is available on an object
62
+ c.user.keys
63
+ => ["id", "name", "slug", "avatar", "urls", "location",
64
+ "biography", "backed_projects_count", "created_projects_count",
65
+ "unread_messages_count", "unanswered_surveys_count",
66
+ "starred_projects_count", "social", "send_newsletters",
67
+ "category_wheel", "notify_of_backings", "notify_of_updates",
68
+ "notify_of_follower", "notify_of_friend_activity",
69
+ "notify_of_comments", "notify_mobile_of_backings",
70
+ "notify_mobile_of_updates", "notify_mobile_of_follower",
71
+ "notify_mobile_of_friend_activity", "notify_mobile_of_comments",
72
+ "updated_at", "created_at"]
73
+
74
+
75
+ # In addition, User types have "backed_projects" and "starred_projects" available.
76
+ c.user.backed_projects
77
+ => [<Project: 'The Veronica Mars Movie Project'>, <Project: 'RiffTrax Wants to Riff TWILIGHT Live in Theaters Nationwide!'>, <Project: 'To Be Or Not To Be: That Is The Adventure'>, <Project: 'Planetary Annihilation - A Next Generation RTS'>, <Project: 'OUYA: A New Kind of Video Game Console'>, <Project: 'Gotham Knight Terrors: Comedic Batman Short'>, <Project: 'Internet Meme Playing Cards'>, <Project: 'GOOD JOB, BRAIN! - A Trivia & Quiz Show Podcast'>, <Project: 'Elevation Dock: The Best Dock For iPhone'>, <Project: 'Trebuchette - the snap-together, desktop trebuchet'>]
78
+
79
+ # Some values are already converted to their appropriate types. Some aren't yet.
80
+ c.user.backed_projects.first.creator.class
81
+ => Kickscraper::User
82
+
83
+ # You can chain methods together and dig into the objects.
84
+ # So get the thumbnail of the person that created the latest project we backed
85
+ c.user.backed_projects.first.creator.avatar.thumb
86
+
87
+ # You can search for projects
88
+ c.search_projects('veronica mars')
89
+ => [<Project: 'The Veronica Mars Movie Project'>, <Project: 'Prodigal Daughter - TV Show Pilot'>]
90
+
91
+ # and view their rewards or updates, in addition to any of the data found in the "keys"
92
+ vm = c.search_projects('veronica mars').first
93
+ vm.updates
94
+ => [...]
95
+ vm.successful?
96
+ => true
97
+ vm.video.high
98
+ => "https://d2pq0u4uni88oo.cloudfront.net/projects/56284/video-217182-h264_high.mp4?2013"
99
+
100
+ # When searching for projects (or using convenient methods like recently_launched_projects()),
101
+ # you can continue the search until Kickstarter returns no more results
102
+ c.recently_launched_projects
103
+ => [ _array of projects_ ]
104
+ c.load_more_projects if c.more_projects_available?
105
+ => [ _array of additional projects_ ]
106
+
107
+ # Print all the updates for all the current user's projects
108
+ c.user.backed_projects.each { |project|
109
+ puts project.name.upcase
110
+ project.updates.reverse.each { |update|
111
+ # strip the HTML out of the body, since this outputs to a terminal
112
+ puts "Update #{update.sequence}: #{update.body.gsub(/<\/?[^>]*>/, "")}\n\n"
113
+ }
114
+ puts "\n\n"
115
+ }
116
+
117
+ # Get info about Kickstarter categories or find projects by category
118
+ client.categories
119
+ => [<Category: 'Art'>, <Category: 'Conceptual Art'>, <Category: 'Crafts'>, <Category: 'Animation'>, <Category: 'Tabletop Games'>, <Category: 'Classical Music'>, <Category: 'Art Book'>, <Category: 'Hardware'>, <Category: 'Comics'>, <Category: 'Digital Art'>, <Category: 'Graphic Design'>, <Category: 'Documentary'>, <Category: 'Video Games'>, <Category: 'Country & Folk'>, <Category: 'Children's Book'>, <Category: 'Open Software'>, <Category: 'Dance'>, <Category: 'Illustration'>, <Category: 'Product Design'>, <Category: 'Narrative Film'>, <Category: 'Electronic Music'>, <Category: 'Fiction'>, <Category: 'Design'>, <Category: 'Journalism'>, <Category: 'Painting'>, <Category: 'Short Film'>, <Category: 'Hip-Hop'>, <Category: 'Fashion'>, <Category: 'Performance Art'>, <Category: 'Webseries'>, <Category: 'Indie Rock'>, <Category: 'Nonfiction'>, <Category: 'Film & Video'>, <Category: 'Jazz'>, <Category: 'Periodical'>, <Category: 'Food'>, <Category: 'Pop'>, <Category: 'Poetry'>, <Category: 'Mixed Media'>, <Category: 'Games'>, <Category: 'Rock'>, <Category: 'Public Art'>, <Category: 'Radio & Podcast'>, <Category: 'Music'>, <Category: 'Sculpture'>, <Category: 'World Music'>, <Category: 'Photography'>, <Category: 'Publishing'>, <Category: 'Technology'>, <Category: 'Theater'>]
120
+
121
+ category = client.category('product design')
122
+ => <Category: 'Product Design'>
123
+
124
+ category.keys
125
+ => ["id", "name", "position", "parent_id", "parent", "projects_count", "urls"]
126
+
127
+ category.projects_count
128
+ => 234
129
+
130
+ ps = category.projects
131
+ => [<Project: 'The Skinny Mirror - The Mirror That Compliments You'>, <Project: 'Rustic Fort'>, <Project: 'pushXpro: Revolutionizing the push-up exercise'>, <Project: 'Introducing Nubrella, the world's first hands-free umbrella.'>, <Project: 'Stream: The Pebble Smartwatch Dock'>, <Project: 'Get iPatched - Protect Yourself From Big Brother & Hackers'>, <Project: 'TrueRec introduces the DFP Kayak - Dive Fish Paddle'>, <Project: 'MagCozy: A Leash for Your MagSafe 2 Adapter'>, <Project: 'ECO Friendly Bamboo Party Trays'>, <Project: 'PJ reefs Miniature Saltwater Aquarium'>]
132
+
133
+
134
+ ## Contributing
135
+
136
+ There are two good ways to contribute:
137
+
138
+ First, by using it and creating Issues as you find problems or rough spots. Second, by taking matters into your own hands:
139
+
140
+ 1. Fork it
141
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
142
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
143
+ 4. Push to the branch (`git push origin my-new-feature`)
144
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :console do
4
+ require 'irb'
5
+ require 'irb/completion'
6
+ require 'kickscraper'
7
+
8
+ require './spec/test_constants'
9
+
10
+ Kickscraper.configure do |config|
11
+ config.email = KICKSCRAPER_TEST_API_EMAIL
12
+ config.password = KICKSCRAPER_TEST_API_PASSWORD
13
+ config.proxy = LOCAL_PROXY if defined? LOCAL_PROXY
14
+ end
15
+
16
+ ARGV.clear
17
+ IRB.start
18
+ end
@@ -0,0 +1,18 @@
1
+ require 'kickscraper'
2
+
3
+ p "configuring.."
4
+ Kickscraper.configure do |config|
5
+ config.email = 'your-kickstarter-email-address@domain.com'
6
+ config.password = 'This is not my real password. Seriously.'
7
+ end
8
+
9
+ p "logging in.."
10
+ c = Kickscraper.client
11
+ p "got access token #{Kickscraper.token.gsub(/\w{30}$/, "X" * 30)}"
12
+ puts " Active | Met Goal |"
13
+ puts "--------------------"
14
+ c.user.backed_projects.each {|x|
15
+ print (x.active? ? ' X |' : ' |')
16
+ print (x.successful? ? ' X | ' : ' | ')
17
+ puts x.name
18
+ }
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'kickscraper/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "kickscraper"
8
+ spec.version = Kickscraper::VERSION
9
+ spec.authors = ["Mark Olson", "Ben Rugg", "James Allen", "Mark Anderson"]
10
+ spec.email = ["theothermarkolson@gmail.com"]
11
+ spec.description = %q{Interact with Kickstarter through their API}
12
+ spec.summary = %q{API library for Kickstarter}
13
+ spec.homepage = "https://github.com/markolson/kickscraper"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec-core"
24
+
25
+ spec.add_runtime_dependency('faraday', ['>= 0.7', '< 0.9'])
26
+ spec.add_runtime_dependency('faraday_middleware', '~> 0.8')
27
+ spec.add_runtime_dependency('multi_json', '>= 1.0.3', '~> 1.0')
28
+ spec.add_runtime_dependency('hashie', '~> 2')
29
+ spec.add_runtime_dependency('uri-query_params')
30
+
31
+ end
@@ -0,0 +1,19 @@
1
+ require 'hashie'
2
+ require 'json'
3
+
4
+ require "kickscraper/version"
5
+ require "kickscraper/configure"
6
+ require "kickscraper/response"
7
+ require "kickscraper/connection"
8
+ require "kickscraper/client"
9
+ require "kickscraper/api"
10
+
11
+
12
+ module Kickscraper
13
+ extend Configure
14
+ attr_accessor :client
15
+
16
+ def self.client
17
+ @client ||= Kickscraper::Client.new
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ module Kickscraper
2
+ class Api
3
+ extend Connection
4
+ include Hashie::Extensions::Coercion
5
+ attr_accessor :raw
6
+
7
+ def initialize(blob)
8
+ @raw = blob
9
+ end
10
+
11
+ def method_missing(name)
12
+ @raw.send(name) if @raw.respond_to? name
13
+ end
14
+
15
+ def self.coerce(raw)
16
+ a = self.new(raw)
17
+ self::do_coercion(a)
18
+ a
19
+ end
20
+
21
+ def self.do_coercion(instance)
22
+ self.key_coercions.each{ |k,v| instance.raw[k] = v.coerce(instance.raw[k]) }
23
+ end
24
+
25
+ def uid
26
+ self.id == Kickscraper.client.user.id ? 'self' : self.id
27
+ end
28
+
29
+ end
30
+ end
31
+
32
+ Dir[File.expand_path('../client/*.rb', __FILE__)].each{|f| load f}
@@ -0,0 +1,225 @@
1
+ require 'uri'
2
+
3
+ module Kickscraper
4
+ class Client
5
+ include Connection
6
+
7
+ attr_accessor :user
8
+
9
+ def initialize
10
+ @more_projects_available = false
11
+
12
+ if Kickscraper.email.nil?
13
+ @user = nil
14
+ else
15
+ if Kickscraper.token.nil?
16
+ token_response = connection.post('xauth/access_token?client_id=2II5GGBZLOOZAA5XBU1U0Y44BU57Q58L8KOGM7H0E0YFHP3KTG', {'email' => Kickscraper.email, 'password' => Kickscraper.password }.to_json)
17
+ if token_response.body.error_messages
18
+ raise token_response.body.error_messages.join("\n")
19
+ return
20
+ end
21
+ Kickscraper.token = token_response.body.access_token
22
+ @user = User.coerce(token_response.body.user)
23
+ end
24
+ end
25
+ end
26
+
27
+ def find_user(id)
28
+ self::process_api_call "user", id.to_s
29
+ end
30
+
31
+ def find_project(id_or_slug)
32
+ self::process_api_call "project", id_or_slug.to_s
33
+ end
34
+
35
+ def search_projects(search_terms, page = nil)
36
+ self::process_api_call "projects", "advanced", search_terms, page
37
+ end
38
+
39
+ def ending_soon_projects(page = nil)
40
+ self::process_api_call "projects", "ending-soon", "", page
41
+ end
42
+
43
+ def popular_projects(page = nil)
44
+ self::process_api_call "projects", "popular", "", page
45
+ end
46
+
47
+ def recently_launched_projects(page = nil)
48
+ self::process_api_call "projects", "recently-launched", "", page
49
+ end
50
+
51
+ alias_method :newest_projects, :recently_launched_projects
52
+
53
+ def more_projects_available?
54
+ @more_projects_available
55
+ end
56
+
57
+ alias_method :can_load_more_projects, :more_projects_available?
58
+
59
+ def load_more_projects
60
+ if self::more_projects_available?
61
+ self::process_api_call @last_api_call_params[:request_for], @last_api_call_params[:additional_path], @last_api_call_params[:search_terms], (@last_api_call_params[:page] + 1)
62
+ else
63
+ []
64
+ end
65
+ end
66
+
67
+ def categories
68
+ self::process_api_call "categories", ""
69
+ end
70
+
71
+ def category(id_or_name = nil)
72
+ return categories.find{|i| i.name.downcase.start_with? id_or_name.downcase} if id_or_name.is_a? String
73
+ self::process_api_call "category", id_or_name.to_s
74
+ end
75
+
76
+
77
+ def process_api_call(request_for, additional_path, search_terms = "", page = nil)
78
+
79
+ # save the parameters for this call, so we can repeat it to get the next page of results
80
+ @last_api_call_params = {
81
+ request_for: request_for,
82
+ additional_path: additional_path,
83
+ search_terms: search_terms,
84
+ page: page.nil? ? 1 : page
85
+ }
86
+
87
+ # make the api call (to the API resource we want)
88
+ response = self::make_api_call(request_for, additional_path, search_terms, page)
89
+
90
+ # handle the response, returning an object with the results
91
+ self::coerce_api_response(request_for, response)
92
+ end
93
+
94
+
95
+ def process_api_url(request_for, api_url, coerce_response = true)
96
+
97
+ # make the api call to whatever url we specified
98
+ response = connection.get(api_url)
99
+
100
+
101
+ # if we want to coerce the response, do it now
102
+ if coerce_response
103
+
104
+ self::coerce_api_response(request_for, response)
105
+
106
+ # else, just return the raw body
107
+ else
108
+
109
+ response.body
110
+ end
111
+ end
112
+
113
+
114
+ def coerce_api_response(expected_type, response)
115
+
116
+ # define what we should return as an empty response, based on the expected type
117
+ types_that_should_return_an_array = ["projects", "comments", "updates", "categories"]
118
+ empty_response = (types_that_should_return_an_array.include? expected_type) ? [] : nil
119
+
120
+
121
+ # get the body from the response
122
+ body = response.body
123
+
124
+
125
+ # if we got an error response back, stop here and return an empty response
126
+ return empty_response if response.headers['status'].to_i >= 400 || !response.headers['content-type'].start_with?('application/json')
127
+ return empty_response if (body.respond_to?("error_messages") && !body.error_messages.empty?) || (body.respond_to?("http_code") && body.http_code == 404)
128
+
129
+
130
+ # otherwise, take the response from the api and coerce it to the type we want
131
+ case expected_type.downcase
132
+ when "user"
133
+
134
+ User.coerce body
135
+
136
+ when "project"
137
+
138
+ Project.coerce body
139
+
140
+ when "projects"
141
+
142
+ # if the body is just an array of projects, with no root keys, then coerce
143
+ # the array
144
+ if body.is_a?(Array)
145
+
146
+ @more_projects_available = false
147
+ body.map { |project| Project.coerce project }
148
+
149
+
150
+ # else, if the body doesn't have any projects, return an empty array
151
+ elsif body.projects.nil?
152
+
153
+ @more_projects_available = false
154
+ return empty_response
155
+
156
+
157
+ # else, determine if we can load more projects and then return an array of projects
158
+ else
159
+
160
+ if @last_api_call_params && !body.total_hits.nil?
161
+ @more_projects_available = @last_api_call_params[:page] * 20 < body.total_hits # (there is a huge assumption here that Kickstarter will always return 20 projects per page!)
162
+ end
163
+
164
+ return body.projects.map { |project| Project.coerce project }
165
+ end
166
+
167
+ when "comments"
168
+
169
+ return empty_response if body.comments.nil?
170
+ body.comments.map { |comment| Comment.coerce comment }
171
+
172
+ when "updates"
173
+
174
+ return empty_response if body.updates.nil?
175
+ body.updates.map { |update| Update.coerce update }
176
+
177
+ when "categories"
178
+
179
+ return empty_response if body.categories.nil?
180
+ body.categories.map { |category| Category.coerce category }
181
+
182
+ when "category"
183
+
184
+ Category.coerce body
185
+
186
+ else
187
+ raise ArgumentError, "invalid api request type"
188
+ end
189
+ end
190
+
191
+
192
+ def make_api_call(request_for, additional_path = "", search_terms = "", page = nil)
193
+
194
+ # set the url/path differently for each type of request
195
+ case request_for.downcase
196
+ when "user"
197
+ api_or_search = "api"
198
+ path = "/v1/users"
199
+ when "project"
200
+ api_or_search = "api"
201
+ path = "/v1/projects"
202
+ when "projects"
203
+ api_or_search = "search"
204
+ path = "/discover"
205
+ when "category", "categories"
206
+ api_or_search = "api"
207
+ path = "/v1/categories"
208
+ end
209
+
210
+
211
+ # add the additional path if we have it
212
+ path += "/" + CGI.escape(additional_path) unless additional_path.empty?
213
+
214
+
215
+ # create the params hash and add the params we want
216
+ params = {}
217
+ params[:term] = search_terms unless search_terms.empty?
218
+ params[:page] = page unless page.nil?
219
+
220
+
221
+ # make the connection and return the response
222
+ connection(api_or_search).get(path, params)
223
+ end
224
+ end
225
+ end