etsy 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +102 -19
- data/Rakefile +5 -5
- data/lib/etsy.rb +104 -26
- data/lib/etsy/basic_client.rb +26 -0
- data/lib/etsy/image.rb +19 -16
- data/lib/etsy/listing.rb +30 -27
- data/lib/etsy/model.rb +12 -49
- data/lib/etsy/request.rb +41 -18
- data/lib/etsy/response.rb +17 -11
- data/lib/etsy/secure_client.rb +79 -0
- data/lib/etsy/shop.rb +42 -18
- data/lib/etsy/user.rb +39 -34
- data/lib/etsy/verification_request.rb +17 -0
- data/lib/etsy/version.rb +4 -4
- data/test/fixtures/image/findAllListingImages.json +102 -0
- data/test/fixtures/listing/findAllShopListingsActive.json +69 -0
- data/test/fixtures/shop/findAllShop.json +1 -0
- data/test/fixtures/shop/findAllShop.single.json +1 -0
- data/test/fixtures/shop/getShop.multiple.json +1 -0
- data/test/fixtures/shop/getShop.single.json +32 -0
- data/test/fixtures/user/getUser.multiple.json +29 -0
- data/test/fixtures/user/getUser.single.json +13 -0
- data/test/fixtures/user/getUser.single.private.json +18 -0
- data/test/test_helper.rb +27 -42
- data/test/unit/etsy/basic_client_test.rb +28 -0
- data/test/unit/etsy/image_test.rb +36 -15
- data/test/unit/etsy/listing_test.rb +84 -66
- data/test/unit/etsy/request_test.rb +121 -39
- data/test/unit/etsy/response_test.rb +20 -20
- data/test/unit/etsy/secure_client_test.rb +110 -0
- data/test/unit/etsy/shop_test.rb +74 -40
- data/test/unit/etsy/user_test.rb +65 -61
- data/test/unit/etsy/verification_request_test.rb +26 -0
- data/test/unit/etsy_test.rb +91 -10
- metadata +61 -17
- data/test/fixtures/getShopDetails.json +0 -70
- data/test/fixtures/getShopListings.json +0 -135
- data/test/fixtures/getUserDetails.json +0 -34
data/README.rdoc
CHANGED
@@ -8,35 +8,103 @@ The Etsy gem provides a friendly Ruby interface to the Etsy API
|
|
8
8
|
|
9
9
|
Installing the latest stable version is simple:
|
10
10
|
|
11
|
-
|
11
|
+
$ gem install etsy
|
12
12
|
|
13
13
|
If you want to be on the bleeding edge, install from GitHub:
|
14
14
|
|
15
|
-
|
15
|
+
$ git clone git://github.com/reagent/etsy.git
|
16
|
+
$ cd etsy
|
17
|
+
$ rake gem && gem install pkg/etsy-<version>.gem
|
16
18
|
|
17
19
|
== Usage
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
+
=== Read-Only Mode
|
22
|
+
|
23
|
+
The Etsy API has two modes: read-only, and read-write. Read-only mode only requires an
|
24
|
+
API key (available from http://developer.etsy.com):
|
21
25
|
|
22
26
|
require 'rubygems'
|
23
27
|
require 'etsy'
|
28
|
+
|
24
29
|
Etsy.api_key = 'foobar'
|
25
30
|
|
26
|
-
From there, you can make any calls to the API that you need.
|
31
|
+
From there, you can make any non-authenticated calls to the API that you need.
|
32
|
+
|
33
|
+
== Authenticated Calls
|
34
|
+
|
35
|
+
The Etsy API has support for both retrieval of extended information and write
|
36
|
+
support for authenticated users. Authentication can either be performed from
|
37
|
+
the console or from within a Ruby web application.
|
38
|
+
|
39
|
+
=== Console
|
40
|
+
|
41
|
+
For simple authentication from the console, configure the necessary parameters:
|
42
|
+
|
43
|
+
require 'rubygems'
|
44
|
+
require 'etsy'
|
45
|
+
|
46
|
+
Etsy.api_key = 'key'
|
47
|
+
Etsy.api_secret = 'secret'
|
48
|
+
Etsy.access_mode = :read_write
|
49
|
+
|
50
|
+
From there, you will need to paste the verification URL into a browser:
|
51
|
+
|
52
|
+
Etsy.verification_url
|
53
|
+
|
54
|
+
Once you have allowed access, you can now generate an access token by supplying
|
55
|
+
the verifier displayed on the Etsy site:
|
56
|
+
|
57
|
+
request = Etsy.request_token
|
58
|
+
access = Etsy.access_token(request.token, request.secret, 'abc123')
|
59
|
+
|
60
|
+
Authenticated calls can now be made by passing an access token and secret:
|
61
|
+
|
62
|
+
Etsy.myself(access.token, access.secret)
|
63
|
+
|
64
|
+
=== Web Application
|
65
|
+
|
66
|
+
The process for authenticating via a web application is similar, but requires the
|
67
|
+
configuration of a callback URL:
|
68
|
+
|
69
|
+
require 'rubygems'
|
70
|
+
require 'etsy'
|
71
|
+
|
72
|
+
Etsy.api_key = 'key'
|
73
|
+
Etsy.api_secret = 'secret'
|
74
|
+
Etsy.access_mode = :read_write
|
75
|
+
Etsy.callback_url = 'http://localhost:4567/authorize'
|
76
|
+
|
77
|
+
In this mode, you'll need to store the request token and secret before redirecting
|
78
|
+
to the verification URL. A simple example using Sinatra:
|
79
|
+
|
80
|
+
enable :sessions
|
81
|
+
|
82
|
+
get '/' do
|
83
|
+
session[:request_token] = Etsy.request_token.token
|
84
|
+
session[:request_secret] = Etsy.request_token.secret
|
85
|
+
redirect Etsy.verification_url
|
86
|
+
end
|
87
|
+
|
88
|
+
get '/authorize' do
|
89
|
+
access_token = Etsy.access_token(
|
90
|
+
session[:request_token],
|
91
|
+
session[:request_secret],
|
92
|
+
params[:oauth_verifier]
|
93
|
+
)
|
94
|
+
|
95
|
+
# access_token.token and access_token.secret can now be saved for future API calls
|
96
|
+
end
|
27
97
|
|
28
98
|
=== Users
|
29
99
|
|
30
100
|
If you're starting with a user, the easiest way is to use the Etsy.user method:
|
31
101
|
|
32
102
|
>> user = Etsy.user('littletjane')
|
33
|
-
=> #<Etsy::User:0x107f82c @result=[{"city"=>"Washington, DC", ... >
|
103
|
+
=> #<Etsy::User:0x107f82c @result=[{"city"=>"Washington, DC", ... >
|
34
104
|
>> user.username
|
35
105
|
=> "littletjane"
|
36
106
|
>> user.id
|
37
107
|
=> 5327518
|
38
|
-
>> user.url
|
39
|
-
=> "http://www.etsy.com/shop.php?user_id=5327518"
|
40
108
|
|
41
109
|
For more information about what is available for a user, check out the documentation
|
42
110
|
for Etsy::User.
|
@@ -46,17 +114,15 @@ for Etsy::User.
|
|
46
114
|
Each user may optionally have a shop. If a user is a seller, he / she also has an
|
47
115
|
associated shop object:
|
48
116
|
|
49
|
-
>> user.seller?
|
50
|
-
=> true
|
51
117
|
>> shop = user.shop
|
52
118
|
=> #<Etsy::Shop:0x102578c @result={"is_vacation"=>"", "announcement"=> ... >
|
53
119
|
>> shop.name
|
54
120
|
=> "littletjane"
|
55
121
|
>> shop.title
|
56
122
|
=> "a cute and crafty mix of handmade goods."
|
57
|
-
|
123
|
+
|
58
124
|
More information about shops can be found in the documentation for Etsy::Shop.
|
59
|
-
|
125
|
+
|
60
126
|
== Listings
|
61
127
|
|
62
128
|
Shops contain multiple listings:
|
@@ -85,23 +151,40 @@ Each listing has one or more images available:
|
|
85
151
|
>> listing.images
|
86
152
|
=> [#<Etsy::Image:0x18f85e4 @result={} ... >,
|
87
153
|
#<Etsy::Image:0x18f85d0 @result={} ... >]
|
88
|
-
>> listing.images.first.
|
89
|
-
=> "http://ny-
|
90
|
-
>> listing.images.first.
|
91
|
-
=> "http://ny-
|
154
|
+
>> listing.images.first.square
|
155
|
+
=> "http://ny-image0.etsy.com/il_75x75.189111072.jpg"
|
156
|
+
>> listing.images.first.full
|
157
|
+
=> "http://ny-image0.etsy.com/il_fullxfull.189111072.jpg"
|
92
158
|
|
93
159
|
Listings also have a primary image:
|
94
160
|
|
95
161
|
>> listing.image
|
96
162
|
=> #<Etsy::Image:0x18c3060 @result={} ... >
|
97
|
-
>> listing.image.
|
98
|
-
=> "http://ny-
|
163
|
+
>> listing.image.full
|
164
|
+
=> "http://ny-image0.etsy.com/il_fullxfull.189111072.jpg"
|
99
165
|
|
100
166
|
More information is available in the documentation for Etsy::Image.
|
101
167
|
|
168
|
+
== Contributing
|
169
|
+
|
170
|
+
I have a "commit bit" policy for contributions to this repository. Once I accept
|
171
|
+
your patch, I will give you full commit access. To submit patches:
|
172
|
+
|
173
|
+
1. Fork this repository
|
174
|
+
2. Implement the desired feature with tests (and documentation if necessary)
|
175
|
+
3. Send me a pull request
|
176
|
+
|
177
|
+
I ask that you not submit patches that include changes to the version or gemspec.
|
178
|
+
|
179
|
+
== Contributors
|
180
|
+
|
181
|
+
These people have helped make the Etsy gem what it is today:
|
182
|
+
|
183
|
+
* {Katrina Owen}[http://github.com/kowen]
|
184
|
+
|
102
185
|
== License
|
103
186
|
|
104
|
-
Copyright (c) 2009 Patrick Reagan (reaganpr@gmail.com)
|
187
|
+
Copyright (c) 2009 - 2010 Patrick Reagan (reaganpr@gmail.com)
|
105
188
|
|
106
189
|
Permission is hereby granted, free of charge, to any person
|
107
190
|
obtaining a copy of this software and associated documentation
|
data/Rakefile
CHANGED
@@ -17,9 +17,9 @@ spec = Gem::Specification.new do |s|
|
|
17
17
|
s.email = 'reaganpr@gmail.com'
|
18
18
|
s.homepage = 'http://sneaq.net'
|
19
19
|
s.files = %w(README.rdoc Rakefile) + Dir.glob("{lib,test}/**/*")
|
20
|
-
|
21
|
-
|
22
|
-
s.add_dependency('
|
20
|
+
|
21
|
+
s.add_dependency('json', '~> 1.4.0')
|
22
|
+
s.add_dependency('oauth', '~> 0.4.0')
|
23
23
|
end
|
24
24
|
|
25
25
|
Rake::GemPackageTask.new(spec) do |pkg|
|
@@ -32,8 +32,8 @@ Rake::TestTask.new do |t|
|
|
32
32
|
t.verbose = true
|
33
33
|
end
|
34
34
|
|
35
|
-
desc 'Generate the gemspec
|
36
|
-
task :
|
35
|
+
desc 'Generate the gemspec for this Gem'
|
36
|
+
task :gemspec do
|
37
37
|
file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
|
38
38
|
File.open(file, 'w') {|f| f << spec.to_ruby }
|
39
39
|
puts "Created gemspec: #{file}"
|
data/lib/etsy.rb
CHANGED
@@ -2,10 +2,15 @@ $:.unshift File.dirname(__FILE__)
|
|
2
2
|
|
3
3
|
require 'net/http'
|
4
4
|
require 'json'
|
5
|
+
require 'oauth'
|
5
6
|
|
6
7
|
require 'etsy/request'
|
7
8
|
require 'etsy/response'
|
8
9
|
|
10
|
+
require 'etsy/basic_client'
|
11
|
+
require 'etsy/secure_client'
|
12
|
+
require 'etsy/verification_request'
|
13
|
+
|
9
14
|
require 'etsy/model'
|
10
15
|
require 'etsy/user'
|
11
16
|
require 'etsy/shop'
|
@@ -15,51 +20,124 @@ require 'etsy/image'
|
|
15
20
|
# = Etsy: A friendly Ruby interface to the Etsy API
|
16
21
|
#
|
17
22
|
# == Quick Start
|
18
|
-
#
|
19
|
-
# Getting started is easy.
|
20
|
-
# developer site (http://developer.etsy.com/).
|
21
|
-
#
|
22
|
-
#
|
23
|
+
#
|
24
|
+
# Getting started is easy. First, you will need a valid API key from the Etsy
|
25
|
+
# developer site (http://developer.etsy.com/).
|
26
|
+
#
|
23
27
|
# To start using the API, require the etsy gem and set it up to use your API key:
|
24
|
-
#
|
28
|
+
#
|
25
29
|
# require 'rubygems'
|
26
30
|
# require 'etsy'
|
27
|
-
#
|
31
|
+
#
|
28
32
|
# Etsy.api_key = 'itsasecret'
|
29
|
-
#
|
33
|
+
#
|
30
34
|
# Now you can make API calls that originate from an Etsy user:
|
31
|
-
#
|
35
|
+
#
|
32
36
|
# # Find a user by username
|
33
37
|
# user = Etsy.user('littletjane')
|
34
|
-
#
|
38
|
+
#
|
35
39
|
# # Grab that user's shop information
|
36
|
-
# user.seller?
|
37
40
|
# user.shop
|
38
41
|
# user.shop.title
|
39
|
-
#
|
42
|
+
#
|
40
43
|
# # ... and the listings in the shop
|
41
44
|
# listing = user.shop.listings.first
|
42
45
|
# listing.title
|
43
46
|
# listing.description
|
44
|
-
#
|
47
|
+
#
|
45
48
|
# To see what else is available for a user, check out the full documentation for
|
46
|
-
# the Etsy::User class.
|
49
|
+
# the Etsy::User class. Information about making authenticated calls is available
|
50
|
+
# in the README.
|
47
51
|
#
|
48
52
|
module Etsy
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
+
class Error < RuntimeError; end
|
54
|
+
|
55
|
+
class << self
|
56
|
+
attr_accessor :api_key, :api_secret
|
57
|
+
attr_writer :callback_url
|
58
|
+
end
|
59
|
+
|
60
|
+
# Set the environment, accepts either :sandbox or :production. Defaults to :sandbox
|
61
|
+
# and will raise an exception when set to an unrecognized environment.
|
62
|
+
#
|
63
|
+
def self.environment=(environment)
|
64
|
+
unless [:sandbox, :production].include?(environment)
|
65
|
+
raise(ArgumentError, "environment must be set to either :sandbox or :production")
|
66
|
+
end
|
67
|
+
@environment = environment
|
68
|
+
end
|
69
|
+
|
70
|
+
# The currently configured environment.
|
71
|
+
#
|
72
|
+
def self.environment
|
73
|
+
@environment || :sandbox
|
74
|
+
end
|
75
|
+
|
76
|
+
# Set the access mode, can either be :read_only or :read_write. Defaults to :read_only
|
77
|
+
# and will raise an exception when set to an invalid value.
|
78
|
+
#
|
79
|
+
def self.access_mode=(mode)
|
80
|
+
unless [:read_only, :read_write].include?(mode)
|
81
|
+
raise(ArgumentError, "access mode must be set to either :read_only or :read_write")
|
82
|
+
end
|
83
|
+
@access_mode = mode
|
84
|
+
end
|
85
|
+
|
86
|
+
# The currently configured access mode
|
87
|
+
#
|
88
|
+
def self.access_mode
|
89
|
+
@access_mode || :read_only
|
53
90
|
end
|
54
|
-
|
55
|
-
#
|
56
|
-
|
57
|
-
|
91
|
+
|
92
|
+
# The configured callback URL or 'oob' if no callback URL is configured. This controls
|
93
|
+
# whether or not we need to pass the OAuth verifier by hand.
|
94
|
+
#
|
95
|
+
def self.callback_url
|
96
|
+
@callback_url || 'oob'
|
58
97
|
end
|
59
|
-
|
98
|
+
|
60
99
|
# Find a user by username. See Etsy::User for more information.
|
100
|
+
#
|
61
101
|
def self.user(username)
|
62
|
-
User.
|
102
|
+
User.find(username)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Convenience method for accessing the authenticated user's own user information. Requires
|
106
|
+
# authentication.
|
107
|
+
#
|
108
|
+
def self.myself(token, secret)
|
109
|
+
User.myself(token, secret)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Generate a request token for authorization.
|
113
|
+
#
|
114
|
+
def self.request_token
|
115
|
+
verification_request.request_token
|
116
|
+
end
|
117
|
+
|
118
|
+
# Generate an access token from the request token, secret, and verifier. The verifier can
|
119
|
+
# either be passed manually or from the params in the callback URL.
|
120
|
+
#
|
121
|
+
def self.access_token(request_token, request_secret, verifier)
|
122
|
+
@access_token ||= begin
|
123
|
+
client = Etsy::SecureClient.new({
|
124
|
+
:request_token => request_token,
|
125
|
+
:request_secret => request_secret,
|
126
|
+
:verifier => verifier
|
127
|
+
})
|
128
|
+
client.client
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Generate the URL to begin the verification process for a user.
|
133
|
+
#
|
134
|
+
def self.verification_url
|
135
|
+
verification_request.url
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def self.verification_request
|
141
|
+
@verification_request ||= VerificationRequest.new
|
63
142
|
end
|
64
|
-
|
65
|
-
end
|
143
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Etsy
|
2
|
+
|
3
|
+
# = BasicClient
|
4
|
+
#
|
5
|
+
# Used for calling public API methods.
|
6
|
+
#
|
7
|
+
class BasicClient
|
8
|
+
|
9
|
+
# Create a new client that will connect to the specified host
|
10
|
+
#
|
11
|
+
def initialize(host)
|
12
|
+
@host = host
|
13
|
+
end
|
14
|
+
|
15
|
+
def client # :nodoc:
|
16
|
+
@client ||= Net::HTTP.new(@host)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Fetch a raw response from the specified endpoint
|
20
|
+
#
|
21
|
+
def get(endpoint)
|
22
|
+
client.get(endpoint)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/etsy/image.rb
CHANGED
@@ -1,27 +1,30 @@
|
|
1
1
|
module Etsy
|
2
|
-
|
2
|
+
|
3
3
|
# = Image
|
4
4
|
#
|
5
|
-
# Represents an image resource of an Etsy listing and contains multiple sizes.
|
5
|
+
# Represents an image resource of an Etsy listing and contains multiple sizes.
|
6
6
|
# Sizes available are:
|
7
7
|
#
|
8
|
-
# [
|
9
|
-
# [
|
10
|
-
# [
|
11
|
-
# [
|
12
|
-
# [medium] The medium image for this listing (200x200 pixels)
|
13
|
-
# [large] The largest image available for this listing (430x? pixels)
|
8
|
+
# [square] The square image thumbnail (75x75 pixels)
|
9
|
+
# [small] The small image thumbnail (170x135 pixels)
|
10
|
+
# [thumbnail] The thumbnail for the image, no more than 570px wide
|
11
|
+
# [full] The full image for this listing, no more than 1500px wide
|
14
12
|
#
|
15
13
|
class Image
|
16
|
-
|
14
|
+
|
17
15
|
include Etsy::Model
|
18
|
-
|
19
|
-
attribute :
|
20
|
-
attribute :
|
21
|
-
attribute :
|
22
|
-
attribute :
|
23
|
-
|
24
|
-
|
16
|
+
|
17
|
+
attribute :square, :from => :url_75x75
|
18
|
+
attribute :small, :from => :url_170x135
|
19
|
+
attribute :thumbnail, :from => :url_570xN
|
20
|
+
attribute :full, :from => :url_fullxfull
|
21
|
+
|
22
|
+
# Fetch all images for a given listing.
|
23
|
+
#
|
24
|
+
def self.find_all_by_listing_id(listing_id)
|
25
|
+
response = Request.get("/listings/#{listing_id}/images")
|
26
|
+
[response.result].flatten.map {|data| new(data) }
|
27
|
+
end
|
25
28
|
|
26
29
|
end
|
27
30
|
end
|
data/lib/etsy/listing.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Etsy
|
2
|
-
|
2
|
+
|
3
3
|
# = Listing
|
4
4
|
#
|
5
5
|
# Represents a single Etsy listing. Has the following attributes:
|
@@ -24,53 +24,56 @@ module Etsy
|
|
24
24
|
# [alchemy?] Is this listing an Alchemy item? (i.e. requested by an Etsy user)
|
25
25
|
#
|
26
26
|
class Listing
|
27
|
-
|
27
|
+
|
28
28
|
include Etsy::Model
|
29
|
-
|
29
|
+
|
30
30
|
STATES = %w(active removed sold_out expired alchemy)
|
31
|
-
|
32
|
-
finder :all, '/shops/:user_id/listings'
|
33
|
-
|
31
|
+
|
34
32
|
attribute :id, :from => :listing_id
|
35
33
|
attribute :view_count, :from => :views
|
36
|
-
attribute :created, :from => :
|
34
|
+
attribute :created, :from => :creation_tsz
|
37
35
|
attribute :currency, :from => :currency_code
|
38
|
-
attribute :ending, :from => :
|
39
|
-
|
40
|
-
attributes :title, :description, :state, :url, :price, :quantity,
|
36
|
+
attribute :ending, :from => :ending_tsz
|
37
|
+
|
38
|
+
attributes :title, :description, :state, :url, :price, :quantity,
|
41
39
|
:tags, :materials
|
42
40
|
|
41
|
+
# Retrieve all active listings for a given shop. Pulls back the first 25 listings.
|
42
|
+
#
|
43
|
+
def self.find_all_by_shop_id(shop_id)
|
44
|
+
response = Request.get("/shops/#{shop_id}/listings/active")
|
45
|
+
[response.result].flatten.map {|data| new(data) }
|
46
|
+
end
|
47
|
+
|
48
|
+
# The collection of images associated with this listing.
|
49
|
+
#
|
50
|
+
def images
|
51
|
+
@images ||= Image.find_all_by_listing_id(id)
|
52
|
+
end
|
53
|
+
|
54
|
+
# The primary image for this listing.
|
55
|
+
#
|
56
|
+
def image
|
57
|
+
images.first
|
58
|
+
end
|
59
|
+
|
43
60
|
STATES.each do |state|
|
44
61
|
define_method "#{state}?" do
|
45
62
|
self.state == state.sub('_', '')
|
46
63
|
end
|
47
64
|
end
|
48
|
-
|
65
|
+
|
49
66
|
# Time that this listing was created
|
50
67
|
#
|
51
68
|
def created_at
|
52
69
|
Time.at(created)
|
53
70
|
end
|
54
|
-
|
71
|
+
|
55
72
|
# Time that this listing is ending (will be removed from store)
|
56
73
|
#
|
57
74
|
def ending_at
|
58
75
|
Time.at(ending)
|
59
76
|
end
|
60
|
-
|
61
|
-
# The list of images associated with this listing. See Etsy::Image
|
62
|
-
# for more information
|
63
|
-
#
|
64
|
-
def images
|
65
|
-
@result['all_images'].map {|image_data| Image.new(image_data) }
|
66
|
-
end
|
67
|
-
|
68
|
-
# The primary image for this listing. See Etsy::Image for more
|
69
|
-
# information
|
70
|
-
#
|
71
|
-
def image
|
72
|
-
images.first
|
73
|
-
end
|
74
|
-
|
77
|
+
|
75
78
|
end
|
76
79
|
end
|