syobocal 0.10.0 → 0.14.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.
- checksums.yaml +4 -4
- data/README.md +174 -7
- data/Rakefile +9 -1
- data/data/.gitkeep +0 -0
- data/lib/syobocal/calchk.rb +5 -5
- data/lib/syobocal/comment/cast.rb +15 -0
- data/lib/syobocal/comment/element/blank.rb +25 -0
- data/lib/syobocal/comment/element/header1.rb +27 -0
- data/lib/syobocal/comment/element/header2.rb +27 -0
- data/lib/syobocal/comment/element/link.rb +30 -0
- data/lib/syobocal/comment/element/list.rb +27 -0
- data/lib/syobocal/comment/element/root.rb +17 -0
- data/lib/syobocal/comment/element/row.rb +36 -0
- data/lib/syobocal/comment/element/text.rb +21 -0
- data/lib/syobocal/comment/element/text_node.rb +55 -0
- data/lib/syobocal/comment/helper/fragment.rb +80 -0
- data/lib/syobocal/comment/music.rb +15 -0
- data/lib/syobocal/comment/music_data.rb +15 -0
- data/lib/syobocal/comment/parser.rb +103 -0
- data/lib/syobocal/comment/person.rb +33 -0
- data/lib/syobocal/comment/section.rb +136 -0
- data/lib/syobocal/comment/staff.rb +15 -0
- data/lib/syobocal/db.rb +38 -38
- data/lib/syobocal/json.rb +18 -18
- data/lib/syobocal/rss.rb +19 -19
- data/lib/syobocal/rss2.rb +13 -13
- data/lib/syobocal/sub_titles/parser.rb +21 -0
- data/lib/syobocal/sub_titles/sub_title.rb +15 -0
- data/lib/syobocal/util.rb +6 -6
- data/lib/syobocal/version.rb +1 -1
- data/lib/syobocal.rb +30 -11
- data/scripts/download_db_title_lookup.rb +15 -0
- data/scripts/parse_comment.rb +20 -0
- data/syobocal.gemspec +19 -12
- metadata +101 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 737c5456c21b70925571795e9b8f81b35ba89738065dfaec3332c11d3920e19f
|
4
|
+
data.tar.gz: 26ef1a657a390e37fef06c2a0ac7ed681a3bbd951fb6f779f248a61bc08aaddd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb49862e127d7abba99f6a09fe93e2f357d4e331243e6236314c23c1d9e5384cd39af7501009c77ab07ac0a7f8a00ae71082f5f5b6fbb1060da31051160d03b7
|
7
|
+
data.tar.gz: 3aaf9a0a8bdfbf76e703608dadbd97d6f6971ec787772e0507aeeae47109dc179654e5fd56c4e50e0b691df8df5c8cd3525f7395d42c0e43f68de059038efa60
|
data/README.md
CHANGED
@@ -2,8 +2,7 @@
|
|
2
2
|
|
3
3
|
## 概要
|
4
4
|
|
5
|
-
syobocalは[しょぼいカレンダー](http://cal.syoboi.jp/)からデータを取得するためのRuby
|
6
|
-
sch_uploadを除いて仕様が公開されている以下20種類のエンドポイントすべてに対応しています。
|
5
|
+
syobocalは[しょぼいカレンダー](http://cal.syoboi.jp/)からデータを取得するためのRubyスクリプトです。`sch_upload`を除いて仕様が公開されている以下20種類のエンドポイントすべてに対応しています。
|
7
6
|
|
8
7
|
- cal_chk.php
|
9
8
|
- db.php
|
@@ -49,12 +48,180 @@ sch_uploadを除いて仕様が公開されている以下20種類のエンド
|
|
49
48
|
XMLやRSSをパースした結果は値が適切な型に変換されたハッシュの配列です。
|
50
49
|
JSONをパースした結果はJSONのデータ構造をそのまま返します。
|
51
50
|
|
52
|
-
|
51
|
+
いくつか典型的だろう使い方を紹介します。
|
52
|
+
|
53
|
+
以下は`db.php`からアニメごとに採番された`TID`とタイトルを一覧で取得する例です。
|
54
|
+
|
55
|
+
```ruby
|
53
56
|
require 'syobocal'
|
54
57
|
require 'pp'
|
55
|
-
|
56
|
-
pp Syobocal::DB::TitleLookup.get(
|
57
|
-
|
58
|
+
|
59
|
+
pp Syobocal::DB::TitleLookup.get({'TID' => '*', 'Fields' => 'TID,Title'})
|
60
|
+
# [{:tid=>1, :title=>"魔法遣いに大切なこと"},
|
61
|
+
# {:tid=>2, :title=>"ソニックX"},
|
62
|
+
# {:tid=>3, :title=>"デ・ジ・キャラットにょ"},
|
63
|
+
# {:tid=>1224, :title=>"魔法先生ネギま!(実写版)"},
|
64
|
+
# {:tid=>3601, :title=>"新妹魔王の契約者"},
|
65
|
+
# {:tid=>3588, :title=>"アイドルマスター シンデレラガールズ"},
|
66
|
+
# {:tid=>4, :title=>"妄想科学シリーズ ワンダバスタイル"},
|
67
|
+
# {:tid=>1086, :title=>"ひとひら"},
|
68
|
+
# {:tid=>5, :title=>"DEARBOYS"},
|
69
|
+
# {:tid=>6, :title=>"LAST EXILE"},
|
70
|
+
# ...
|
71
|
+
```
|
72
|
+
|
73
|
+
以下は`TID`指定で特定のアニメのデータを取得する例です。
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
require 'syobocal'
|
77
|
+
require 'pp'
|
78
|
+
|
79
|
+
title = Syobocal::DB::TitleLookup.get({'TID' => '2077'})
|
80
|
+
pp title.first
|
81
|
+
# {:tid=>2077,
|
82
|
+
# :last_update=>2013-12-02 02:17:55 +0900,
|
83
|
+
# :title=>"魔法少女まどか☆マギカ",
|
84
|
+
# :short_title=>"まどか☆マギカ",
|
85
|
+
# :title_yomi=>"まほうしょうじょまどかまぎか",
|
86
|
+
# :title_en=>"PUELLA MAGI MADOKA MAGICA",
|
87
|
+
# :comment=>
|
88
|
+
# "*リンク\n" +
|
89
|
+
# "-[[公式 http://www.madoka-magica.com/tv/]]\n" +
|
90
|
+
# "-[[MBS http://www.mbs.jp/madoka-magica/]]\n" +
|
91
|
+
# "-[[ニコニコチャンネル http://ch.nicovideo.jp/ch260]]\n" +
|
92
|
+
# "-[[TOKYO MX http://www.mxtv.co.jp/madoka_magica/]]\n" +
|
93
|
+
# "-[[BS11デジタル http://www.bs11.jp/anime/2360/]]\n" +
|
94
|
+
# "*メモ\n" +
|
95
|
+
# "-アニマックスでのリピート放送は放送日の27:00~と日曜26:00~\n" +
|
96
|
+
# "*スタッフ\n" +
|
97
|
+
# ":原作:Magica Quartet\n" +
|
98
|
+
# ":監督:新房昭之\n" +
|
99
|
+
# ":キャラクター原案:蒼樹うめ\n" +
|
100
|
+
# ":キャラクターデザイン:岸田隆宏\n" +
|
101
|
+
# ...
|
102
|
+
```
|
103
|
+
|
104
|
+
APIリクエスト時のパラメータは`syobocal`ライブラリとして解釈しません。しょぼいカレンダーのAPI仕様を参照して下さい。また`'TID' => '*'`に代表される大量のデータ取得はしばしば10MB以上の巨大なレスポンスを返します。しょぼいカレンダー側も大変だろうと思うので、使い方は考えて下さい。
|
105
|
+
|
106
|
+
### コメントパーサ(ベータ版)
|
107
|
+
|
108
|
+
しょぼいカレンダーの`comment`属性にはスタッフやキャストなど多岐に渡る情報が専用の記法で格納されています。バージョン`0.11`以降の`syobocal`ライブラリはコメントパーサ(`Syobocal::Comment::Parser`)を備え、それらの情報を意味的に扱いやすい形で取り出すことができます。コメントパーサはベータ版です。インタフェースは変更される可能性があります。
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
require 'syobocal'
|
112
|
+
require 'pp'
|
113
|
+
|
114
|
+
title = Syobocal::DB::TitleLookup.get({'TID' => '2077'})
|
115
|
+
comment = title.first[:comment]
|
116
|
+
parser = Syobocal::Comment::Parser.new(comment)
|
117
|
+
|
118
|
+
pp parser.links.first
|
119
|
+
# #<Syobocal::Comment::Element::Link:0x0000564600b42e30
|
120
|
+
# @str="公式",
|
121
|
+
# @url="http://www.madoka-magica.com/tv/">
|
122
|
+
|
123
|
+
pp parser.staffs.first
|
124
|
+
# #<Syobocal::Comment::Staff:0x00005645ffc60048
|
125
|
+
# @people=
|
126
|
+
# [#<Syobocal::Comment::Person:0x00005645ffc60368
|
127
|
+
# @name="Magica Quartet",
|
128
|
+
# @note=nil>],
|
129
|
+
# @role="原作">
|
130
|
+
|
131
|
+
pp parser.casts.first
|
132
|
+
# #<Syobocal::Comment::Cast:0x0000564600051b70
|
133
|
+
# @character="鹿目まどか",
|
134
|
+
# @people=
|
135
|
+
# [#<Syobocal::Comment::Person:0x0000564600051cd8 @name="悠木碧", @note=nil>]>
|
136
|
+
|
137
|
+
pp parser.musics.first
|
138
|
+
# #<Syobocal::Comment::Music:0x0000561287c8f1a0
|
139
|
+
# @category="オープニングテーマ",
|
140
|
+
# @data_list=
|
141
|
+
# [#<Syobocal::Comment::MusicData:0x0000561287c8b8e8
|
142
|
+
# @attr="作詞・作曲",
|
143
|
+
# @attr_note=nil,
|
144
|
+
# @attr_text="作詞・作曲",
|
145
|
+
# @people=
|
146
|
+
# [#<Syobocal::Comment::Person:0x0000561287c8ba28 @name="渡辺翔", @note=nil>],
|
147
|
+
# @value="渡辺翔">,
|
148
|
+
# #<Syobocal::Comment::MusicData:0x0000561287c8a3d0
|
149
|
+
# @attr="主題歌協力",
|
150
|
+
# @attr_note=nil,
|
151
|
+
# @attr_text="主題歌協力",
|
152
|
+
# @people=
|
153
|
+
# [#<Syobocal::Comment::Person:0x0000561287c8a510 @name="外村敬一", @note=nil>],
|
154
|
+
# @value="外村敬一">,
|
155
|
+
# #<Syobocal::Comment::MusicData:0x0000561287c88e90
|
156
|
+
# @attr="歌",
|
157
|
+
# @attr_note=nil,
|
158
|
+
# @attr_text="歌",
|
159
|
+
# @people=
|
160
|
+
# [#<Syobocal::Comment::Person:0x0000561287c88fd0
|
161
|
+
# @name="ClariS",
|
162
|
+
# @note=nil>],
|
163
|
+
# @value="ClariS">,
|
164
|
+
# #<Syobocal::Comment::MusicData:0x0000561287c8f2e0
|
165
|
+
# @attr="使用話数",
|
166
|
+
# @attr_note=nil,
|
167
|
+
# @attr_text="使用話数",
|
168
|
+
# @people=
|
169
|
+
# [#<Syobocal::Comment::Person:0x0000561287c8f560 @name="#1~#9", @note=nil>,
|
170
|
+
# #<Syobocal::Comment::Person:0x0000561287c8f420 @name="#11", @note=nil>],
|
171
|
+
# @value="#1~#9、#11">],
|
172
|
+
# @title="コネクト">
|
173
|
+
```
|
174
|
+
|
175
|
+
### サブタイトルパーサ(ベータ版)
|
176
|
+
|
177
|
+
しょぼいカレンダーの`sub_titles`属性をパースして扱いやすい形にして取り出すパーサです。
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
require 'syobocal'
|
181
|
+
require 'pp'
|
182
|
+
|
183
|
+
title = Syobocal::DB::TitleLookup.get({'TID' => '2077'})
|
184
|
+
sub_titles = title.first[:sub_titles]
|
185
|
+
parser = Syobocal::SubTitles::Parser.new(sub_titles)
|
186
|
+
|
187
|
+
pp parser.parse
|
188
|
+
# [#<Syobocal::SubTitles::SubTitle:0x00005591edc37830
|
189
|
+
# @episode="01",
|
190
|
+
# @title="夢の中で会った、ような・・・・・">,
|
191
|
+
# #<Syobocal::SubTitles::SubTitle:0x00005591edc37448
|
192
|
+
# @episode="02",
|
193
|
+
# @title="それはとっても嬉しいなって">,
|
194
|
+
# #<Syobocal::SubTitles::SubTitle:0x00005591edc370b0
|
195
|
+
# @episode="03",
|
196
|
+
# @title="もう何も恐くない">,
|
197
|
+
# #<Syobocal::SubTitles::SubTitle:0x00005591edc36d40
|
198
|
+
# @episode="04",
|
199
|
+
# @title="奇跡も、魔法も、あるんだよ">,
|
200
|
+
# #<Syobocal::SubTitles::SubTitle:0x00005591edc36c00
|
201
|
+
# @episode="05",
|
202
|
+
# @title="後悔なんて、あるわけない">,
|
203
|
+
# #<Syobocal::SubTitles::SubTitle:0x00005591edc36700
|
204
|
+
# @episode="06",
|
205
|
+
# @title="こんなの絶対おかしいよ">,
|
206
|
+
# #<Syobocal::SubTitles::SubTitle:0x00005591edc36408
|
207
|
+
# @episode="07",
|
208
|
+
# @title="本当の気持ちと向き合えますか?">,
|
209
|
+
# #<Syobocal::SubTitles::SubTitle:0x00005591edc36110
|
210
|
+
# @episode="08",
|
211
|
+
# @title="あたしって、ほんとバカ">,
|
212
|
+
# #<Syobocal::SubTitles::SubTitle:0x00005591edc35c88
|
213
|
+
# @episode="09",
|
214
|
+
# @title="そんなの、あたしが許さない">,
|
215
|
+
# #<Syobocal::SubTitles::SubTitle:0x00005591edc34d38
|
216
|
+
# @episode="10",
|
217
|
+
# @title="もう誰にも頼らない">,
|
218
|
+
# #<Syobocal::SubTitles::SubTitle:0x00005591edc34a68
|
219
|
+
# @episode="11",
|
220
|
+
# @title="最後に残った道しるべ">,
|
221
|
+
# #<Syobocal::SubTitles::SubTitle:0x00005591edc34900
|
222
|
+
# @episode="12",
|
223
|
+
# @title="わたしの、最高の友達">]
|
224
|
+
```
|
58
225
|
|
59
226
|
## おまけ
|
60
227
|
|
@@ -109,7 +276,7 @@ $ syobocal-anime
|
|
109
276
|
|
110
277
|
## 作者
|
111
278
|
|
112
|
-
- [xmisao](
|
279
|
+
- [xmisao](https://www.xmisao.com/)
|
113
280
|
|
114
281
|
## ライセンス
|
115
282
|
|
data/Rakefile
CHANGED
data/data/.gitkeep
ADDED
File without changes
|
data/lib/syobocal/calchk.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module Syobocal
|
2
2
|
module CalChk
|
3
3
|
def get(params = {})
|
4
|
-
parse(open(url(params)))
|
4
|
+
parse(URI.open(url(params)))
|
5
5
|
end
|
6
6
|
|
7
7
|
def url(params = {})
|
8
|
-
|
8
|
+
"http://cal.syoboi.jp/cal_chk.php" + Syobocal::Util.format_params(params)
|
9
9
|
end
|
10
10
|
|
11
11
|
def parse(xml)
|
@@ -13,14 +13,14 @@ module Syobocal
|
|
13
13
|
|
14
14
|
result = Result.new
|
15
15
|
|
16
|
-
syobocal = xml.elements[
|
16
|
+
syobocal = xml.elements["syobocal"]
|
17
17
|
result.url = syobocal.attribute("url").to_s
|
18
18
|
result.version = syobocal.attribute("version").to_s
|
19
19
|
result.last_update = Time.parse(syobocal.attribute("LastUpdate").to_s)
|
20
20
|
result.spid = syobocal.attribute("SPID").to_s
|
21
21
|
result.spname = syobocal.attribute("SPNAME").to_s
|
22
22
|
|
23
|
-
xml.elements.each(
|
23
|
+
xml.elements.each("syobocal/ProgItems/ProgItem") { |item|
|
24
24
|
result << {
|
25
25
|
:pid => item.attribute("PID").to_s.to_i,
|
26
26
|
:tid => item.attribute("TID").to_s.to_i,
|
@@ -32,7 +32,7 @@ module Syobocal
|
|
32
32
|
:st_offset => item.attribute("StOffset").to_s.to_i,
|
33
33
|
:sub_title => item.attribute("SubTitle").to_s,
|
34
34
|
:title => item.attribute("Title").to_s,
|
35
|
-
:prog_comment => item.attribute("ProgComment").to_s
|
35
|
+
:prog_comment => item.attribute("ProgComment").to_s,
|
36
36
|
}
|
37
37
|
}
|
38
38
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Syobocal
|
2
|
+
module Comment
|
3
|
+
class Cast
|
4
|
+
attr_reader :character, :people
|
5
|
+
|
6
|
+
def initialize(character, people)
|
7
|
+
@character, @people = character, people
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
other.instance_of?(self.class) && other.character == character && other.people == people
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Syobocal
|
2
|
+
module Comment
|
3
|
+
module Element
|
4
|
+
class Blank
|
5
|
+
def initialize; end
|
6
|
+
|
7
|
+
def ==(other)
|
8
|
+
other.instance_of?(self.class)
|
9
|
+
end
|
10
|
+
|
11
|
+
def inner_text
|
12
|
+
""
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.match?(line)
|
16
|
+
line.match(/\A\s*\Z/)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.parse(line)
|
20
|
+
Element::Blank.new
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Syobocal
|
2
|
+
module Comment
|
3
|
+
module Element
|
4
|
+
class Header1
|
5
|
+
attr_reader :text_node
|
6
|
+
|
7
|
+
def initialize(text_node)
|
8
|
+
@text_node = text_node
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(other)
|
12
|
+
other.instance_of?(self.class) && other.text_node == text_node
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.match?(line)
|
16
|
+
!Header2.match?(line) && line.start_with?("*")
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.parse(line)
|
20
|
+
txt = line.match(/\A\*(.*)\Z/)[1]
|
21
|
+
|
22
|
+
Element::Header1.new(Element::TextNode.parse(txt))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Syobocal
|
2
|
+
module Comment
|
3
|
+
module Element
|
4
|
+
class Header2
|
5
|
+
attr_reader :text_node
|
6
|
+
|
7
|
+
def initialize(text_node)
|
8
|
+
@text_node = text_node
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(other)
|
12
|
+
other.instance_of?(self.class) && other.text_node == text_node
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.match?(line)
|
16
|
+
line.start_with?("**")
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.parse(line)
|
20
|
+
txt = line.match(/\A\*\*(.*)\Z/)[1]
|
21
|
+
|
22
|
+
Element::Header2.new(Element::TextNode.parse(txt))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Syobocal
|
2
|
+
module Comment
|
3
|
+
module Element
|
4
|
+
class Link
|
5
|
+
attr_reader :str, :url
|
6
|
+
|
7
|
+
def initialize(str, url)
|
8
|
+
@str, @url = str, url
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(other)
|
12
|
+
other.instance_of?(self.class) && other.str == str && other.url == url
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.create(text)
|
16
|
+
inner_str = text.match(/\A\[\[(.*)\]\]\Z/)[1]
|
17
|
+
|
18
|
+
sep = inner_str.split(/ /)
|
19
|
+
|
20
|
+
if ["http://", "https://", "archive://"].any? { |scheme| sep.last.start_with?(scheme) }
|
21
|
+
link = sep.pop
|
22
|
+
new(sep.join(" "), link)
|
23
|
+
else
|
24
|
+
new(sep.join(" "), nil)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Syobocal
|
2
|
+
module Comment
|
3
|
+
module Element
|
4
|
+
class List
|
5
|
+
attr_reader :text_node
|
6
|
+
|
7
|
+
def initialize(text_node)
|
8
|
+
@text_node = text_node
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(other)
|
12
|
+
other.instance_of?(self.class) && other.text_node == text_node
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.match?(line)
|
16
|
+
line.start_with?("-")
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.parse(line)
|
20
|
+
txt = line.match(/\A-(.*)\Z/)[1]
|
21
|
+
|
22
|
+
Element::List.new(Element::TextNode.parse(txt))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Syobocal
|
2
|
+
module Comment
|
3
|
+
module Element
|
4
|
+
class Row
|
5
|
+
attr_reader :attr_node, :value_node
|
6
|
+
|
7
|
+
def initialize(attr_node, value_node)
|
8
|
+
@attr_node, @value_node = attr_node, value_node
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(other)
|
12
|
+
other.instance_of?(self.class) && other.attr_node == attr_node && other.value_node == value_node
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.match?(line)
|
16
|
+
line.start_with?(":")
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.parse(line)
|
20
|
+
if line.scan(":").length == 1
|
21
|
+
# NOTE :が1つしか含まれない行は:以降が値となる
|
22
|
+
m = line.match(/\A:(.*)\Z/)
|
23
|
+
attr = Element::Blank.new
|
24
|
+
value = Element::TextNode.parse(m[1])
|
25
|
+
else
|
26
|
+
m = line.match(/\A:([^:]*?):(.*)\Z/)
|
27
|
+
attr = Element::TextNode.parse(m[1])
|
28
|
+
value = Element::TextNode.parse(m[2])
|
29
|
+
end
|
30
|
+
|
31
|
+
Element::Row.new(attr, value)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Syobocal
|
2
|
+
module Comment
|
3
|
+
module Element
|
4
|
+
class Text
|
5
|
+
attr_reader :str
|
6
|
+
|
7
|
+
def initialize(str)
|
8
|
+
@str = str
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(other)
|
12
|
+
other.instance_of?(self.class) && other.str == str
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.create(text)
|
16
|
+
new(text)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Syobocal
|
2
|
+
module Comment
|
3
|
+
module Element
|
4
|
+
class TextNode
|
5
|
+
attr_reader :text_elements
|
6
|
+
|
7
|
+
SUBJECT_SEPARATOR = "、"
|
8
|
+
ENCODED_SEPARATOR = "\t"
|
9
|
+
|
10
|
+
def initialize(text_elements)
|
11
|
+
@text_elements = text_elements
|
12
|
+
end
|
13
|
+
|
14
|
+
def inner_text
|
15
|
+
text_elements.map(&:str).join("")
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(other)
|
19
|
+
other.instance_of?(self.class) && other.text_elements == text_elements
|
20
|
+
end
|
21
|
+
|
22
|
+
def split
|
23
|
+
buffer = ""
|
24
|
+
|
25
|
+
text_elements.each do |node|
|
26
|
+
case node
|
27
|
+
when Element::Text
|
28
|
+
buffer << node.str.gsub(SUBJECT_SEPARATOR, ENCODED_SEPARATOR)
|
29
|
+
when Element::Link
|
30
|
+
buffer << node.str
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
buffer.scan(/[^#{ENCODED_SEPARATOR}\(]+(?:\(.*?\))?/).map { |str| str.gsub(ENCODED_SEPARATOR, SUBJECT_SEPARATOR).strip }
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.match?(line)
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.parse(text)
|
42
|
+
elements = text.split(/(\[\[[^\[\]]*?\]\])/).select { |s| !s.empty? }.map do |s|
|
43
|
+
if s.match(/\A\[\[[^\[\]]*?\]\]\Z/)
|
44
|
+
Link.create(s)
|
45
|
+
else
|
46
|
+
Text.create(s)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Element::TextNode.new(elements)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Syobocal
|
2
|
+
module Comment
|
3
|
+
module Helper
|
4
|
+
class Fragment
|
5
|
+
CHILD_BEGIN = '('
|
6
|
+
CHILD_END = ')'
|
7
|
+
SEPARATOR = '、'
|
8
|
+
|
9
|
+
attr_reader :text, :child, :following
|
10
|
+
|
11
|
+
def initialize(text, child, following)
|
12
|
+
@text, @child, @following = text, child, following
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.parse(str)
|
16
|
+
chars = str.each_char.to_a
|
17
|
+
|
18
|
+
parse_chars(chars)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.parse_chars(chars)
|
22
|
+
text = ""
|
23
|
+
child = nil
|
24
|
+
following = nil
|
25
|
+
|
26
|
+
until chars.empty?
|
27
|
+
c = chars.shift
|
28
|
+
|
29
|
+
case c
|
30
|
+
when CHILD_BEGIN
|
31
|
+
child = parse_chars(chars)
|
32
|
+
when CHILD_END
|
33
|
+
return Fragment.new(text, child, following)
|
34
|
+
when SEPARATOR
|
35
|
+
following = parse_chars(chars)
|
36
|
+
else
|
37
|
+
text << c
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Fragment.new(text, child, following)
|
42
|
+
end
|
43
|
+
|
44
|
+
def pretty_output(level = 0)
|
45
|
+
line = level.times.map{' '}.join
|
46
|
+
|
47
|
+
line << text
|
48
|
+
|
49
|
+
puts line
|
50
|
+
|
51
|
+
child&.pretty_output(level + 1)
|
52
|
+
|
53
|
+
following&.pretty_output(level)
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_a
|
57
|
+
array = [self]
|
58
|
+
target = self
|
59
|
+
|
60
|
+
while target.following
|
61
|
+
array << target.following
|
62
|
+
target = target.following
|
63
|
+
end
|
64
|
+
|
65
|
+
array
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_s
|
69
|
+
to_a.map{|f|
|
70
|
+
if f.child
|
71
|
+
"#{f.text}(#{f.child.to_s})"
|
72
|
+
else
|
73
|
+
"#{f.text}"
|
74
|
+
end
|
75
|
+
}.join(SEPARATOR)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Syobocal
|
2
|
+
module Comment
|
3
|
+
class Music
|
4
|
+
attr_reader :title, :category, :data_list
|
5
|
+
|
6
|
+
def initialize(title, category, data_list)
|
7
|
+
@title, @category, @data_list = title, category, data_list
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
other.instance_of?(self.class) && other.title == title && other.category == category && other.data_list == data_list
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Syobocal
|
2
|
+
module Comment
|
3
|
+
class MusicData
|
4
|
+
attr_reader :attr, :attr_text, :attr_note, :value, :people
|
5
|
+
|
6
|
+
def initialize(attr, attr_text, attr_note, value, people)
|
7
|
+
@attr, @attr_text, @attr_note, @value, @people = attr, attr_text, attr_note, value, people
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
other.instance_of?(self.class) && other.attr == attr && other.attr_text == attr_text && other.attr_note == other.attr_note && other.value == value && other.people == people
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|