ruby-maidcafe 0.0.1

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/History.txt ADDED
@@ -0,0 +1,5 @@
1
+ == 0.0.1 / 2007-06-24
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
5
+
data/Manifest.txt ADDED
@@ -0,0 +1,11 @@
1
+ History.txt
2
+ Rakefile
3
+ Manifest.txt
4
+ README.txt
5
+ bin/ruby-maidcafe
6
+ lib/ruby-maidcafe.rb
7
+ lib/cui.rb
8
+ test/test_ruby-maidcafe.rb
9
+ test/test_resultset.rb
10
+ test/data/shop_t_13.xml
11
+ test/data/t_list.xml
data/README.txt ADDED
@@ -0,0 +1,103 @@
1
+ ruby-maidcafe
2
+ by ANDO Yasushi (andyjpn@gmail.com)
3
+ (http://d.hatena.ne.jp/technohippy/)
4
+
5
+ == DESCRIPTION:
6
+
7
+ By utilizing this library, you can retrieve information of Japanese
8
+ Maidcafe via Maidcafe API web service (http://moeten.info/maidcafe/?m=api).
9
+ This can be used as interactive command line tool, non-interactive one,
10
+ and library.
11
+
12
+ == SYNOPSIS:
13
+
14
+ In your code:
15
+
16
+ * get shop list
17
+ api = Maidcafe::API.new
18
+ rs = api.list :shop
19
+ puts rs.description
20
+ rs.items.each do |item|
21
+ puts item.name
22
+ puts item.id
23
+ end
24
+ * get shop information in osaka
25
+ rs = api.shop :prefecture => Maidcafe::Prefecture::OSAKA
26
+ puts rs.description
27
+ rs.items.each do |item|
28
+ puts item.name
29
+ puts item.description
30
+ puts item.opening_hour
31
+ end
32
+
33
+ In your shell:
34
+
35
+ * get shop list
36
+ bin/ruby-maidcafe -s list
37
+ * get shop information in category #1
38
+ bin/ruby-maidcafe -c 1 shop
39
+ * get event information in osaka (#27)
40
+ bin/ruby-maidcafe -t 27 event
41
+ * interactive retrieval
42
+ $ bin/ruby-maidcafe
43
+
44
+ 1 : お店
45
+ 2 : 開催中のイベント
46
+ :
47
+ 8 : お店からのイベントのお知らせ
48
+ 選択してください: 1
49
+ お店
50
+
51
+ 都道府県名を指定しますか (y/N)y
52
+ 1 : 北海道
53
+ 2 : 栃木県
54
+ :
55
+ 10 : 鹿児島県
56
+ 選択してください: 1
57
+ 北海道
58
+
59
+ カテゴリを指定しますか (y/N)
60
+
61
+ お店番号を指定しますか (y/N)
62
+ 1 : ロミオ†ジュリエッタ - すべてがメイドさんの手作りメニュー☆
63
+ 2 : 萌えカフェ&BAR ゆるふわ - メイドさん達と楽しくお話しながら、...
64
+ 選択してください: 1
65
+ 店名:ロミオ†ジュリエッタ
66
+ Tel: 011-222-0832
67
+ 説明:すべてがメイドさんの手作りメニュー☆
68
+ 日替わりで担当のメイドがsweetsを作っています。
69
+ また、土日はランチもありますよ♪
70
+ メイドによりメニューは色々変わりますので
71
+ それもひとつのお楽しみです!
72
+ イベントも満載ですよ♪
73
+
74
+ [push enter key]
75
+
76
+ == INSTALL:
77
+
78
+ * gem instal ruby-maidcafe
79
+
80
+ == LICENSE:
81
+
82
+ (The MIT License)
83
+
84
+ Copyright (c) 2007 ANDO Yasushi
85
+
86
+ Permission is hereby granted, free of charge, to any person obtaining
87
+ a copy of this software and associated documentation files (the
88
+ 'Software'), to deal in the Software without restriction, including
89
+ without limitation the rights to use, copy, modify, merge, publish,
90
+ distribute, sublicense, and/or sell copies of the Software, and to
91
+ permit persons to whom the Software is furnished to do so, subject to
92
+ the following conditions:
93
+
94
+ The above copyright notice and this permission notice shall be
95
+ included in all copies or substantial portions of the Software.
96
+
97
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
98
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
99
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
100
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
101
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
102
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
103
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/ruby-maidcafe.rb'
6
+
7
+ Hoe.new('ruby-maidcafe', Maidcafe::VERSION) do |p|
8
+ p.rubyforge_name = 'ruby-maidcafe'
9
+ p.summary = 'ruby interface for Maidcafe API (http://moeten.info/maidcafe/?m=api)'
10
+ p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
11
+ p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
12
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
13
+ end
14
+
15
+ # vim: syntax=Ruby
data/bin/ruby-maidcafe ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # get category list in tokyo (13)
4
+ # bin/ruby-maidcafe -c list -t 13
5
+ #
6
+ # get shop list
7
+ # bin/ruby-maidcafe -s list
8
+ #
9
+ # get shop information
10
+ # bin/ruby-maidcafe -c 1 shop
11
+ #
12
+ # get event information in osaka
13
+ # bin/ruby-maidcafe -t 27 event
14
+
15
+ $KCODE = 'u'
16
+ $:.unshift File.join(File.dirname(__FILE__), '../lib')
17
+ require 'lib/ruby-maidcafe'
18
+
19
+ if ARGV.empty?
20
+ require 'lib/cui'
21
+ Maidcafe::CUI.start
22
+ else
23
+ require 'optparse'
24
+ COMMAND_LINE = "#{$0} #{ARGV.join(' ')}"
25
+ OPT = {}
26
+ opts = OptionParser.new
27
+ opts.on('-t <prefecture>'){|v| OPT[:tid] = v}
28
+ opts.on('-c <category>'){|v| OPT[:cid] = v}
29
+ opts.on('-s <shop>'){|v| OPT[:sid] = v}
30
+ opts.on('-g', '--gps <latlng>') do |v|
31
+ OPT[:gps_lat], OPT[:gps_lon] = latlng.split(',').map{|e| e.to_f}
32
+ end
33
+ opts.on('-w', '--wgps <latlng>') do |v|
34
+ OPT[:wgps_lat], OPT[:wgps_lon] = latlng.split(',').map{|e| e.to_f}
35
+ end
36
+ opts.on_tail('-v', '--version', 'Show version.'){puts(opts.ver); exit}
37
+ opts.on_tail('-h', '--help', 'Show this message.'){puts(opts.help); exit}
38
+
39
+ ::Version = Maidcafe::VERSION
40
+ opts.order! ARGV
41
+ CMD = ARGV.shift
42
+
43
+ api = Maidcafe::API.new
44
+ rs = api.invoke(CMD, OPT)
45
+ rs.items.each do |item|
46
+ puts item.to_s
47
+ puts
48
+ end
49
+ end
data/lib/cui.rb ADDED
@@ -0,0 +1,71 @@
1
+ module Maidcafe
2
+ module CUI
3
+ def prompt(api, condition, mandatory=false)
4
+ puts
5
+ unless mandatory
6
+ print "#{{:tid => '都道府県名', :cid => 'カテゴリ', :sid => 'お店番号'}[condition]}を指定しますか (y/N)"
7
+ return nil unless gets =~ /[yY]/
8
+ end
9
+ rs = api.invoke nil, condition => 'list'
10
+ rs.items.each_with_index do |t, index|
11
+ puts "#{index+1} : #{t.name}"
12
+ end
13
+ print '選択してください: '
14
+ input = gets
15
+ exit if input =~ /^\s*(quit|exit)\s*$/i
16
+ type_id = input.to_i
17
+ type_id <= 0 ? nil : rs.items[type_id - 1]
18
+ end
19
+
20
+ def puts_name(obj)
21
+ puts obj.name if obj
22
+ end
23
+
24
+ def show_opt(opt, key, obj)
25
+ puts_name obj
26
+ opt[key] = obj.id if obj
27
+ end
28
+
29
+ def start
30
+ api = Maidcafe::API.new
31
+ type = nil
32
+ while !type
33
+ type = prompt api, :type, true
34
+ end
35
+ puts_name type
36
+
37
+ opts = {}
38
+
39
+ prefecture = prompt api, :tid
40
+ show_opt opts, :tid, prefecture
41
+
42
+ category = prompt api, :cid
43
+ show_opt opts, :cid, category
44
+
45
+ shop = prompt api, :sid
46
+ show_opt opts, :sid, shop
47
+
48
+ rs = api.invoke type.keytype, opts
49
+ while true
50
+ if rs.items.empty?
51
+ puts "データが存在しません"
52
+ exit
53
+ end
54
+ rs.items.each_with_index do |i, index|
55
+ puts "#{index+1} : #{i.name} - #{i.description.split("\n")[0]}"
56
+ end
57
+ print '選択してください: '
58
+ input = gets
59
+ exit if input =~ /^\s*(quit|exit)\s*$/i
60
+ item_id = input.to_i
61
+ if 0 < item_id
62
+ puts rs.items[item_id - 1].to_s
63
+ end
64
+ puts "\n[push enter key]"
65
+ gets
66
+ end
67
+ end
68
+
69
+ module_function :start, :prompt, :puts_name, :show_opt
70
+ end
71
+ end
@@ -0,0 +1,231 @@
1
+ require 'erb'
2
+ require 'open-uri'
3
+ require 'rexml/document'
4
+
5
+ def dputs(str)
6
+ puts str if $DEBUG
7
+ end
8
+
9
+ module Maidcafe
10
+ VERSION = '0.0.1'
11
+
12
+ class API
13
+ BASE_URL = 'http://moeten.info/maidcafe'
14
+
15
+ def list(type)
16
+ case type
17
+ when :type
18
+ invoke 'list'
19
+ when :tid, :prefecture
20
+ invoke nil, :tid => 'list'
21
+ when :cid, :category
22
+ invoke nil, :cid => 'list'
23
+ when :sid, :shop
24
+ invoke nil, :sid => 'list'
25
+ else
26
+ throw ArgumentError.new
27
+ end
28
+ end
29
+
30
+ def read(type, args)
31
+ open(request_url(type, args)) {|f| REXML::Document.new f}.write
32
+ end
33
+
34
+ def invoke(type, args=[])
35
+ url = request_url(type, args)
36
+ dputs "access to #{url}"
37
+ ResultSet.new(type, open(url) {|f| REXML::Document.new f})
38
+ end
39
+
40
+ def request_url(type, args)
41
+ type_arg = type ? "type=#{type}" : ''
42
+ other_args = args.to_a.map{|e| "#{e[0]}=#{e[1]}"}.join '&'
43
+ "#{BASE_URL}/?#{type_arg}&#{other_args}&m=api"
44
+ end
45
+
46
+ def method_missing(name, *args, &block)
47
+ invoke name, args
48
+ end
49
+ end
50
+
51
+ class ResultItem
52
+ def initialize(xml)
53
+ read xml
54
+ end
55
+
56
+ def read(xml)
57
+ attr_to_tag.each do |attr, tag|
58
+ send "#{attr}=", element_text(xml, tag)
59
+ end
60
+ end
61
+
62
+ def attr_to_tag
63
+ {}
64
+ end
65
+
66
+ def element_text(xml, tag)
67
+ (tag = xml.elements[tag]) ? tag.text : nil
68
+ end
69
+
70
+ def to_s
71
+ "ID: #{id}\n名前: #{name}"
72
+ end
73
+ end
74
+
75
+ class ResultSet < ResultItem
76
+ attr_accessor :title, :link, :description, :language, :generator, :items
77
+
78
+ def initialize(type, xml)
79
+ @items = []
80
+ read xml
81
+ xml.elements.each('channel/item') do |item|
82
+ @items.push(
83
+ if item.elements['image']; Moe.new(type, item)
84
+ elsif item.elements['tid']; Prefecture.new(item)
85
+ elsif item.elements['cid']; Category.new(item)
86
+ elsif item.elements['sid']; Shop.new(item)
87
+ elsif item.elements['keytype']; Type.new(item)
88
+ else; raise ArgumentError.new(item.inspect)
89
+ end
90
+ )
91
+ end
92
+ end
93
+
94
+ def attr_to_tag
95
+ {:title => 'channel/title', :link => 'channel/link', :description => 'channel/description',
96
+ :language => 'channel/language', :generator => 'channel/generator'}
97
+ end
98
+ end
99
+
100
+ class Location
101
+ attr_accessor :latitude, :longitude
102
+ def initialize(lat, lng)
103
+ @latitude = lat
104
+ @longitude = lng
105
+ end
106
+ end
107
+
108
+ class Moe < ResultItem
109
+ attr_accessor :name, :category, :prefecture, :description, :images,
110
+ :url, :url_mobile, :tel, :address, :gps, :wgps, :map_urls
111
+
112
+ def initialize(type, xml)
113
+ read(xml)
114
+ @specialities = self.class.const_get(type.to_s.capitalize).new(xml)
115
+ @images = [element_text(xml, 'image/image1'),
116
+ element_text(xml, 'image/image2'),
117
+ element_text(xml, 'image/image3')].compact
118
+ @map_urls = [element_text(xml, 'map/map_livedoor'),
119
+ element_text(xml, 'map/map_navitime'),
120
+ element_text(xml, 'map/map_mapfan'),
121
+ element_text(xml, 'map/map_mapion'),
122
+ element_text(xml, 'map/map_googlemap'),
123
+ element_text(xml, 'map/map_alps')].compact
124
+ @gps = Location.new element_text(xml, 'gps_lat'), element_text(xml, 'gps_lon')
125
+ @wgps = Location.new element_text(xml, 'wgps_lat'), element_text(xml, 'wgps_lon')
126
+ end
127
+
128
+ def attr_to_tag
129
+ {:name => 'shopname', :category => 'category', :prefecture => 'todouhuken',
130
+ :description => 'setumei', :url => 'url', :url_mobile => 'url_mobile',
131
+ :tel => 'tel', :address => 'address'}
132
+ end
133
+
134
+ def to_s
135
+ ERB.new(<<-end_of_template).result(binding)
136
+ 店名: <%= name %>
137
+ 住所: <%= address %>
138
+ Tel: <%= tel %>
139
+ 説明: <%= description.gsub("\n", "\n ").chomp %>
140
+ end_of_template
141
+ end
142
+
143
+ def method_missing(name, *args, &block)
144
+ @specialities.send name, *args
145
+ end
146
+
147
+ class Shop < ResultItem
148
+ attr_accessor :opening_hour, :menu
149
+ def attr_to_tag; {:opening_hour => 'eigyoujikan', :menu => 'menu'} end
150
+ end
151
+
152
+ class Event < ResultItem
153
+ end
154
+
155
+ class Coupon < ResultItem
156
+ attr_accessor :coupon, :submit_condition, :number_condition
157
+ def attr_to_tag
158
+ {:coupon => 'coupon', :submit_condition => 'teiji_zyouken',
159
+ :number_condition => 'riyou_zyouken'}
160
+ end
161
+ end
162
+
163
+ class Baito < ResultItem
164
+ attr_accessor :hourly_wage, :work_hours, :ages, :hischool, :cv, :interview_time, :q_and_a
165
+ def attr_to_tag
166
+ {:hourly_wage => 'jikyuu', :work_hours => 'kinmujikan', :ages => 'nenren',
167
+ :hischool => 'koukousei', :cv => 'rirekisyo', :interview_time => 'mensetubi',
168
+ :q_and_a => 'qanda'}
169
+ end
170
+ end
171
+
172
+ class Opendate < ResultItem
173
+ end
174
+
175
+ class Newshop < ResultItem
176
+ attr_accessor :date, :wdate
177
+ def attr_to_tag; {:date => 'date', :wdate => 'mdate'} end
178
+ end
179
+
180
+ class Dayinfo < ResultItem
181
+ end
182
+
183
+ class Eventinfo < ResultItem
184
+ end
185
+ end
186
+
187
+ class Type < ResultItem
188
+ SHOP = 'shop'
189
+ EVENT = 'event'
190
+ COUPON = 'coupon'
191
+ BAITO = 'baito'
192
+ OPENDATE = 'opendate'
193
+ NEWSHOP = 'newshop'
194
+ DAYINFO = 'dayinfo'
195
+ EVENTINFO = 'eventinfo'
196
+
197
+ attr_accessor :type, :keytype
198
+ def attr_to_tag; {:type => 'type', :keytype => 'keytype'} end
199
+ def name; type end
200
+ end
201
+
202
+ class Prefecture < ResultItem
203
+ ANY = nil
204
+ HOKAIDO = 1
205
+ TOCHIGI = 9
206
+ SAITAMA = 11
207
+ TOKYO = 13
208
+ KANAGAWA = 14
209
+ GIFU = 21
210
+ AICHI = 23
211
+ OSAKA = 27
212
+ FUKUOKA = 40
213
+ KAGOSHIMA = 46
214
+
215
+ attr_accessor :name, :tid
216
+ def attr_to_tag; {:name => 'todouhuken', :tid => 'tid'} end
217
+ def id; tid end
218
+ end
219
+
220
+ class Category < ResultItem
221
+ attr_accessor :name, :cid
222
+ def attr_to_tag; {:name => 'category', :cid => 'cid'} end
223
+ def id; cid end
224
+ end
225
+
226
+ class Shop < ResultItem
227
+ attr_accessor :name, :sid
228
+ def attr_to_tag; {:name => 'shopname', :sid => 'sid'} end
229
+ def id; sid end
230
+ end
231
+ end