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 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