Module: H
- Extended by:
- Helpers
- Defined in:
- lib/h/h.rb,
lib/h/units.rb,
lib/h/fields.rb,
lib/h/helpers.rb,
lib/h/active_record_extensions.rb
Overview
Localized formatting for Human-iteraction
Defined Under Namespace
Modules: ActiveRecordExtensions, Fields, Helpers, Units
Class Method Summary collapse
- .date_from(txt, options = {}) ⇒ Object
- .date_to(value, options = {}) ⇒ Object
- .datetime_from(txt, options = {}) ⇒ Object
- .datetime_to(value, options = {}) ⇒ Object
- .dms_from(txt, options = {}) ⇒ Object
- .dms_to(value, options = {}) ⇒ Object
-
.from(txt, options = {}) ⇒ Object
Produce data from human-localized text (from user interface).
- .integer_from(txt, options = {}) ⇒ Object
- .integer_to(value, options = {}) ⇒ Object
- .latitude_to(value, options = {}) ⇒ Object
- .logical_from(txt, options = {}) ⇒ Object
- .logical_to(value, options = {}) ⇒ Object
- .longitude_to(value, options = {}) ⇒ Object
- .magnitude_from(txt, options = {}) ⇒ Object
- .magnitude_to(v, options = {}) ⇒ Object
- .number_from(txt, options = {}) ⇒ Object
- .number_to(value, options = {}) ⇒ Object
- .time_from(txt, options = {}) ⇒ Object
- .time_to(value, options = {}) ⇒ Object
-
.to(value, options = {}) ⇒ Object
Generate human-localized text (for user interface) from data.
Class Method Details
.date_from(txt, options = {}) ⇒ Object
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/h/h.rb', line 121 def date_from(txt, ={}) = ().merge() type = check_type([:type] || Date) return nil if txt.to_s.strip.empty? || txt==[:blank] return txt if txt.respond_to?(:strftime) translate_month_and_day_names! txt, [:locale] input_formats(type).each do |original_format| next unless txt =~ /^#{apply_regex(original_format)}$/ txt = DateTime.strptime(txt, original_format) return Date == type ? txt.to_date : Time.zone.local(txt.year, txt.mon, txt.mday, txt.hour, txt.min, txt.sec) end default_parse(txt, type) end |
.date_to(value, options = {}) ⇒ Object
116 117 118 119 |
# File 'lib/h/h.rb', line 116 def date_to(value, ={}) return [:blank] || '' if value.nil? I18n.l(value, ) end |
.datetime_from(txt, options = {}) ⇒ Object
152 153 154 |
# File 'lib/h/h.rb', line 152 def datetime_from(txt, ={}) date_from value, .reverse_merge(:type=>DateTime) end |
.datetime_to(value, options = {}) ⇒ Object
148 149 150 |
# File 'lib/h/h.rb', line 148 def datetime_to(value, ={}) date_to value, .reverse_merge(:type=>DateTime) end |
.dms_from(txt, options = {}) ⇒ Object
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/h/h.rb', line 171 def dms_from(txt, ={}) original_txt = txt = ().merge() return nil if txt.to_s.strip.empty? || txt==[:blank] neg_signs = [[:south], [:west]] << '-' pos_signs = [[:north], [:east]] << '+' neg_signs, pos_signs = [neg_signs, pos_signs].map {|signs| (signs.map{|s| s.mb_chars.upcase.to_s} + signs.map{|s| s.mb_chars.downcase.to_s}).uniq } signs = neg_signs + pos_signs seps = Array([:deg_seps]) + Array([:min_seps]) + Array([:sec_seps]) neg = false txt = txt.to_s.strip neg_signs.each do |sign| if txt.start_with?(sign) txt = txt[sign.size..-1] neg = true break end if txt.end_with?(sign) txt = txt[0...-sign.size] neg = true break end end unless neg pos_signs.each do |sign| if txt.start_with?(sign) txt = txt[sign.size..-1] break end if txt.end_with?(sign) txt = txt[0...-sign.size] break end end end = ().except(:precision).merge() txt = numbers_to_ruby(txt.strip, ) default_units = 0 v = 0 seps = (seps.map{|s| Regexp.escape(s)}<<"\\s+")*"|" scanned_txt = "" txt.scan(/((\d+(?:\.\d*)?(?:[eE][+-]?\d+)?)(#{seps})?\s*)/) do |match| scanned_txt << match[0] number = match[1] sep = match[2] if Array([:deg_seps]).include?(sep) units = :deg elsif Array([:min_seps]).include?(sep) units = :min elsif Array([:sec_seps]).include?(sep) units = :sec else units = DMS_UNITS[default_units] end raise ArgumentError, "Invalid degrees-minutes-seconds value #{original_txt}" unless units default_units = DMS_UNITS.index(units) + 1 x = number.to_f x *= DMS_FACTORS[units] v += x end raise ArgumentError, "Invalid degrees-minutes-seconds value #{original_txt} [#{txt}] [#{scanned_txt}]" unless txt==scanned_txt v = -v if neg v end |
.dms_to(value, options = {}) ⇒ Object
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
# File 'lib/h/h.rb', line 253 def dms_to(value, ={}) longitude = [:longitude] latitude = [:latitude] latitude = true if longitude==false && !.has_key?(:latitude) longitude = true if latitude==false && !.has_key?(:longitude) = ().except(:precision).merge() precision = [:precision] return [:blank] || '' if value.nil? if value.kind_of?(String) # TODO: recognize nan/infinite values value = value.to_f else if value.respond_to?(:nan?) && value.nan? return [:nan] || "--" elsif value.respond_to?(:infinite?) && value.infinite? inf = [:inf] || '∞' return value<0 ? "-#{inf}" : inf end end if value.to_s.start_with?('-') # value<0 # we use to_s to handle negative zero value = -value neg = true end deg = value.floor value -= deg value *= 60 min = value.floor value -= min value *= 60 sec = value.round(SEC_PRECISION) txt = [] txt << integer_to(deg, .except(:precision)) + Array([:deg_seps]).first if min>0 || sec>0 txt << integer_to(min, .except(:precision)) + Array([:min_seps]).first if sec>0 txt << number_to(sec, ) + Array([:sec_seps]).first end end txt = txt*" " if longitude || latitude if longitude letter = neg ? [:west] : [:east] else letter = neg ? [:south] : [:north] end txt = [:prefix] ? "#{letter} #{txt}" : "#{txt} #{letter}" else txt = "-#{txt}" if neg end txt end |
.from(txt, options = {}) ⇒ Object
Produce data from human-localized text (from user interface)
10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/h/h.rb', line 10 def from(txt, ={}) type = [:type] || Float type = Float if type==:number type = check_type(type) if type.ancestors.include?(Numeric) number_from(txt, ) elsif !(type.instance_methods & [:strftime, 'strftime']).empty? date_from(txt, ) elsif type==:logical || type==:boolean logical_from(txt, ) else nil end end |
.integer_from(txt, options = {}) ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/h/h.rb', line 103 def integer_from(txt, ={}) = ().merge() if txt.to_s.strip.empty? || txt==[:blank] nil else txt = txt.tr(' ','') txt = txt.tr([:delimiter],'') if [:delimiter] txt = txt.tr([:separator],'.') end raise ArgumentError, "Invalid integer #{txt}" unless /\A[+-]?\d+(?:\.0*)?\Z/.match(txt) txt.to_i end |
.integer_to(value, options = {}) ⇒ Object
93 94 95 96 97 98 99 100 101 |
# File 'lib/h/h.rb', line 93 def integer_to(value, ={}) = ().merge() if value.nil? [:blank] || '' else value = value.to_s digit_grouping value, 3, [:delimiter], value.index(/\d/), value.size end end |
.latitude_to(value, options = {}) ⇒ Object
249 250 251 |
# File 'lib/h/h.rb', line 249 def latitude_to(value, ={}) dms_to value, .merge(:latitude=>true) end |
.logical_from(txt, options = {}) ⇒ Object
161 162 163 164 165 166 167 168 169 |
# File 'lib/h/h.rb', line 161 def logical_from(txt, ={}) = ().merge() txt = normalize_txt(txt) trues = [:trues] trues ||= [normalize_txt([:true])] falses = [:falses] falses ||= [normalize_txt([:falses])] trues.include?(txt) ? true : falses.include?(txt) ? false : nil end |
.logical_to(value, options = {}) ⇒ Object
156 157 158 159 |
# File 'lib/h/h.rb', line 156 def logical_to(value, ={}) = ().merge() value.nil? ? [:blank] : (value ? [:true] : [:false]) end |
.longitude_to(value, options = {}) ⇒ Object
245 246 247 |
# File 'lib/h/h.rb', line 245 def longitude_to(value, ={}) dms_to value, .merge(:longitude=>true) end |
.magnitude_from(txt, options = {}) ⇒ Object
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/h/units.rb', line 64 def magnitude_from(txt, ={}) return nil if txt.to_s.strip.empty? || txt==[:blank] norm_units = [:units] if txt.match(/^\s*([0-9\.,+-]+)\s*([a-zA-Z\"\'][a-zA-Z1-3\_\/\*\^\"\']*)\s*$/) txt = $1 from_units = $2 || norm_units else from_units = norm_units end from_units = H::Units.normalize_units(from_units) raise ArgumentError, "Invalid units for #{norm_units}: #{from_units}}" unless from_units v = number_from(txt, ) v *= ::Units.u(from_units) v.in(norm_units) end |
.magnitude_to(v, options = {}) ⇒ Object
56 57 58 59 60 61 62 |
# File 'lib/h/units.rb', line 56 def magnitude_to(v, ={}) return [:blank] || '' if v.nil? norm_units = [:units] txt = number_to(v, ) txt << " #{H::Units.denormalize_units(norm_units)}" txt end |
.number_from(txt, options = {}) ⇒ Object
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/h/h.rb', line 76 def number_from(txt, ={}) = ().except(:precision).merge() type = check_type([:type] || ([:precision]==0 ? Integer : Float)) return nil if txt.to_s.strip.empty? || txt==[:blank] txt = numbers_to_ruby(txt.tr(' ',''), ) raise ArgumentError, "Invalid number #{txt}" unless /\A[+-]?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?\Z/.match(txt) if type==Float txt.to_f elsif type==Integer txt.to_i else type.new txt end end |
.number_to(value, options = {}) ⇒ Object
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/h/h.rb', line 39 def number_to(value, ={}) = ().except(:precision).merge() precision = [:precision] return [:blank] || '' if value.nil? unless value.kind_of?(String) value = round(value,precision) if value.respond_to?(:nan?) && value.nan? return [:nan] || "--" elsif value.respond_to?(:infinite?) && value.infinite? inf = [:inf] || '∞' return value<0 ? "-#{inf}" : inf else value = value.to_i if precision==0 value = value.to_s value = value[0...-2] if value.end_with?('.0') end # else: TODO recognize nan/infinite values end if [:delimiter] txt = value.to_s.tr(' ','').tr('.,',[:separator]+[:delimiter]).tr([:delimiter],'') else txt = value.to_s.tr(' ,','').tr('.',[:separator]) end raise ArgumentError, "Invalid number #{txt}" unless /\A[+-]?\d+(?:#{Regexp.escape([:separator])}\d*)?(?:[eE][+-]?\d+)?\Z/.match(txt) if precision && precision>0 p = txt.index([:separator]) if p.nil? txt << [:separator] p = txt.size - 1 end p += 1 txt << "0"*(precision-txt.size+p) if txt.size-p < precision end digit_grouping txt, 3, [:delimiter], txt.index(/\d/), txt.index([:separator]) || txt.size end |
.time_from(txt, options = {}) ⇒ Object
144 145 146 |
# File 'lib/h/h.rb', line 144 def time_from(txt, ={}) date_from value, .reverse_merge(:type=>Time) end |
.time_to(value, options = {}) ⇒ Object
140 141 142 |
# File 'lib/h/h.rb', line 140 def time_to(value, ={}) date_to value, .reverse_merge(:type=>Time) end |
.to(value, options = {}) ⇒ Object
Generate human-localized text (for user interface) from data
26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/h/h.rb', line 26 def to(value, ={}) case value when Numeric number_to(value, ) when Time, Date, DateTime date_to(value, ) when TrueClass, FalseClass logical_to(value, ) else [:blank] || '' end end |