AvantiConveniences 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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