class Pincerna::Weather

Gets weather forecast from Yahoo! Weather.

Constants

API_KEY

Yahoo! API key.

ICON

The icon to show for each feedback item.

MATCHER

The expression to match.

RELEVANT_MATCHES

Relevant groups in the match.

URL

The URL of the webservice.

Public Instance Methods

get_forecast(places, scale = "c") click to toggle source

Gets weather forecast for one or more places.

@param places [Array] The places to query. @param scale [String] The unit system to use: `f` for the US system (Farenheit) and `c` for the International System one (Celsius). @return [Array|NilClass] An array with forecasts data or `nil` if the query failed.

# File lib/pincerna/weather.rb, line 76
def get_forecast(places, scale = "c")
  client = Weatherman::Client.new(unit: scale)
  temperature_unit = "°#{scale.upcase}"

  places.map do |place|
    Pincerna::Cache.instance.use("forecast:#{place[:woeid]}", Pincerna::Cache::EXPIRATIONS[:short]) {
      parse_forecast_response(place, client.lookup_by_woeid(place[:woeid]), temperature_unit)
    }
  end
end
get_wind_direction(degrees) click to toggle source

Converts the degrees direction of the wind to the cardinal points notation (like NE or SW).

@param degrees [Fixnum] The direction in degrees. @return [String] The direction in cardinal points notation.

# File lib/pincerna/weather.rb, line 91
def get_wind_direction(degrees)
  # Normalize value
  degrees += 360 if degrees < 0
  degrees = degrees % 360

  # Get the position
  directions = ["N", "NE", "NE", "E", "E", "SE", "SE", "S", "S", "SW", "SW", "W", "W", "NW", "NW", "N"]
  position = ((degrees.to_f / 22.5) - 0.5).ceil.to_i % directions.count # The mod operation is needed for values close to 360 who, after ceiling, would otherwise overflow.
  directions[position]
end
lookup_places(query) click to toggle source

Lookups a place on Yahoo! to obtain WOEID(s).

@param query [String] The place to search. @return [Array] A list of matching places data.

# File lib/pincerna/weather.rb, line 59
def lookup_places(query)
  if query !~ /^(\d+)$/ then
    Pincerna::Cache.instance.use("woeid:#{query}", Pincerna::Cache::EXPIRATIONS[:long]) do
      response = fetch_remote_resource(URL % CGI.escape(query), {appid: API_KEY, format: :json})
      response["places"].fetch("place", []).map { |place| parse_place(place) }
    end
  else
    # We already have the woeid. The name will be given by Yahoo!
    [{woeid: query}]
  end
end
perform_filtering(query, scale) click to toggle source

Gets forecast for a place.

@param query [String] A place to search. @return [Array] A list of items to process.

# File lib/pincerna/weather.rb, line 35
def perform_filtering(query, scale)
  places = lookup_places(query)
  places.empty? ? nil : get_forecast(places, scale) if !places.empty?
end
process_results(results) click to toggle source

Processes items to obtain feedback items.

@param results [Array] The items to process. @return [Array] The feedback items.

# File lib/pincerna/weather.rb, line 44
def process_results(results)
  results.map do |result|
    # Format results
    current = result[:current]
    forecast = result[:forecast]
    combined = "#{current[:temperature]}, #{current[:description].downcase.capitalize}, wind #{current[:wind][:speed]} #{current[:wind][:direction]} - Next: #{forecast[:high]} / #{forecast[:low]}, #{forecast[:description]}"

    {title: result[:name], arg: result[:link], subtitle: combined, icon: result[:image]}
  end
end

Private Instance Methods

download_image(url) click to toggle source

Gets and downloads an image for a forecast.

@param url [String] The image URL. @return [String] The path of the downloaded image.

# File lib/pincerna/weather.rb, line 107
def download_image(url)
  # Extract the URL and use it to build the path
  rv = (@cache_dir + "/weather/#{File.basename(URI.parse(url).path)}")

  if !File.exists?(rv) then
    # Create the directory and download the file
    FileUtils.mkdir_p(@cache_dir + "/weather/")
    open(rv, 'wb') {|f| f.write(open(url).read) }
  end

  rv
end
extract_forecast_media(response) click to toggle source

Extracts forecast media from a response.

@param response The response to analyze. @return [Array] An array of media.

# File lib/pincerna/weather.rb, line 184
def extract_forecast_media(response)
  [response.description_image.attr("src"), response.document_root.at_xpath("link").content.to_s]
end
format_forecast(place, image, link, current, forecast, wind, temperature_unit, speed_unit) click to toggle source

Formats a weather forecast.

@param place [Hash] The basic place information. @param image [String] The icon for the current weather conditions. @param link [String] The link to view weather conditions on Yahoo!. @param current [Hash] The current weather conditions. @param forecast [Hash] The weather forecast for tomorrow. @param wind [Hash] The current wind conditions. @param temperature_unit [String] The temperature unit. @param speed_unit [String] The speed unit. @return [Hash] The parsed forecast.

# File lib/pincerna/weather.rb, line 163
def format_forecast(place, image, link, current, forecast, wind, temperature_unit, speed_unit)
  place.merge({
    image: image,
    link: link,
    current: {
      description: current["text"],
      temperature: "#{current["temp"]} #{temperature_unit}",
      wind: {speed: "#{wind["speed"]} #{speed_unit}", direction: get_wind_direction(wind["direction"])}
    },
    forecast: {
      description: forecast["text"],
      high: "#{forecast["high"]} #{temperature_unit}",
      low: "#{forecast["low"]} #{temperature_unit}"
    },
  })
end
get_name(location) click to toggle source

Gets a location name.

@param location [Hash] The location data. @return [String] The location name.

# File lib/pincerna/weather.rb, line 124
def get_name(location)
  ["city", "region", "country"].map { |field| location[field].strip }.reject(&:empty?).join(", ")
end
parse_forecast_response(place, response, temperature_unit) click to toggle source

Formats a weather forecast.

@param place [Hash] The basic place information. @param response [Weatherman::Response] The forecast response. @param temperature_unit [String] The temperature unit. @return [Hash] A formatted forecast.

# File lib/pincerna/weather.rb, line 145
def parse_forecast_response(place, response, temperature_unit)
  image, link = extract_forecast_media(response)
  place[:name] ||= get_name(response.location)

  format_forecast(place, download_image(image), link, response.condition, response.forecasts.first, response.wind, temperature_unit, response.units["speed"])
end
parse_place(place) click to toggle source

Parses a WOEID lookup.

@param place [Hash] The place to parse. @return [Hash] The parsed place.

# File lib/pincerna/weather.rb, line 132
def parse_place(place)
  {
    woeid: place["woeid"],
    name: ["locality1", "admin3", "admin2", "admin1", "country"].map { |field| place[field] }.reject(&:empty?).uniq.join(", ")
  }
end