rakumarket 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,9 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rakumarket (0.0.3)
4
+ rakumarket (0.2.0)
5
5
  httparty (>= 0.1.0)
6
6
  nibbler (~> 1.3.0)
7
+ oj
7
8
 
8
9
  GEM
9
10
  remote: http://rubygems.org/
@@ -17,6 +18,7 @@ GEM
17
18
  multi_json (1.3.6)
18
19
  multi_xml (0.5.1)
19
20
  nibbler (1.3.0)
21
+ oj (1.2.11)
20
22
  rspec (2.8.0)
21
23
  rspec-core (~> 2.8.0)
22
24
  rspec-expectations (~> 2.8.0)
@@ -25,7 +27,6 @@ GEM
25
27
  rspec-expectations (2.8.0)
26
28
  diff-lcs (~> 1.1.2)
27
29
  rspec-mocks (2.8.0)
28
- vcr (2.2.0)
29
30
  webmock (1.8.7)
30
31
  addressable (>= 2.2.7)
31
32
  crack (>= 0.1.7)
@@ -37,5 +38,4 @@ DEPENDENCIES
37
38
  bundler (>= 1.0.0)
38
39
  rakumarket!
39
40
  rspec (~> 2.6)
40
- vcr
41
41
  webmock
@@ -2,6 +2,8 @@
2
2
 
3
3
  A reader-friendly Ruby abstraction of the Rakuten Market API.
4
4
 
5
+ See the {Github page}[http://bonsaiben.github.com/rakumarket] for detailed usage information.
6
+
5
7
 
6
8
  == Description
7
9
 
@@ -13,7 +15,7 @@ The goal was to abstract Rakuten's complex API away from users and provide a sim
13
15
  * Idiomatic Ruby
14
16
  * Simpler and more readable representation of the underlying API
15
17
  * Well-tested
16
- * Returns Hashie::Mash[https://github.com/intridea/hashie]
18
+ * Concrete classes and methods
17
19
 
18
20
 
19
21
  == Goals
@@ -21,7 +23,6 @@ The goal was to abstract Rakuten's complex API away from users and provide a sim
21
23
  * Completeness
22
24
  * More simplerer
23
25
  * Better-tested
24
- * Concrete classes and methods?
25
26
 
26
27
 
27
28
 
@@ -41,14 +42,21 @@ A Rakuten developer ID is required.
41
42
 
42
43
  Please see the wiki for a more comprehensive documentation.
43
44
 
44
- === {Item Search}[https://github.com/bonsaiben/rakumarket/wiki/Item-Search]
45
+ === Item Search
46
+
47
+ Rakumarket.item_search "roomba", :price => {:maximum => 30000}
45
48
 
46
- Rakumarket.item_search "roomba", options
49
+ === Item Lookup
47
50
 
48
- === {Genre Search}[https://github.com/bonsaiben/rakumarket/wiki/Genre-Search]
51
+ Rakumarket.item_lookup "act-corp:10000580"
52
+
53
+ === Genre Search
49
54
 
50
55
  Rakumarket.genre_search
51
56
 
57
+ === Item Ranking
58
+
59
+ Rakumarket.item_ranking :sex => :male, :age_range => (20..29)
52
60
 
53
61
 
54
62
  == Todo
@@ -58,13 +66,6 @@ Please see the wiki for a more comprehensive documentation.
58
66
 
59
67
 
60
68
 
61
- == Dependencies
62
-
63
- * HTTParty[https://github.com/jnunemaker/httparty]
64
- * Hashie::Mash[https://github.com/intridea/hashie]
65
-
66
-
67
-
68
69
 
69
70
  == License
70
71
 
@@ -94,4 +95,4 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
94
95
 
95
96
  == Credit
96
97
 
97
- Rakumarket is inspired by multiple other Ruby API wrappers including groupon[https://github.com/pengwynn/groupon] and facebooker[https://github.com/mmangino/facebooker].
98
+ Rakumarket is inspired by multiple other Ruby API wrappers including instagram[https://github.com/mislav/instagram], groupon[https://github.com/pengwynn/groupon] and facebooker[https://github.com/mmangino/facebooker].
@@ -20,6 +20,11 @@ module Rakumarket
20
20
  ItemLookupClient.request(params)
21
21
  end
22
22
 
23
+ def self.item_ranking(options={})
24
+ params = options.symbolify_keys!
25
+ ItemRankingClient.request(params)
26
+ end
27
+
23
28
 
24
29
  class << self
25
30
  attr_accessor :developer_id
@@ -39,15 +39,13 @@ private
39
39
  end
40
40
 
41
41
  class TrueToOne
42
- def self.parse value
43
- value ? "1" : "0"
44
- end
42
+ def self.values; {true => "1", false => "0"} end
43
+ def self.parse(value) value ? "1" : "0" end
45
44
  end
46
45
 
47
46
  class TrueToZero
48
- def self.parse value
49
- value ? "0" : "1"
50
- end
47
+ def self.values; {true => "0", false => "1"} end
48
+ def self.parse(value) value ? "0" : "1" end
51
49
  end
52
50
 
53
51
  class ItemSearchClient < Client
@@ -56,21 +54,23 @@ private
56
54
  VERSION = "2010-09-15"
57
55
 
58
56
  class SortOrder
59
- def self.parse(value) ITEM_SEARCH_SORT_ORDERS[value.downcase] end
57
+ def self.values; ITEM_SEARCH_SORT_ORDERS end
58
+ def self.parse(value) values[value.downcase] end
60
59
  end
61
60
 
62
61
  class CountryCode
63
- def self.parse(value) INTERNATIONAL_DELIVERY_AREA_CODES[value] end
62
+ def self.values; INTERNATIONAL_DELIVERY_AREA_CODES end
63
+ def self.parse(value) values[value] end
64
64
  end
65
65
 
66
66
  class NextDayAreaCode
67
- def self.parse(value) ASURAKU_DELIVERY_AREA_CODES[value] end
67
+ def self.values; ASURAKU_DELIVERY_AREA_CODES end
68
+ def self.parse(value) values[value] end
68
69
  end
69
70
 
70
71
  class PurchaseType
71
- def self.parse(value)
72
- {"normal" => 0, "regular" => 1, "distribution" => 2}[value]
73
- end
72
+ def self.values; {"normal" => 0, "regular" => 1, "distribution" => 2} end
73
+ def self.parse(value) values[value] end
74
74
  end
75
75
 
76
76
  parameter :operation, :with => lambda { OPERATION }
@@ -108,6 +108,7 @@ private
108
108
 
109
109
  def request
110
110
  response = super
111
+ response = response.dup['Body']['ItemSearch']
111
112
  ItemList.parse(response)
112
113
  end
113
114
 
@@ -125,7 +126,8 @@ private
125
126
 
126
127
  def request
127
128
  response = super
128
- Genre.parse(response)
129
+ response = response.dup['Body']['GenreSearch'] if response['Body']
130
+ GenreFamily.parse(response)
129
131
  end
130
132
 
131
133
  end
@@ -141,8 +143,46 @@ private
141
143
 
142
144
  def request
143
145
  response = super
146
+ response = response.dup['Body']['ItemCodeSearch']['Items']['Item'].first if response['Body'] && response['Body']['ItemCodeSearch']
144
147
  Item.parse(response)
145
148
  end
146
149
  end
147
150
 
151
+ class ItemRankingClient < Client
152
+ OPERATION = "ItemRanking"
153
+ VERSION = "2010-08-05"
154
+
155
+ class AgeRange
156
+ def self.values
157
+ {
158
+ (10..19) => "10",
159
+ (20..29) => "20",
160
+ (30..39) => "30",
161
+ (40..49) => "40",
162
+ (50..120) => "50"
163
+ }
164
+ end
165
+
166
+ def self.parse(range) self.values[range] end
167
+ end
168
+
169
+ class Gender
170
+ def self.values; {'male' => '0', 'female' => '1'} end
171
+ def self.parse(range) values[range.to_s] end
172
+ end
173
+
174
+ parameter :operation, :with => lambda { OPERATION }
175
+ parameter :version, :with => lambda { VERSION }
176
+ parameter :genre_id => 'genreId'
177
+ parameter :age_range => 'age', :with => AgeRange
178
+ parameter :sex, :with => Gender
179
+ parameter :mobile => 'carrier', :with => TrueToOne
180
+
181
+ def request
182
+ response = super
183
+ response = response.dup['Body']['ItemRanking']
184
+ ItemList.parse(response)
185
+ end
186
+ end
187
+
148
188
  end
@@ -1,30 +1,49 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
+ class NibblerJSON
4
+ def self.inspect_attributes
5
+ attributes = {}
6
+ @rules.each do |k,v|
7
+ attributes[k] = if v[1].respond_to?(:inspect_attributes)
8
+ v[2] ? [v[1].inspect_attributes] : v[1].inspect_attributes
9
+ else
10
+ v[1].respond_to?(:values) ? v[1].values.values.join("|") : ""
11
+ end
12
+ end
13
+ attributes
14
+ end
15
+ end
16
+
3
17
  module Rakumarket
4
18
  class Base < NibblerJSON
5
19
  end
6
20
 
7
21
  class OneToTrue
8
- def self.parse(val) {"1" => true, "0" => false}[val.to_s] end
22
+ def self.values; {"1" => true, "0" => false} end
23
+ def self.parse(val) values[val.to_s] end
9
24
  end
10
25
 
11
26
  class ZeroToTrue
12
- def self.parse(val) {"0" => true, "1" => false}[val.to_s] end
27
+ def self.values; {"0" => true, "1" => false} end
28
+ def self.parse(val) values[val.to_s] end
13
29
  end
14
30
 
15
31
  class ParseTime < Time
32
+ def self.values; {"accepts" => self.superclass.name} end
16
33
  def self.parse(val) super unless val.blank? end
17
34
  end
18
35
 
19
36
  class InternationalDeliveryCountryList
37
+ def self.values; INTERNATIONAL_DELIVERY_AREA_NAMES end
20
38
  def self.parse(val)
21
- val.split('/').map{|a| INTERNATIONAL_DELIVERY_AREA_NAMES[a] || a} if val.respond_to?(:split)
39
+ val.split('/').map{|a| values[a] || a} if val.respond_to?(:split)
22
40
  end
23
41
  end
24
42
 
25
43
  class NextDayAreaList
44
+ def self.values; ASURAKU_DELIVERY_AREA_NAMES end
26
45
  def self.parse(val)
27
- val.split('/').map{|a| ASURAKU_DELIVERY_AREA_NAMES[a] || a} if val.respond_to?(:split)
46
+ val.split('/').map{|a| values[a] || a} if val.respond_to?(:split)
28
47
  end
29
48
  end
30
49
 
@@ -47,6 +66,7 @@ module Rakumarket
47
66
  element 'itemPrice' => :price
48
67
  element 'itemCaption' => :caption
49
68
  element 'itemUrl' => :url
69
+ element 'rank' => :rank
50
70
  element 'genreId' => :genre_id
51
71
  element 'affiliateUrl' => :affiliate_url
52
72
  element 'imageFlag' => :has_image, :with => OneToTrue
@@ -78,7 +98,6 @@ module Rakumarket
78
98
  def shop_of_the_year?; shop_of_the_year end
79
99
 
80
100
  def self.parse(response)
81
- response = response.dup['Body']['ItemCodeSearch']['Items']['Item'].first if response['Body'] && response['Body']['ItemCodeSearch']
82
101
  response['shop'] = {
83
102
  'shopName' => response.delete('shopName'),
84
103
  'shopCode' => response.delete('shopCode'),
@@ -102,7 +121,6 @@ module Rakumarket
102
121
  elements :items, :with => Item
103
122
 
104
123
  def self.parse(response)
105
- response = response.dup['Body']['ItemSearch']
106
124
  response['items'] = response.delete('Items')['Item']
107
125
  super(response)
108
126
  end
@@ -111,11 +129,15 @@ module Rakumarket
111
129
  class Genre < Base
112
130
  element 'genreId' => :id
113
131
  element 'genreName' => :name
132
+ end
133
+
134
+ class GenreFamily < Base
135
+ element 'genreId' => :id
136
+ element 'genreName' => :name
114
137
  element :parent, :with => Genre
115
138
  elements 'child' => :children, :with => Genre
116
139
 
117
140
  def self.parse(response)
118
- response = response.dup['Body']['GenreSearch'] if response['Body']
119
141
  response['genreId'] = response['current'].first['genreId'] if response['current'] && response['current'].any?
120
142
  response['genreName'] = response['current'].first['genreName'] if response['current'] && response['current'].any?
121
143
  super(response)
@@ -25,6 +25,18 @@ module SpitterMethods
25
25
  # Process data by creating a new instance
26
26
  def parse(params) new(params).parse end
27
27
 
28
+ def inspect_options
29
+ options = {}
30
+ @rules.each do |k,v|
31
+ options[k] = if v[1].respond_to?(:inspect_options)
32
+ v[2] ? [v[1].inspect_options] : v[1].inspect_options
33
+ else
34
+ v[1].respond_to?(:values) ? v[1].values.keys.join('|') : ""
35
+ end
36
+ end
37
+ options
38
+ end
39
+
28
40
  private
29
41
 
30
42
  # Make subclasses inherit the parsing rules
@@ -1,3 +1,3 @@
1
1
  module Rakumarket
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -15,10 +15,10 @@ Gem::Specification.new do |s|
15
15
 
16
16
  s.add_dependency "nibbler", "~> 1.3.0"
17
17
  s.add_dependency "httparty", ">= 0.1.0"
18
+ s.add_dependency "oj"
18
19
  s.add_development_dependency "bundler", ">= 1.0.0"
19
20
  s.add_development_dependency "rspec", "~> 2.6"
20
21
  s.add_development_dependency "webmock"
21
- s.add_development_dependency "vcr"
22
22
 
23
23
  s.files = `git ls-files`.split("\n")
24
24
  s.require_path = 'lib'
@@ -0,0 +1,213 @@
1
+ {
2
+ "Body": {
3
+ "GenreSearch": {
4
+ "child": [
5
+ {
6
+ "genreLevel": 1,
7
+ "genreName": "CD・DVD・楽器",
8
+ "genreId": 101240
9
+ },
10
+ {
11
+ "genreLevel": 1,
12
+ "genreName": "インテリア・寝具・収納",
13
+ "genreId": 100804
14
+ },
15
+ {
16
+ "genreLevel": 1,
17
+ "genreName": "おもちゃ・ホビー・ゲーム",
18
+ "genreId": 101164
19
+ },
20
+ {
21
+ "genreLevel": 1,
22
+ "genreName": "キッズ・ベビー・マタニティ",
23
+ "genreId": 100533
24
+ },
25
+ {
26
+ "genreLevel": 1,
27
+ "genreName": "日用品雑貨・文房具・手芸",
28
+ "genreId": 215783
29
+ },
30
+ {
31
+ "genreLevel": 1,
32
+ "genreName": "ジュエリー・アクセサリー",
33
+ "genreId": 216129
34
+ },
35
+ {
36
+ "genreLevel": 1,
37
+ "genreName": "スポーツ・アウトドア",
38
+ "genreId": 101070
39
+ },
40
+ {
41
+ "genreLevel": 1,
42
+ "genreName": "ダイエット・健康",
43
+ "genreId": 100938
44
+ },
45
+ {
46
+ "genreLevel": 1,
47
+ "genreName": "水・ソフトドリンク",
48
+ "genreId": 100316
49
+ },
50
+ {
51
+ "genreLevel": 1,
52
+ "genreName": "パソコン・周辺機器",
53
+ "genreId": 100026
54
+ },
55
+ {
56
+ "genreLevel": 1,
57
+ "genreName": "バッグ・小物・ブランド雑貨",
58
+ "genreId": 216131
59
+ },
60
+ {
61
+ "genreLevel": 1,
62
+ "genreName": "レディースファッション",
63
+ "genreId": 100371
64
+ },
65
+ {
66
+ "genreLevel": 1,
67
+ "genreName": "花・ガーデン・DIY",
68
+ "genreId": 100005
69
+ },
70
+ {
71
+ "genreLevel": 1,
72
+ "genreName": "ペット・ペットグッズ",
73
+ "genreId": 101213
74
+ },
75
+ {
76
+ "genreLevel": 1,
77
+ "genreName": "TV・オーディオ・カメラ",
78
+ "genreId": 211742
79
+ },
80
+ {
81
+ "genreLevel": 1,
82
+ "genreName": "車・バイク",
83
+ "genreId": 101114
84
+ },
85
+ {
86
+ "genreLevel": 1,
87
+ "genreName": "食品",
88
+ "genreId": 100227
89
+ },
90
+ {
91
+ "genreLevel": 1,
92
+ "genreName": "美容・コスメ・香水",
93
+ "genreId": 100939
94
+ },
95
+ {
96
+ "genreLevel": 1,
97
+ "genreName": "本・雑誌・コミック",
98
+ "genreId": 200162
99
+ },
100
+ {
101
+ "genreLevel": 1,
102
+ "genreName": "旅行・出張・チケット",
103
+ "genreId": 101381
104
+ },
105
+ {
106
+ "genreLevel": 1,
107
+ "genreName": "不動産・住まい",
108
+ "genreId": 200163
109
+ },
110
+ {
111
+ "genreLevel": 1,
112
+ "genreName": "学び・サービス・保険",
113
+ "genreId": 101438
114
+ },
115
+ {
116
+ "genreLevel": 1,
117
+ "genreName": "百貨店・総合通販・ギフト",
118
+ "genreId": 100000
119
+ },
120
+ {
121
+ "genreLevel": 1,
122
+ "genreName": "デジタルコンテンツ",
123
+ "genreId": 402853
124
+ },
125
+ {
126
+ "genreLevel": 1,
127
+ "genreName": "車用品・バイク用品",
128
+ "genreId": 503190
129
+ },
130
+ {
131
+ "genreLevel": 1,
132
+ "genreName": "インナー・下着・ナイトウエア",
133
+ "genreId": 100433
134
+ },
135
+ {
136
+ "genreLevel": 1,
137
+ "genreName": "日本酒・焼酎",
138
+ "genreId": 510901
139
+ },
140
+ {
141
+ "genreLevel": 1,
142
+ "genreName": "ビール・洋酒",
143
+ "genreId": 510915
144
+ },
145
+ {
146
+ "genreLevel": 1,
147
+ "genreName": "スイーツ・お菓子",
148
+ "genreId": 551167
149
+ },
150
+ {
151
+ "genreLevel": 1,
152
+ "genreName": "医薬品・コンタクト・介護",
153
+ "genreId": 551169
154
+ },
155
+ {
156
+ "genreLevel": 1,
157
+ "genreName": "メンズファッション",
158
+ "genreId": 551177
159
+ },
160
+ {
161
+ "genreLevel": 1,
162
+ "genreName": "靴",
163
+ "genreId": 558885
164
+ },
165
+ {
166
+ "genreLevel": 1,
167
+ "genreName": "腕時計",
168
+ "genreId": 558929
169
+ },
170
+ {
171
+ "genreLevel": 1,
172
+ "genreName": "キッチン用品・食器・調理器具",
173
+ "genreId": 558944
174
+ },
175
+ {
176
+ "genreLevel": 1,
177
+ "genreName": "家電",
178
+ "genreId": 562637
179
+ }
180
+ ],
181
+ "current": [],
182
+ "parent": []
183
+ }
184
+ },
185
+ "Header": {
186
+ "Status": "Success",
187
+ "Args": {
188
+ "Arg": {
189
+ "apiVersion": {
190
+ "content": true,
191
+ "value": "30"
192
+ },
193
+ "operation": {
194
+ "content": true,
195
+ "value": "GenreSearch"
196
+ },
197
+ "developerId": {
198
+ "content": true,
199
+ "value": "***"
200
+ },
201
+ "genreId": {
202
+ "content": true,
203
+ "value": "0"
204
+ },
205
+ "version": {
206
+ "content": true,
207
+ "value": "2007-04-11"
208
+ }
209
+ }
210
+ },
211
+ "StatusMsg": ""
212
+ }
213
+ }