weather_jp 1.0.4 → 2.0.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/.coveralls.yml +1 -0
- data/.gitignore +1 -0
- data/.travis.yml +3 -5
- data/CHANGELOG.md +20 -0
- data/README.md +33 -36
- data/Rakefile +1 -1
- data/lib/weather_jp.rb +26 -191
- data/lib/weather_jp/adapter.rb +89 -0
- data/lib/weather_jp/city.rb +52 -0
- data/lib/weather_jp/day_weather.rb +124 -38
- data/lib/weather_jp/request_parser.rb +42 -0
- data/lib/weather_jp/version.rb +1 -1
- data/lib/weather_jp/weather.rb +82 -0
- data/spec/fixture/not_found.xml +1 -0
- data/spec/fixture/tokyo.xml +1 -0
- data/spec/spec_helper.rb +32 -28
- data/spec/weather_jp/adapter_spec.rb +44 -0
- data/spec/weather_jp/city_spec.rb +35 -0
- data/spec/weather_jp/day_weather_spec.rb +91 -0
- data/spec/weather_jp/request_parser_spec.rb +31 -0
- data/spec/weather_jp/weather_spec.rb +51 -0
- data/spec/weather_jp_spec.rb +10 -44
- data/weather_jp.gemspec +8 -3
- metadata +108 -30
- data/HISTORY.md +0 -16
- data/README.jp.md +0 -107
- data/spec/day_weather_spec.rb +0 -69
- data/spec/fixture/RSS.rss +0 -1
- data/spec/fixture/ny.rss +0 -33
- data/spec/weather_spec.rb +0 -125
- data/spec/wrapper_spec.rb +0 -56
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88a46156ea60f987d673c2040bd87524a787b34a
|
4
|
+
data.tar.gz: cc561056b3ee51d0e175eadf8f228e875e3ee9ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be3cb9d733a173cd41e077d525acca595f03e64722fa6cf700bd22c9e5f2941ac9196b0752cf7415d67d0ef1436526f736aa88f1df042643c707591e113f13c5
|
7
|
+
data.tar.gz: 0acb01fb7aa2d9031fb0c98267b93f98a84de529516943493354bcd8fd943bc118203ddee7aa64c0ec1c7d382acc0f813256bf2d888b8d11e2fb6c7def750c96
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# CHANGELOG
|
2
|
+
## 2.0.0
|
3
|
+
- `WeatherJp::City` now available. You can get latitude/longitude of the city.
|
4
|
+
- `WeatherJp::Weather#current`. The current weather is a little bit differrnt from day weather forecast.
|
5
|
+
- have: `temperature`, `wind_speed`, `wind_text`
|
6
|
+
- not have: `precip`, `high`, `low`.
|
7
|
+
- `WeatherJp::Weather#each` now contains the `DayWeather` object which represents current weather status. If you do not want current weather status, use `each_without_current` or `except_current` method.
|
8
|
+
- Remove `customize_to_s` method. This feature will be re-added as configurable procedure.
|
9
|
+
- Remove option from `WeatherJp.get`. If you use these option, use `WeatherJp::Weather#for`.
|
10
|
+
- Some deprecations for method names.
|
11
|
+
|
12
|
+
## 1.0.4
|
13
|
+
Bug fixes
|
14
|
+
|
15
|
+
- Rain probability and tempratures are returned as Fixnum. [Sho Hashimoto - @shokai]
|
16
|
+
- Can handle tempratures below zero. [Sho Hashimoto - @shokai]
|
17
|
+
|
18
|
+
## 1.0.0
|
19
|
+
- Add parse method to WeatherJp
|
20
|
+
- see READEME for more description
|
data/README.md
CHANGED
@@ -1,11 +1,20 @@
|
|
1
|
-
# weather_jp [](https://travis-ci.org/taiki45/weather_jp) [](https://coveralls.io/r/taiki45/weather_jp?branch=master) [](https://codeclimate.com/github/taiki45/weather_jp) [](http://badge.fury.io/rb/weather_jp)
|
2
|
+
Japan weather infomation API wrapper. Fetch Japan weather status or forecast as Ruby object easily.
|
3
3
|
|
4
|
-
|
4
|
+
## V2 upgrade guides
|
5
|
+
The backend server returns differrnt contents from before. So V1 is not working. Please update to V2.
|
5
6
|
|
6
|
-
|
7
|
+
And V2 contains some of new features, deprecations and removales.
|
7
8
|
|
8
|
-
|
9
|
+
- `WeatherJp::City` now available. You can get latitude/longitude of the city.
|
10
|
+
- `WeatherJp::Weather#current`. The current weather is a little bit differrnt from day weather forecast.
|
11
|
+
- have: `temperature`, `wind_speed`, `wind_text`
|
12
|
+
- not have: `precip`, `high`, `low`.
|
13
|
+
- `WeatherJp::Weather#each` now contains the `DayWeather` object which represents current weather status. If you do not want current weather status, use `each_without_current` or `except_current` method.
|
14
|
+
- Remove `customize_to_s` method. This feature will be re-added as configurable procedure.
|
15
|
+
- Remove option from `WeatherJp.get`. If you use these option, use `WeatherJp::Weather#for`.
|
16
|
+
|
17
|
+
See deprecations at [API document page](http://rubydoc.info/gems/weather_jp/).
|
9
18
|
|
10
19
|
## Installation
|
11
20
|
|
@@ -27,7 +36,6 @@ Or install it yourself as:
|
|
27
36
|
# creat weather object in differrnt ways
|
28
37
|
tokyo = WeatherJp.get :tokyo
|
29
38
|
akiba = WeatherJp.get "秋葉原"
|
30
|
-
abuja = WeatherJp::Weather.new("アブジャ")
|
31
39
|
tsuyama = WeatherJp.get "津山"
|
32
40
|
|
33
41
|
# get weather info as String
|
@@ -35,53 +43,42 @@ tokyo.today.to_s
|
|
35
43
|
#=> can be "東京都 東京の天気は曇りのち晴れ、最高気温34度...etc"
|
36
44
|
|
37
45
|
# or your have unparsed string
|
38
|
-
WeatherJp.parse("
|
46
|
+
WeatherJp.parse("今日の香川県の天気教えて下さい").to_s
|
39
47
|
#=> "香川県 高松の今日の天気は曇のち晴れ 最高気温25度 最低気温17度 降水確率は20% です。"
|
40
48
|
|
41
49
|
# to get weather info in differrnt ways
|
42
|
-
|
43
|
-
tokyo.
|
44
|
-
tokyo.get_weather(:tomorrow).rain
|
50
|
+
tokyo.today.text #=> can be "晴れ"
|
51
|
+
tokyo.tomorrow.precip #=> can be 10
|
45
52
|
akiba.day_after_tomorrow.to_s
|
46
|
-
WeatherJp.get(:tokyo, :today).forecast
|
47
53
|
|
48
|
-
#
|
54
|
+
# yields DayWeather object
|
49
55
|
tokyo.each do |w|
|
50
56
|
puts w.city_name
|
51
|
-
puts w.
|
52
|
-
puts w.
|
53
|
-
puts w.
|
54
|
-
puts w.
|
55
|
-
puts w.
|
56
|
-
w.
|
57
|
+
puts w.date
|
58
|
+
puts w.date_text
|
59
|
+
puts w.text
|
60
|
+
puts w.high
|
61
|
+
puts w.low
|
62
|
+
puts w.precip
|
57
63
|
end
|
58
64
|
|
59
|
-
|
65
|
+
# You can use WeatherJp::City object
|
66
|
+
tokyo.city => WeatherJp::City
|
67
|
+
[tokyo.city.longitude, tokyo.city.latitude]
|
68
|
+
|
69
|
+
akiba.map {|w| [w.date_text, w.text] }
|
60
70
|
|
61
71
|
# or use as simple Array or Hash
|
62
72
|
tokyo.to_a
|
73
|
+
akiba.weathers
|
63
74
|
tsuyama.each {|w| p w.to_hash }
|
64
|
-
akiba.day_weathers
|
65
|
-
|
66
|
-
# you can cutomize DayWeather#to_s method
|
67
|
-
WeatherJp.get(:tokyo).today.to_s #=> "東京 東京都の天気は晴れ....etc"
|
68
|
-
|
69
|
-
WeatherJp.customize_to_s do
|
70
|
-
word = "#{day}の#{city_name}は#{forecast} "
|
71
|
-
word << "最高気温は#{max_temp} " if max_temp
|
72
|
-
word << "最低気温は#{min_temp} " if min_temp
|
73
|
-
word << "降水確率は#{rain}%" if rain
|
74
|
-
word << "でし"
|
75
|
-
word
|
76
|
-
end
|
77
|
-
|
78
|
-
WeatherJp.get(:tokyo).today.to_s #=> "本日の東京 東京都は晴れ...."
|
79
|
-
|
80
75
|
```
|
81
76
|
|
77
|
+
See more detail in [API document page](http://rubydoc.info/gems/weather_jp/).
|
78
|
+
|
82
79
|
## Requires
|
83
80
|
|
84
|
-
Ruby >=
|
81
|
+
Ruby >= 2.0.0
|
85
82
|
|
86
83
|
## Documents
|
87
84
|
|
data/Rakefile
CHANGED
data/lib/weather_jp.rb
CHANGED
@@ -1,201 +1,36 @@
|
|
1
|
-
|
1
|
+
require 'forwardable'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
require 'active_support'
|
7
|
+
require 'active_support/core_ext/object'
|
8
|
+
require 'rack'
|
9
|
+
|
10
|
+
require 'weather_jp/city'
|
11
|
+
require 'weather_jp/weather'
|
2
12
|
require 'weather_jp/day_weather'
|
13
|
+
require 'weather_jp/adapter'
|
14
|
+
require 'weather_jp/request_parser'
|
3
15
|
require 'weather_jp/version'
|
4
|
-
require 'uri'
|
5
|
-
require 'open-uri'
|
6
|
-
require 'rss'
|
7
|
-
require 'nokogiri'
|
8
16
|
|
9
17
|
module WeatherJp
|
10
18
|
class WeatherJpError < StandardError; end
|
19
|
+
class ServiceUnavailable < WeatherJpError; end
|
11
20
|
|
12
21
|
class << self
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
def parse(str)
|
28
|
-
if day_and_city = parser(str)
|
29
|
-
WeatherJp.get(day_and_city[:city]).get_weather(day_and_city[:day])
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def parser(str)
|
34
|
-
if str =~ /((?<city>.*)の
|
35
|
-
(?<day>今日|きょう|今|いま|明日|あした|明後日|あさって|3日後|4日後|3日後|4日後)
|
36
|
-
の(天気|てんき).*) |
|
37
|
-
((?<day>今日|きょう|今|いま|明日|あした|明後日|あさって|3日後|4日後|3日後|4日後)の
|
38
|
-
(?<city>.*)の(天気|てんき))
|
39
|
-
/ux
|
40
|
-
data = Regexp.last_match
|
41
|
-
day = data[:day]
|
42
|
-
case day
|
43
|
-
when /今日|きょう|今|いま/u
|
44
|
-
day = 'today'
|
45
|
-
when /明日|あした/u
|
46
|
-
day = 'tomorrow'
|
47
|
-
when /明後日|あさって/u
|
48
|
-
day = 'day_after_tomorrow'
|
49
|
-
when /3日後|3日後/u
|
50
|
-
day = 3
|
51
|
-
when /4日後|4日後/u
|
52
|
-
day = 4
|
53
|
-
else
|
54
|
-
raise WeatherJpError, "Can't parse given String"
|
55
|
-
end
|
56
|
-
{day: day, city: data[:city]}
|
57
|
-
else
|
58
|
-
nil
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
class Weather
|
64
|
-
include Enumerable
|
65
|
-
|
66
|
-
def initialize(city_name)
|
67
|
-
@area_code, @city_name, @weathers = Wrapper.get(city_name)
|
68
|
-
@day_weathers = Array.new(@weathers.size) do |n|
|
69
|
-
DayWeather.new(@weathers,@city_name, n)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
attr_reader :city_name, :area_code, :day_weathers
|
74
|
-
|
75
|
-
def to_hash
|
76
|
-
@weathers
|
77
|
-
end
|
78
|
-
|
79
|
-
alias to_a to_hash
|
80
|
-
|
81
|
-
def each
|
82
|
-
@day_weathers.each do |i|
|
83
|
-
yield i
|
84
|
-
end
|
85
|
-
self
|
86
|
-
end
|
87
|
-
|
88
|
-
def get_weather(day = 0)
|
89
|
-
begin
|
90
|
-
day = day.to_sym if day.class == String
|
91
|
-
case day
|
92
|
-
when :today
|
93
|
-
day = 0
|
94
|
-
when :tomorrow
|
95
|
-
day = 1
|
96
|
-
when :day_after_tomorrow
|
97
|
-
day = 2
|
98
|
-
end
|
99
|
-
raise WeatherJpError if @day_weathers[day] == nil
|
100
|
-
@day_weathers[day]
|
101
|
-
rescue
|
102
|
-
raise WeatherJpError,
|
103
|
-
"unvaild argument '#{day}' for get_weather"
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
%w(today tomorrow day_after_tomorrow).each do |s|
|
108
|
-
define_method(s.to_sym) do
|
109
|
-
get_weather(s)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
module Wrapper
|
115
|
-
class << self
|
116
|
-
def get(city_name)
|
117
|
-
area_code, city_name = get_area_code(city_name.to_s)
|
118
|
-
weathers = set_weathers(parse_rss(get_rss(area_code)))
|
119
|
-
[area_code, city_name, weathers]
|
120
|
-
end
|
121
|
-
|
122
|
-
def get_area_code(city_name)
|
123
|
-
get_xml city_name do |xml|
|
124
|
-
parse_xml xml
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def get_xml(city_name)
|
129
|
-
result = []
|
130
|
-
open(URI.encode("http://weather.service.msn.com/" +
|
131
|
-
"find.aspx?outputview=search&weadegreetype=C&culture=ja-JP&" +
|
132
|
-
"weasearchstr=#{city_name}")) do |xml|
|
133
|
-
result = yield xml
|
134
|
-
end
|
135
|
-
result
|
136
|
-
end
|
137
|
-
|
138
|
-
def parse_xml(xml)
|
139
|
-
doc = Nokogiri::XML(xml)
|
140
|
-
begin
|
141
|
-
code =
|
142
|
-
doc.xpath('//weather').attr('weatherlocationcode').value
|
143
|
-
full_name =
|
144
|
-
doc.xpath('//weather').attr('weatherlocationname').value
|
145
|
-
rescue
|
146
|
-
raise WeatherJpError, "Cant't parse XML"
|
147
|
-
end
|
148
|
-
[code.slice(3..-1), full_name]
|
149
|
-
end
|
150
|
-
|
151
|
-
def get_rss(area_code)
|
152
|
-
begin
|
153
|
-
uri = URI.parse(
|
154
|
-
"http://weather.jp.msn.com/" +
|
155
|
-
"RSS.aspx?wealocations=wc:#{area_code}&" +
|
156
|
-
"weadegreetype=C&culture=ja-JP"
|
157
|
-
)
|
158
|
-
RSS::Parser.parse(uri, false)
|
159
|
-
rescue
|
160
|
-
raise WeatherJpError,
|
161
|
-
"the MSN weather sever may be downed, or got invaild city code"
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
def parse_rss(rss)
|
166
|
-
str = rss.channel.item(0).description
|
167
|
-
data = remove_html_tag(str).split(/%/)
|
168
|
-
data.pop
|
169
|
-
data.map {|i| i.delete!('"') }
|
170
|
-
end
|
171
|
-
|
172
|
-
def remove_html_tag(str)
|
173
|
-
str.gsub(/<(\"[^\"]*\"|'[^']*'|[^'\">])*>/,'""')
|
174
|
-
end
|
175
|
-
|
176
|
-
def set_weathers(raw_data)
|
177
|
-
weathers = Array.new
|
178
|
-
begin
|
179
|
-
raw_data.each do |i|
|
180
|
-
h = Hash.new
|
181
|
-
h[:day] = i.slice(/(.*?):\s+(.*?)\./, 1)
|
182
|
-
h[:forecast] = i.slice(/(.*?):\s+(.*?)\./, 2)
|
183
|
-
h[:max_temp] = i.slice(/(最高).*?(-?\d+)/u, 2)
|
184
|
-
h[:min_temp] = i.slice(/(最低).*?(-?\d+)/u, 2)
|
185
|
-
h[:rain] = i.slice(/(降水確率).*?(\d+)/u, 2)
|
186
|
-
[:max_temp, :min_temp, :rain].each do |j|
|
187
|
-
h[j] = h[j].to_i if h[j]
|
188
|
-
end
|
189
|
-
if h[:day].match /の天気/u
|
190
|
-
h[:day] = h[:day].slice /(.*)の天気/u, 1
|
191
|
-
end
|
192
|
-
weathers << h
|
193
|
-
end
|
194
|
-
weathers
|
195
|
-
rescue NameError
|
196
|
-
raise WeatherJpError,
|
197
|
-
"the MSN weather sever may be downed, or something wrong"
|
198
|
-
end
|
22
|
+
# @param [String] city_name
|
23
|
+
# @return [WeatherJp::Weather, nil]
|
24
|
+
def get(city_name)
|
25
|
+
Adapter.get(city_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Request like '明日の東京の天気教えて'.
|
29
|
+
# @param [String] query
|
30
|
+
# @return [WeatherJp::Weather, nil]
|
31
|
+
def parse(query)
|
32
|
+
if request = RequestParser.parser(query)
|
33
|
+
get(request.city).get_weather(request.day)
|
199
34
|
end
|
200
35
|
end
|
201
36
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
|
3
|
+
module WeatherJp
|
4
|
+
class Adapter
|
5
|
+
class << self
|
6
|
+
# Returns nil when not found weather forecasts.
|
7
|
+
# @param [String] city_name
|
8
|
+
# @return [WeatherJp::Weather, nil]
|
9
|
+
def get(city_name)
|
10
|
+
new(city_name).get
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
BASE_URL = 'http://weather.service.msn.com/'
|
15
|
+
ACTION = 'data.aspx'
|
16
|
+
LANG = 'ja-JP'
|
17
|
+
SCALE = 'C'
|
18
|
+
|
19
|
+
attr_reader :name
|
20
|
+
|
21
|
+
def initialize(city_name)
|
22
|
+
@name = city_name
|
23
|
+
end
|
24
|
+
|
25
|
+
def get
|
26
|
+
weather_node ? weather : nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_weather(attrs, day_weathers)
|
30
|
+
end
|
31
|
+
|
32
|
+
def weather
|
33
|
+
Weather.new(city, day_weathers)
|
34
|
+
end
|
35
|
+
|
36
|
+
def day_weathers
|
37
|
+
day_weather_nodes.each_with_index.map do |n, i|
|
38
|
+
attrs = Hash[n.attributes.map {|k, v| [k, v.value] }]
|
39
|
+
DayWeather.new(attrs, city, i - 1)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def day_weather_nodes
|
44
|
+
xml.xpath('/weatherdata/weather/*[attribute::skytextday or attribute::skytext]')
|
45
|
+
end
|
46
|
+
|
47
|
+
# Ruby 2.0.0 version has no `Nokogiri::XML::Element#to_h`.
|
48
|
+
def city
|
49
|
+
@city ||= begin
|
50
|
+
attrs = Hash[weather_node.attributes.map {|k, v| [k, v.value]}]
|
51
|
+
City.new(attrs['weathercityname'], attrs)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def weather_node
|
56
|
+
@weather_node ||= xml.xpath('/weatherdata/weather').first
|
57
|
+
end
|
58
|
+
|
59
|
+
def xml
|
60
|
+
@xml ||= Nokogiri::XML.parse(xml_str)
|
61
|
+
end
|
62
|
+
|
63
|
+
def xml_str
|
64
|
+
@xml_str ||= Reader.read(url)
|
65
|
+
end
|
66
|
+
|
67
|
+
def url
|
68
|
+
"#{BASE_URL}#{ACTION}?#{Rack::Utils.build_query(params)}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def params
|
72
|
+
{
|
73
|
+
weadegreetype: SCALE,
|
74
|
+
culture: LANG,
|
75
|
+
weasearchstr: name,
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
module Reader
|
80
|
+
class << self
|
81
|
+
def read(url)
|
82
|
+
open(url).read
|
83
|
+
rescue OpenURI::HTTPError => e
|
84
|
+
raise ServiceUnavailable.new("status=#{e.message[0..-2]}")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|