etsy 0.1.0 → 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.
- 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
|