s4t-utils 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/LICENSE.txt +34 -0
  2. data/Manifest.txt +52 -0
  3. data/NOTES.txt +7 -0
  4. data/README.txt +2 -0
  5. data/Rakefile +13 -0
  6. data/Rakefile.hoe +22 -0
  7. data/bin/make-s4t-project.rb +159 -0
  8. data/bin/s4t-script-location-file +1 -0
  9. data/data/make-s4t-project/README-skeleton +30 -0
  10. data/data/make-s4t-project/Rakefile.example +32 -0
  11. data/data/make-s4t-project/bin-skeleton +23 -0
  12. data/data/make-s4t-project/main-lib-skeleton +14 -0
  13. data/data/make-s4t-project/set-standalone-test-paths.rb +5 -0
  14. data/data/make-s4t-project/setup.rb +1585 -0
  15. data/data/make-s4t-project/sub-lib-skeleton +3 -0
  16. data/data/make-s4t-project/test-skeleton +28 -0
  17. data/data/make-s4t-project/version-skeleton +3 -0
  18. data/lib/s4t-utils.rb +23 -0
  19. data/lib/s4t-utils/capturing-globals.rb +93 -0
  20. data/lib/s4t-utils/claims.rb +20 -0
  21. data/lib/s4t-utils/command-line.rb +18 -0
  22. data/lib/s4t-utils/error-handling.rb +26 -0
  23. data/lib/s4t-utils/friendly-format.rb +35 -0
  24. data/lib/s4t-utils/hacks.rb +55 -0
  25. data/lib/s4t-utils/load-path-auto-adjuster.rb +120 -0
  26. data/lib/s4t-utils/more-assertions.rb +35 -0
  27. data/lib/s4t-utils/os.rb +28 -0
  28. data/lib/s4t-utils/rake-task-helpers.rb +75 -0
  29. data/lib/s4t-utils/rakefile-common.rb +112 -0
  30. data/lib/s4t-utils/svn-file-movement.rb +104 -0
  31. data/lib/s4t-utils/test-util.rb +19 -0
  32. data/lib/s4t-utils/version.rb +3 -0
  33. data/setup.rb +1585 -0
  34. data/test/capturing-globals-tests.rb +42 -0
  35. data/test/data/make-s4t-project/README-skeleton +30 -0
  36. data/test/data/make-s4t-project/Rakefile.example +32 -0
  37. data/test/data/make-s4t-project/bin-skeleton +23 -0
  38. data/test/data/make-s4t-project/main-lib-skeleton +14 -0
  39. data/test/data/make-s4t-project/set-standalone-test-paths.rb +5 -0
  40. data/test/data/make-s4t-project/setup.rb +1585 -0
  41. data/test/data/make-s4t-project/sub-lib-skeleton +3 -0
  42. data/test/data/make-s4t-project/test-skeleton +28 -0
  43. data/test/data/make-s4t-project/version-skeleton +3 -0
  44. data/test/data/test-data-location-file +1 -0
  45. data/test/error-handling-tests.rb +43 -0
  46. data/test/friendly-format-tests.rb +15 -0
  47. data/test/hacks-tests.rb +42 -0
  48. data/test/load-path-auto-adjuster-tests.rb +88 -0
  49. data/test/rake-task-helper-tests.rb +62 -0
  50. data/test/set-standalone-test-paths.rb +5 -0
  51. data/test/test-location-file +1 -0
  52. data/test/test-util-tests.rb +45 -0
  53. metadata +116 -0
@@ -0,0 +1,3 @@
1
+ module !REPLACE_ME_MODULE!
2
+ # Your code here
3
+ end
@@ -0,0 +1,28 @@
1
+ # This file should be copied into a test ending in 'tests.rb' so that
2
+ # the Rakefile knows it's a test.
3
+
4
+ require "set-standalone-test-paths.rb" unless $started_from_rakefile
5
+ require 'test/unit'
6
+ require 's4t-utils'
7
+ include S4tUtils
8
+
9
+ ## Require either the particular file under test like this:
10
+ # require '!REPLACE_ME_FILE!/my-file'
11
+ ## or the entire package:
12
+ # require '!REPLACE_ME_FILE!'
13
+
14
+ class TestName < Test::Unit::TestCase
15
+ ## You probably want to include your library so that you don't have
16
+ ## to tack !REPLACE_ME_MODULE!:: onto every name, but I won't assume
17
+ ## that.
18
+ # include !REPLACE_ME_MODULE!
19
+
20
+ def setup
21
+ end
22
+
23
+ def teardown
24
+ end
25
+
26
+ def test_something
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module !REPLACE_ME_MODULE!
2
+ Version = '0.1.0'
3
+ end
@@ -0,0 +1,23 @@
1
+ require 's4t-utils/capturing-globals'
2
+ require 's4t-utils/error-handling'
3
+ require 's4t-utils/more-assertions'
4
+ require 's4t-utils/claims'
5
+ require 's4t-utils/friendly-format'
6
+ require 's4t-utils/rake-task-helpers'
7
+ require 's4t-utils/svn-file-movement'
8
+ require 's4t-utils/hacks'
9
+ require 's4t-utils/command-line'
10
+ require 's4t-utils/test-util'
11
+ require 's4t-utils/os'
12
+
13
+ require 'pp'
14
+
15
+ # Tolerate typos
16
+ S4TUtils=S4tUtils
17
+ S4tUtil=S4tUtils
18
+ S4TUtil=S4tUtils
19
+
20
+ # Note, unless otherwise noted, all methods defined in S4tUtils
21
+ # are both module functions and instance functions.
22
+ module S4tUtils
23
+ end
@@ -0,0 +1,93 @@
1
+ require 'stringio'
2
+
3
+ module S4tUtils
4
+
5
+ module_function
6
+
7
+ # Run the block, capturing output to $stderr in a string.
8
+ # That string is the method's return value.
9
+ def capturing_stderr
10
+ old_stderr = $stderr
11
+ new_stderr = StringIO.new
12
+ begin
13
+ $stderr = new_stderr
14
+ yield
15
+ ensure
16
+ $stderr = old_stderr
17
+ end
18
+ new_stderr.string
19
+ end
20
+
21
+ # Run the block, replacing the values of environment variables
22
+ # with the values given in the hash _settings_. The environment
23
+ # variables are restored when the method returns.
24
+ def with_environment_vars(settings)
25
+ begin
26
+ old = {}
27
+ settings.each { | key, value |
28
+ old[key] = ENV[key]
29
+ ENV[key] = value
30
+ }
31
+ yield
32
+ ensure
33
+ settings.each_key { | key |
34
+ ENV[key] = old[key]
35
+ }
36
+ end
37
+ end
38
+
39
+ # Run the block with the _HOME_ environment variable set to the
40
+ # current working directory.
41
+ def with_home_right_here
42
+ begin
43
+ old_home = ENV['HOME']
44
+ ENV['HOME'] = '.'
45
+ yield
46
+ ensure
47
+ ENV['HOME'] = @old_home
48
+ end
49
+ end
50
+
51
+ # Run the block with the given _file_ (named by a string) deleted before
52
+ # and after.
53
+ def erasing_local_config_file(file)
54
+ with_home_right_here {
55
+ begin
56
+ File.delete(file) if File.exist?(file)
57
+ yield
58
+ ensure
59
+ File.delete(file) if File.exist?(file)
60
+ end
61
+ }
62
+ end
63
+
64
+ # Run the block. During the execution, the contents of _file_ (named by
65
+ # a string) is replaced with _contents_.
66
+ def with_local_config_file(file, contents)
67
+ erasing_local_config_file(file) do
68
+ File.open(file, 'w') do | io |
69
+ io.puts(contents.to_s)
70
+ end
71
+ yield
72
+ end
73
+ end
74
+
75
+ # Run the block. During execution, _ARGV_'s is set as if the
76
+ # script had been executed with _string_ as its argument list.
77
+ # If the block tries to exit, with_command_args will instead throw
78
+ # a StandardError.
79
+ def with_command_args(string)
80
+ begin
81
+ old_argv = ARGV.dup
82
+ ARGV.replace(string.split)
83
+ yield
84
+ rescue SystemExit => ex
85
+ replacement = StandardError.new(ex.message)
86
+ replacement.set_backtrace(ex.backtrace)
87
+ raise replacement
88
+ ensure
89
+ ARGV.replace(old_argv)
90
+ end
91
+ end
92
+
93
+ end
@@ -0,0 +1,20 @@
1
+ module S4tUtils
2
+
3
+ module_function
4
+
5
+ # A StandardError is thrown if the _fact_ the user claims is true
6
+ # is actually false. The _block_ is called to provide the exception
7
+ # message.
8
+ def user_claims(fact, &block)
9
+ raise StandardError.new(block.call) unless fact
10
+ end
11
+
12
+ # A StandardError is thrown if the _fact_ the user disputes is
13
+ # nevertheless true. The _block_ is called to provide the exception
14
+ # message.
15
+ def user_disputes(fact, &block)
16
+ user_claims(!fact, &block)
17
+ end
18
+ end
19
+
20
+
@@ -0,0 +1,18 @@
1
+ module S4tUtils
2
+
3
+ module_function
4
+
5
+ # Ask the question contained in the _question_lines_, prompt, and
6
+ # wait for an answer. If the stripped value read from STDIN is
7
+ # empty, use the _default_answer_.
8
+ def ask(default_answer, *question_lines)
9
+ puts question_lines
10
+ print "[#{default_answer}] => "
11
+ answer = STDIN.readline.strip
12
+ answer = default_answer.to_s if answer == ''
13
+ answer
14
+ end
15
+
16
+ end
17
+
18
+
@@ -0,0 +1,26 @@
1
+ module S4tUtils
2
+
3
+ module_function
4
+
5
+ # Typically used to wrap the execution of an entire script.
6
+ # If an exception is thrown, a terse message is printed (to $stderr)
7
+ # instead of a stack dump. The message printed is gotten from the
8
+ # exception.
9
+ def with_pleasant_exceptions
10
+ yield
11
+ rescue SystemExit
12
+ raise
13
+ rescue Exception => ex
14
+ $stderr.puts(ex.message)
15
+ end
16
+
17
+ # with_pleasant_exceptions swallows the stack trace, which you
18
+ # want to see during debugging. The easy way to see it is to add
19
+ # 'out' to that message, producing this one. To reduce the chance
20
+ # you'll forget to make exceptions pleasant again, a note that
21
+ # exceptions are turned off is always printed to $stderr.
22
+ def without_pleasant_exceptions
23
+ $stderr.puts "Note: exception handling turned off."
24
+ yield
25
+ end
26
+ end
@@ -0,0 +1,35 @@
1
+ #--
2
+ # Note: see also <http://englishext.rubyforge.org/>
3
+ #++
4
+
5
+ module S4tUtils
6
+
7
+ module_function
8
+
9
+ # Use _connector_ to join _array_ into a human-friendly list.
10
+ #
11
+ #
12
+ # friendly_list("or", [1]) => "'1'"
13
+ # friendly_list("or"), [1, 2] => "'1' or '2'"
14
+ # friendly_list("or"), [1, 2, 3] => "'1', '2', or '3'"
15
+ def friendly_list(connector, array)
16
+ quoted = array.collect { | elt | "'" + elt.to_s + "'" }
17
+ case array.length
18
+ when 0
19
+ ""
20
+ when 1
21
+ quoted[0]
22
+ when 2
23
+ quoted[0] + " #{connector} " + quoted[1]
24
+ else
25
+ quoted[0...-1].join(", ") + ", #{connector} #{quoted.last}"
26
+ end
27
+ end
28
+
29
+ # Produces a version of a string that can be typed after a :
30
+ # (Can also be safely given at a command-line prompt.)
31
+ def symbol_safe_name(name)
32
+ name.to_s.gsub(/\W/, '')
33
+ end
34
+ end
35
+
@@ -0,0 +1,55 @@
1
+ module S4tUtils
2
+
3
+ module_function
4
+
5
+ # The return value of prog1 is the _retval_. Before that's returned,
6
+ # though, the _retval_ is yielded to the block. This method is an
7
+ # alternative to stashing a value in a temporary, fiddling around, then
8
+ # returning the temporary. Here's an example:
9
+ #
10
+ # prog1(1+1) { | s | puts "Sum is #{s}."} # => 2
11
+ #
12
+ # The name "prog1" is ancient Lisp jargon.
13
+ def prog1(retval)
14
+ yield(retval)
15
+ retval
16
+ end
17
+
18
+ # A way of putting debugging statements in code that requires less
19
+ # typing than +puts+.
20
+ #
21
+ # pi [1, 2, 3], 'input' # => 'input: [1, 2, 3]
22
+ #
23
+ # The _arg_ is printed using +inspect+. If _leader_ isn't given,
24
+ # nothing is printed before _arg_.
25
+ #
26
+ # pi returns its _arg_, which is occasionally useful for sticking
27
+ # debugging into the middle of complicated expressions.
28
+ def pi(arg, leader=nil)
29
+ leader = (leader == nil) ? '' : leader + ': '
30
+ prog1(arg) { puts leader + arg.inspect }
31
+ end
32
+
33
+
34
+
35
+ # An ArgForwarder is associated with a _target_. It forwards messages
36
+ # sent to it to the target, passing along any arguments to the method.
37
+ # So far, so boring. But an ArgForwarder is also created with some
38
+ # _added_args_. When the ArgForwarder forwards, it prepends those
39
+ # _added_args_ to the message's given arguments.
40
+ #
41
+ # array = []
42
+ # forwarder = ArgForwarder.new(array, 5)
43
+ # forwarder.push
44
+ # assert_equal([5], array)
45
+ class ArgForwarder
46
+ def initialize(target, *added_args)
47
+ @target = target
48
+ @added_args = added_args
49
+ end
50
+
51
+ def method_missing(method, *args) # :nodoc:
52
+ @target.send(method, *(@added_args + args))
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,120 @@
1
+ # This is loaded by an executable script in one of two cases:
2
+ #
3
+ # It's a development version living in the bin part of this directory
4
+ # structure (but it can invoked from any place).
5
+ #
6
+ # project/
7
+ # bin/
8
+ # lib/
9
+ # project/
10
+ # third-party/
11
+ # s4t-utils/
12
+ # this file
13
+ #
14
+ # It's a deployed version living in some random place, with
15
+ # the site_ruby directory in the page.
16
+ #
17
+ # site_ruby/1.8/
18
+ # project/
19
+ # third-party/
20
+ # s4t-utils/
21
+ # this file
22
+ #
23
+ #
24
+ # In order for this file to have been required in both cases, the following
25
+ # code is executed in the caller:
26
+ #
27
+ #
28
+ # $:.unshift((Pathname.new(__FILE__).parent.parent + 'lib').to_s)
29
+ # require 'package/third-party/s4t-utils/load-path-auto-adjuster'
30
+ #
31
+ # In the first case, that will put something like "../lib" on the load
32
+ # path. In the second case, it will put harmless garbage on the path
33
+ # (harmless because it won't contain this file, which will still be
34
+ # found somewhere later in the load path).
35
+ #
36
+ # The first thing this file does is pop that off, it having done its job.
37
+ # In the first (development) case, it puts the following on the load path:
38
+ # project/lib & project/lib/project/third-party & project
39
+ # ('project' is added so that <require 'test/util-file'> works.)
40
+ #
41
+ # In the second, it adds only the third-party library and takes care
42
+ # to add it just after whatever component in the path contains this
43
+ # file. (It will thus not interfere with clashing packages earlier
44
+ # in the path.)
45
+ # site_ruby/1.8/project/third-party
46
+ # since site_ruby/1.8 (or the equivalent) is already on there.
47
+
48
+ require 'rubygems'
49
+ require 'pathname'
50
+
51
+ module S4tUtils
52
+ module Hidden # :nodoc: all
53
+
54
+ class Arranger
55
+ def initialize(third_party)
56
+ @third_party_lib = third_party
57
+ @project_lib = third_party.parent.parent
58
+ @test_util_root = @project_lib.parent
59
+ end
60
+
61
+ def self.arrange_path_around(third_party)
62
+ new(third_party).arrange
63
+ end
64
+
65
+ def arrange
66
+ add_third_party_gems
67
+ if project_lib_already_in_path?
68
+ just_add_third_party_after_project_lib
69
+ else
70
+ add_everything_at_front
71
+ end
72
+ end
73
+
74
+
75
+ def add_third_party_gems
76
+ # When RubyGems 0.8.11 gets clear_paths, it does not clear the
77
+ # cache used by Gem's overriding version of require(), so if
78
+ # this is loaded after the first Gem is required, it will have
79
+ # no effect on later uses of require(). (But it does affect
80
+ # require_gem.)
81
+ #
82
+ ENV['GEM_PATH']=(@third_party_lib+'gems').to_s
83
+ Gem.clear_paths
84
+ end
85
+
86
+ def project_lib_already_in_path?
87
+ $:.include?(@project_lib.to_s)
88
+ end
89
+
90
+ def just_add_third_party_after_project_lib
91
+ $:.each_with_index do | path_element, index |
92
+ if path_element == @project_lib.to_s
93
+ $:[index+1,0] = @third_party_lib.to_s
94
+ return
95
+ end
96
+ end
97
+ fail "No place to put third_party library."
98
+ end
99
+
100
+ def add_everything_at_front
101
+ $:.unshift(@test_util_root.to_s)
102
+ $:.unshift(@third_party_lib.to_s)
103
+ $:.unshift(@project_lib.to_s) # This is now first
104
+ end
105
+ end
106
+
107
+ def self.auto_adjust_load_path
108
+ $:.shift # Remove extra element used to find this file.
109
+
110
+ # Having loaded us, __FILE__ is something like this:
111
+ # ...lib.../package/third-party/s4t-utils/load-path-auto-adjuster.rb
112
+ relative_third_party = Pathname.new(__FILE__).parent.parent
113
+ # Pathname#real_path doesn't work on Windows (1.8.2). Grr.
114
+ third_party = Pathname.new(File.expand_path(relative_third_party.to_s))
115
+ Arranger.arrange_path_around(third_party)
116
+ end
117
+
118
+ auto_adjust_load_path
119
+ end
120
+ end
@@ -0,0 +1,35 @@
1
+ module Test
2
+ module Unit
3
+
4
+ # Some additional Test::Unit assertions.
5
+ module Assertions
6
+ # Same as +assert+. I just like it better.
7
+ def assert_true(boolean, message = nil)
8
+ assert(boolean, message)
9
+ end
10
+
11
+ # Assert that the _boolean_ is false.
12
+ def assert_false(boolean, message = nil)
13
+ _wrap_assertion do
14
+ assert_block(build_message(message, "<?> should be false or nil.", boolean)) { !boolean }
15
+ end
16
+ end
17
+
18
+ # Like +assert_raise+, but the raised exception must contain a
19
+ # +message+ matching (as in +assert_match+) the _message_ argument.
20
+ def assert_raise_with_matching_message(exception_class, message, &block)
21
+ exception = assert_raise(exception_class, &block)
22
+ assert_match(message, exception.message)
23
+ end
24
+ alias_method :assert_raises_with_matching_message,
25
+ :assert_raise_with_matching_message
26
+
27
+ # Assert that the block tried to +exit+.
28
+ def assert_wants_to_exit
29
+ assert_raise(SystemExit) do
30
+ yield
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end