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.
Files changed (38) hide show
  1. data/README.rdoc +102 -19
  2. data/Rakefile +5 -5
  3. data/lib/etsy.rb +104 -26
  4. data/lib/etsy/basic_client.rb +26 -0
  5. data/lib/etsy/image.rb +19 -16
  6. data/lib/etsy/listing.rb +30 -27
  7. data/lib/etsy/model.rb +12 -49
  8. data/lib/etsy/request.rb +41 -18
  9. data/lib/etsy/response.rb +17 -11
  10. data/lib/etsy/secure_client.rb +79 -0
  11. data/lib/etsy/shop.rb +42 -18
  12. data/lib/etsy/user.rb +39 -34
  13. data/lib/etsy/verification_request.rb +17 -0
  14. data/lib/etsy/version.rb +4 -4
  15. data/test/fixtures/image/findAllListingImages.json +102 -0
  16. data/test/fixtures/listing/findAllShopListingsActive.json +69 -0
  17. data/test/fixtures/shop/findAllShop.json +1 -0
  18. data/test/fixtures/shop/findAllShop.single.json +1 -0
  19. data/test/fixtures/shop/getShop.multiple.json +1 -0
  20. data/test/fixtures/shop/getShop.single.json +32 -0
  21. data/test/fixtures/user/getUser.multiple.json +29 -0
  22. data/test/fixtures/user/getUser.single.json +13 -0
  23. data/test/fixtures/user/getUser.single.private.json +18 -0
  24. data/test/test_helper.rb +27 -42
  25. data/test/unit/etsy/basic_client_test.rb +28 -0
  26. data/test/unit/etsy/image_test.rb +36 -15
  27. data/test/unit/etsy/listing_test.rb +84 -66
  28. data/test/unit/etsy/request_test.rb +121 -39
  29. data/test/unit/etsy/response_test.rb +20 -20
  30. data/test/unit/etsy/secure_client_test.rb +110 -0
  31. data/test/unit/etsy/shop_test.rb +74 -40
  32. data/test/unit/etsy/user_test.rb +65 -61
  33. data/test/unit/etsy/verification_request_test.rb +26 -0
  34. data/test/unit/etsy_test.rb +91 -10
  35. metadata +61 -17
  36. data/test/fixtures/getShopDetails.json +0 -70
  37. data/test/fixtures/getShopListings.json +0 -135
  38. 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
- sudo gem install etsy
11
+ $ gem install etsy
12
12
 
13
13
  If you want to be on the bleeding edge, install from GitHub:
14
14
 
15
- sudo gem install reagent-etsy --source=http://gems.github.com
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
- The Etsy API is read-only - all you need to gain access is an API Key (available
20
- from http://developer.etsy.com). Once you have your API key, set it in your script:
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.small_square
89
- => "http://ny-image2.etsy.com/il_25x25.67765346.jpg"
90
- >> listing.images.first.large
91
- => "http://ny-image2.etsy.com/il_430xN.67765346.jpg"
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.large
98
- => "http://ny-image2.etsy.com/il_430xN.67765346.jpg"
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
- # s.executables = ['etsy']
21
-
22
- s.add_dependency('json', '~> 1.1.0')
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 to serve this Gem from Github'
36
- task :github do
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. First, you will need a valid API key from the Etsy
20
- # developer site (http://developer.etsy.com/). Since the API is read-only at
21
- # the moment, that's all you need to do.
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
- # Set the API key for all requests
51
- def self.api_key=(api_key)
52
- @api_key = api_key
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
- # Retrieve the API key
56
- def self.api_key
57
- @api_key
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.find_by_username(username)
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
- # [small_square] The smallest square image (25x25 pixels)
9
- # [medium_square] The medium square image (50x50 pixels)
10
- # [large_square] The largest square image (75x75 pixels)
11
- # [small] The small image for this listing (155x125 pixels)
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 :small_square, :from => :image_url_25x25
20
- attribute :medium_square, :from => :image_url_50x50
21
- attribute :large_square, :from => :image_url_75x75
22
- attribute :small, :from => :image_url_155x125
23
- attribute :medium, :from => :image_url_200x200
24
- attribute :large, :from => :image_url_430xN
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 => :creation_epoch
34
+ attribute :created, :from => :creation_tsz
37
35
  attribute :currency, :from => :currency_code
38
- attribute :ending, :from => :ending_epoch
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