ruby-maidcafe 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -0
- data/Manifest.txt +11 -0
- data/README.txt +103 -0
- data/Rakefile +15 -0
- data/bin/ruby-maidcafe +49 -0
- data/lib/cui.rb +71 -0
- data/lib/ruby-maidcafe.rb +231 -0
- data/test/data/shop_t_13.xml +10049 -0
- data/test/data/t_list.xml +49 -0
- data/test/test_resultset.rb +27 -0
- data/test/test_ruby-maidcafe.rb +42 -0
- metadata +65 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
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
|