AvantiConveniences 1.0.6

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.
@@ -0,0 +1,42 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{AvantiConveniences}
5
+ s.version = "1.0.4"
6
+
7
+
8
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9
+ s.authors = ["Jamie Flournoy"]
10
+ s.date = %q{2009-03-20}
11
+ s.description = %q{AvantiConveniences is a set of convenience code for Ruby on Rails applications.}
12
+ s.email = ["jamie@pervasivecode.com"]
13
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
14
+ s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "lib/arg_checks.rb", "lib/avanti_conveniences.rb", "lib/hash_extensions.rb", "lib/string_extensions.rb", "lib/text_formatter.rb", "lib/uri_extensions.rb", "test/test_arg_checks.rb", "test/test_avanti_conveniences.rb", "test/test_hash_extensions.rb", "test/test_string_extensions.rb", "test/test_text_formatter.rb", "test/test_uri_extensions.rb"]
15
+ s.has_rdoc = true
16
+ s.homepage = %q{http://www.pervasivecode.com/blog/avanticonveniences/}
17
+ s.rdoc_options = ["--main", "README.txt"]
18
+ s.require_paths = ["lib"]
19
+ s.rubyforge_project = %q{avanticonveniences}
20
+ s.rubygems_version = %q{1.3.1}
21
+ s.summary = %q{AvantiConveniences is a set of convenience code for Ruby on Rails applications.}
22
+ s.test_files = ["test/test_arg_checks.rb", "test/test_avanti_conveniences.rb", "test/test_hash_extensions.rb", "test/test_string_extensions.rb", "test/test_text_formatter.rb", "test/test_uri_extensions.rb"]
23
+
24
+ if s.respond_to? :specification_version then
25
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
26
+ s.specification_version = 2
27
+
28
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
29
+ s.add_runtime_dependency(%q<activesupport>, [">= 1.2.6"])
30
+ s.add_runtime_dependency(%q<text-hyphen>, [">= 1.0.0"])
31
+ s.add_development_dependency(%q<hoe>, [">= 1.11.0"])
32
+ else
33
+ s.add_dependency(%q<activesupport>, [">= 1.2.6"])
34
+ s.add_dependency(%q<text-hyphen>, [">= 1.0.0"])
35
+ s.add_dependency(%q<hoe>, [">= 1.11.0"])
36
+ end
37
+ else
38
+ s.add_dependency(%q<activesupport>, [">= 1.2.6"])
39
+ s.add_dependency(%q<text-hyphen>, [">= 1.0.0"])
40
+ s.add_dependency(%q<hoe>, [">= 1.11.0"])
41
+ end
42
+ end
@@ -0,0 +1,32 @@
1
+ === 1.0.6 / 2009-12-22
2
+
3
+ * Added Array#average, Die.roll, Float::INFINITY, Time#at_midnight
4
+ * Updated to use Hoe 2.0.
5
+
6
+ === 1.0.5 / 2009-07-27
7
+
8
+ * Added Hash#map_keys, Hash#map_values, and Hash#map_pairs.
9
+
10
+ === 1.0.4 / 2009-03-20
11
+
12
+ * Fixed the 'rcov' Rake task so it worked with the latest Hoe.
13
+ * Added a bit of documentation that was missing.
14
+
15
+ === 1.0.3 / 2008-11-11
16
+
17
+ * Bug fix for a hang in TextFormatter.hyphenate_word, when given a wprd that contains multiple hyphens.
18
+
19
+ === 1.0.2 / 2008-09-29
20
+
21
+ * Made TextFormatter.split_long_words take a fourth optional argument to control insertion of newlines in split text.
22
+
23
+ === 1.0.1 / 2008-08-30
24
+
25
+ * Changed the output of ArgChecks.arg_type so that it includes the to_s representation of the object itself in the exception message.
26
+ * Added ArgChecks.arg_responds_to.
27
+
28
+ === 1.0.0 / 2008-08-04
29
+
30
+ * First release.
31
+
32
+ * Birthday!
@@ -0,0 +1,26 @@
1
+ AvantiConveniences.gemspec
2
+ History.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ lib/arg_checks.rb
7
+ lib/array_extensions.rb
8
+ lib/avanti_conveniences.rb
9
+ lib/die.rb
10
+ lib/float_extensions.rb
11
+ lib/hash_extensions.rb
12
+ lib/string_extensions.rb
13
+ lib/text_formatter.rb
14
+ lib/time_extensions.rb
15
+ lib/uri_extensions.rb
16
+ test/test_arg_checks.rb
17
+ test/test_array_extensions.rb
18
+ test/test_avanti_conveniences.rb
19
+ test/test_die.rb
20
+ test/test_float_extensions.rb
21
+ test/test_hash_extensions.rb
22
+ test/test_string_extensions.rb
23
+ test/test_text_formatter.rb
24
+ test/test_time_extensions.rb
25
+ test/test_uri_extensions.rb
26
+
@@ -0,0 +1,80 @@
1
+ = AvantiConveniences
2
+
3
+ http://www.pervasivecode.com/blog/avanticonveniences/
4
+ Author: Jamie Flournoy (jamie@pervasivecode.com)
5
+
6
+ == DESCRIPTION:
7
+
8
+ AvantiConveniences is a set of convenience code for Ruby on Rails applications.
9
+
10
+ == FEATURES/PROBLEMS:
11
+
12
+ ArgChecks helps you implement simple sanity-checking of arguments, like
13
+ permanent assertions or a poor man's Design by Contract facility, so you can
14
+ write code that will Fail Fast (see http://c2.com/cgi/wiki?FailFast).
15
+
16
+ ArrayExtensions adds Array#average.
17
+
18
+ Die adds Die.roll which is semantic sugar around rand(n).
19
+
20
+ FloatExtensions adds Float::INFINITY, which is semantic sugar for people
21
+ who don't want to see '1.0 / 0' in your code.
22
+
23
+ HashExtensions adds a few methods that help you easily replace all the keys
24
+ and/or values at once.
25
+
26
+ StringExtensions provides String quoting (not escaping) with single quotes,
27
+ double quotes, or a caller-specified quoting character, and a String#dehumanize
28
+ method to do the reverse of the String#humanize method provided by ActiveSupport.
29
+
30
+ TextFormatter provides methods for hyphenating words for word-wrapping.
31
+
32
+ TimeExtensions provides Time#at_midnight, which is useful if you want to work
33
+ with a lot of timestamps that need to be bucketed by day.
34
+
35
+ URIExtensions provides URI::Generic#query_from_hash, which will create a URI
36
+ query string from a Hash.
37
+
38
+ == SYNOPSIS:
39
+
40
+ See individual classes for details. All of them are single method invocations
41
+ that are very simple to use. For additional examples look in the test/
42
+ directory.
43
+
44
+ == REQUIREMENTS:
45
+
46
+ Ruby 1.8.5 (might work with earlier versions), ActiveSupport 1.2.6, Text-Hyphen
47
+ 1.0.0, and Hoe 1.7.0.
48
+
49
+ == INSTALL:
50
+
51
+ If you haven't done this before:
52
+ gem sources -a http://gems.github.com
53
+
54
+ Then:
55
+ sudo gem install JamieFlournoy-AvantiConveniences
56
+
57
+ == LICENSE:
58
+
59
+ (The MIT License)
60
+
61
+ Copyright (c) 2008 Pervasive Code
62
+
63
+ Permission is hereby granted, free of charge, to any person obtaining
64
+ a copy of this software and associated documentation files (the
65
+ 'Software'), to deal in the Software without restriction, including
66
+ without limitation the rights to use, copy, modify, merge, publish,
67
+ distribute, sublicense, and/or sell copies of the Software, and to
68
+ permit persons to whom the Software is furnished to do so, subject to
69
+ the following conditions:
70
+
71
+ The above copyright notice and this permission notice shall be
72
+ included in all copies or substantial portions of the Software.
73
+
74
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
75
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
76
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
77
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
78
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
79
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
80
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,77 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ # Hoe::plugin :gemcutter
7
+
8
+ hoe = Hoe.spec 'AvantiConveniences' do
9
+ developer('Jamie Flournoy', 'jamie@pervasivecode.com')
10
+ extra_deps << ['activesupport', '>= 1.2.6']
11
+ extra_deps << ['text-hyphen', '>= 1.0.0']
12
+ extra_dev_deps << ['thoughtbot-shoulda', '>= 2.10.1']
13
+ end
14
+
15
+ task :gemspec do
16
+ File.open("#{hoe.name}.gemspec", "w") {|f| f << hoe.spec.to_ruby }
17
+ end
18
+ task :package => :gemspec
19
+
20
+
21
+ # The rcov task in this file worked until Hoe was updated; this code keeps the old behavior
22
+ # and disables Hoe's rcov task.
23
+ Rake::TaskManager.class_eval do
24
+ def remove_task(task_name)
25
+ @tasks.delete(task_name.to_s)
26
+ end
27
+ end
28
+ def remove_task(task_name)
29
+ Rake.application.remove_task(task_name)
30
+ end
31
+ remove_task 'rcov'
32
+
33
+
34
+ task :rcov do
35
+ sh 'rcov test/test_*.rb'
36
+ end
37
+ task :clean_rcov do
38
+ sh 'rm -rf ./coverage/*'
39
+ end
40
+ task :clean => [:clean_rcov]
41
+
42
+
43
+ # vim: syntax=Ruby
44
+
45
+
46
+ # stats
47
+ begin
48
+ gem 'rails'
49
+ require 'code_statistics'
50
+ namespace :spec do
51
+ desc "Use Rails's rake:stats task for a gem"
52
+ task :statsetup do
53
+ class CodeStatistics
54
+ def calculate_statistics
55
+ @pairs.inject({}) do |stats, pair|
56
+ if 3 == pair.size
57
+ stats[pair.first] = calculate_directory_statistics(pair[1], pair[2]); stats
58
+ else
59
+ stats[pair.first] = calculate_directory_statistics(pair.last); stats
60
+ end
61
+ end
62
+ end
63
+ end
64
+ ::STATS_DIRECTORIES = [['Libraries', 'lib', /\.(sql|rhtml|erb|rb|yml)$/],
65
+ ['Tests', 'test', /\.(sql|rhtml|erb|rb|yml)$/]]
66
+ ::CodeStatistics::TEST_TYPES << "Tests"
67
+ end
68
+ end
69
+ desc "Report code statistics (KLOCs, etc) from the application"
70
+ task :stats => "spec:statsetup" do
71
+ CodeStatistics.new(*STATS_DIRECTORIES).to_s
72
+ end
73
+ rescue Gem::LoadError => le
74
+ task :stats do
75
+ raise RuntimeError, "'rails' gem not found - you must install it in order to use this task.\n"
76
+ end
77
+ end
@@ -0,0 +1,79 @@
1
+ # Include this module in your classes that need sanity checking of method arguments.
2
+ # If any of these checks is not satisfied, it will raise an ArgumentError with a
3
+ # detailed message about what went wrong.
4
+ module ArgChecks
5
+ # Require that all arguments be of the specified class, or one of its subclasses (uses is_a?).
6
+ # Consider using arg_responds_to? or respond_to? instead, to make your API more flexible.
7
+ def arg_type(klass, *args)
8
+ raise ArgumentError, "First argument must be a class. Got #{klass.to_s}" unless klass.is_a? Class
9
+ args.each{|obj| raise ArgumentError, "Wrong type for argument '#{obj}' (should be #{klass})" unless obj.is_a? klass }
10
+ end
11
+
12
+ # Require that all arguments respond_to? the specified method.
13
+ def arg_responds_to(method, *args)
14
+ arg_type Symbol, method
15
+ args.each{|obj| raise ArgumentError, "Object #{obj} (class #{obj.class}) doesn't respond to #{method}." unless obj.respond_to? method}
16
+ end
17
+
18
+ # Require that all arguments be non-nil.
19
+ def arg_required(*args)
20
+ args.each{|obj| raise ArgumentError, "Argument cannot be nil" if obj.nil? }
21
+ end
22
+
23
+ # Require that the second argument be non-nil. Upon failure the first argument is used in the error message.
24
+ def named_arg_required(name, obj)
25
+ named_arg_required('name', name) unless name.eql?('name')
26
+ raise ArgumentError, "Argument \"#{name}\" cannot be nil" if obj.nil?
27
+ end
28
+
29
+ # Require that all values in the hash be non-nil. Returns the list of all keys whose values were nil.
30
+ def named_args_required(obj_hash)
31
+ keys_with_nil_values = obj_hash.select{|k,v| v.nil?}.collect{|e| e[0]}
32
+ raise ArgumentError, "Nil argument not allowed in #{keys_with_nil_values.join(', ')}" unless keys_with_nil_values.empty?
33
+ end
34
+
35
+ # Require that all arguments be greater than or equal to max.
36
+ def arg_max(max, *args)
37
+ args.each{|obj| raise ArgumentError, "Argument exceeds maxmimum value (max is #{max.to_s})" unless max >= obj}
38
+ end
39
+
40
+ # Require that all arguments be less than or equal to min.
41
+ def arg_min(min, *args)
42
+ args.each{|obj| raise ArgumentError, "Argument is lower than minimum value (min is #{min.to_s})" unless min <= obj }
43
+ end
44
+
45
+ # Require that all arguments return a set of keys containing at least the specified elements.
46
+ def arg_hash_keys_required(required_keys, *args)
47
+ args.each do |h|
48
+ missing = required_keys - h.keys
49
+ raise ArgumentError, "Hash is missing required keys (#{missing.join(', ')}). Got:\n#{h.inspect}" if missing.length > 0
50
+ end
51
+ end
52
+
53
+ # Require that all arguments return a set of keys containing exactly the specified elements.
54
+ def arg_hash_keys_exact(exact_keys, *args)
55
+ args.each do |h|
56
+ missing = exact_keys - h.keys
57
+ extra = h.keys - exact_keys
58
+ raise ArgumentError, "Hash keys don't match required set (#{exact_keys.join(', ')}). " +
59
+ "Missing required keys (#{missing.join(', ')}); extra keys not allowed (#{extra.join(', ')}).\n" +
60
+ "Got:\n#{h.inspect}" if (missing.length > 0) || (extra.length >0)
61
+ end
62
+ end
63
+
64
+ # Require that all arguments return a set of keys containing a set of required keys, zero or more of the optional keys, and nothing else.
65
+ def arg_hash_keys_limit(required_keys, optional_keys, *args)
66
+ args.each do |h|
67
+ missing = required_keys - h.keys
68
+ extra = h.keys - required_keys - optional_keys
69
+
70
+ unless missing.empty? && extra.empty?
71
+ msg = ""
72
+ msg << "Hash is missing required keys (#{missing.join(', ')}).\n" unless missing.empty?
73
+ msg << "Hash has extra keys which aren't allowed (#{extra.join(', ')}).\n" unless extra.empty?
74
+ msg << " Got: #{h.inspect}"
75
+ raise ArgumentError, msg
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,5 @@
1
+ class Array
2
+ def average
3
+ self.sum / self.size
4
+ end
5
+ end
@@ -0,0 +1,13 @@
1
+ require File.dirname(__FILE__) + '/arg_checks'
2
+ require File.dirname(__FILE__) + '/array_extensions'
3
+ require File.dirname(__FILE__) + '/die'
4
+ require File.dirname(__FILE__) + '/float_extensions'
5
+ require File.dirname(__FILE__) + '/hash_extensions'
6
+ require File.dirname(__FILE__) + '/string_extensions'
7
+ require File.dirname(__FILE__) + '/text_formatter'
8
+ require File.dirname(__FILE__) + '/time_extensions'
9
+ require File.dirname(__FILE__) + '/uri_extensions'
10
+
11
+ class AvantiConveniences
12
+ VERSION = '1.0.6'
13
+ end
@@ -0,0 +1,5 @@
1
+ class Die
2
+ def Die.roll(sides)
3
+ (rand * sides).to_i + 1
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ class Float
2
+ INFINITY = 1.0 / 0
3
+ end
@@ -0,0 +1,22 @@
1
+ class Hash
2
+ # Call a block for every key in the Hash, and replace each key with the block's return value.
3
+ # This returns a new Hash, so you do not need to call #rehash on it.
4
+ def map_keys(&block)
5
+ Hash[* self.to_a.map{|k,v| [yield(k), v] }.flatten ]
6
+ end
7
+ alias_method :rekey, :map_keys unless Hash.new.respond_to? :rekey
8
+
9
+ # Call a block for every value in the Hash, and replace each value with the block's return value.
10
+ def map_values(&block)
11
+ Hash[* self.to_a.map{|k,v| [k, yield(v)] }.flatten ]
12
+ end
13
+ alias_method :revalue, :map_keys unless Hash.new.respond_to? :revalue
14
+
15
+ # Call a block for every (key, value) pair in the hash, passing them in as a 2-element Array.
16
+ # The block should return a similar 2-element array of (key, value) which will be used to replace
17
+ # the original key and value.
18
+ def map_pairs(&block)
19
+ Hash[* self.to_a.map{|k,v| yield(k,v) }.flatten ]
20
+ end
21
+
22
+ end
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ gem 'activesupport', '>= 1.2.6'
3
+ require 'active_support' # for titleize
4
+
5
+ class String
6
+ # Convert a string consisting of whitespace separated words into an underscore-delimited
7
+ # set of lowercase words. This is the opposite of what ActiveSupport's String.humanize
8
+ # method docs.
9
+ # Example: 'Foo Bar' -> 'foo_bar'
10
+ def dehumanize
11
+ titleize.gsub(/\W+/,'_').gsub(/\s+/,'_').gsub(/^_+|_+$/,'').underscore
12
+ end
13
+
14
+ # Return this string, surrounded by quote_char on either side.
15
+ def quote(quote_char)
16
+ "#{quote_char}#{self}#{quote_char}"
17
+ end
18
+
19
+ # Return this string, surrounded by a single quote (') on either side.
20
+ def single_quote; self.quote("'"); end
21
+
22
+ # Return this string, surrounded by a double quote (") on either side.
23
+ def double_quote; self.quote('"'); end
24
+
25
+ end
@@ -0,0 +1,83 @@
1
+ require 'rubygems'
2
+
3
+ # Note: Text::Hyphen 1.0.0 uses attr_accessor in a couple of places
4
+ # where it should use attr_reader, because a setter is defined
5
+ # immediately afterward; this causes 'method redefined' warnings
6
+ # when running 'rake test' since the Hoe gem enables the ruby -w flag.
7
+ # To fix this warning, Text::Hyphen must be fixed. so...
8
+ # TODO patch Text::Hyphen to fix the 'method redefined' warnings.
9
+ gem 'text-hyphen', '>= 1.0.0'
10
+ require 'text/hyphen'
11
+
12
+ # Several methods take a language_code argument. This is the language code
13
+ # (like 'en_us') required by Text::Hyphen.
14
+ class TextFormatter
15
+ # Replace all words longer than max_width with the results of split_word.
16
+ # Leading and trailing whitespace is eliminated and inline whitespace runs are replaced with a space.
17
+ def split_long_words(string, max_width, language_code, append_newlines = false)
18
+ words = string.split
19
+ words.collect{|w| w.length > max_width ? split_word(w, max_width, language_code, append_newlines) : w}.flatten.join(' ')
20
+ end
21
+
22
+ # Split one word and return it as an array of substrings no longer than the specified size.
23
+ # Splitting results consist of a hyphenated version (if possible) or by cutting the word into
24
+ # segments no longer than max_width (if hyphenation fails, such as for the word 'xxxxxxxxxxx')
25
+ # Every substring except the last will have a newline appended unless append_newlines is false.
26
+ # The result for a 'word' string containing whitespace is undefined.
27
+ def split_word(word, size, langauge_code, append_newlines = true)
28
+ hyphenated = hyphenate_word(word,size,langauge_code)
29
+ hyphenated_chopped = hyphenated.collect{|w| w.length > size ? chop_word(w, size) : w}.flatten
30
+ if append_newlines
31
+ hyphenated_chopped.collect!{|w| "#{w}\n"}
32
+ hyphenated_chopped.last.chop!
33
+ end
34
+ return hyphenated_chopped
35
+ end
36
+
37
+ # Try to hyphenate a word into chunks just barely short enough to fit within the specified size
38
+ def hyphenate_word(word, size, language_code)
39
+ # handle pre-hyphenated words, which hyphenate_to balks on
40
+ hyphenated = word.split(/-/)
41
+ if hyphenated.size > 1
42
+ hyphenated.collect!{|w| w.length > size ? self.hyphenate_word(w, size, language_code) : w}
43
+ hyphenated.flatten!
44
+ last = hyphenated.pop
45
+ hyphenated.collect!{|w| w[-1,1].eql?('-') ? w : "#{w}-"}
46
+ hyphenated.push(last)
47
+ return hyphenated
48
+ end
49
+
50
+ h = TextFormatter.text_hyphenator(language_code)
51
+ hyphenated = h.hyphenate_to(word, size).compact
52
+
53
+ loop_limit = word.length
54
+ while (hyphenated.size > 1 && hyphenated.last.length > size) do
55
+ last = hyphenated.pop
56
+ part1, part2 = h.hyphenate_to(last, size).compact
57
+ hyphenated.push(part1)
58
+ hyphenated.push(part2) unless part2.nil?
59
+
60
+ loop_limit -= 1
61
+ break if last.eql?(part1) || (loop_limit < 0)
62
+ end
63
+ return hyphenated
64
+ end
65
+
66
+ # Simply chop overly long "words" into chunks no longer than the specified size
67
+ def chop_word(word, size)
68
+ chopped = []
69
+ while (!word.nil? && word.length > size) do
70
+ chopped.push word[0,size]
71
+ word = word[size..-1]
72
+ end
73
+ chopped.push(word) # get the last chunk
74
+ return chopped
75
+ end
76
+
77
+ # Get a Text::Hyphen instance for a specified language
78
+ def TextFormatter.text_hyphenator(language_code)
79
+ @@hyphenators = Hash.new unless defined? @@hyphenators
80
+ @@hyphenators[language_code] ||= Text::Hyphen.new(:language => language_code, :left => 2, :right => 2)
81
+ return @@hyphenators[language_code]
82
+ end
83
+ end
@@ -0,0 +1,6 @@
1
+ class Time
2
+ def at_midnight
3
+ Time.mktime(self.year, self.month, self.day, 0, 0, 0)
4
+ end
5
+ end
6
+
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ gem 'activesupport'
3
+ require 'active_support/core_ext/hash/indifferent_access'
4
+
5
+ require 'uri'
6
+ require File.dirname(__FILE__) + '/hash_extensions'
7
+
8
+ class URI::Generic
9
+ # Create a URI query string from a Hash. The keys in the returned query string
10
+ # are sorted for easier testing.
11
+ # Example: {:foo => 'bar', :biz => 'b a z'} -> '?biz=b%20a%20z&foo=bar'
12
+ def self.query_from_hash(q_hash)
13
+ return '' if q_hash.nil?
14
+
15
+ # Reveal cases where the caller thought they were using HashWithIndifferentAccess
16
+ # but actually they used Hash and probably got duplicate keys (:foo vs 'foo')
17
+ q_hash = q_hash.rekey{|k| k.is_a?(Symbol) ? ":#{k}" : k} unless q_hash.is_a?(HashWithIndifferentAccess)
18
+
19
+ pairs = []
20
+ q_hash.each_pair{|k,v| pairs << "#{k}=#{URI.escape(v.to_s)}" unless v.nil?}
21
+ '?' + pairs.sort.join('&')
22
+ end
23
+ end
@@ -0,0 +1,113 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/arg_checks'
3
+
4
+ class TestArgChecks < Test::Unit::TestCase
5
+ include ArgChecks
6
+
7
+ def test_arg_type
8
+ assert_nothing_raised(ArgumentError) { arg_dummy('s', 3.0) }
9
+ assert_raise(ArgumentError) { arg_dummy('s', 3) }
10
+ assert_raise(ArgumentError) { arg_dummy('blah blah', 'x') }
11
+ assert_raise(ArgumentError) { arg_dummy(3.0, 3.0) }
12
+
13
+ assert_nothing_raised(ArgumentError) { arg_dummy2('s', -1.0, 10.0, 'x') }
14
+ assert_raise(ArgumentError) { arg_dummy2(-1.0, 10.0, 'x', 'blah') }
15
+ end
16
+
17
+ def test_arg_required
18
+ assert_raise(ArgumentError) { arg_dummy5(nil, nil) }
19
+ assert_raise(ArgumentError) { arg_dummy5(nil, 3.0) }
20
+ assert_raise(ArgumentError) { arg_dummy5('blah', nil) }
21
+ assert_nothing_raised(ArgumentError) { arg_dummy5('blah', 3.0) }
22
+ end
23
+
24
+ def arg_dummy5(arg1, arg2)
25
+ arg_required arg1, arg2
26
+ end
27
+
28
+ def arg_dummy(arg1_string, arg2_float)
29
+ arg_type String, arg1_string
30
+ arg_type Float, arg2_float
31
+ end
32
+
33
+ def arg_dummy2(arg1_string, arg2_float, arg3_float, arg4_string)
34
+ arg_type String, arg1_string, arg4_string
35
+ arg_type Float, arg2_float, arg3_float
36
+ end
37
+
38
+ def test_arg_min
39
+ assert_raise(ArgumentError) {arg_dummy3(5, 'z')}
40
+ assert_raise(ArgumentError) {arg_dummy3(100, 'x')}
41
+ assert_nothing_raised(ArgumentError) {arg_dummy3(100, 'z')}
42
+ end
43
+
44
+ def arg_dummy3(arg1, arg2)
45
+ arg_min 6, arg1
46
+ arg_min 'y', arg2
47
+ end
48
+
49
+ def test_arg_max
50
+ assert_raise(ArgumentError) {arg_dummy4(100, 'z')}
51
+ assert_raise(ArgumentError) {arg_dummy4(5, 'z')}
52
+ assert_nothing_raised(ArgumentError) {arg_dummy4(5, 'x')}
53
+ end
54
+
55
+ def arg_dummy4(arg1, arg2)
56
+ arg_max 6, arg1
57
+ arg_max 'y', arg2
58
+ end
59
+
60
+ @@hash = {'k1' => 'v1', 'k2'=>'v2'}
61
+
62
+ def test_arg_hash_keys_exact
63
+ assert_nothing_raised(ArgumentError) { arg_hash_keys_exact %w{k1 k2}, @@hash }
64
+
65
+ assert_raise(ArgumentError) { arg_hash_keys_exact %w{k1}, @@hash}
66
+ assert_raise(ArgumentError) { arg_hash_keys_exact %w{k2}, @@hash }
67
+ assert_raise(ArgumentError) { arg_hash_keys_exact %w{k1 k2 k3}, @@hash }
68
+ end
69
+
70
+ def test_arg_hash_keys_required
71
+ assert_nothing_raised(ArgumentError) { arg_hash_keys_required %w{k1 k2}, @@hash }
72
+ assert_nothing_raised(ArgumentError) { arg_hash_keys_required %w{k1}, @@hash }
73
+ assert_nothing_raised(ArgumentError) { arg_hash_keys_required %w{k2}, @@hash }
74
+
75
+ assert_raise(ArgumentError) { arg_hash_keys_required %w{k1 k2 k3}, @@hash }
76
+ end
77
+
78
+ def test_arg_hash_keys_limit
79
+ assert_nothing_raised(ArgumentError) { arg_hash_keys_limit %w{k1 k2}, %w{k3}, @@hash }
80
+ assert_nothing_raised(ArgumentError) { arg_hash_keys_limit %w{k1}, %w{k2 k3}, @@hash }
81
+ assert_nothing_raised(ArgumentError) { arg_hash_keys_limit %w{k2}, %w{k1 k3}, @@hash }
82
+ assert_nothing_raised(ArgumentError) { arg_hash_keys_limit [], %w{k1 k2 k3}, @@hash }
83
+
84
+ assert_raise(ArgumentError) { arg_hash_keys_limit %w{k1 k2 k3}, [], @@hash }
85
+ assert_raise(ArgumentError) { arg_hash_keys_limit %w{k1 k3}, %w{k2}, @@hash }
86
+ assert_raise(ArgumentError) { arg_hash_keys_limit %w{k3}, %w{k1, k2}, @@hash }
87
+ assert_raise(ArgumentError) { arg_hash_keys_limit [], [], @@hash }
88
+ end
89
+
90
+ def test_named_arg_required
91
+ assert_nothing_raised(ArgumentError){ named_arg_required('foo', 1) }
92
+ assert_raise(ArgumentError){ named_arg_required('bar', nil) }
93
+ end
94
+
95
+ def test_named_args_required
96
+ assert_nothing_raised(ArgumentError){ named_args_required({}) }
97
+ assert_nothing_raised(ArgumentError){ named_args_required(:foo => 1) }
98
+ assert_raise(ArgumentError){ named_args_required(:bar => nil) }
99
+ assert_raise(ArgumentError){ named_args_required(:foo => 1, :bar => nil) }
100
+ end
101
+
102
+ def test_arg_respond_to
103
+ assert_nothing_raised(ArgumentError) { arg_dummy6(:round, 1.1) }
104
+ assert_nothing_raised(ArgumentError) { arg_dummy6(:round, Float::EPSILON) }
105
+ assert_raise(ArgumentError) { arg_dummy6(:round, 'a') }
106
+ assert_raise(ArgumentError) { arg_dummy6(:round, [0]) }
107
+ assert_raise(ArgumentError) { arg_dummy6(:round, Object) }
108
+ end
109
+
110
+ def arg_dummy6(arg1, arg2)
111
+ arg_responds_to arg1, arg2
112
+ end
113
+ end
@@ -0,0 +1,36 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/array_extensions'
3
+ require 'shoulda'
4
+
5
+ module StringMath
6
+ def +(other)
7
+ result = super(other)
8
+ result.extend StringMath
9
+ result
10
+ end
11
+
12
+ def /(denominator)
13
+ chunk_size = (self.size.to_f / denominator).to_i
14
+ self[0..(chunk_size - 1)]
15
+ end
16
+ end
17
+
18
+
19
+ class TestArrayExtensions < Test::Unit::TestCase
20
+
21
+ context 'Array.average' do
22
+ should 'return the average of 3 integers' do
23
+ assert_equal 5, [1,3,11].average
24
+ end
25
+
26
+ should 'return the average of 3 floats' do
27
+ assert_equal 5.21, [1.1, 3.3, 11.23].average
28
+ end
29
+
30
+ should 'work on anything that supports + and / operators' do
31
+ test_data = [1,3,11].map{|n| ('x' * n).extend StringMath}
32
+ assert_equal 'xxxxx', test_data.average
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,9 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/avanti_conveniences'
3
+
4
+ class TestAvantiConveniences < Test::Unit::TestCase
5
+ def test_version
6
+ assert defined? AvantiConveniences::VERSION
7
+ assert_not_nil AvantiConveniences::VERSION
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/die'
3
+ require 'shoulda'
4
+
5
+ class TestDie < Test::Unit::TestCase
6
+
7
+ context 'Die.roll' do
8
+ setup do
9
+ seed = 1234567890
10
+ srand(seed)
11
+ @expected_rand = rand
12
+ srand(seed)
13
+ end
14
+
15
+ should 'return a value between 1 and the number of sides' do
16
+ sides = 1000
17
+ expected = (@expected_rand * sides + 1).to_i
18
+ assert_equal expected, Die.roll(sides)
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,17 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/float_extensions'
3
+ require 'shoulda'
4
+
5
+ class TestFloatExtensions < Test::Unit::TestCase
6
+
7
+ context 'Float::INFINITY' do
8
+ should 'be greater than 10**100' do
9
+ assert Float::INFINITY > 10**100
10
+ end
11
+ should 'be equal to the absolute value of negative infinity' do
12
+ negative_infinity = -1.0 / 0
13
+ assert_equal negative_infinity.abs, Float::INFINITY
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,23 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/hash_extensions'
3
+
4
+ class TestHashExtensions < Test::Unit::TestCase
5
+ def test_map_keys
6
+ expected = {10 => :a, 20 => :b}
7
+ actual = {1 => :a, 2 => :b}.map_keys{|k| k * 10}
8
+ assert_equal expected, actual
9
+ end
10
+
11
+ def test_map_values
12
+ expected = {:a => 10, :b => 20}
13
+ actual = {:a => 1, :b => 2}.map_values{|k| k * 10}
14
+ assert_equal expected, actual
15
+ end
16
+
17
+ def test_map_pairs
18
+ expected = {'A' => 10, 'B' => 20}
19
+ actual = {'a' => 1, 'b' => 2}.map_pairs{|k, v| [k.upcase, v * 10] }
20
+ assert_equal expected, actual
21
+ end
22
+
23
+ end
@@ -0,0 +1,57 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/string_extensions'
3
+ require 'shoulda'
4
+
5
+ gem 'activesupport', '>= 1.2.6'
6
+ require 'active_support' # for .humanize
7
+
8
+ class TestStringExtensions < Test::Unit::TestCase
9
+ context 'String#dehumanize' do
10
+ [ ['Hello, World', 'hello_world'],
11
+ ['$$$123#456', '123_456'],
12
+ ['a_b_c_', 'a_b_c'],
13
+ ['/etc/passwd', 'etc_passwd'],
14
+ ['Foo', 'foo'],
15
+ ['Foo Bar', 'foo_bar'],
16
+ ['Foo_Bar', 'foo_bar'],
17
+ ["foo\tbar\nbiz \t\n\t \n\t\n baz_ ", 'foo_bar_biz_baz'],
18
+ ].each do |input, expected|
19
+ should("convert #{input} into #{expected}") do
20
+ assert_equal expected, input.dehumanize
21
+ end
22
+ end
23
+
24
+ %w{1 x hello_world 123_456 foo_bar}.each do |round_trippable|
25
+ should "undo humanize(#{round_trippable})" do
26
+ assert_equal round_trippable, round_trippable.humanize.dehumanize
27
+ end
28
+ end
29
+
30
+ ['1', 'X', 'A herring', 'Hello world'].each do |rehumanizable|
31
+ should "return a value for #{rehumanizable} that humanize can undo correctly" do
32
+ assert_equal rehumanizable, rehumanizable.dehumanize.humanize
33
+ end
34
+
35
+ end
36
+
37
+
38
+ end
39
+
40
+ def test_quote
41
+ d = {'Foo' => '~Foo~',
42
+ 'Foo~' => '~Foo~~',
43
+ 'Foo Bar' => '~Foo Bar~'}
44
+ d.each{|i,o| assert_equal o, i.quote('~')}
45
+ end
46
+
47
+ def test_single_quote
48
+ d = {'Foo Bar' => "'Foo Bar'"}
49
+ d.each{|i,o| assert_equal o, i.single_quote}
50
+ end
51
+
52
+ def test_double_quote
53
+ d = {'Foo Bar' => '"Foo Bar"'}
54
+ d.each{|i,o| assert_equal o, i.double_quote}
55
+ end
56
+
57
+ end
@@ -0,0 +1,81 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/text_formatter'
3
+
4
+ class TestTextFormatter < Test::Unit::TestCase
5
+ def setup
6
+ @tf = TextFormatter.new
7
+ end
8
+
9
+ def test_chop_word_1
10
+ assert_equal %w{xxx xxx xxx x}, @tf.chop_word('xxxxxxxxxx', 3)
11
+ end
12
+ def test_chop_word_2
13
+ assert_equal %w{xxx xx}, @tf.chop_word('xxxxx', 3)
14
+ end
15
+ def test_chop_word_3
16
+ assert_equal %w{xx}, @tf.chop_word('xx', 3)
17
+ end
18
+
19
+ def test_hyphenate_word_1
20
+ assert_equal ["Hobo-", 'ken'], @tf.hyphenate_word('Hoboken',5,'en_us')
21
+ end
22
+ def test_hyphenate_word_2
23
+ assert_equal ["spectac-", 'ular'], @tf.hyphenate_word('spectacular',8,'en_us')
24
+ end
25
+ def test_hyphenate_word_3
26
+ assert_equal ["antidisestab-","lishmentarian-","ism"], @tf.hyphenate_word('antidisestablishmentarianism', 15, 'en_us')
27
+ end
28
+ def test_hyphenate_word_4
29
+ x = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
30
+ assert_equal [x], @tf.hyphenate_word(x, 5, 'en_us')
31
+ end
32
+
33
+ # Real words
34
+ def test_split_word_1
35
+ assert_equal ["Hobo-\n", 'ken'], @tf.split_word('Hoboken',5,'en_us')
36
+ end
37
+ def test_split_word_2
38
+ assert_equal ["spectac-\n", 'ular'], @tf.split_word('spectacular',8,'en_us')
39
+ end
40
+ def test_split_word_3
41
+ assert_equal ["spectac-", 'ular'], @tf.split_word('spectacular',8,'en_us', false)
42
+ end
43
+ def test_split_word_4
44
+ assert_equal ["antidisestab-\n","lishmentarian-\n","ism"], @tf.split_word('antidisestablishmentarianism', 15, 'en_us')
45
+ end
46
+ def test_split_word_5
47
+ assert_equal ["antidisestab-","lishmentarian-","ism"], @tf.split_word('antidisestablishmentarianism', 15, 'en_us', false)
48
+ end
49
+
50
+ # long strings get chopped
51
+ def test_split_word_6
52
+ x = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
53
+ split_x = []
54
+ 15.times{split_x.push("xxxxx\n")}
55
+ split_x.push('x')
56
+ assert_equal split_x, @tf.split_word(x, 5, 'en_us')
57
+ end
58
+
59
+ # pre-hyphenated words should be easy to handle
60
+ def test_split_word_7
61
+ assert_equal ["prepared-","from-","recipe"], @tf.split_word('prepared-from-recipe', 15, 'en_us', false)
62
+ end
63
+ def test_split_word_8
64
+ assert_equal ["pre-","pared-","from-","recipe"], @tf.split_word('prepared-from-recipe', 6, 'en_us', false)
65
+ end
66
+
67
+ def test_split_long_words_1
68
+ assert_equal "Hobo- ken", @tf.split_long_words('Hoboken',5,'en_us')
69
+ end
70
+ def test_split_long_words_2
71
+ assert_equal "spectac- ular", @tf.split_long_words('spectacular',8,'en_us')
72
+ end
73
+ def test_split_long_words_3
74
+ assert_equal "antidisestab- lishmentarian- ism", @tf.split_long_words('antidisestablishmentarianism', 15, 'en_us')
75
+ end
76
+ def test_split_long_words_4
77
+ i = ["Hoboken New Jersey \n\n \t is a\t \nnice \n\t\n\t place.",5,'en_us']
78
+ o = "Hobo- ken New Jer- sey is a nice place ."
79
+ assert_equal o, @tf.split_long_words(*i)
80
+ end
81
+ end
@@ -0,0 +1,23 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/array_extensions'
3
+ require 'shoulda'
4
+
5
+ gem 'activesupport', '>= 1.2.6'
6
+ require 'active_support' # for .hour, .hours, .second
7
+
8
+ class TestArrayExtensions < Test::Unit::TestCase
9
+
10
+ context 'Time#at_midnight' do
11
+ [ ['2009-12-01 01:00:00', 1.hour ],
12
+ ['2009-12-01 00:00:00', 0 ],
13
+ ['2009-12-01 23:59:59', (24.hours - 1.second) ],
14
+ ].each do |input, expected|
15
+ should "be #{expected} seconds before #{input}" do
16
+ t = Time.parse(input)
17
+ assert_equal expected, t - t.at_midnight
18
+ end
19
+ end
20
+ end
21
+
22
+
23
+ end
@@ -0,0 +1,14 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/uri_extensions'
3
+
4
+ class TestUriExtensions < Test::Unit::TestCase
5
+ def test_uri_generic_query_from_hash
6
+ assert_equal '?a=1&b=2', URI::Generic.query_from_hash('a'=>1, 'b'=>2)
7
+ assert_equal '?:a=1&a=11&b=2', URI::Generic.query_from_hash(:a=>1, 'b'=>2, 'a'=>11)
8
+ assert_equal '?biz=b%20a%20z&foo=bar', URI::Generic.query_from_hash('foo' => 'bar', 'biz' => 'b a z')
9
+
10
+ h = HashWithIndifferentAccess.new
11
+ h.merge! :a=>1, :b=>2
12
+ assert_equal '?a=1&b=2', URI::Generic.query_from_hash(h)
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: AvantiConveniences
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.6
5
+ platform: ruby
6
+ authors:
7
+ - Jamie Flournoy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-22 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.6
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: text-hyphen
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.0
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: thoughtbot-shoulda
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.10.1
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: hoe
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 2.3.3
54
+ version:
55
+ description: AvantiConveniences is a set of convenience code for Ruby on Rails applications.
56
+ email:
57
+ - jamie@pervasivecode.com
58
+ executables: []
59
+
60
+ extensions: []
61
+
62
+ extra_rdoc_files:
63
+ - History.txt
64
+ - Manifest.txt
65
+ - README.txt
66
+ files:
67
+ - AvantiConveniences.gemspec
68
+ - History.txt
69
+ - Manifest.txt
70
+ - README.txt
71
+ - Rakefile
72
+ - lib/arg_checks.rb
73
+ - lib/array_extensions.rb
74
+ - lib/avanti_conveniences.rb
75
+ - lib/die.rb
76
+ - lib/float_extensions.rb
77
+ - lib/hash_extensions.rb
78
+ - lib/string_extensions.rb
79
+ - lib/text_formatter.rb
80
+ - lib/time_extensions.rb
81
+ - lib/uri_extensions.rb
82
+ - test/test_arg_checks.rb
83
+ - test/test_array_extensions.rb
84
+ - test/test_avanti_conveniences.rb
85
+ - test/test_die.rb
86
+ - test/test_float_extensions.rb
87
+ - test/test_hash_extensions.rb
88
+ - test/test_string_extensions.rb
89
+ - test/test_text_formatter.rb
90
+ - test/test_time_extensions.rb
91
+ - test/test_uri_extensions.rb
92
+ has_rdoc: true
93
+ homepage: http://www.pervasivecode.com/blog/avanticonveniences/
94
+ licenses: []
95
+
96
+ post_install_message:
97
+ rdoc_options:
98
+ - --main
99
+ - README.txt
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: "0"
107
+ version:
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: "0"
113
+ version:
114
+ requirements: []
115
+
116
+ rubyforge_project: avanticonveniences
117
+ rubygems_version: 1.3.5
118
+ signing_key:
119
+ specification_version: 3
120
+ summary: AvantiConveniences is a set of convenience code for Ruby on Rails applications.
121
+ test_files:
122
+ - test/test_arg_checks.rb
123
+ - test/test_array_extensions.rb
124
+ - test/test_avanti_conveniences.rb
125
+ - test/test_die.rb
126
+ - test/test_float_extensions.rb
127
+ - test/test_hash_extensions.rb
128
+ - test/test_string_extensions.rb
129
+ - test/test_text_formatter.rb
130
+ - test/test_time_extensions.rb
131
+ - test/test_uri_extensions.rb