Sign in

Зарегистрируйтесь, чтобы стать полноправным участником сообщества Masterpro.ws.

OpenWeatherMap API: еще один погодный информер на Ruby on Rails

 

Продолжаем наши уроки Ruby on Rails для начинающих.

Не раз убеждался, что изучение той или иной технической дисциплины лучше всего дается "на примерах". Нет, разумеется, доки читать необходимо, куда без них. Но для закрепления пройденного материала, более того - для наилучшего понимания пройденного, самое оптимальное, что можно придумать - это найти живой пример, не очень сложный и обязательно (обязательно!) интересный и эффектный... и как следут наиграться с ним, повертев со всех сторон и выжав из него как можно больше. Самый лучший способ, поверьте! Как жаль, что далеко не все профессиональные педагоги это понимают. Возможно, во мне говорит моя застарелая аллергия на среднюю мою, (о, очень среднюю) школу, в которой довелось учиться, не знаю... анализировать самого себя, как известно, весьма непросто. Ну, как бы там ни было. Закончим на этой ностальгической нотке вступительную часть статьи и сразу перейдем к делу. Ruby on rails уже установлен на вашем ПК, как описано в материале Ruby on Rails - это просто? Тот непреложный факт, что Rails относится к среде Модель - Представление - Контроллер (MVC, Model - View - Controller), получает входящие запросы от браузера, декодирует запрос для поиска контроллера и вызывает в контроллере метод, т.е. действие - усвоили? Далее контроллер инициирует соответствующее представление и отображает полученные результаты... отлично, вот именно этим всем мы с вами сегодня и займемся, разобрав на примере, как же все происходит. Будет интересно!

 

 

"Живой пример, интересный и эффектный", подыщем для сегодняшнего урока на гитхабе, а где же еще? - вот уж где-где, а на GitHub есть буквально все, не хуже чем когда-то в Одессе. Воспользуемся готовым приложением, доступным по ссылке IzzySmith/rails_coding_challenge, и представляющим из себя погодный информер, умеющий получить и сравнить weather condition (состояние погоды) нескольких произвольных локаций по версии популярной погодной станции openweathermap.org. Заходим, таким образом, в пустой каталог, созданный специально для сегодняшней работы, и выполняем git clone.

Получится примерно как-то вот так:

 

$ git clone https://github.com/IzzySmith/rails_coding_challenge.git
Клонирование в «rails_coding_challenge»…
remote: Counting objects: 6428, done.
remote: Total 6428 (delta 0), reused 0 (delta 0), pack-reused 6428
Получение объектов: 100% (6428/6428), 25.35 MiB | 1.76 MiB/s, готово.
Определение изменений: 100% (701/701), готово.
Проверка соединения… готово.

 

Перейдя в каталог приложения:

 

$ cd rails_coding_challenge/WeatherApp/

 

выполняем

 

$ bundle install

 

и сразу запускаем web-сервер:

 

$ rails s
=> Booting Puma
=> Rails 5.1.1 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.8.2 (ruby 2.3.3-p222), codename: Sassy Salamander
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop

 

 

Отлично, все получилось... уже можно зайти на localhost:3000 и сходу полюбоваться полученным результатом... видите, как быстро? да, рельсы реально рулят.

Давайте попробуем разобраться, что же именно мы с вами только что увидели. Припомните то, что чуть выше уже было сказано в контексте MVC, и давайте-ка для начала заглянем в файл app/models/weather.rb. Прежде всего мы с вами увидим здесь class Weather и base_uri, который вполне уже можно попытаться открыть в браузере, моментально получив простенький отлуп:

 

{"cod":"400","message":" is not a city id"}

 

Все верно, идентификатор-то мы с вами и забыли подставить. Открываем app/controller/weather_controller.rb, где видим пять используемых приложением ID городов:

 

@cities = "524901,703448,6058560,1819729,4321929"

 

Подставляем в запрос любой из них, скажем 6058560 (London):

 

http://api.openweathermap.org/data/2.5/group?appid=*******************************&id=6058560

 

получая в ответ JSON:

 

{"cnt":1,"list":[{"coord":{"lon":-81.23,"lat":42.98},"sys":{"type":1,"id":3678,"message":0.0023,"country":"CA","sunrise":1498384045,"sunset":1498439286},"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"main":{"temp":290.15,"pressure":1015,"humidity":67,"temp_min":290.15,"temp_max":290.15},"visibility":24140,"wind":{"speed":5.1,"deg":280},"clouds":{"all":75},"dt":1498412645,"id":6058560,"name":"London"}]}

 

Как видите, ничего такого сверхестественного... наше с вами Ruby on Rails приложение получает посредством API погодной станции данные в формате JSON и далее выводит полученные данные на страничке в виде таблицы; чтобы убедиться в справедливости сказанного, достаточно открыть app/views/weather/index.html.erb:

 

<h1> Compare the Weather of Cities Around the World </h1>

<h4> Compare all cities </h4>

<table class="CitiesTable">

  <tr>
    <th>City Name</th>
    <th>Pressure</th>
    <th>Temperature</th>
    <th>Humidity</th>
    <th>Percentage Cloud Cover</th>
  </tr>


<% @lookup.each do |l| %>
  <tr>
    <td><%= l["name"] %></td>
    <td><%= l["main"]["pressure"] %></td>
    <td><%= l["main"]["temp"] %></td>
    <td><%= l["main"]["humidity"] %></td>
    <td><%= l["clouds"]["all"] %></td>
  </tr>
<% end %>

</table>

<h4> Best Weather </h4>


<table class="CitiesTable">
    <tr>Hottest Temperature</tr>
    <tr>Highest Pressure</tr>
    <tr>Lowest Humidity</tr>
    <tr>Lowest Percentage Cloud Cover</tr>

<% @best.each do |b| %>
    <td><%= b[0] %></td>
    <td><%= b[1] %></td>
    <td><%= b[2] %></td>
    <td><%= b[3] %></td>
<% end %>
</table>

 

Вот, собственно, и все, ничего здесь особо сложного.

В скобках отметим, что наш с вами успешный запрос к API содержал, помимо id, еще и appid; посмотрите внимательно. Что же это за параметр такой? - а это API Key, который вы в любой момент можете абсолютно свободно получить, зайдя на openweathermap.org. Добрый дядя-разработчик Rails-приложения подарил нам свой ключ, но, вполне возможно, не только мы с вами сегодня клонировали его репозиторий с GitHub, а? Я бы все-таки рекомендовал потратить пару минут и, получив свой личный API Key - заменить им дефолтный в файле app/models/weather.rb. Мало ли что, а вдруг эта самая статья станет вдруг сверхпопулярной, и сразу сотня новоявленных адептов Ruby on Rails вознамерится получать погоду от OpenWetherMap по одному и тому же ключу?

 

Рабочий пример описанного в статье кода, в числе других Rails Examples - всегда возможно найти в тестовом блоге автора на herokuapp.com, welcome.

 

Отлично, с этим разобрались. Погодный информер на индексной страничке работает, давайте теперь подумаем, каким образом мы сможем его отредактировать под свои цели и задачи... для этого предлагаю создать еще один тестовый контроллер, воспользовавшись стандартным генерирующим сценарием Rails:

 

$ rails generate controller Test first
Running via Spring preloader in process 21552
      create  app/controllers/test_controller.rb
       route  get 'test/first'
      invoke  erb
      create    app/views/test
      create    app/views/test/first.html.erb
      invoke  test_unit
      create    test/controllers/test_controller_test.rb
      invoke  helper
      create    app/helpers/test_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/test.coffee
      invoke    scss
      create      app/assets/stylesheets/test.scss




Давайте-ка еще раз внимательно рассмотрим JSON, полученный от API OpenWetherMap. Как думаете, возможно ли получить из него какие-нибудь иные показания, кроме тех, которые уже получены? Вот и проверим. Открываем app/wiews/test/first.html.erb, который приводим к следующему:

 

<table class="CitiesTable">

<tr>
    <th>City Name</th>
    <th>Pressure</th>
    <th>Temperature</th>
    <th>Temp min</th>
    <th>Temp max</th>
    <th>Humidity</th>
    <th>Percentage Cloud Cover</th>
    <th>Visibility</th>
    <th>Wind</th>
    <th>Deg</th>
    <th>Weather condition</th>
    <th>Description</th>
  </tr>
  
<% @lookup.each do |l| %>
  <tr>
    <td><%= l["name"] %></td>
    <td><%= l["main"]["pressure"] %></td>
    <td><%= l["main"]["temp"] %></td>
    <td><%= l["main"]["temp_min"] %></td>
    <td><%= l["main"]["temp_max"] %></td>
    <td><%= l["main"]["humidity"] %></td>
    <td><%= l["clouds"]["all"] %></td>
    <td><%= l["visibility"] %></td>
    <td><%= l["wind"]["speed"] %></td>
    <td><%= l["wind"]["deg"] %></td>
    <td><%= l["weather"][0]["main"] %></td>
    <td><%= l["weather"][0]["description"] %></td>
  </tr>
<% end %>

</table>

<table class="CitiesTable">
<tr>
    <th>City Name</th>
    <th>Country</th>
    <th>Lon</th>
    <th>Lat</th>
  </tr>

<% @lookup.each do |l| %>
  <tr>
    <td><%= l["name"] %></td>
    <td><%= l["sys"]["country"] %></td>
    <td><%= l["coord"]["lon"] %></td>
    <td><%= l["coord"]["lat"] %></td>
  </tr>
<% end %>

</table>

 


Сохраняем файл и открываем localhost:3000/test/first, но... снова облом:

 

NoMethodError in Test#first

 

Про контроллер-то мы забыли! Открываем app/controllers/test_controller.rb и быстрехонько приводим его к следующему состоянию, которое обязано напоминать (но не на все 100%, будьте внимательны!) содержание app/controllers/weather_controller.rb, полученного нами уже "готовым к использованию" из GitHub:

 

class TestController < ApplicationController
  def first
    @cities = "524901,703448,6058560,1819729,4321929"
    @lookup = Weather.call(@cities)
    @temp = Weather.max_value(Weather.make_hash(@lookup, "temp"))
    @pressure = Weather.max_value(Weather.make_hash(@lookup, "pressure"))
    @humidity = Weather.min_value(Weather.make_hash(@lookup, "humidity"))
    @clouds = Weather.min_value(Weather.cloud_hash(@lookup))
    @best = [@temp, @pressure, @humidity, @clouds]
  end
end

 

Попробуем понять, что же это такое у нас получилось, в этом самом исходном файле контроллера. TestController - это класс, наследуемый из ApplicationController, автоматически перенимающий, таким образом, все исходное поведение контроллера и содержащий метод действия first... что же касается собственно Представления (понятия схемы MVC мы очень кратко коснулись в начале статьи), то его шаблон находится, как и следовало ожидать, учитывая логику Rails - в директории app/views/test, это файл first.html.erb, который мы только что отредактировали. Почему именно он? - потому что по умолчанию Rails ищет шаблоны в файле с тем же именем, что и у действия, занимающегося его обработкой.

И вот теперь на localhost:3000/test/first мы видим уже целый ряд новых погодных значений для всех пяти геолокаций; кроме того, получаем еще одну табличку, содержащую географические координаты городов, также двухбуквенный ISO 3166 код страны. Ок, а как получить на страничке погоду всего лишь для одного города? - ничто не может быть проще, выбросьте/добавьте идентификаторы городов в исходном файле контроллера, отвечающего за отображаемое представление, всего делов. Либо отредактируйте base_uri, о котором речь чуть была выше, напрямую включив в запрос (через &) необходимый идентификатор... кстати, почему это в нашем с вами погодном информере температура отображена трехзначным числом, это что еще за апокалипсис? вроде глобальное потепление пока еще не наступило?- да все в порядке, по-дефолту здесь градусы Кельвина. Листаем документацию OpenWeatherMap:

 

Temperature is available in Fahrenheit, Celsius and Kelvin units.

For temperature in Fahrenheit use units=imperial
For temperature in Celsius use units=metric
Temperature in Kelvin is used by default, no need to use units parameter in API call

 

Соответственно, актуальную погоду только для Москвы, скажем, с привычной температурой по Цельсию, мы с вами можем получить, например, отредактировав файл app/models/weather.rb:

 

class Weather
  include HTTParty
  
  base_uri "http://api.openweathermap.org/data/2.5/group?appid=******************************&id=524901&units=metric"
  format :json

  #call the api with HTTParty and parse the JSON response 
  def self.call list_ids
    response = HTTParty.get(base_uri)
    body = JSON.parse(response.body)
    list = body["list"]
  end
  
  -------------

 

Существуют и иные способы; но, может быть. попробуете найти самостоятельно?

 

Оставить комментарий

Добавьте ваш комментарий