weather_jp 1.0.4 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f4992900d6163c74063fc1434e7ab0a08cc972c9
4
- data.tar.gz: 592b2ac4cd43a52cde680f3416c59a73fd88b80f
3
+ metadata.gz: 88a46156ea60f987d673c2040bd87524a787b34a
4
+ data.tar.gz: cc561056b3ee51d0e175eadf8f228e875e3ee9ab
5
5
  SHA512:
6
- metadata.gz: 37f838fd46bdeb49ec6980cdf67194d5711904acd656a2ba0bff179c63d8fc16579e6d8e7d4af53c9296df5af0fad50d2cb65f094fa694c361ff1fff672b2429
7
- data.tar.gz: e328c8712472a997cc4a466e7d7d563964469ebec89f0c07fa56028c75923ea2addd881b25f757198cf1c974c492f55c3abe9f517d2f514b3e6797948117c3c8
6
+ metadata.gz: be3cb9d733a173cd41e077d525acca595f03e64722fa6cf700bd22c9e5f2941ac9196b0752cf7415d67d0ef1436526f736aa88f1df042643c707591e113f13c5
7
+ data.tar.gz: 0acb01fb7aa2d9031fb0c98267b93f98a84de529516943493354bcd8fd943bc118203ddee7aa64c0ec1c7d382acc0f813256bf2d888b8d11e2fb6c7def750c96
@@ -0,0 +1 @@
1
+ service_name: travis-ci
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ .ruby-version
@@ -1,7 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.0.0
4
- - 1.9.3
5
- - 1.9.2
6
- notifications:
7
- email: false
3
+ - "2.0"
4
+ - "2.1"
5
+ - "2.2"
@@ -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 [![Build Status](https://secure.travis-ci.org/taiki45/weather_jp.png)](http://travis-ci.org/taiki45/weather_jp)
2
- Japan weather info API wrapper.
1
+ # weather_jp [![Build Status](https://travis-ci.org/taiki45/weather_jp.svg?branch=master)](https://travis-ci.org/taiki45/weather_jp) [![Coverage Status](https://coveralls.io/repos/taiki45/weather_jp/badge.svg?branch=master)](https://coveralls.io/r/taiki45/weather_jp?branch=master) [![Code Climate](https://codeclimate.com/github/taiki45/weather_jp/badges/gpa.svg)](https://codeclimate.com/github/taiki45/weather_jp) [![Gem Version](https://badge.fury.io/rb/weather_jp.svg)](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
- Fetch Japan weather info as Ruby object easily.
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
- http://taiki45.github.com/weather_jp
7
+ And V2 contains some of new features, deprecations and removales.
7
8
 
8
- https://rubygems.org/gems/weather_jp
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("今日のうどん県の天気教えて下され〜〜").to_s
46
+ WeatherJp.parse("今日の香川県の天気教えて下さい").to_s
39
47
  #=> "香川県 高松の今日の天気は曇のち晴れ 最高気温25度 最低気温17度 降水確率は20% です。"
40
48
 
41
49
  # to get weather info in differrnt ways
42
- akiba.get_weather(4) #=> <#DayWeather object>
43
- tokyo.today.forecast #=> can be "晴れ"
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
- # use Weather object
54
+ # yields DayWeather object
49
55
  tokyo.each do |w|
50
56
  puts w.city_name
51
- puts w.day
52
- puts w.forecast
53
- puts w.max_temp
54
- puts w.min_temp
55
- puts w.rain
56
- w.each_pair {|k,v| puts k, v }
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
- akiba.map {|w| [w.day, w.forecast] }
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 >= 1.9.2
81
+ Ruby >= 2.0.0
85
82
 
86
83
  ## Documents
87
84
 
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ end
12
12
 
13
13
  require "rspec/core/rake_task"
14
14
  RSpec::Core::RakeTask.new(:spec) do |task|
15
- task.rspec_opts = ["-c", "-fs"]
15
+ task.rspec_opts = ["-c", "-fd"]
16
16
  end
17
17
 
18
18
  task :default => :spec
@@ -1,201 +1,36 @@
1
- # -*- coding:utf-8 -*-
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
- def get(city_name, option = nil)
14
- if option
15
- Weather.new(city_name).get_weather(option)
16
- else
17
- Weather.new(city_name)
18
- end
19
- end
20
-
21
- def customize_to_s(&code)
22
- Weather::DayWeather.class_eval do
23
- define_method(:to_s, &code)
24
- end
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