├── pkg └── coerce-0.0.6.gem ├── README ├── coerce.gemspec ├── lib └── coerce.rb └── Rakefile /pkg/coerce-0.0.6.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sferik/coerce/master/pkg/coerce-0.0.6.gem -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | NAME 2 | coerce 3 | 4 | DESCRIPTION 5 | a ruby library full of common cast/coercion operations 6 | 7 | INSTALL 8 | gem install coerce 9 | 10 | USAGE 11 | 12 | :~/git/coerce $ irb -r lib/coerce.rb 13 | 14 | ruby-1.8.7-p330 :001 > Coerce.integer('42') 15 | => 42 16 | 17 | ruby-1.8.7-p330 :002 > Coerce.number('42.0') 18 | => 42.0 19 | 20 | ruby-1.8.7-p330 :003 > Coerce.time('yesterday') 21 | => Wed Aug 24 12:00:00 -0600 2011 22 | 23 | ruby-1.8.7-p330 :004 > Coerce.list_of_times('yesterday, today') 24 | => [Wed Aug 24 12:00:00 -0600 2011, Thu Aug 25 19:30:00 -0600 2011] 25 | 26 | ruby-1.8.7-p330 :005 > Coerce.list_of_floats("42, 4.2\n\n 420") 27 | => [42.0, 4.2, 420.0] 28 | 29 | -------------------------------------------------------------------------------- /coerce.gemspec: -------------------------------------------------------------------------------- 1 | ## coerce.gemspec 2 | # 3 | 4 | Gem::Specification::new do |spec| 5 | spec.name = "coerce" 6 | spec.version = "0.0.6" 7 | spec.platform = Gem::Platform::RUBY 8 | spec.summary = "coerce" 9 | spec.description = "description: coerce kicks the ass" 10 | spec.license = "same as ruby's" 11 | 12 | spec.files = 13 | ["README", "Rakefile", "coerce.gemspec", "lib", "lib/coerce.rb"] 14 | 15 | spec.executables = [] 16 | 17 | spec.require_path = "lib" 18 | 19 | spec.test_files = nil 20 | 21 | 22 | spec.add_dependency(*["chronic", ">= 0.6.2"]) 23 | 24 | 25 | spec.extensions.push(*[]) 26 | 27 | spec.rubyforge_project = "codeforpeople" 28 | spec.author = "Ara T. Howard" 29 | spec.email = "ara.t.howard@gmail.com" 30 | spec.homepage = "https://github.com/ahoward/coerce" 31 | end 32 | -------------------------------------------------------------------------------- /lib/coerce.rb: -------------------------------------------------------------------------------- 1 | module Coerce 2 | ## built-in 3 | # 4 | require 'uri' 5 | require 'time' 6 | require 'date' 7 | require 'pathname' 8 | require 'chronic' 9 | 10 | ## version 11 | # 12 | Coerce::Version = '0.0.6' 13 | 14 | def self.version 15 | Coerce::Version 16 | end 17 | 18 | ## dependencies 19 | # 20 | def self.dependencies 21 | { 22 | 'chronic' => [ 'chronic' , '>= 0.6.2' ] 23 | } 24 | end 25 | 26 | begin 27 | require 'rubygems' 28 | rescue LoadError 29 | nil 30 | end 31 | 32 | if defined?(gem) 33 | self.dependencies.each do |lib, dependency| 34 | gem(*dependency) 35 | require(lib) 36 | end 37 | end 38 | 39 | ## 40 | # 41 | def self.export m 42 | module_function m 43 | public m 44 | end 45 | 46 | List = [] 47 | 48 | def self.coerce m, &b 49 | define_method m, &b 50 | export m 51 | List << m.to_s 52 | end 53 | 54 | coerce :boolean do |obj| 55 | case obj.to_s 56 | when %r/^(true|t|1|yes|y)$/i 57 | true 58 | when %r/^(false|f|0|no|n)$/i 59 | false 60 | else 61 | !!obj 62 | end 63 | end 64 | 65 | coerce :integer do |obj| 66 | Float(obj).to_i 67 | end 68 | 69 | coerce :float do |obj| 70 | Float obj 71 | end 72 | 73 | coerce :number do |obj| 74 | Float obj rescue Integer obj 75 | end 76 | 77 | coerce :string do |obj| 78 | String obj 79 | end 80 | 81 | coerce :symbol do |obj| 82 | String(obj).to_sym 83 | end 84 | 85 | coerce :uri do |obj| 86 | ::URI.parse obj.to_s 87 | end 88 | 89 | coerce :url do |obj| 90 | ::URI.parse(obj.to_s).to_s 91 | end 92 | 93 | coerce :time do |obj| 94 | ::Chronic.parse(obj.to_s) 95 | end 96 | 97 | coerce :date do |obj| 98 | begin 99 | ::Date.parse(::Chronic.parse(obj.to_s).to_s) 100 | rescue 101 | ::Date.parse(obj.to_s) 102 | end 103 | end 104 | 105 | coerce :pathname do |obj| 106 | Pathname.new(obj.to_s) 107 | end 108 | 109 | coerce :path do |obj| 110 | File.expand_path(obj.to_s) 111 | end 112 | 113 | coerce :input do |obj| 114 | case obj.to_s 115 | when '-' 116 | io = STDIN.dup 117 | io.fattr(:path){ '/dev/stdin' } 118 | io 119 | else 120 | io = open(obj.to_s, 'r+') 121 | at_exit{ io.close } 122 | io 123 | end 124 | end 125 | 126 | coerce :output do |obj| 127 | case obj.to_s 128 | when '-' 129 | io = STDOUT.dup 130 | io.fattr(:path){ '/dev/stdout' } 131 | io 132 | else 133 | io = open(obj.to_s, 'w+') 134 | at_exit{ io.close } 135 | io 136 | end 137 | end 138 | 139 | coerce :slug do |obj| 140 | string = [obj].flatten.compact.join('-') 141 | words = string.to_s.scan(%r/\w+/) 142 | words.map!{|word| word.gsub %r/[^0-9a-zA-Z_-]/, ''} 143 | words.delete_if{|word| word.nil? or word.strip.empty?} 144 | String(words.join('-').downcase) 145 | end 146 | 147 | coerce :list do |*objs| 148 | [*objs].flatten.join(',').split(/[\n,]/).map{|item| item.strip}.delete_if{|item| item.strip.empty?} 149 | end 150 | 151 | coerce :array do |*objs| 152 | [*objs].flatten.join(',').split(/[\n,]/).map{|item| item.strip}.delete_if{|item| item.strip.empty?} 153 | end 154 | 155 | coerce :hash do |*objs| 156 | list = Coerce.list(*objs) 157 | hash = Hash.new 158 | list.each do |pair| 159 | k, v = pair.split(/[=:]+/, 2) 160 | key = k.to_s.strip 161 | val = v.to_s.strip 162 | hash[key] = val 163 | end 164 | hash 165 | end 166 | 167 | # add list_of_xxx methods 168 | # 169 | List.dup.each do |type| 170 | next if type.to_s =~ %r/list/ 171 | %W" list_of_#{ type } list_of_#{ type }s ".each do |m| 172 | define_method m do |*objs| 173 | list(*objs).map{|obj| send type, obj} 174 | end 175 | export m 176 | List << m 177 | end 178 | end 179 | 180 | # add list_of_xxx_from_file 181 | # 182 | List.dup.each do |type| 183 | next if type.to_s =~ %r/list/ 184 | %W" list_of_#{ type }_from_file list_of_#{ type }s_from_file ".each do |m| 185 | define_method m do |*args| 186 | buf = nil 187 | if args.size == 1 and args.first.respond_to?(:read) 188 | buf = args.first.read 189 | else 190 | open(*args){|io| buf = io.read} 191 | end 192 | send(m.sub(/_from_file/, ''), buf) 193 | end 194 | export m 195 | List << m 196 | end 197 | end 198 | 199 | def self.[] sym 200 | prefix = sym.to_s.downcase.to_sym 201 | candidates = List.select{|m| m =~ %r/^#{ prefix }/i} 202 | m = candidates.shift 203 | raise ArgumentError, "unsupported coercion: #{ sym.inspect } (#{ List.join ',' })" unless 204 | m 205 | raise ArgumentError, "ambiguous coercion: #{ sym.inspect } (#{ List.join ',' })" unless 206 | candidates.empty? or m.to_s == sym.to_s 207 | this = self 208 | lambda{|obj| method(m).call obj} 209 | end 210 | end 211 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | This.rubyforge_project = 'codeforpeople' 2 | This.author = "Ara T. Howard" 3 | This.email = "ara.t.howard@gmail.com" 4 | This.homepage = "https://github.com/ahoward/#{ This.lib }" 5 | 6 | 7 | task :default do 8 | puts((Rake::Task.tasks.map{|task| task.name.gsub(/::/,':')} - ['default']).sort) 9 | end 10 | 11 | task :test do 12 | run_tests! 13 | end 14 | 15 | namespace :test do 16 | task(:unit){ run_tests!(:unit) } 17 | task(:functional){ run_tests!(:functional) } 18 | task(:integration){ run_tests!(:integration) } 19 | end 20 | 21 | def run_tests!(which = nil) 22 | which ||= '**' 23 | test_dir = File.join(This.dir, "test") 24 | test_glob ||= File.join(test_dir, "#{ which }/**_test.rb") 25 | test_rbs = Dir.glob(test_glob).sort 26 | 27 | div = ('=' * 119) 28 | line = ('-' * 119) 29 | 30 | test_rbs.each_with_index do |test_rb, index| 31 | testno = index + 1 32 | command = "#{ This.ruby } -I ./lib -I ./test/lib #{ test_rb }" 33 | 34 | puts 35 | say(div, :color => :cyan, :bold => true) 36 | say("@#{ testno } => ", :bold => true, :method => :print) 37 | say(command, :color => :cyan, :bold => true) 38 | say(line, :color => :cyan, :bold => true) 39 | 40 | system(command) 41 | 42 | say(line, :color => :cyan, :bold => true) 43 | 44 | status = $?.exitstatus 45 | 46 | if status.zero? 47 | say("@#{ testno } <= ", :bold => true, :color => :white, :method => :print) 48 | say("SUCCESS", :color => :green, :bold => true) 49 | else 50 | say("@#{ testno } <= ", :bold => true, :color => :white, :method => :print) 51 | say("FAILURE", :color => :red, :bold => true) 52 | end 53 | say(line, :color => :cyan, :bold => true) 54 | 55 | exit(status) unless status.zero? 56 | end 57 | end 58 | 59 | 60 | task :gemspec do 61 | ignore_extensions = ['git', 'svn', 'tmp', /sw./, 'bak', 'gem'] 62 | ignore_directories = ['pkg'] 63 | ignore_files = ['test/log', 'a.rb'] + Dir['db/*'] + %w'db' 64 | 65 | shiteless = 66 | lambda do |list| 67 | list.delete_if do |entry| 68 | next unless test(?e, entry) 69 | extension = File.basename(entry).split(%r/[.]/).last 70 | ignore_extensions.any?{|ext| ext === extension} 71 | end 72 | list.delete_if do |entry| 73 | next unless test(?d, entry) 74 | dirname = File.expand_path(entry) 75 | ignore_directories.any?{|dir| File.expand_path(dir) == dirname} 76 | end 77 | list.delete_if do |entry| 78 | next unless test(?f, entry) 79 | filename = File.expand_path(entry) 80 | ignore_files.any?{|file| File.expand_path(file) == filename} 81 | end 82 | end 83 | 84 | lib = This.lib 85 | object = This.object 86 | version = This.version 87 | files = shiteless[Dir::glob("**/**")] 88 | executables = shiteless[Dir::glob("bin/*")].map{|exe| File.basename(exe)} 89 | #has_rdoc = true #File.exist?('doc') 90 | test_files = test(?e, "test/#{ lib }.rb") ? "test/#{ lib }.rb" : nil 91 | summary = object.respond_to?(:summary) ? object.summary : "summary: #{ lib } kicks the ass" 92 | description = object.respond_to?(:description) ? object.description : "description: #{ lib } kicks the ass" 93 | license = object.respond_to?(:license) ? object.license : "same as ruby's" 94 | 95 | if This.extensions.nil? 96 | This.extensions = [] 97 | extensions = This.extensions 98 | %w( Makefile configure extconf.rb ).each do |ext| 99 | extensions << ext if File.exists?(ext) 100 | end 101 | end 102 | extensions = [extensions].flatten.compact 103 | 104 | # TODO 105 | if This.dependencies.nil? 106 | dependencies = [] 107 | else 108 | case This.dependencies 109 | when Hash 110 | dependencies = This.dependencies.values 111 | when Array 112 | dependencies = This.dependencies 113 | end 114 | end 115 | 116 | template = 117 | if test(?e, 'gemspec.erb') 118 | Template{ IO.read('gemspec.erb') } 119 | else 120 | Template { 121 | <<-__ 122 | ## <%= lib %>.gemspec 123 | # 124 | 125 | Gem::Specification::new do |spec| 126 | spec.name = <%= lib.inspect %> 127 | spec.version = <%= version.inspect %> 128 | spec.platform = Gem::Platform::RUBY 129 | spec.summary = <%= lib.inspect %> 130 | spec.description = <%= description.inspect %> 131 | spec.license = <%= license.inspect %> 132 | 133 | spec.files =\n<%= files.sort.pretty_inspect %> 134 | spec.executables = <%= executables.inspect %> 135 | 136 | spec.require_path = "lib" 137 | 138 | spec.test_files = <%= test_files.inspect %> 139 | 140 | <% dependencies.each do |lib_version| %> 141 | spec.add_dependency(*<%= Array(lib_version).flatten.inspect %>) 142 | <% end %> 143 | 144 | spec.extensions.push(*<%= extensions.inspect %>) 145 | 146 | spec.rubyforge_project = <%= This.rubyforge_project.inspect %> 147 | spec.author = <%= This.author.inspect %> 148 | spec.email = <%= This.email.inspect %> 149 | spec.homepage = <%= This.homepage.inspect %> 150 | end 151 | __ 152 | } 153 | end 154 | 155 | Fu.mkdir_p(This.pkgdir) 156 | gemspec = "#{ lib }.gemspec" 157 | open(gemspec, "w"){|fd| fd.puts(template)} 158 | This.gemspec = gemspec 159 | end 160 | 161 | task :gem => [:clean, :gemspec] do 162 | Fu.mkdir_p(This.pkgdir) 163 | before = Dir['*.gem'] 164 | cmd = "gem build #{ This.gemspec }" 165 | `#{ cmd }` 166 | after = Dir['*.gem'] 167 | gem = ((after - before).first || after.first) or abort('no gem!') 168 | Fu.mv(gem, This.pkgdir) 169 | This.gem = File.join(This.pkgdir, File.basename(gem)) 170 | end 171 | 172 | task :readme do 173 | samples = '' 174 | prompt = '~ > ' 175 | lib = This.lib 176 | version = This.version 177 | 178 | Dir['sample*/*'].sort.each do |sample| 179 | samples << "\n" << " <========< #{ sample } >========>" << "\n\n" 180 | 181 | cmd = "cat #{ sample }" 182 | samples << Util.indent(prompt + cmd, 2) << "\n\n" 183 | samples << Util.indent(`#{ cmd }`, 4) << "\n" 184 | 185 | cmd = "ruby #{ sample }" 186 | samples << Util.indent(prompt + cmd, 2) << "\n\n" 187 | 188 | cmd = "ruby -e'STDOUT.sync=true; exec %(ruby -I ./lib #{ sample })'" 189 | samples << Util.indent(`#{ cmd } 2>&1`, 4) << "\n" 190 | end 191 | 192 | template = 193 | if test(?e, 'readme.erb') 194 | Template{ IO.read('readme.erb') } 195 | else 196 | Template { 197 | <<-__ 198 | NAME 199 | #{ lib } 200 | 201 | DESCRIPTION 202 | 203 | INSTALL 204 | gem install #{ lib } 205 | 206 | SAMPLES 207 | #{ samples } 208 | __ 209 | } 210 | end 211 | 212 | open("README", "w"){|fd| fd.puts template} 213 | end 214 | 215 | 216 | task :clean do 217 | Dir[File.join(This.pkgdir, '**/**')].each{|entry| Fu.rm_rf(entry)} 218 | end 219 | 220 | 221 | task :release => [:clean, :gemspec, :gem] do 222 | gems = Dir[File.join(This.pkgdir, '*.gem')].flatten 223 | raise "which one? : #{ gems.inspect }" if gems.size > 1 224 | raise "no gems?" if gems.size < 1 225 | 226 | cmd = "gem push #{ This.gem }" 227 | puts cmd 228 | puts 229 | system(cmd) 230 | abort("cmd(#{ cmd }) failed with (#{ $?.inspect })") unless $?.exitstatus.zero? 231 | 232 | cmd = "rubyforge login && rubyforge add_release #{ This.rubyforge_project } #{ This.lib } #{ This.version } #{ This.gem }" 233 | puts cmd 234 | puts 235 | system(cmd) 236 | abort("cmd(#{ cmd }) failed with (#{ $?.inspect })") unless $?.exitstatus.zero? 237 | end 238 | 239 | 240 | 241 | 242 | 243 | BEGIN { 244 | # support for this rakefile 245 | # 246 | $VERBOSE = nil 247 | 248 | require 'ostruct' 249 | require 'erb' 250 | require 'fileutils' 251 | require 'rbconfig' 252 | require 'pp' 253 | 254 | # fu shortcut 255 | # 256 | Fu = FileUtils 257 | 258 | # cache a bunch of stuff about this rakefile/environment 259 | # 260 | This = OpenStruct.new 261 | 262 | This.file = File.expand_path(__FILE__) 263 | This.dir = File.dirname(This.file) 264 | This.pkgdir = File.join(This.dir, 'pkg') 265 | 266 | # grok lib 267 | # 268 | lib = ENV['LIB'] 269 | unless lib 270 | lib = File.basename(Dir.pwd).sub(/[-].*$/, '') 271 | end 272 | This.lib = lib 273 | 274 | # grok version 275 | # 276 | version = ENV['VERSION'] 277 | unless version 278 | require "./lib/#{ This.lib }" 279 | This.name = lib.capitalize 280 | This.object = eval(This.name) 281 | version = This.object.send(:version) 282 | end 283 | This.version = version 284 | 285 | # see if dependencies are export by the module 286 | # 287 | if This.object.respond_to?(:dependencies) 288 | This.dependencies = This.object.dependencies 289 | end 290 | 291 | # we need to know the name of the lib an it's version 292 | # 293 | abort('no lib') unless This.lib 294 | abort('no version') unless This.version 295 | 296 | # discover full path to this ruby executable 297 | # 298 | c = Config::CONFIG 299 | bindir = c["bindir"] || c['BINDIR'] 300 | ruby_install_name = c['ruby_install_name'] || c['RUBY_INSTALL_NAME'] || 'ruby' 301 | ruby_ext = c['EXEEXT'] || '' 302 | ruby = File.join(bindir, (ruby_install_name + ruby_ext)) 303 | This.ruby = ruby 304 | 305 | # some utils 306 | # 307 | module Util 308 | def indent(s, n = 2) 309 | s = unindent(s) 310 | ws = ' ' * n 311 | s.gsub(%r/^/, ws) 312 | end 313 | 314 | def unindent(s) 315 | indent = nil 316 | s.each_line do |line| 317 | next if line =~ %r/^\s*$/ 318 | indent = line[%r/^\s*/] and break 319 | end 320 | indent ? s.gsub(%r/^#{ indent }/, "") : s 321 | end 322 | extend self 323 | end 324 | 325 | # template support 326 | # 327 | class Template 328 | def initialize(&block) 329 | @block = block 330 | @template = block.call.to_s 331 | end 332 | def expand(b=nil) 333 | ERB.new(Util.unindent(@template)).result((b||@block).binding) 334 | end 335 | alias_method 'to_s', 'expand' 336 | end 337 | def Template(*args, &block) Template.new(*args, &block) end 338 | 339 | # colored console output support 340 | # 341 | This.ansi = { 342 | :clear => "\e[0m", 343 | :reset => "\e[0m", 344 | :erase_line => "\e[K", 345 | :erase_char => "\e[P", 346 | :bold => "\e[1m", 347 | :dark => "\e[2m", 348 | :underline => "\e[4m", 349 | :underscore => "\e[4m", 350 | :blink => "\e[5m", 351 | :reverse => "\e[7m", 352 | :concealed => "\e[8m", 353 | :black => "\e[30m", 354 | :red => "\e[31m", 355 | :green => "\e[32m", 356 | :yellow => "\e[33m", 357 | :blue => "\e[34m", 358 | :magenta => "\e[35m", 359 | :cyan => "\e[36m", 360 | :white => "\e[37m", 361 | :on_black => "\e[40m", 362 | :on_red => "\e[41m", 363 | :on_green => "\e[42m", 364 | :on_yellow => "\e[43m", 365 | :on_blue => "\e[44m", 366 | :on_magenta => "\e[45m", 367 | :on_cyan => "\e[46m", 368 | :on_white => "\e[47m" 369 | } 370 | def say(phrase, *args) 371 | options = args.last.is_a?(Hash) ? args.pop : {} 372 | options[:color] = args.shift.to_s.to_sym unless args.empty? 373 | keys = options.keys 374 | keys.each{|key| options[key.to_s.to_sym] = options.delete(key)} 375 | 376 | color = options[:color] 377 | bold = options.has_key?(:bold) 378 | 379 | parts = [phrase] 380 | parts.unshift(This.ansi[color]) if color 381 | parts.unshift(This.ansi[:bold]) if bold 382 | parts.push(This.ansi[:clear]) if parts.size > 1 383 | 384 | method = options[:method] || :puts 385 | 386 | Kernel.send(method, parts.join) 387 | end 388 | 389 | # always run out of the project dir 390 | # 391 | Dir.chdir(This.dir) 392 | } 393 | --------------------------------------------------------------------------------