Skip to content

Commit

Permalink
Initial commit with basic API work from last night and this evening.
Browse files Browse the repository at this point in the history
  • Loading branch information
markolson committed Mar 15, 2013
0 parents commit 2a7a955
Show file tree
Hide file tree
Showing 21 changed files with 439 additions and 0 deletions.
17 changes: 17 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
*.gem
*.rbc
.bundle
.config
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
4 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
source 'https://rubygems.org'

# Specify your gem's dependencies in kickscraper.gemspec
gemspec
22 changes: 22 additions & 0 deletions LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Copyright (c) 2013 Mark Olson

MIT License

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
109 changes: 109 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Kickscraper

Kickscraper is a library for interfacing with Kickstarter's undocumented/unannounced API.

## Installation

$ gem install kickscraper

Or for use in another app, add it to your Gemfile

gem 'kickscraper', :git => 'git://github.com/markolson/kickscraper.git'

## Status

After several hours of quick hacking and copy-pasting, Kickscraper is able to authenticate against the Kickstarter API, store the returned oauth token and user object, fetch the user's backed projects, and view updates for those projects.

Left to do:

* Gettings/caching for different endpoints
* Error handling
* Pagination support
* Refactor to use object.urls.api values to construct gettings dynamically?
* Testing!

## Sample Usage

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.

require 'kickscraper'

Kickscraper.configure do |config|
config.email = 'your-kickstarter-email-address@domain.com'
config.password = 'This is not my real password. Seriously.'
end

client = Kickscraper.client
puts " A | C |"
puts "------------------------"
client.user.backed_projects.each {|x|
print (x.active? ? ' X |' : ' |')
print (x.successful? ? ' X | ' : ' | ')
puts x.name
}

## API Examples
c = Kickscraper.client

c.user.class
=> Kickscraper::User

# List what data is available on an object
c.user.keys
=> ["id", "name", "slug", "avatar", "urls", "location",
"biography", "backed_projects_count", "created_projects_count",
"unread_messages_count", "unanswered_surveys_count",
"starred_projects_count", "social", "send_newsletters",
"category_wheel", "notify_of_backings", "notify_of_updates",
"notify_of_follower", "notify_of_friend_activity",
"notify_of_comments", "notify_mobile_of_backings",
"notify_mobile_of_updates", "notify_mobile_of_follower",
"notify_mobile_of_friend_activity", "notify_mobile_of_comments",
"updated_at", "created_at"]


# In addition, User types have "backed_projects" and "starred_projects" available.
c.user.backed_projects
=> [<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'>]

# Some values are already converted to their appropriate types. Some aren't yet.
c.user.backed_projects.first.creator.class
=> Kickscraper::User

# get the thumbnail of the person that created the latest project we backed
c.user.backed_projects.first.creator.avatar.thumb

# You can search for projects
c.search_projects('veronica mars')
=> [<Project: 'The Veronica Mars Movie Project'>, <Project: 'Prodigal Daughter - TV Show Pilot'>]

# and view their rewards or updates, in addition to any of the data found in the "keys"
vm = c.search_projects('veronica mars').first
vm.updates
=> [...]
vm.successful?
=> true
vm.video.high
=> "https://d2pq0u4uni88oo.cloudfront.net/projects/56284/video-217182-h264_high.mp4?2013"

# print all the updates for all the current user's projects
c.user.backed_projects.each { |project|
puts project.name.upcase
project.updates.reverse.each { |update|
# strip the HTML out of the body, since this outputs to a terminal
puts "Update #{update.sequence}: #{update.body.gsub(/<\/?[^>]*>/, "")}\n\n"
}
puts "\n\n"
}

## Contributing

There are two good way to contributes:

First, by using it and creating Issues (or pinging me on twitter @mark_olson) as you find problems or rough spots. Second, by taking matters into your own hands:

1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request
1 change: 1 addition & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require "bundler/gem_tasks"
18 changes: 18 additions & 0 deletions examples/my_projects.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require 'kickscraper'

p "configuring.."
Kickscraper.configure do |config|
config.email = 'your-kickstarter-email-address@domain.com'
config.password = 'This is not my real password. Seriously.'
end

p "logging in.."
c = Kickscraper.client
p "got access token #{Kickscraper.token.gsub(/\w{30}$/, "X" * 30)}"
puts " A | C |"
puts "------------------------"
c.user.backed_projects.each {|x|
print (x.active? ? ' X |' : ' |')
print (x.successful? ? ' X | ' : ' | ')
puts x.name
}
30 changes: 30 additions & 0 deletions kickscraper.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'kickscraper/version'

Gem::Specification.new do |spec|
spec.name = "kickscraper"
spec.version = Kickscraper::VERSION
spec.authors = ["Mark Olson"]
spec.email = ["theothermarkolson@gmail.com"]
spec.description = %q{Interact with Kickstarter through their API}
spec.summary = %q{API library for Kickstarter}
spec.homepage = "https://github.com/markolson/kickscraper"
spec.license = "MIT"

spec.files = `git ls-files`.split($/)
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]

spec.add_development_dependency "bundler", "~> 1.3"
spec.add_development_dependency "rake"

spec.add_runtime_dependency('faraday', ['>= 0.7', '< 0.9'])
spec.add_runtime_dependency('faraday_middleware', '~> 0.8')
spec.add_runtime_dependency('multi_json', '>= 1.0.3', '~> 1.0')
spec.add_runtime_dependency('hashie', '~> 2')
spec.add_runtime_dependency('uri-query_params')

end
19 changes: 19 additions & 0 deletions lib/kickscraper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require 'hashie'
require 'json'

require "kickscraper/version"
require "kickscraper/configure"
require "kickscraper/response"
require "kickscraper/connection"
require "kickscraper/client"
require "kickscraper/api"


module Kickscraper
extend Configure
attr_accessor :client

def self.client
@client ||= Kickscraper::Client.new
end
end
30 changes: 30 additions & 0 deletions lib/kickscraper/api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module Kickscraper
class Api
extend Connection
include Hashie::Extensions::Coercion
attr_accessor :raw

def initialize(blob)
@raw = blob
end

def method_missing(name)
@raw.send(name) if @raw.respond_to? name
end

def self.coerce(raw)
a = self.new(raw)
self.key_coercions.each{|k,v|
a.raw[k] = v.coerce(a.raw[k])
}
a
end

def uid
self.id == Kickscraper.client.user.id ? 'self' : self.id
end

end
end

Dir[File.expand_path('../client/*.rb', __FILE__)].each{|f| load f}
36 changes: 36 additions & 0 deletions lib/kickscraper/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
module Kickscraper
class Client
include Connection
attr_accessor :user
def initialize
if Kickscraper.token.nil?
token_response = connection.post('xauth/access_token?client_id=2II5GGBZLOOZAA5XBU1U0Y44BU57Q58L8KOGM7H0E0YFHP3KTG', {'email' => Kickscraper.email, 'password' => Kickscraper.password }.to_json)
if token_response.body.error_messages
raise token_response.body.error_messages.join("\n")
return
end
Kickscraper.token = token_response.body.access_token
@user = User.coerce(token_response.body.user)
end
end

def find_user(id)
User.coerce(connection.get("/v1/users/#{id}").body)
end

def find_project(id)
Project.coerce(connection.get("/v1/projects/#{id}").body)
end

def search_projects(q)
connection.get("/v1/projects/search?q=#{q}").body.projects.map { |project|
Project.coerce(project)
}
end

def raw
connection
end

end
end
7 changes: 7 additions & 0 deletions lib/kickscraper/client/category.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Kickscraper
class Category < Api
def say
p "hello"
end
end
end
Empty file.
Empty file.
Empty file.
47 changes: 47 additions & 0 deletions lib/kickscraper/client/project.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
require_relative 'user.rb'
require_relative 'update.rb'

module Kickscraper
class Project < Api
coerce_key :creator, Kickscraper::User
coerce_key :category, Kickscraper::Category

attr_accessor :rewards, :updates, :comments, :full

def to_s
name
end

def inspect
"<Project: '#{to_s}'>"
end

def reload!
@raw = Kickscraper.client.raw.get(self.urls.api.project).body
end

def rewards
reload! unless @rewards
@rewards ||= raw['rewards']
end

def successful?
pledged >= goal
end

def active?
state == "live"
end

def comments
return [] unless self.urls.api.comments
end

def updates
reload! unless self.urls.api.updates
@updates ||= Kickscraper.client.raw.get(URI.parse(self.urls.api.updates).path).body.updates.map {|o|
Update.coerce(o)
}
end
end
end
5 changes: 5 additions & 0 deletions lib/kickscraper/client/update.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Kickscraper
class Update < Api

end
end
27 changes: 27 additions & 0 deletions lib/kickscraper/client/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module Kickscraper
class User < Api
attr_accessor :backed_projects, :created_projects

def to_s
name
end

def reload!
@raw = Kickscraper.client.raw.get(self.urls.api.user).body
end

def backed_projects
return [] unless self.urls.api.backed_projects
@backed_projects ||= Kickscraper.client.raw.get(URI.parse(self.urls.api.backed_projects).path).body.projects.map {|project|
Project.coerce(project)
}
end

def starred_projects
return [] unless self.urls.api.starred_projects
@starred_projects ||= Kickscraper.client.raw.get(URI.parse(self.urls.api.starred_projects).path).body.projects.map {|project|
Project.coerce(project)
}
end
end
end
Loading

0 comments on commit 2a7a955

Please sign in to comment.