attack-barometer 0.1.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.
- data/LICENSE +20 -0
 - data/README.rdoc +266 -0
 - data/VERSION.yml +4 -0
 - data/bin/barometer +63 -0
 - data/lib/barometer/base.rb +52 -0
 - data/lib/barometer/data/current.rb +93 -0
 - data/lib/barometer/data/distance.rb +131 -0
 - data/lib/barometer/data/forecast.rb +66 -0
 - data/lib/barometer/data/geo.rb +98 -0
 - data/lib/barometer/data/location.rb +20 -0
 - data/lib/barometer/data/measurement.rb +161 -0
 - data/lib/barometer/data/pressure.rb +133 -0
 - data/lib/barometer/data/speed.rb +147 -0
 - data/lib/barometer/data/sun.rb +35 -0
 - data/lib/barometer/data/temperature.rb +164 -0
 - data/lib/barometer/data/units.rb +55 -0
 - data/lib/barometer/data/zone.rb +124 -0
 - data/lib/barometer/data.rb +15 -0
 - data/lib/barometer/extensions/graticule.rb +50 -0
 - data/lib/barometer/extensions/httparty.rb +21 -0
 - data/lib/barometer/query.rb +228 -0
 - data/lib/barometer/services/google.rb +146 -0
 - data/lib/barometer/services/noaa.rb +6 -0
 - data/lib/barometer/services/service.rb +324 -0
 - data/lib/barometer/services/weather_bug.rb +6 -0
 - data/lib/barometer/services/weather_dot_com.rb +6 -0
 - data/lib/barometer/services/wunderground.rb +285 -0
 - data/lib/barometer/services/yahoo.rb +274 -0
 - data/lib/barometer/services.rb +6 -0
 - data/lib/barometer/weather.rb +187 -0
 - data/lib/barometer.rb +52 -0
 - data/spec/barometer_spec.rb +162 -0
 - data/spec/data_current_spec.rb +225 -0
 - data/spec/data_distance_spec.rb +336 -0
 - data/spec/data_forecast_spec.rb +150 -0
 - data/spec/data_geo_spec.rb +90 -0
 - data/spec/data_location_spec.rb +59 -0
 - data/spec/data_measurement_spec.rb +411 -0
 - data/spec/data_pressure_spec.rb +336 -0
 - data/spec/data_speed_spec.rb +374 -0
 - data/spec/data_sun_spec.rb +76 -0
 - data/spec/data_temperature_spec.rb +396 -0
 - data/spec/data_zone_spec.rb +133 -0
 - data/spec/fixtures/current_calgary_ab.xml +1 -0
 - data/spec/fixtures/forecast_calgary_ab.xml +1 -0
 - data/spec/fixtures/geocode_40_73.xml +1 -0
 - data/spec/fixtures/geocode_90210.xml +1 -0
 - data/spec/fixtures/geocode_T5B4M9.xml +1 -0
 - data/spec/fixtures/geocode_calgary_ab.xml +1 -0
 - data/spec/fixtures/geocode_newyork_ny.xml +1 -0
 - data/spec/fixtures/google_calgary_ab.xml +1 -0
 - data/spec/fixtures/yahoo_90210.xml +1 -0
 - data/spec/query_spec.rb +469 -0
 - data/spec/service_google_spec.rb +144 -0
 - data/spec/service_wunderground_spec.rb +330 -0
 - data/spec/service_yahoo_spec.rb +299 -0
 - data/spec/services_spec.rb +1106 -0
 - data/spec/spec_helper.rb +14 -0
 - data/spec/units_spec.rb +101 -0
 - data/spec/weather_spec.rb +265 -0
 - metadata +119 -0
 
| 
         @@ -0,0 +1,164 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Barometer
         
     | 
| 
      
 2 
     | 
    
         
            +
              #
         
     | 
| 
      
 3 
     | 
    
         
            +
              # A simple Temperature class
         
     | 
| 
      
 4 
     | 
    
         
            +
              # 
         
     | 
| 
      
 5 
     | 
    
         
            +
              # Think of this like the Integer class. Enhancement
         
     | 
| 
      
 6 
     | 
    
         
            +
              # is that you can create a number (in a certain unit), then
         
     | 
| 
      
 7 
     | 
    
         
            +
              # get that number back already converted to another unit.
         
     | 
| 
      
 8 
     | 
    
         
            +
              #
         
     | 
| 
      
 9 
     | 
    
         
            +
              # All comparison operations will be done in the absolute
         
     | 
| 
      
 10 
     | 
    
         
            +
              # scale of Kelvin (K)
         
     | 
| 
      
 11 
     | 
    
         
            +
              #
         
     | 
| 
      
 12 
     | 
    
         
            +
              class Temperature < Barometer::Units
         
     | 
| 
      
 13 
     | 
    
         
            +
                
         
     | 
| 
      
 14 
     | 
    
         
            +
                METRIC_UNITS = "C"
         
     | 
| 
      
 15 
     | 
    
         
            +
                IMPERIAL_UNITS = "F"
         
     | 
| 
      
 16 
     | 
    
         
            +
                
         
     | 
| 
      
 17 
     | 
    
         
            +
                attr_accessor :celsius, :fahrenheit, :kelvin
         
     | 
| 
      
 18 
     | 
    
         
            +
                
         
     | 
| 
      
 19 
     | 
    
         
            +
                def initialize(metric=true)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @celsius = nil
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @fahrenheit = nil
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @kelvin = nil
         
     | 
| 
      
 23 
     | 
    
         
            +
                  super(metric)
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
                
         
     | 
| 
      
 26 
     | 
    
         
            +
                def metric_default=(value); self.c = value; end
         
     | 
| 
      
 27 
     | 
    
         
            +
                def imperial_default=(value); self.f = value; end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                #
         
     | 
| 
      
 30 
     | 
    
         
            +
                # CONVERTERS
         
     | 
| 
      
 31 
     | 
    
         
            +
                #
         
     | 
| 
      
 32 
     | 
    
         
            +
                
         
     | 
| 
      
 33 
     | 
    
         
            +
                def self.c_to_k(c)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  return nil unless c && (c.is_a?(Integer) || c.is_a?(Float))
         
     | 
| 
      
 35 
     | 
    
         
            +
                  273.15 + c.to_f
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
                
         
     | 
| 
      
 38 
     | 
    
         
            +
                # Tf = (9/5)*Tc+32
         
     | 
| 
      
 39 
     | 
    
         
            +
                def self.c_to_f(c)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  return nil unless c && (c.is_a?(Integer) || c.is_a?(Float))
         
     | 
| 
      
 41 
     | 
    
         
            +
                   ((9.0/5.0)*c.to_f)+32.0
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
                
         
     | 
| 
      
 44 
     | 
    
         
            +
                # Tc = (5/9)*(Tf-32)
         
     | 
| 
      
 45 
     | 
    
         
            +
                def self.f_to_c(f)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  return nil unless f && (f.is_a?(Integer) || f.is_a?(Float))
         
     | 
| 
      
 47 
     | 
    
         
            +
                  (5.0/9.0)*(f.to_f-32.0)
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
                
         
     | 
| 
      
 50 
     | 
    
         
            +
                def self.f_to_k(f)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  return nil unless f && (f.is_a?(Integer) || f.is_a?(Float))
         
     | 
| 
      
 52 
     | 
    
         
            +
                  c = self.f_to_c(f.to_f)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  self.c_to_k(c)
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
                
         
     | 
| 
      
 56 
     | 
    
         
            +
                def self.k_to_c(k)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  return nil unless k && (k.is_a?(Integer) || k.is_a?(Float))
         
     | 
| 
      
 58 
     | 
    
         
            +
                  k.to_f - 273.15
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
                
         
     | 
| 
      
 61 
     | 
    
         
            +
                def self.k_to_f(k)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  return nil unless k && (k.is_a?(Integer) || k.is_a?(Float))
         
     | 
| 
      
 63 
     | 
    
         
            +
                  c = self.k_to_c(k.to_f)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  self.c_to_f(c)
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
                
         
     | 
| 
      
 67 
     | 
    
         
            +
                #
         
     | 
| 
      
 68 
     | 
    
         
            +
                # ACCESSORS
         
     | 
| 
      
 69 
     | 
    
         
            +
                #
         
     | 
| 
      
 70 
     | 
    
         
            +
                
         
     | 
| 
      
 71 
     | 
    
         
            +
                # store celsius and kelvin
         
     | 
| 
      
 72 
     | 
    
         
            +
                def c=(c)
         
     | 
| 
      
 73 
     | 
    
         
            +
                  return if !c || !(c.is_a?(Integer) || c.is_a?(Float))
         
     | 
| 
      
 74 
     | 
    
         
            +
                  @celsius = c.to_f
         
     | 
| 
      
 75 
     | 
    
         
            +
                  @kelvin = Temperature.c_to_k(c.to_f)
         
     | 
| 
      
 76 
     | 
    
         
            +
                  self.update_fahrenheit(c.to_f)
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
                
         
     | 
| 
      
 79 
     | 
    
         
            +
                # store fahrenheit and kelvin
         
     | 
| 
      
 80 
     | 
    
         
            +
                def f=(f)
         
     | 
| 
      
 81 
     | 
    
         
            +
                  return if !f || !(f.is_a?(Integer) || f.is_a?(Float))
         
     | 
| 
      
 82 
     | 
    
         
            +
                  @fahrenheit = f.to_f
         
     | 
| 
      
 83 
     | 
    
         
            +
                  @kelvin = Temperature.f_to_k(f.to_f)
         
     | 
| 
      
 84 
     | 
    
         
            +
                  self.update_celsius(f.to_f)
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
                
         
     | 
| 
      
 87 
     | 
    
         
            +
                # store kelvin, convert to all
         
     | 
| 
      
 88 
     | 
    
         
            +
                def k=(k)
         
     | 
| 
      
 89 
     | 
    
         
            +
                  return if !k || !(k.is_a?(Integer) || k.is_a?(Float))
         
     | 
| 
      
 90 
     | 
    
         
            +
                  @kelvin = k.to_f
         
     | 
| 
      
 91 
     | 
    
         
            +
                  @celsius = Temperature.k_to_c(k.to_f)
         
     | 
| 
      
 92 
     | 
    
         
            +
                  @fahrenheit = Temperature.k_to_f(k.to_f)
         
     | 
| 
      
 93 
     | 
    
         
            +
                end
         
     | 
| 
      
 94 
     | 
    
         
            +
                
         
     | 
| 
      
 95 
     | 
    
         
            +
                # return the stored celsius or convert from Kelvin
         
     | 
| 
      
 96 
     | 
    
         
            +
                def c(as_integer=true)
         
     | 
| 
      
 97 
     | 
    
         
            +
                  c = (@celsius || Temperature.k_to_c(@kelvin))
         
     | 
| 
      
 98 
     | 
    
         
            +
                  c ? (as_integer ? c.to_i : (100*c).round/100.0) : nil
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
                
         
     | 
| 
      
 101 
     | 
    
         
            +
                # return the stored fahrenheit or convert from Kelvin
         
     | 
| 
      
 102 
     | 
    
         
            +
                def f(as_integer=true)
         
     | 
| 
      
 103 
     | 
    
         
            +
                  f = (@fahrenheit || Temperature.k_to_f(@kelvin))
         
     | 
| 
      
 104 
     | 
    
         
            +
                  f ? (as_integer ? f.to_i : (100*f).round/100.0) : nil
         
     | 
| 
      
 105 
     | 
    
         
            +
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
                
         
     | 
| 
      
 107 
     | 
    
         
            +
                #
         
     | 
| 
      
 108 
     | 
    
         
            +
                # OPERATORS
         
     | 
| 
      
 109 
     | 
    
         
            +
                #
         
     | 
| 
      
 110 
     | 
    
         
            +
                
         
     | 
| 
      
 111 
     | 
    
         
            +
                def <=>(other)
         
     | 
| 
      
 112 
     | 
    
         
            +
                  @kelvin <=> other.kelvin
         
     | 
| 
      
 113 
     | 
    
         
            +
                end
         
     | 
| 
      
 114 
     | 
    
         
            +
                
         
     | 
| 
      
 115 
     | 
    
         
            +
                #
         
     | 
| 
      
 116 
     | 
    
         
            +
                # HELPERS
         
     | 
| 
      
 117 
     | 
    
         
            +
                #
         
     | 
| 
      
 118 
     | 
    
         
            +
                
         
     | 
| 
      
 119 
     | 
    
         
            +
                # will just return the value (no units)
         
     | 
| 
      
 120 
     | 
    
         
            +
                def to_i(metric=nil)
         
     | 
| 
      
 121 
     | 
    
         
            +
                  (metric || (metric.nil? && self.metric?)) ? self.c : self.f
         
     | 
| 
      
 122 
     | 
    
         
            +
                end
         
     | 
| 
      
 123 
     | 
    
         
            +
                
         
     | 
| 
      
 124 
     | 
    
         
            +
                # will just return the value (no units) with more precision
         
     | 
| 
      
 125 
     | 
    
         
            +
                def to_f(metric=nil)
         
     | 
| 
      
 126 
     | 
    
         
            +
                  (metric || (metric.nil? && self.metric?)) ? self.c(false) : self.f(false)
         
     | 
| 
      
 127 
     | 
    
         
            +
                end
         
     | 
| 
      
 128 
     | 
    
         
            +
                
         
     | 
| 
      
 129 
     | 
    
         
            +
                # will return the value with units
         
     | 
| 
      
 130 
     | 
    
         
            +
                def to_s(metric=nil)
         
     | 
| 
      
 131 
     | 
    
         
            +
                  (metric || (metric.nil? && self.metric?)) ? "#{self.c} #{METRIC_UNITS}" : "#{self.f} #{IMPERIAL_UNITS}"
         
     | 
| 
      
 132 
     | 
    
         
            +
                end
         
     | 
| 
      
 133 
     | 
    
         
            +
                
         
     | 
| 
      
 134 
     | 
    
         
            +
                # will just return the units (no value)
         
     | 
| 
      
 135 
     | 
    
         
            +
                def units(metric=nil)
         
     | 
| 
      
 136 
     | 
    
         
            +
                  (metric || (metric.nil? && self.metric?)) ? METRIC_UNITS : IMPERIAL_UNITS
         
     | 
| 
      
 137 
     | 
    
         
            +
                end
         
     | 
| 
      
 138 
     | 
    
         
            +
                
         
     | 
| 
      
 139 
     | 
    
         
            +
                # when we set fahrenheit, it is possible the a non-equivalent value of
         
     | 
| 
      
 140 
     | 
    
         
            +
                # celsius remains.  if so, clear it.
         
     | 
| 
      
 141 
     | 
    
         
            +
                def update_celsius(f)
         
     | 
| 
      
 142 
     | 
    
         
            +
                  return unless @celsius
         
     | 
| 
      
 143 
     | 
    
         
            +
                  difference = Temperature.f_to_c(f.to_f) - @celsius
         
     | 
| 
      
 144 
     | 
    
         
            +
                  # only clear celsius if the stored celsius is off be more then 1 degree
         
     | 
| 
      
 145 
     | 
    
         
            +
                  # then the conversion of fahrenheit
         
     | 
| 
      
 146 
     | 
    
         
            +
                  @celsius = nil unless difference.abs <= 1.0
         
     | 
| 
      
 147 
     | 
    
         
            +
                end
         
     | 
| 
      
 148 
     | 
    
         
            +
                
         
     | 
| 
      
 149 
     | 
    
         
            +
                # when we set celsius, it is possible the a non-equivalent value of
         
     | 
| 
      
 150 
     | 
    
         
            +
                # fahrenheit remains.  if so, clear it.
         
     | 
| 
      
 151 
     | 
    
         
            +
                def update_fahrenheit(c)
         
     | 
| 
      
 152 
     | 
    
         
            +
                  return unless @fahrenheit
         
     | 
| 
      
 153 
     | 
    
         
            +
                  difference = Temperature.c_to_f(c.to_f) - @fahrenheit
         
     | 
| 
      
 154 
     | 
    
         
            +
                  # only clear fahrenheit if the stored fahrenheit is off be more then 1 degree
         
     | 
| 
      
 155 
     | 
    
         
            +
                  # then the conversion of celsius
         
     | 
| 
      
 156 
     | 
    
         
            +
                  @fahrenheit = nil unless difference.abs <= 1.0
         
     | 
| 
      
 157 
     | 
    
         
            +
                end
         
     | 
| 
      
 158 
     | 
    
         
            +
                
         
     | 
| 
      
 159 
     | 
    
         
            +
                def nil?
         
     | 
| 
      
 160 
     | 
    
         
            +
                  (@celsius || @fahrenheit || @kelvin) ? false : true
         
     | 
| 
      
 161 
     | 
    
         
            +
                end
         
     | 
| 
      
 162 
     | 
    
         
            +
                
         
     | 
| 
      
 163 
     | 
    
         
            +
              end
         
     | 
| 
      
 164 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,55 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Barometer
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Units
         
     | 
| 
      
 3 
     | 
    
         
            +
                include Comparable
         
     | 
| 
      
 4 
     | 
    
         
            +
                
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_accessor :metric
         
     | 
| 
      
 6 
     | 
    
         
            +
                
         
     | 
| 
      
 7 
     | 
    
         
            +
                def initialize(metric=true)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @metric = metric
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
                
         
     | 
| 
      
 11 
     | 
    
         
            +
                #
         
     | 
| 
      
 12 
     | 
    
         
            +
                # HELPERS
         
     | 
| 
      
 13 
     | 
    
         
            +
                #
         
     | 
| 
      
 14 
     | 
    
         
            +
                
         
     | 
| 
      
 15 
     | 
    
         
            +
                def metric?; @metric; end
         
     | 
| 
      
 16 
     | 
    
         
            +
                def metric!; @metric=true; end
         
     | 
| 
      
 17 
     | 
    
         
            +
                def imperial!; @metric=false; end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                # assigns a value to the right attribute based on metric setting    
         
     | 
| 
      
 20 
     | 
    
         
            +
                def <<(value)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  return unless value
         
     | 
| 
      
 22 
     | 
    
         
            +
                
         
     | 
| 
      
 23 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 24 
     | 
    
         
            +
                    if value.is_a?(Array)
         
     | 
| 
      
 25 
     | 
    
         
            +
                      value_m = value[0].to_f
         
     | 
| 
      
 26 
     | 
    
         
            +
                      value_i = value[1].to_f
         
     | 
| 
      
 27 
     | 
    
         
            +
                      value_b = nil
         
     | 
| 
      
 28 
     | 
    
         
            +
                    else
         
     | 
| 
      
 29 
     | 
    
         
            +
                      value_m = nil
         
     | 
| 
      
 30 
     | 
    
         
            +
                      value_i = nil
         
     | 
| 
      
 31 
     | 
    
         
            +
                      value_b = value.to_f
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
                  rescue
         
     | 
| 
      
 34 
     | 
    
         
            +
                    # do nothing
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                
         
     | 
| 
      
 37 
     | 
    
         
            +
                  if self.metric?
         
     | 
| 
      
 38 
     | 
    
         
            +
                    self.metric_default = value_m || value_b
         
     | 
| 
      
 39 
     | 
    
         
            +
                  else
         
     | 
| 
      
 40 
     | 
    
         
            +
                    self.imperial_default = value_i || value_b
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
                
         
     | 
| 
      
 44 
     | 
    
         
            +
                # STUB: define this method to actually retireve the metric_default
         
     | 
| 
      
 45 
     | 
    
         
            +
                def metric_default=(value)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  raise NotImplementedError
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                # STUB: define this method to actually retireve the imperial_default
         
     | 
| 
      
 50 
     | 
    
         
            +
                def imperial_default=(value)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  raise NotImplementedError
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
      
 55 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,124 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'tzinfo'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Barometer
         
     | 
| 
      
 5 
     | 
    
         
            +
              #
         
     | 
| 
      
 6 
     | 
    
         
            +
              # A simple Zone class
         
     | 
| 
      
 7 
     | 
    
         
            +
              # 
         
     | 
| 
      
 8 
     | 
    
         
            +
              # Used for building and converting timezone aware date and times
         
     | 
| 
      
 9 
     | 
    
         
            +
              # Really, these are just wrappers for TZInfo conversions.
         
     | 
| 
      
 10 
     | 
    
         
            +
              #
         
     | 
| 
      
 11 
     | 
    
         
            +
              class Zone
         
     | 
| 
      
 12 
     | 
    
         
            +
                
         
     | 
| 
      
 13 
     | 
    
         
            +
                attr_accessor :timezone, :tz
         
     | 
| 
      
 14 
     | 
    
         
            +
                
         
     | 
| 
      
 15 
     | 
    
         
            +
                def initialize(timezone)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @timezone = timezone
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @tz = TZInfo::Timezone.get(timezone)
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
                
         
     | 
| 
      
 20 
     | 
    
         
            +
                # what is the Timezone Short Code for the current timezone
         
     | 
| 
      
 21 
     | 
    
         
            +
                def code
         
     | 
| 
      
 22 
     | 
    
         
            +
                  return "" unless @tz
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @tz.period_for_utc(Time.now.utc).zone_identifier.to_s
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
                
         
     | 
| 
      
 26 
     | 
    
         
            +
                # is the current timezone in daylights savings mode?
         
     | 
| 
      
 27 
     | 
    
         
            +
                def dst?
         
     | 
| 
      
 28 
     | 
    
         
            +
                  return nil unless @tz
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @tz.period_for_utc(Time.now.utc).dst?
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
                
         
     | 
| 
      
 32 
     | 
    
         
            +
                # return Time.now.utc for the set timezone
         
     | 
| 
      
 33 
     | 
    
         
            +
                def now
         
     | 
| 
      
 34 
     | 
    
         
            +
                  Barometer::Zone.now(@timezone)
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
                
         
     | 
| 
      
 37 
     | 
    
         
            +
                # return Date.today for the set timezone
         
     | 
| 
      
 38 
     | 
    
         
            +
                def today
         
     | 
| 
      
 39 
     | 
    
         
            +
                  Barometer::Zone.today(@timezone)
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
                
         
     | 
| 
      
 42 
     | 
    
         
            +
                def local_to_utc(local_time)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  @tz.local_to_utc(local_time)
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
                
         
     | 
| 
      
 46 
     | 
    
         
            +
                def utc_to_local(utc_time)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  @tz.utc_to_local(utc_time)
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
                
         
     | 
| 
      
 50 
     | 
    
         
            +
                #
         
     | 
| 
      
 51 
     | 
    
         
            +
                # Class Methods
         
     | 
| 
      
 52 
     | 
    
         
            +
                #
         
     | 
| 
      
 53 
     | 
    
         
            +
                
         
     | 
| 
      
 54 
     | 
    
         
            +
                # return the local current time, providing a timezone
         
     | 
| 
      
 55 
     | 
    
         
            +
                # (ie 'Europe/Paris') will give the local time for the
         
     | 
| 
      
 56 
     | 
    
         
            +
                # timezone, otherwise it will be Time.now
         
     | 
| 
      
 57 
     | 
    
         
            +
                def self.now(timezone=nil)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  if timezone
         
     | 
| 
      
 59 
     | 
    
         
            +
                    utc = Time.now.utc
         
     | 
| 
      
 60 
     | 
    
         
            +
                    tz = TZInfo::Timezone.get(timezone)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    tz.utc_to_local(utc)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  else
         
     | 
| 
      
 63 
     | 
    
         
            +
                    Time.now
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
                
         
     | 
| 
      
 67 
     | 
    
         
            +
                # return the local current date, providing a timezone
         
     | 
| 
      
 68 
     | 
    
         
            +
                # (ie 'Europe/Paris') will give the local date for the
         
     | 
| 
      
 69 
     | 
    
         
            +
                # timezone, otherwise it will be Date.today
         
     | 
| 
      
 70 
     | 
    
         
            +
                def self.today(timezone=nil)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  if timezone
         
     | 
| 
      
 72 
     | 
    
         
            +
                    utc = Time.now.utc
         
     | 
| 
      
 73 
     | 
    
         
            +
                    tz = TZInfo::Timezone.get(timezone)
         
     | 
| 
      
 74 
     | 
    
         
            +
                    now = tz.utc_to_local(utc)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    Date.new(now.year, now.month, now.day)
         
     | 
| 
      
 76 
     | 
    
         
            +
                  else
         
     | 
| 
      
 77 
     | 
    
         
            +
                    Date.today
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
                
         
     | 
| 
      
 81 
     | 
    
         
            +
                # takes a time (any timezone), and a TimeZone Short Code (ie PST) and
         
     | 
| 
      
 82 
     | 
    
         
            +
                # converts the time to UTC accorsing to that time_zone
         
     | 
| 
      
 83 
     | 
    
         
            +
                # NOTE: No Tests
         
     | 
| 
      
 84 
     | 
    
         
            +
                def self.code_to_utc(time, timezone_code)
         
     | 
| 
      
 85 
     | 
    
         
            +
                  raise ArgumentError unless time.is_a?(Time)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  offset = Time.zone_offset(timezone_code) || 0
         
     | 
| 
      
 87 
     | 
    
         
            +
                  
         
     | 
| 
      
 88 
     | 
    
         
            +
                  Time.utc(
         
     | 
| 
      
 89 
     | 
    
         
            +
                    time.year, time.month, time.day,
         
     | 
| 
      
 90 
     | 
    
         
            +
                    time.hour, time.min, time.sec, time.usec
         
     | 
| 
      
 91 
     | 
    
         
            +
                  ) - offset
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
                
         
     | 
| 
      
 94 
     | 
    
         
            +
                # takes a string with TIME only information and merges it with a string that
         
     | 
| 
      
 95 
     | 
    
         
            +
                # has DATE only information and creates a UTC TIME object with time and date
         
     | 
| 
      
 96 
     | 
    
         
            +
                # info.  if you supply the timezone code (ie PST), it will apply the timezone
         
     | 
| 
      
 97 
     | 
    
         
            +
                # offset to the final time
         
     | 
| 
      
 98 
     | 
    
         
            +
                def self.merge(time, date, timezone_code=nil)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  raise ArgumentError unless (time.is_a?(Time) || time.is_a?(String))
         
     | 
| 
      
 100 
     | 
    
         
            +
                  raise ArgumentError unless (date.is_a?(Time) || date.is_a?(Date) || date.is_a?(String))
         
     | 
| 
      
 101 
     | 
    
         
            +
                  
         
     | 
| 
      
 102 
     | 
    
         
            +
                  if time.is_a?(String)
         
     | 
| 
      
 103 
     | 
    
         
            +
                    reference_time = Time.parse(time)
         
     | 
| 
      
 104 
     | 
    
         
            +
                  elsif time.is_a?(Time)
         
     | 
| 
      
 105 
     | 
    
         
            +
                    reference_time = time
         
     | 
| 
      
 106 
     | 
    
         
            +
                  end
         
     | 
| 
      
 107 
     | 
    
         
            +
                  
         
     | 
| 
      
 108 
     | 
    
         
            +
                  if date.is_a?(String)
         
     | 
| 
      
 109 
     | 
    
         
            +
                    reference_date = Date.parse(date)
         
     | 
| 
      
 110 
     | 
    
         
            +
                  elsif date.is_a?(Time)
         
     | 
| 
      
 111 
     | 
    
         
            +
                    reference_date = Date.new(date.year, date.month, date.day)
         
     | 
| 
      
 112 
     | 
    
         
            +
                  elsif date.is_a?(Date)
         
     | 
| 
      
 113 
     | 
    
         
            +
                    reference_date = date
         
     | 
| 
      
 114 
     | 
    
         
            +
                  end
         
     | 
| 
      
 115 
     | 
    
         
            +
                  
         
     | 
| 
      
 116 
     | 
    
         
            +
                  new_time = Time.utc(
         
     | 
| 
      
 117 
     | 
    
         
            +
                    reference_date.year, reference_date.month, reference_date.day,
         
     | 
| 
      
 118 
     | 
    
         
            +
                    reference_time.hour, reference_time.min, reference_time.sec
         
     | 
| 
      
 119 
     | 
    
         
            +
                  )
         
     | 
| 
      
 120 
     | 
    
         
            +
                  timezone_code ? Barometer::Zone.code_to_utc(new_time,timezone_code) : new_time
         
     | 
| 
      
 121 
     | 
    
         
            +
                end
         
     | 
| 
      
 122 
     | 
    
         
            +
                
         
     | 
| 
      
 123 
     | 
    
         
            +
              end
         
     | 
| 
      
 124 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            $:.unshift(File.dirname(__FILE__))
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # measurements and units
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'data/measurement'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'data/current'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'data/forecast'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'data/zone'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'data/sun'
         
     | 
| 
      
 9 
     | 
    
         
            +
            require 'data/geo'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require 'data/location'
         
     | 
| 
      
 11 
     | 
    
         
            +
            require 'data/units'
         
     | 
| 
      
 12 
     | 
    
         
            +
            require 'data/temperature'
         
     | 
| 
      
 13 
     | 
    
         
            +
            require 'data/distance'
         
     | 
| 
      
 14 
     | 
    
         
            +
            require 'data/speed'
         
     | 
| 
      
 15 
     | 
    
         
            +
            require 'data/pressure'
         
     | 
| 
         @@ -0,0 +1,50 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Graticule
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Location
         
     | 
| 
      
 3 
     | 
    
         
            +
                
         
     | 
| 
      
 4 
     | 
    
         
            +
                attr_accessor :country_code
         
     | 
| 
      
 5 
     | 
    
         
            +
                
         
     | 
| 
      
 6 
     | 
    
         
            +
                def attributes
         
     | 
| 
      
 7 
     | 
    
         
            +
                  [:latitude, :longitude, :street, :locality, :region, :postal_code, :country, :precision, :cuntry_code].inject({}) do |result,attr|
         
     | 
| 
      
 8 
     | 
    
         
            +
                    result[attr] = self.send(attr) unless self.send(attr).blank?
         
     | 
| 
      
 9 
     | 
    
         
            +
                    result
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
                
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
              
         
     | 
| 
      
 15 
     | 
    
         
            +
              module Geocoder
         
     | 
| 
      
 16 
     | 
    
         
            +
                
         
     | 
| 
      
 17 
     | 
    
         
            +
                class Google < Rest
         
     | 
| 
      
 18 
     | 
    
         
            +
                  
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # Locates +address+ returning a Location
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # add ability to bias towards a country
         
     | 
| 
      
 21 
     | 
    
         
            +
                  def locate(address, country_bias=nil)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    get :q => (address.is_a?(String) ? address : location_from_params(address).to_s),
         
     | 
| 
      
 23 
     | 
    
         
            +
                      :gl => country_bias
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  
         
     | 
| 
      
 26 
     | 
    
         
            +
                  private
         
     | 
| 
      
 27 
     | 
    
         
            +
                  
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # Extracts a Location from +xml+.
         
     | 
| 
      
 29 
     | 
    
         
            +
                  def parse_response(xml) #:nodoc:
         
     | 
| 
      
 30 
     | 
    
         
            +
                    longitude, latitude, = xml.elements['/kml/Response/Placemark/Point/coordinates'].text.split(',').map { |v| v.to_f }
         
     | 
| 
      
 31 
     | 
    
         
            +
                    returning Location.new(:latitude => latitude, :longitude => longitude) do |l|
         
     | 
| 
      
 32 
     | 
    
         
            +
                      address = REXML::XPath.first(xml, '//xal:AddressDetails',
         
     | 
| 
      
 33 
     | 
    
         
            +
                        'xal' => "urn:oasis:names:tc:ciq:xsdschema:xAL:2.0")
         
     | 
| 
      
 34 
     | 
    
         
            +
             
         
     | 
| 
      
 35 
     | 
    
         
            +
                      if address
         
     | 
| 
      
 36 
     | 
    
         
            +
                        l.street = value(address.elements['.//ThoroughfareName/text()'])
         
     | 
| 
      
 37 
     | 
    
         
            +
                        l.locality = value(address.elements['.//LocalityName/text()'])
         
     | 
| 
      
 38 
     | 
    
         
            +
                        l.region = value(address.elements['.//AdministrativeAreaName/text()'])
         
     | 
| 
      
 39 
     | 
    
         
            +
                        l.postal_code = value(address.elements['.//PostalCodeNumber/text()'])
         
     | 
| 
      
 40 
     | 
    
         
            +
                        l.country = value(address.elements['.//CountryName/text()'])
         
     | 
| 
      
 41 
     | 
    
         
            +
                        l.country_code = value(address.elements['.//CountryNameCode/text()'])
         
     | 
| 
      
 42 
     | 
    
         
            +
                        l.precision = PRECISION[address.attribute('Accuracy').value.to_i] || :unknown
         
     | 
| 
      
 43 
     | 
    
         
            +
                      end
         
     | 
| 
      
 44 
     | 
    
         
            +
                    end
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
                  
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
                
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #
         
     | 
| 
      
 2 
     | 
    
         
            +
            # extends HTTParty by adding configurable timeout support
         
     | 
| 
      
 3 
     | 
    
         
            +
            #
         
     | 
| 
      
 4 
     | 
    
         
            +
            module HTTParty
         
     | 
| 
      
 5 
     | 
    
         
            +
              class Request
         
     | 
| 
      
 6 
     | 
    
         
            +
                
         
     | 
| 
      
 7 
     | 
    
         
            +
                private
         
     | 
| 
      
 8 
     | 
    
         
            +
                
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def http
         
     | 
| 
      
 10 
     | 
    
         
            +
                    http = Net::HTTP.new(uri.host, uri.port, options[:http_proxyaddr], options[:http_proxyport])
         
     | 
| 
      
 11 
     | 
    
         
            +
                    http.use_ssl = (uri.port == 443)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    http.verify_mode = OpenSSL::SSL::VERIFY_NONE
         
     | 
| 
      
 13 
     | 
    
         
            +
                    if options[:timeout] && options[:timeout].is_a?(Integer)
         
     | 
| 
      
 14 
     | 
    
         
            +
                      http.open_timeout = options[:timeout]
         
     | 
| 
      
 15 
     | 
    
         
            +
                      http.read_timeout = options[:timeout]
         
     | 
| 
      
 16 
     | 
    
         
            +
                    end
         
     | 
| 
      
 17 
     | 
    
         
            +
                    http
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,228 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Barometer
         
     | 
| 
      
 2 
     | 
    
         
            +
              #
         
     | 
| 
      
 3 
     | 
    
         
            +
              # This class represents a query and can answer the
         
     | 
| 
      
 4 
     | 
    
         
            +
              # questions that a Barometer will need to measure the weather
         
     | 
| 
      
 5 
     | 
    
         
            +
              #
         
     | 
| 
      
 6 
     | 
    
         
            +
              # Summary:
         
     | 
| 
      
 7 
     | 
    
         
            +
              #   When you create a new Query, you set the query string
         
     | 
| 
      
 8 
     | 
    
         
            +
              #   ie: "New York, NY" or "90210"
         
     | 
| 
      
 9 
     | 
    
         
            +
              #   The class will then determine the query string format
         
     | 
| 
      
 10 
     | 
    
         
            +
              #   ie: :zipcode, :postalcode, :geocode, :coordinates
         
     | 
| 
      
 11 
     | 
    
         
            +
              #   Now, when a Weather API driver asks for the query, it will prefer
         
     | 
| 
      
 12 
     | 
    
         
            +
              #   certain formats, and only permit certain formats.  The Query class
         
     | 
| 
      
 13 
     | 
    
         
            +
              #   will attempt to either return the query string as-is if acceptable,
         
     | 
| 
      
 14 
     | 
    
         
            +
              #   or it will attempt to convert it to a format that is acceptable
         
     | 
| 
      
 15 
     | 
    
         
            +
              #   (most likely this conversion will in Googles geocoding service using
         
     | 
| 
      
 16 
     | 
    
         
            +
              #   the Graticule gem).  Worst case scenario is that the Weather API will
         
     | 
| 
      
 17 
     | 
    
         
            +
              #   not accept the query string.
         
     | 
| 
      
 18 
     | 
    
         
            +
              #
         
     | 
| 
      
 19 
     | 
    
         
            +
              class Query
         
     | 
| 
      
 20 
     | 
    
         
            +
                
         
     | 
| 
      
 21 
     | 
    
         
            +
                # OPTIONAL: key required by Google for geocoding
         
     | 
| 
      
 22 
     | 
    
         
            +
                @@google_geocode_key = nil
         
     | 
| 
      
 23 
     | 
    
         
            +
                def self.google_geocode_key; @@google_geocode_key || Barometer.google_geocode_key; end;
         
     | 
| 
      
 24 
     | 
    
         
            +
                def self.google_geocode_key=(key); @@google_geocode_key = key; end;
         
     | 
| 
      
 25 
     | 
    
         
            +
                
         
     | 
| 
      
 26 
     | 
    
         
            +
                attr_reader   :format
         
     | 
| 
      
 27 
     | 
    
         
            +
                attr_accessor :q, :preferred, :country_code, :geo
         
     | 
| 
      
 28 
     | 
    
         
            +
                
         
     | 
| 
      
 29 
     | 
    
         
            +
                def initialize(query=nil)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  @q = query
         
     | 
| 
      
 31 
     | 
    
         
            +
                  self.analyze!
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
                
         
     | 
| 
      
 34 
     | 
    
         
            +
                # analyze the saved query to determine the format.  for the format of
         
     | 
| 
      
 35 
     | 
    
         
            +
                # :zipcode and :postalcode the country_code can also be set
         
     | 
| 
      
 36 
     | 
    
         
            +
                def analyze!
         
     | 
| 
      
 37 
     | 
    
         
            +
                  return unless @q
         
     | 
| 
      
 38 
     | 
    
         
            +
                  if Barometer::Query.is_us_zipcode?(@q)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    @format = :zipcode
         
     | 
| 
      
 40 
     | 
    
         
            +
                  elsif Barometer::Query.is_canadian_postcode?(@q)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    @format = :postalcode
         
     | 
| 
      
 42 
     | 
    
         
            +
                  elsif Barometer::Query.is_coordinates?(@q)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    @format = :coordinates
         
     | 
| 
      
 44 
     | 
    
         
            +
                  else
         
     | 
| 
      
 45 
     | 
    
         
            +
                    @format = :geocode
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
                  @country_code = Barometer::Query.format_to_country_code(@format)
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
                
         
     | 
| 
      
 50 
     | 
    
         
            +
                # take a list of acceptable (and ordered by preference) formats and convert
         
     | 
| 
      
 51 
     | 
    
         
            +
                # the current query (q) into the most preferred and acceptable format. as a
         
     | 
| 
      
 52 
     | 
    
         
            +
                # side effect of some conversions, the country_code might be known, then save it
         
     | 
| 
      
 53 
     | 
    
         
            +
                def convert!(preferred_formats=nil)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  raise ArgumentError unless (preferred_formats && preferred_formats.size > 0)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # reset preferred
         
     | 
| 
      
 56 
     | 
    
         
            +
                  @preferred = nil
         
     | 
| 
      
 57 
     | 
    
         
            +
                  
         
     | 
| 
      
 58 
     | 
    
         
            +
                  # go through each acceptable format and try to convert to that
         
     | 
| 
      
 59 
     | 
    
         
            +
                  converted = false
         
     | 
| 
      
 60 
     | 
    
         
            +
                  geocoded = false
         
     | 
| 
      
 61 
     | 
    
         
            +
                  preferred_formats.each do |preferred_format|
         
     | 
| 
      
 62 
     | 
    
         
            +
                    # we are already in this format, return this
         
     | 
| 
      
 63 
     | 
    
         
            +
                    if preferred_format == @format
         
     | 
| 
      
 64 
     | 
    
         
            +
                      converted = true
         
     | 
| 
      
 65 
     | 
    
         
            +
                      @preferred ||= @q
         
     | 
| 
      
 66 
     | 
    
         
            +
                    end
         
     | 
| 
      
 67 
     | 
    
         
            +
                    
         
     | 
| 
      
 68 
     | 
    
         
            +
                    unless converted
         
     | 
| 
      
 69 
     | 
    
         
            +
                      case preferred_format
         
     | 
| 
      
 70 
     | 
    
         
            +
                      when :coordinates
         
     | 
| 
      
 71 
     | 
    
         
            +
                        geocoded = true
         
     | 
| 
      
 72 
     | 
    
         
            +
                        @preferred, @country_code, @geo = Barometer::Query.to_coordinates(@q, @format)
         
     | 
| 
      
 73 
     | 
    
         
            +
                      when :geocode
         
     | 
| 
      
 74 
     | 
    
         
            +
                        geocoded = true
         
     | 
| 
      
 75 
     | 
    
         
            +
                        @preferred, @country_code, @geo = Barometer::Query.to_geocode(@q, @format)
         
     | 
| 
      
 76 
     | 
    
         
            +
                      end
         
     | 
| 
      
 77 
     | 
    
         
            +
                    end
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
                  
         
     | 
| 
      
 80 
     | 
    
         
            +
                  # if we haven't already geocoded and we are forcing it, do it now
         
     | 
| 
      
 81 
     | 
    
         
            +
                  if !geocoded && Barometer.force_geocode
         
     | 
| 
      
 82 
     | 
    
         
            +
                    not_used_coords, not_used_code, @geo = Barometer::Query.to_coordinates(@q, @format)
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
                  
         
     | 
| 
      
 85 
     | 
    
         
            +
                  @preferred
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
                
         
     | 
| 
      
 88 
     | 
    
         
            +
                #
         
     | 
| 
      
 89 
     | 
    
         
            +
                # HELPERS
         
     | 
| 
      
 90 
     | 
    
         
            +
                #
         
     | 
| 
      
 91 
     | 
    
         
            +
                
         
     | 
| 
      
 92 
     | 
    
         
            +
                def zipcode?; @format == :zipcode; end
         
     | 
| 
      
 93 
     | 
    
         
            +
                def postalcode?; @format == :postalcode; end
         
     | 
| 
      
 94 
     | 
    
         
            +
                def coordinates?; @format == :coordinates; end
         
     | 
| 
      
 95 
     | 
    
         
            +
                def geocode?; @format == :geocode; end
         
     | 
| 
      
 96 
     | 
    
         
            +
                
         
     | 
| 
      
 97 
     | 
    
         
            +
                def self.is_us_zipcode?(query)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  us_zipcode_regex = /(^[0-9]{5}$)|(^[0-9]{5}-[0-9]{4}$)/
         
     | 
| 
      
 99 
     | 
    
         
            +
                  return !(query =~ us_zipcode_regex).nil?
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
                
         
     | 
| 
      
 102 
     | 
    
         
            +
                def self.is_canadian_postcode?(query)
         
     | 
| 
      
 103 
     | 
    
         
            +
                  # Rules: no D, F, I, O, Q, or U anywhere
         
     | 
| 
      
 104 
     | 
    
         
            +
                  # Basic validation: ^[ABCEGHJ-NPRSTVXY]{1}[0-9]{1}[ABCEGHJ-NPRSTV-Z]{1}[ ]?[0-9]{1}[ABCEGHJ-NPRSTV-Z]{1}[0-9]{1}$
         
     | 
| 
      
 105 
     | 
    
         
            +
                  # Extended validation: ^(A(0[ABCEGHJ-NPR]|1[ABCEGHK-NSV-Y]|2[ABHNV]|5[A]|8[A])|B(0[CEHJ-NPRSTVW]|1[ABCEGHJ-NPRSTV-Y]|2[ABCEGHJNRSTV-Z]|3[ABEGHJ-NPRSTVZ]|4[ABCEGHNPRV]|5[A]|6[L]|9[A])|C(0[AB]|1[ABCEN])|E(1[ABCEGHJNVWX]|2[AEGHJ-NPRSV]|3[ABCELNVYZ]|4[ABCEGHJ-NPRSTV-Z]|5[ABCEGHJ-NPRSTV]|6[ABCEGHJKL]|7[ABCEGHJ-NP]|8[ABCEGJ-NPRST]|9[ABCEGH])|G(0[ACEGHJ-NPRSTV-Z]|1[ABCEGHJ-NPRSTV-Y]|2[ABCEGJ-N]|3[ABCEGHJ-NZ]|4[ARSTVWXZ]|5[ABCHJLMNRTVXYZ]|6[ABCEGHJKLPRSTVWXZ]|7[ABGHJKNPSTXYZ]|8[ABCEGHJ-NPTVWYZ]|9[ABCHNPRTX])|H(0[HM]|1[ABCEGHJ-NPRSTV-Z]|2[ABCEGHJ-NPRSTV-Z]|3[ABCEGHJ-NPRSTV-Z]|4[ABCEGHJ-NPRSTV-Z]|5[AB]|7[ABCEGHJ-NPRSTV-Y]|8[NPRSTYZ]|9[ABCEGHJKPRSWX])|J(0[ABCEGHJ-NPRSTV-Z]|1[ACEGHJ-NRSTXZ]|2[ABCEGHJ-NRSTWXY]|3[ABEGHLMNPRTVXYZ]|4[BGHJ-NPRSTV-Z]|5[ABCJ-MRTV-Z]|6[AEJKNRSTVWYXZ]|7[ABCEGHJ-NPRTV-Z]|8[ABCEGHLMNPRTVXYZ]|9[ABEHJLNTVXYZ])|K(0[ABCEGHJ-M]|1[ABCEGHJ-NPRSTV-Z]|2[ABCEGHJ-MPRSTVW]|4[ABCKMPR]|6[AHJKTV]|7[ACGHK-NPRSV]|8[ABHNPRV]|9[AHJKLV])|L(0[[ABCEGHJ-NPRS]]|1[ABCEGHJ-NPRSTV-Z]|2[AEGHJMNPRSTVW]|3[BCKMPRSTVXYZ]|4[ABCEGHJ-NPRSTV-Z]|5[ABCEGHJ-NPRSTVW]|6[ABCEGHJ-MPRSTV-Z]|7[ABCEGJ-NPRST]|8[EGHJ-NPRSTVW]|9[ABCGHK-NPRSTVWYZ])|M(1[BCEGHJ-NPRSTVWX]|2[HJ-NPR]|3[ABCHJ-N]|4[ABCEGHJ-NPRSTV-Y]|5[ABCEGHJ-NPRSTVWX]|6[ABCEGHJ-NPRS]|7[AY]|8[V-Z]|9[ABCLMNPRVW])|N(0[ABCEGHJ-NPR]|1[ACEGHKLMPRST]|2[ABCEGHJ-NPRTVZ]|3[ABCEHLPRSTVWY]|4[BGKLNSTVWXZ]|5[ACHLPRV-Z]|6[ABCEGHJ-NP]|7[AGLMSTVWX]|8[AHMNPRSTV-Y]|9[ABCEGHJKVY])|P(0[ABCEGHJ-NPRSTV-Y]|1[ABCHLP]|2[ABN]|3[ABCEGLNPY]|4[NPR]|5[AEN]|6[ABC]|7[ABCEGJKL]|8[NT]|9[AN])|R(0[ABCEGHJ-M]|1[ABN]|2[CEGHJ-NPRV-Y]|3[ABCEGHJ-NPRSTV-Y]|4[AHJKL]|5[AGH]|6[MW]|7[ABCN]|8[AN]|9[A])|S(0[ACEGHJ-NP]|2[V]|3[N]|4[AHLNPRSTV-Z]|6[HJKVWX]|7[HJ-NPRSTVW]|9[AHVX])|T(0[ABCEGHJ-MPV]|1[ABCGHJ-MPRSV-Y]|2[ABCEGHJ-NPRSTV-Z]|3[ABCEGHJ-NPRZ]|4[ABCEGHJLNPRSTVX]|5[ABCEGHJ-NPRSTV-Z]|6[ABCEGHJ-NPRSTVWX]|7[AENPSVXYZ]|8[ABCEGHLNRSVWX]|9[ACEGHJKMNSVWX])|V(0[ABCEGHJ-NPRSTVWX]|1[ABCEGHJ-NPRSTV-Z]|2[ABCEGHJ-NPRSTV-Z]|3[ABCEGHJ-NRSTV-Y]|4[ABCEGK-NPRSTVWXZ]|5[ABCEGHJ-NPRSTV-Z]|6[ABCEGHJ-NPRSTV-Z]|7[ABCEGHJ-NPRSTV-Y]|8[ABCGJ-NPRSTV-Z]|9[ABCEGHJ-NPRSTV-Z])|X(0[ABCGX]|1[A])|Y(0[AB]|1[A]))[ ]?[0-9]{1}[ABCEGHJ-NPRSTV-Z]{1}[0-9]{1}$
         
     | 
| 
      
 106 
     | 
    
         
            +
                  ca_postcode_regex = /^[A-Z]{1}[\d]{1}[A-Z]{1}[ ]?[\d]{1}[A-Z]{1}[\d]{1}$/
         
     | 
| 
      
 107 
     | 
    
         
            +
                  return !(query =~ ca_postcode_regex).nil?
         
     | 
| 
      
 108 
     | 
    
         
            +
                end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                def self.is_coordinates?(query)
         
     | 
| 
      
 111 
     | 
    
         
            +
                  coordinates_regex = /^[-]?[0-9\.]+[,]{1}[-]?[0-9\.]+$/
         
     | 
| 
      
 112 
     | 
    
         
            +
                  return !(query =~ coordinates_regex).nil?
         
     | 
| 
      
 113 
     | 
    
         
            +
                end
         
     | 
| 
      
 114 
     | 
    
         
            +
                
         
     | 
| 
      
 115 
     | 
    
         
            +
                #
         
     | 
| 
      
 116 
     | 
    
         
            +
                # CONVERTERS
         
     | 
| 
      
 117 
     | 
    
         
            +
                #
         
     | 
| 
      
 118 
     | 
    
         
            +
                
         
     | 
| 
      
 119 
     | 
    
         
            +
                # this will take all query formats and convert them to coordinates
         
     | 
| 
      
 120 
     | 
    
         
            +
                # accepts- :zipcode, :postalcode, :geocode
         
     | 
| 
      
 121 
     | 
    
         
            +
                # returns- :coordinates
         
     | 
| 
      
 122 
     | 
    
         
            +
                # if the conversion fails, return nil
         
     | 
| 
      
 123 
     | 
    
         
            +
                def self.to_coordinates(query, format)
         
     | 
| 
      
 124 
     | 
    
         
            +
                  country_code = self.format_to_country_code(format)
         
     | 
| 
      
 125 
     | 
    
         
            +
                  geo = self.geocode(query, country_code)
         
     | 
| 
      
 126 
     | 
    
         
            +
                  country_code ||= geo.country_code if geo
         
     | 
| 
      
 127 
     | 
    
         
            +
                  return nil unless geo && geo.longitude && geo.latitude
         
     | 
| 
      
 128 
     | 
    
         
            +
                  ["#{geo.latitude},#{geo.longitude}", country_code, geo]
         
     | 
| 
      
 129 
     | 
    
         
            +
                end
         
     | 
| 
      
 130 
     | 
    
         
            +
                
         
     | 
| 
      
 131 
     | 
    
         
            +
                # this will take all query formats and convert them to coorinates
         
     | 
| 
      
 132 
     | 
    
         
            +
                # accepts- :zipcode, :postalcode, :coordinates
         
     | 
| 
      
 133 
     | 
    
         
            +
                # returns- :geocode
         
     | 
| 
      
 134 
     | 
    
         
            +
                def self.to_geocode(query, format)
         
     | 
| 
      
 135 
     | 
    
         
            +
                  perform_geocode = false
         
     | 
| 
      
 136 
     | 
    
         
            +
                  perform_geocode = true if self.has_geocode_key?
         
     | 
| 
      
 137 
     | 
    
         
            +
                  
         
     | 
| 
      
 138 
     | 
    
         
            +
                  # some formats can't convert, no need to geocode then
         
     | 
| 
      
 139 
     | 
    
         
            +
                  skip_formats = [:postalcode]
         
     | 
| 
      
 140 
     | 
    
         
            +
                  perform_geocode = false if skip_formats.include?(format)
         
     | 
| 
      
 141 
     | 
    
         
            +
                  
         
     | 
| 
      
 142 
     | 
    
         
            +
                  country_code = self.format_to_country_code(format)
         
     | 
| 
      
 143 
     | 
    
         
            +
                  if perform_geocode
         
     | 
| 
      
 144 
     | 
    
         
            +
                    geo = self.geocode(query, country_code)
         
     | 
| 
      
 145 
     | 
    
         
            +
                    country_code ||= geo.country_code if geo
         
     | 
| 
      
 146 
     | 
    
         
            +
                    return nil unless geo && geo.locality && geo.region && geo.country
         
     | 
| 
      
 147 
     | 
    
         
            +
                    return ["#{geo.locality}, #{geo.region}, #{geo.country}", country_code, geo]
         
     | 
| 
      
 148 
     | 
    
         
            +
                  else
         
     | 
| 
      
 149 
     | 
    
         
            +
                    # without geocoding, the best we can do is just make use the given query as
         
     | 
| 
      
 150 
     | 
    
         
            +
                    # the query for the "geocode" format
         
     | 
| 
      
 151 
     | 
    
         
            +
                    return [query, country_code, nil]
         
     | 
| 
      
 152 
     | 
    
         
            +
                  end
         
     | 
| 
      
 153 
     | 
    
         
            +
                  return nil
         
     | 
| 
      
 154 
     | 
    
         
            +
                end
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
                #
         
     | 
| 
      
 157 
     | 
    
         
            +
                # --- TODO ---
         
     | 
| 
      
 158 
     | 
    
         
            +
                # The following methods need more coverage tests
         
     | 
| 
      
 159 
     | 
    
         
            +
                #
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                def self.has_geocode_key?
         
     | 
| 
      
 162 
     | 
    
         
            +
                  # quick check to see that the Google API key exists for geocoding
         
     | 
| 
      
 163 
     | 
    
         
            +
                  self.google_geocode_key && !self.google_geocode_key.nil?
         
     | 
| 
      
 164 
     | 
    
         
            +
                end
         
     | 
| 
      
 165 
     | 
    
         
            +
                
         
     | 
| 
      
 166 
     | 
    
         
            +
                # if Graticule exists, use it, otherwise use HTTParty
         
     | 
| 
      
 167 
     | 
    
         
            +
                def self.geocode(query, country_code=nil)   
         
     | 
| 
      
 168 
     | 
    
         
            +
                  use_graticule = false
         
     | 
| 
      
 169 
     | 
    
         
            +
                  unless Barometer::skip_graticule
         
     | 
| 
      
 170 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 171 
     | 
    
         
            +
                      require 'rubygems'
         
     | 
| 
      
 172 
     | 
    
         
            +
                      require 'graticule'
         
     | 
| 
      
 173 
     | 
    
         
            +
                      $:.unshift(File.dirname(__FILE__))
         
     | 
| 
      
 174 
     | 
    
         
            +
                      # load some changes to Graticule
         
     | 
| 
      
 175 
     | 
    
         
            +
                      # TODO: attempt to get changes into Graticule gem
         
     | 
| 
      
 176 
     | 
    
         
            +
                      require 'extensions/graticule'
         
     | 
| 
      
 177 
     | 
    
         
            +
                      use_graticule = true
         
     | 
| 
      
 178 
     | 
    
         
            +
                    rescue LoadError
         
     | 
| 
      
 179 
     | 
    
         
            +
                      # do nothing, we will use HTTParty
         
     | 
| 
      
 180 
     | 
    
         
            +
                    end
         
     | 
| 
      
 181 
     | 
    
         
            +
                  end
         
     | 
| 
      
 182 
     | 
    
         
            +
              
         
     | 
| 
      
 183 
     | 
    
         
            +
                  if use_graticule
         
     | 
| 
      
 184 
     | 
    
         
            +
                    geo = self.geocode_graticule(query, country_code)
         
     | 
| 
      
 185 
     | 
    
         
            +
                  else
         
     | 
| 
      
 186 
     | 
    
         
            +
                    geo = self.geocode_httparty(query, country_code)
         
     | 
| 
      
 187 
     | 
    
         
            +
                  end
         
     | 
| 
      
 188 
     | 
    
         
            +
                  geo
         
     | 
| 
      
 189 
     | 
    
         
            +
                end
         
     | 
| 
      
 190 
     | 
    
         
            +
                
         
     | 
| 
      
 191 
     | 
    
         
            +
                def self.geocode_graticule(query, country_code=nil)
         
     | 
| 
      
 192 
     | 
    
         
            +
                  return nil unless self.has_geocode_key?
         
     | 
| 
      
 193 
     | 
    
         
            +
                  geocoder = Graticule.service(:google).new(self.google_geocode_key)
         
     | 
| 
      
 194 
     | 
    
         
            +
                  location = geocoder.locate(query, country_code)
         
     | 
| 
      
 195 
     | 
    
         
            +
                  geo = Barometer::Geo.new(location)
         
     | 
| 
      
 196 
     | 
    
         
            +
                end
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
                def self.geocode_httparty(query, country_code=nil)
         
     | 
| 
      
 199 
     | 
    
         
            +
                  return nil unless self.has_geocode_key?
         
     | 
| 
      
 200 
     | 
    
         
            +
                  location = Barometer::Service.get(
         
     | 
| 
      
 201 
     | 
    
         
            +
                    "http://maps.google.com/maps/geo",
         
     | 
| 
      
 202 
     | 
    
         
            +
                    :query => {
         
     | 
| 
      
 203 
     | 
    
         
            +
                      :gl => country_code,
         
     | 
| 
      
 204 
     | 
    
         
            +
                      :key => self.google_geocode_key,
         
     | 
| 
      
 205 
     | 
    
         
            +
                      :output => "xml",
         
     | 
| 
      
 206 
     | 
    
         
            +
                      :q => query
         
     | 
| 
      
 207 
     | 
    
         
            +
                    },
         
     | 
| 
      
 208 
     | 
    
         
            +
                    :format => :xml
         
     | 
| 
      
 209 
     | 
    
         
            +
                  )['kml']['Response']
         
     | 
| 
      
 210 
     | 
    
         
            +
                  #puts location.inspect
         
     | 
| 
      
 211 
     | 
    
         
            +
                  geo = Barometer::Geo.new(location)
         
     | 
| 
      
 212 
     | 
    
         
            +
                end
         
     | 
| 
      
 213 
     | 
    
         
            +
                
         
     | 
| 
      
 214 
     | 
    
         
            +
                def self.format_to_country_code(format)
         
     | 
| 
      
 215 
     | 
    
         
            +
                  return nil unless format
         
     | 
| 
      
 216 
     | 
    
         
            +
                  case format
         
     | 
| 
      
 217 
     | 
    
         
            +
                  when :zipcode
         
     | 
| 
      
 218 
     | 
    
         
            +
                    country_code = "US"
         
     | 
| 
      
 219 
     | 
    
         
            +
                  when :postalcode
         
     | 
| 
      
 220 
     | 
    
         
            +
                    country_code = "CA"
         
     | 
| 
      
 221 
     | 
    
         
            +
                  else
         
     | 
| 
      
 222 
     | 
    
         
            +
                    country_code = nil
         
     | 
| 
      
 223 
     | 
    
         
            +
                  end
         
     | 
| 
      
 224 
     | 
    
         
            +
                  country_code
         
     | 
| 
      
 225 
     | 
    
         
            +
                end
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
              end
         
     | 
| 
      
 228 
     | 
    
         
            +
            end  
         
     |