rakumarket 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.
@@ -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
+ }