weather-forecasts 1.1.1
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 +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/Gemfile +10 -0
- data/LICENSE +20 -0
- data/README.md +135 -0
- data/bin/wf-console +21 -0
- data/lib/weather-forecasts.rb +1 -0
- data/lib/weather_forecasts.rb +23 -0
- data/lib/weather_forecasts/client.rb +65 -0
- data/lib/weather_forecasts/client/error.rb +18 -0
- data/lib/weather_forecasts/client/help_console.rb +9 -0
- data/lib/weather_forecasts/client/query.rb +155 -0
- data/lib/weather_forecasts/client/query/query_property.rb +68 -0
- data/lib/weather_forecasts/client/query/query_utilities.rb +16 -0
- data/lib/weather_forecasts/client/query/select_by_days_query.rb +35 -0
- data/lib/weather_forecasts/client/query/select_coordinates_by_cities_query.rb +47 -0
- data/lib/weather_forecasts/client/query/select_coordinates_by_zip_query.rb +34 -0
- data/lib/weather_forecasts/client/query/select_corner_coordinates_query.rb +38 -0
- data/lib/weather_forecasts/client/query/select_gridpoint_coordinates_query.rb +35 -0
- data/lib/weather_forecasts/client/query/select_linepoint_coordinates_query.rb +33 -0
- data/lib/weather_forecasts/client/query/select_query.rb +48 -0
- data/lib/weather_forecasts/client/query/select_square_coordinates_query.rb +35 -0
- data/lib/weather_forecasts/client/query/selection_attributes.rb +19 -0
- data/lib/weather_forecasts/client/version.rb +5 -0
- data/lib/weather_forecasts/dwml.rb +38 -0
- data/lib/weather_forecasts/dwml/data_extractor.rb +55 -0
- data/lib/weather_forecasts/dwml/head_extractor.rb +49 -0
- data/lib/weather_forecasts/dwml/location.rb +33 -0
- data/lib/weather_forecasts/dwml/parameter_extractor.rb +281 -0
- data/lib/weather_forecasts/dwml/time_layout.rb +55 -0
- data/spec/fixtures/vcr_cassettes/select_by_days_query.yml +557 -0
- data/spec/fixtures/vcr_cassettes/select_coordinates_by_cities_query.yml +437 -0
- data/spec/fixtures/vcr_cassettes/select_coordinates_by_zip_query.yml +437 -0
- data/spec/fixtures/vcr_cassettes/select_corner_coordinates_query.yml +438 -0
- data/spec/fixtures/vcr_cassettes/select_gridpoint_coordinates_query.yml +467 -0
- data/spec/fixtures/vcr_cassettes/select_linepoint_coordinates_query.yml +440 -0
- data/spec/fixtures/vcr_cassettes/select_query.yml +1647 -0
- data/spec/fixtures/vcr_cassettes/select_square_coordinates_query.yml +443 -0
- data/spec/lib/ndfd/client/query/query_property_spec.rb +41 -0
- data/spec/lib/ndfd/client/query/select_by_days_query_spec.rb +35 -0
- data/spec/lib/ndfd/client/query/select_coordinates_by_cities_query_spec.rb +33 -0
- data/spec/lib/ndfd/client/query/select_coordinates_by_zip_query_spec.rb +34 -0
- data/spec/lib/ndfd/client/query/select_corner_coordinates_query_spec.rb +35 -0
- data/spec/lib/ndfd/client/query/select_gridpoint_coordinates_query_spec.rb +37 -0
- data/spec/lib/ndfd/client/query/select_linepoint_coordinates_query_spec.rb +36 -0
- data/spec/lib/ndfd/client/query/select_query_spec.rb +46 -0
- data/spec/lib/ndfd/client/query/select_square_coordinates_query_spec.rb +37 -0
- data/spec/lib/ndfd/client/query/selection_attributes_spec.rb +13 -0
- data/spec/lib/ndfd/client/query_spec.rb +91 -0
- data/spec/lib/ndfd/client_spec.rb +61 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/support/resources/sample_document.xml +61 -0
- data/weather-forecasts.gemspec +39 -0
- metadata +224 -0
| @@ -0,0 +1,68 @@ | |
| 1 | 
            +
            module WeatherForecasts
         | 
| 2 | 
            +
              class Client
         | 
| 3 | 
            +
                class QueryProperty
         | 
| 4 | 
            +
                  attr_accessor :name, :type, :options, :required_keys, :required, :default
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def initialize(attr, parameters = {})
         | 
| 7 | 
            +
                    @name          = attr
         | 
| 8 | 
            +
                    @default       = parameters[:default]
         | 
| 9 | 
            +
                    @options       = parameters[:options]
         | 
| 10 | 
            +
                    @type          = parameters[:type] || String
         | 
| 11 | 
            +
                    @required      = parameters[:required] || false
         | 
| 12 | 
            +
                    @required_keys = parameters[:required_keys] || []
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def valid?(conditions)
         | 
| 16 | 
            +
                    @conditions = conditions
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    validate_required
         | 
| 19 | 
            +
                    validate_type
         | 
| 20 | 
            +
                    validate_required_keys
         | 
| 21 | 
            +
                    validate_options
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  protected
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def validate_required
         | 
| 27 | 
            +
                    if required && @conditions[name.to_sym].blank?
         | 
| 28 | 
            +
                      raise RequiredPropertyError, "The property, #{name.to_s}, is required."
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def validate_type
         | 
| 33 | 
            +
                    unless @conditions[name.to_sym].is_a?(type)
         | 
| 34 | 
            +
                      raise InvalidPropertyTypeError, "The type for #{name} should be an instance of #{type.to_s}."
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  def validate_required_keys
         | 
| 39 | 
            +
                    return if required_keys.blank?
         | 
| 40 | 
            +
                    params_sets = @conditions[name.to_sym]
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    if params_sets.present? && params_sets.is_a?(Array)
         | 
| 43 | 
            +
                      params_sets.each { |hsh| check_required_keys_missing(hsh.keys) }
         | 
| 44 | 
            +
                    elsif params_sets.present? && params_sets.is_a?(Hash)
         | 
| 45 | 
            +
                      check_required_keys_missing(params_sets.keys)
         | 
| 46 | 
            +
                    else
         | 
| 47 | 
            +
                      raise_required_keys_missing_error(required_keys)
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  def validate_options
         | 
| 52 | 
            +
                    value = @conditions[name.to_sym]
         | 
| 53 | 
            +
                    if options.present? && !options.include?(value)
         | 
| 54 | 
            +
                      raise InvalidOptionSpecifiedError, "The option, #{value}, is not valid.  Please use one of the following: #{options.join(', ')}"
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  def check_required_keys_missing(keys)
         | 
| 59 | 
            +
                    missing_keys = required_keys - keys
         | 
| 60 | 
            +
                    raise_required_keys_missing_error(missing_keys) if missing_keys.present?
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  def raise_required_keys_missing_error(missing_keys)
         | 
| 64 | 
            +
                    raise RequiredKeysMissingError, "Required keys (#{missing_keys.join(', ')}) are missing for one of the data points (also, they must be in an Array)."
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
            end
         | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            module WeatherForecasts
         | 
| 2 | 
            +
              class Client
         | 
| 3 | 
            +
                module QueryUtilities
         | 
| 4 | 
            +
                  def build_coordinates(coordinates)
         | 
| 5 | 
            +
                    coordinates.map { |hsh| "#{hsh[:latitude]},#{hsh[:longitude]}" }.join(" ")
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def transform_coordinate_list(document)
         | 
| 9 | 
            +
                    document.xpath("//latLonList").text.split.map do |coord|
         | 
| 10 | 
            +
                      latitude, longitude = coord.split(",").map(&:to_f)
         | 
| 11 | 
            +
                      { :latitude => latitude, :longitude => longitude }
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
            end
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            module WeatherForecasts
         | 
| 2 | 
            +
              class Client
         | 
| 3 | 
            +
                class SelectByDaysQuery < Query
         | 
| 4 | 
            +
                  include QueryUtilities
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  property :coordinates, :type => Array, :required => true, :required_keys => [:latitude, :longitude]
         | 
| 7 | 
            +
                  property :days,        :type => Fixnum, :required => true
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  property :unit,        :type => String, :options => ["e", "m"], :default => "e"
         | 
| 10 | 
            +
                  property :format,      :type => String, :options => ["24 hourly", "12 hourly"], :default => "24 hourly"
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  property :start_date,  :type => ActiveSupport::TimeWithZone
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def execute
         | 
| 15 | 
            +
                    validate
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    response = soap_client.call(:ndf_dgen_by_day_lat_lon_list, :message => build_message)
         | 
| 18 | 
            +
                    document = Nokogiri::XML(response.body[:ndf_dgen_by_day_lat_lon_list_response][:dwml_by_day_out])
         | 
| 19 | 
            +
                    DWML.new(document).process
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  protected
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def build_message
         | 
| 25 | 
            +
                    {
         | 
| 26 | 
            +
                      :listLatLong => build_coordinates(conditions[:coordinates]),
         | 
| 27 | 
            +
                      :startDate   => conditions[:start_date].iso8601,
         | 
| 28 | 
            +
                      :numDay      => conditions[:days],
         | 
| 29 | 
            +
                      :Unit        => conditions[:unit],
         | 
| 30 | 
            +
                      :format      => conditions[:format]
         | 
| 31 | 
            +
                    }
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
| @@ -0,0 +1,47 @@ | |
| 1 | 
            +
            module WeatherForecasts
         | 
| 2 | 
            +
              class Client
         | 
| 3 | 
            +
                class SelectCoordinatesByCitiesQuery < Query
         | 
| 4 | 
            +
                  CITY_OPTIONS = {
         | 
| 5 | 
            +
                    :all                    => 1234,
         | 
| 6 | 
            +
                    :primary                => 1,
         | 
| 7 | 
            +
                    :secondary              => 2,
         | 
| 8 | 
            +
                    :tertiary               => 3,
         | 
| 9 | 
            +
                    :quaternary             => 4,
         | 
| 10 | 
            +
                    :primary_and_secondary  => 12,
         | 
| 11 | 
            +
                    :tertiary_and_quaternar => 34
         | 
| 12 | 
            +
                  }
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  property :display, :type => Symbol, :options => CITY_OPTIONS.keys, :required => true
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def execute
         | 
| 17 | 
            +
                    validate
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    response = soap_client.call(:lat_lon_list_city_names, :message => build_message)
         | 
| 20 | 
            +
                    document = parse_xml(response)
         | 
| 21 | 
            +
                    transform_document_to_mapped_list(document)
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  protected
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def build_message
         | 
| 27 | 
            +
                    {
         | 
| 28 | 
            +
                      :displayLevel => CITY_OPTIONS[conditions[:display]]
         | 
| 29 | 
            +
                    }
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def parse_xml(response)
         | 
| 33 | 
            +
                    Nokogiri::XML(response.body[:lat_lon_list_city_names_response][:list_lat_lon_out])
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def transform_document_to_mapped_list(document)
         | 
| 37 | 
            +
                    cities = document.xpath("//cityNameList").text.split('|')
         | 
| 38 | 
            +
                    coords = document.xpath("//latLonList").text.split
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    results = [cities, coords].transpose.inject({}) do |memo, (city, coord)|
         | 
| 41 | 
            +
                      latitude, longitude = coord.split(",").map(&:to_f)
         | 
| 42 | 
            +
                      memo.merge(city => { :latitude => latitude, :longitude => longitude } )
         | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
            end
         | 
| @@ -0,0 +1,34 @@ | |
| 1 | 
            +
            module WeatherForecasts
         | 
| 2 | 
            +
              class Client
         | 
| 3 | 
            +
                class SelectCoordinatesByZipQuery < Query
         | 
| 4 | 
            +
                  property :zip, :type => Array, :required => true
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def execute
         | 
| 7 | 
            +
                    validate
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                    response = soap_client.call(:lat_lon_list_zip_code, :message => build_message)
         | 
| 10 | 
            +
                    document = parse_xml(response)
         | 
| 11 | 
            +
                    transform_document_to_mapped_list(document)
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  protected
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def build_message
         | 
| 17 | 
            +
                    {
         | 
| 18 | 
            +
                      :zipCodeList => conditions[:zip].join(" ")
         | 
| 19 | 
            +
                    }
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def parse_xml(response)
         | 
| 23 | 
            +
                    Nokogiri::XML(response.body[:lat_lon_list_zip_code_response][:list_lat_lon_out])
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def transform_document_to_mapped_list(document)
         | 
| 27 | 
            +
                    document.xpath("//latLonList").text.split.each_with_index.inject({}) do |memo, (coord, i)|
         | 
| 28 | 
            +
                      latitude, longitude = coord.split(",").map(&:to_f)
         | 
| 29 | 
            +
                      memo.merge(conditions[:zip][i] => { :latitude => latitude, :longitude => longitude })
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
            end
         | 
| @@ -0,0 +1,38 @@ | |
| 1 | 
            +
            module WeatherForecasts
         | 
| 2 | 
            +
              class Client
         | 
| 3 | 
            +
                class SelectCornerCoordinatesQuery < Query
         | 
| 4 | 
            +
                  include QueryUtilities
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  SECTOR_GRID_OPTIONS = [
         | 
| 7 | 
            +
                    :conus,
         | 
| 8 | 
            +
                    :alaska,
         | 
| 9 | 
            +
                    :nhemi,
         | 
| 10 | 
            +
                    :guam,
         | 
| 11 | 
            +
                    :hawaii,
         | 
| 12 | 
            +
                    :puertori
         | 
| 13 | 
            +
                  ]
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  property :sector, :type => Symbol, :options => SECTOR_GRID_OPTIONS, :required => true
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  def execute
         | 
| 18 | 
            +
                    validate
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    response = soap_client.call(:corner_points, :message => build_message)
         | 
| 21 | 
            +
                    document = parse_xml(response)
         | 
| 22 | 
            +
                    transform_coordinate_list(document)
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  protected
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  def build_message
         | 
| 28 | 
            +
                    {
         | 
| 29 | 
            +
                      :sector   => conditions[:sector].to_s,
         | 
| 30 | 
            +
                    }
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  def parse_xml(response)
         | 
| 34 | 
            +
                    Nokogiri::XML(response.body[:corner_points_response][:list_lat_lon_out])
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
            end
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            module WeatherForecasts
         | 
| 2 | 
            +
              class Client
         | 
| 3 | 
            +
                class SelectGridpointCoordinatesQuery < Query
         | 
| 4 | 
            +
                  include QueryUtilities
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  property :lower_left_coordinate,  :type => Hash, :required => true, :required_keys => [:latitude, :longitude]
         | 
| 7 | 
            +
                  property :upper_right_coordinate, :type => Hash, :required => true, :required_keys => [:latitude, :longitude]
         | 
| 8 | 
            +
                  property :resolution,             :type => Numeric, :default => 5
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def execute
         | 
| 11 | 
            +
                    validate
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    response = soap_client.call(:lat_lon_list_subgrid, :message => build_message)
         | 
| 14 | 
            +
                    document = parse_xml(response)
         | 
| 15 | 
            +
                    transform_coordinate_list(document)
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  protected
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def build_message
         | 
| 21 | 
            +
                    {
         | 
| 22 | 
            +
                      :lowerLeftLatitude   => conditions[:lower_left_coordinate][:latitude],
         | 
| 23 | 
            +
                      :lowerLeftLongitude  => conditions[:lower_left_coordinate][:longitude],
         | 
| 24 | 
            +
                      :upperRightLatitude  => conditions[:upper_right_coordinate][:latitude],
         | 
| 25 | 
            +
                      :upperRightLongitude => conditions[:upper_right_coordinate][:longitude],
         | 
| 26 | 
            +
                      :resolution          => conditions[:resolution]
         | 
| 27 | 
            +
                    }
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  def parse_xml(response)
         | 
| 31 | 
            +
                    Nokogiri::XML(response.body[:lat_lon_list_subgrid_response][:list_lat_lon_out])
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            module WeatherForecasts
         | 
| 2 | 
            +
              class Client
         | 
| 3 | 
            +
                class SelectLinepointCoordinatesQuery < Query
         | 
| 4 | 
            +
                  include QueryUtilities
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  property :start_coordinate, :type => Hash, :required => true, :required_keys => [:latitude, :longitude]
         | 
| 7 | 
            +
                  property :stop_coordinate,  :type => Hash, :required => true, :required_keys => [:latitude, :longitude]
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def execute
         | 
| 10 | 
            +
                    validate
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    response = soap_client.call(:lat_lon_list_line, :message => build_message)
         | 
| 13 | 
            +
                    document = parse_xml(response)
         | 
| 14 | 
            +
                    transform_coordinate_list(document)
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  protected
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def build_message
         | 
| 20 | 
            +
                    {
         | 
| 21 | 
            +
                      :endPoint1Lat => conditions[:start_coordinate][:latitude],
         | 
| 22 | 
            +
                      :endPoint1Lon => conditions[:start_coordinate][:longitude],
         | 
| 23 | 
            +
                      :endPoint2Lat => conditions[:stop_coordinate][:latitude],
         | 
| 24 | 
            +
                      :endPoint2Lon => conditions[:stop_coordinate][:longitude]
         | 
| 25 | 
            +
                    }
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def parse_xml(response)
         | 
| 29 | 
            +
                    Nokogiri::XML(response.body[:lat_lon_list_line_response][:list_lat_lon_out])
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -0,0 +1,48 @@ | |
| 1 | 
            +
            module WeatherForecasts
         | 
| 2 | 
            +
              class Client
         | 
| 3 | 
            +
                class SelectQuery < Query
         | 
| 4 | 
            +
                  include QueryUtilities
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  set_selection_attributes *FORECAST_ELEMENTS.keys
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  property :coordinates, :type => Array, :required => true, :required_keys => [:latitude, :longitude]
         | 
| 9 | 
            +
                  property :product,     :type => String, :options => ["time-series", "glance"], :required => true, :default => "time-series"
         | 
| 10 | 
            +
                  property :unit,        :type => String, :options => ["e", "m"], :default => "e"
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  property :start_time,  :type => ActiveSupport::TimeWithZone
         | 
| 13 | 
            +
                  property :end_time,    :type => ActiveSupport::TimeWithZone
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def execute
         | 
| 16 | 
            +
                    validate
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    response = soap_client.call(:ndf_dgen_lat_lon_list, :message => build_message)
         | 
| 19 | 
            +
                    document = Nokogiri::XML(response.body[:ndf_dgen_lat_lon_list_response][:dwml_out])
         | 
| 20 | 
            +
                    transform_to_hash(document)
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  protected
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  def transform_to_hash(doc)
         | 
| 26 | 
            +
                    WeatherForecasts::DWML.new(doc).process
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  def build_message
         | 
| 30 | 
            +
                    {
         | 
| 31 | 
            +
                      :listLatLon        => build_coordinates(conditions[:coordinates]),
         | 
| 32 | 
            +
                      :product           => conditions[:product],
         | 
| 33 | 
            +
                      :startTime         => conditions[:start_time].iso8601,
         | 
| 34 | 
            +
                      :endTime           => conditions[:end_time].iso8601,
         | 
| 35 | 
            +
                      :Unit              => conditions[:unit],
         | 
| 36 | 
            +
                      :weatherParameters => build_weather_parameters
         | 
| 37 | 
            +
                    }
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  def build_weather_parameters
         | 
| 41 | 
            +
                    available_selections.attributes.inject({}) do |memo, attr|
         | 
| 42 | 
            +
                      is_requested = select_attributes.include?(attr) ? 1 : 0
         | 
| 43 | 
            +
                      memo.merge!(attr => is_requested)
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
            end
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            module WeatherForecasts
         | 
| 2 | 
            +
              class Client
         | 
| 3 | 
            +
                class SelectSquareCoordinatesQuery < Query
         | 
| 4 | 
            +
                  include QueryUtilities
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  property :center_point, :type => Hash, :required => true, :required_keys => [:latitude, :longitude]
         | 
| 7 | 
            +
                  property :distance,     :type => Hash, :required => true, :required_keys => [:latitude, :longitude]
         | 
| 8 | 
            +
                  property :resolution,   :type => Numeric, :default => 5
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def execute
         | 
| 11 | 
            +
                    validate
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    response = soap_client.call(:lat_lon_list_square, :message => build_message)
         | 
| 14 | 
            +
                    document = parse_xml(response)
         | 
| 15 | 
            +
                    transform_coordinate_list(document)
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  protected
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def build_message
         | 
| 21 | 
            +
                    {
         | 
| 22 | 
            +
                      :centerPointLat => conditions[:center_point][:latitude],
         | 
| 23 | 
            +
                      :centerPointLon => conditions[:center_point][:longitude],
         | 
| 24 | 
            +
                      :distanceLat    => conditions[:distance][:latitude],
         | 
| 25 | 
            +
                      :distanceLon    => conditions[:distance][:longitude],
         | 
| 26 | 
            +
                      :resolution     => conditions[:resolution]
         | 
| 27 | 
            +
                    }
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  def parse_xml(response)
         | 
| 31 | 
            +
                    Nokogiri::XML(response.body[:lat_lon_list_square_response][:list_lat_lon_out])
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
| @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            require 'active_support/core_ext/array/wrap'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module WeatherForecasts
         | 
| 4 | 
            +
              class Client
         | 
| 5 | 
            +
                class SelectionAttributes
         | 
| 6 | 
            +
                  attr_reader :attributes
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def initialize(attributes)
         | 
| 9 | 
            +
                    @attributes = Array.wrap(attributes)
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def valid?(attr)
         | 
| 13 | 
            +
                    unless attributes.include?(attr)
         | 
| 14 | 
            +
                      raise InvalidSelectionAttributeError, "The selection attribute, `#{attr}`, is not a valid attribute on this query."
         | 
| 15 | 
            +
                    end
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
            end
         | 
| @@ -0,0 +1,38 @@ | |
| 1 | 
            +
            require 'multi_json'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'weather_forecasts/dwml/head_extractor'
         | 
| 4 | 
            +
            require 'weather_forecasts/dwml/data_extractor'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            # Note: See http://graphical.weather.gov/xml/mdl/XML/Design/MDL_XML_Design.pdf
         | 
| 8 | 
            +
            # for authoritative type definitions
         | 
| 9 | 
            +
            ################################################################################
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            module WeatherForecasts
         | 
| 12 | 
            +
              class DWML
         | 
| 13 | 
            +
                attr_reader :output, :xmldoc
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def initialize(xmldoc)
         | 
| 16 | 
            +
                  @xmldoc = xmldoc
         | 
| 17 | 
            +
                  @output = {}
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def process
         | 
| 21 | 
            +
                  build_head
         | 
| 22 | 
            +
                  build_data
         | 
| 23 | 
            +
                  output
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                protected
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def build_head
         | 
| 29 | 
            +
                  extractor = HeadExtractor.new(xmldoc.xpath("//dwml/head").first)
         | 
| 30 | 
            +
                  @output.merge!(extractor.process)
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def build_data
         | 
| 34 | 
            +
                  extractor = DataExtractor.new(xmldoc.xpath("//dwml/data").first)
         | 
| 35 | 
            +
                  @output.merge!(extractor.process)
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
            end
         |